root / hw / smbus.c @ 5fafdf24
History | View | Annotate | Download (7.4 kB)
1 | 5fafdf24 | ths | /*
|
---|---|---|---|
2 | 0ff596d0 | pbrook | * QEMU SMBus device emulation.
|
3 | 0ff596d0 | pbrook | *
|
4 | 0ff596d0 | pbrook | * Copyright (c) 2007 CodeSourcery.
|
5 | 0ff596d0 | pbrook | * Written by Paul Brook
|
6 | 0ff596d0 | pbrook | *
|
7 | 0ff596d0 | pbrook | * This code is licenced under the LGPL.
|
8 | 0ff596d0 | pbrook | */
|
9 | 0ff596d0 | pbrook | |
10 | 0ff596d0 | pbrook | /* TODO: Implement PEC. */
|
11 | 0ff596d0 | pbrook | |
12 | 0ff596d0 | pbrook | #include "vl.h" |
13 | 0ff596d0 | pbrook | |
14 | 0ff596d0 | pbrook | //#define DEBUG_SMBUS 1
|
15 | 0ff596d0 | pbrook | |
16 | 0ff596d0 | pbrook | #ifdef DEBUG_SMBUS
|
17 | 0ff596d0 | pbrook | #define DPRINTF(fmt, args...) \
|
18 | 0ff596d0 | pbrook | do { printf("smbus(%02x): " fmt , dev->i2c.address, ##args); } while (0) |
19 | 0ff596d0 | pbrook | #define BADF(fmt, args...) \
|
20 | 0ff596d0 | pbrook | do { fprintf(stderr, "smbus: error: " fmt , ##args); exit(1);} while (0) |
21 | 0ff596d0 | pbrook | #else
|
22 | 0ff596d0 | pbrook | #define DPRINTF(fmt, args...) do {} while(0) |
23 | 0ff596d0 | pbrook | #define BADF(fmt, args...) \
|
24 | 0ff596d0 | pbrook | do { fprintf(stderr, "smbus: error: " fmt , ##args);} while (0) |
25 | 0ff596d0 | pbrook | #endif
|
26 | 0ff596d0 | pbrook | |
27 | 0ff596d0 | pbrook | enum {
|
28 | 0ff596d0 | pbrook | SMBUS_IDLE, |
29 | 0ff596d0 | pbrook | SMBUS_WRITE_DATA, |
30 | 0ff596d0 | pbrook | SMBUS_RECV_BYTE, |
31 | 0ff596d0 | pbrook | SMBUS_READ_DATA, |
32 | 0ff596d0 | pbrook | SMBUS_DONE, |
33 | 0ff596d0 | pbrook | SMBUS_CONFUSED = -1
|
34 | 0ff596d0 | pbrook | }; |
35 | 0ff596d0 | pbrook | |
36 | 0ff596d0 | pbrook | static void smbus_do_quick_cmd(SMBusDevice *dev, int recv) |
37 | 0ff596d0 | pbrook | { |
38 | 0ff596d0 | pbrook | DPRINTF("Quick Command %d\n", recv);
|
39 | 0ff596d0 | pbrook | if (dev->quick_cmd)
|
40 | 0ff596d0 | pbrook | dev->quick_cmd(dev, recv); |
41 | 0ff596d0 | pbrook | } |
42 | 0ff596d0 | pbrook | |
43 | 0ff596d0 | pbrook | static void smbus_do_write(SMBusDevice *dev) |
44 | 0ff596d0 | pbrook | { |
45 | 0ff596d0 | pbrook | if (dev->data_len == 0) { |
46 | 0ff596d0 | pbrook | smbus_do_quick_cmd(dev, 0);
|
47 | 0ff596d0 | pbrook | } else if (dev->data_len == 1) { |
48 | 0ff596d0 | pbrook | DPRINTF("Send Byte\n");
|
49 | 0ff596d0 | pbrook | if (dev->send_byte) {
|
50 | 0ff596d0 | pbrook | dev->send_byte(dev, dev->data_buf[0]);
|
51 | 0ff596d0 | pbrook | } |
52 | 0ff596d0 | pbrook | } else {
|
53 | 0ff596d0 | pbrook | dev->command = dev->data_buf[0];
|
54 | 0ff596d0 | pbrook | DPRINTF("Command %d len %d\n", dev->command, dev->data_len - 1); |
55 | 0ff596d0 | pbrook | if (dev->write_data) {
|
56 | 0ff596d0 | pbrook | dev->write_data(dev, dev->command, dev->data_buf + 1,
|
57 | 0ff596d0 | pbrook | dev->data_len - 1);
|
58 | 0ff596d0 | pbrook | } |
59 | 0ff596d0 | pbrook | } |
60 | 0ff596d0 | pbrook | } |
61 | 0ff596d0 | pbrook | |
62 | 0ff596d0 | pbrook | void smbus_i2c_event(i2c_slave *s, enum i2c_event event) |
63 | 0ff596d0 | pbrook | { |
64 | 0ff596d0 | pbrook | SMBusDevice *dev = (SMBusDevice *)s; |
65 | 0ff596d0 | pbrook | switch (event) {
|
66 | 0ff596d0 | pbrook | case I2C_START_SEND:
|
67 | 0ff596d0 | pbrook | switch (dev->mode) {
|
68 | 0ff596d0 | pbrook | case SMBUS_IDLE:
|
69 | 0ff596d0 | pbrook | DPRINTF("Incoming data\n");
|
70 | 0ff596d0 | pbrook | dev->mode = SMBUS_WRITE_DATA; |
71 | 0ff596d0 | pbrook | break;
|
72 | 0ff596d0 | pbrook | default:
|
73 | 0ff596d0 | pbrook | BADF("Unexpected send start condition in state %d\n", dev->mode);
|
74 | 0ff596d0 | pbrook | dev->mode = SMBUS_CONFUSED; |
75 | 0ff596d0 | pbrook | break;
|
76 | 0ff596d0 | pbrook | } |
77 | 0ff596d0 | pbrook | break;
|
78 | 0ff596d0 | pbrook | |
79 | 0ff596d0 | pbrook | case I2C_START_RECV:
|
80 | 0ff596d0 | pbrook | switch (dev->mode) {
|
81 | 0ff596d0 | pbrook | case SMBUS_IDLE:
|
82 | 0ff596d0 | pbrook | DPRINTF("Read mode\n");
|
83 | 0ff596d0 | pbrook | dev->mode = SMBUS_RECV_BYTE; |
84 | 0ff596d0 | pbrook | break;
|
85 | 0ff596d0 | pbrook | case SMBUS_WRITE_DATA:
|
86 | 0ff596d0 | pbrook | if (dev->data_len == 0) { |
87 | 0ff596d0 | pbrook | BADF("Read after write with no data\n");
|
88 | 0ff596d0 | pbrook | dev->mode = SMBUS_CONFUSED; |
89 | 0ff596d0 | pbrook | } else {
|
90 | 0ff596d0 | pbrook | if (dev->data_len > 1) { |
91 | 0ff596d0 | pbrook | smbus_do_write(dev); |
92 | 0ff596d0 | pbrook | } else {
|
93 | 0ff596d0 | pbrook | dev->command = dev->data_buf[0];
|
94 | 0ff596d0 | pbrook | DPRINTF("%02x: Command %d\n", dev->i2c.address,
|
95 | 0ff596d0 | pbrook | dev->command); |
96 | 0ff596d0 | pbrook | } |
97 | 0ff596d0 | pbrook | DPRINTF("Read mode\n");
|
98 | 0ff596d0 | pbrook | dev->data_len = 0;
|
99 | 0ff596d0 | pbrook | dev->mode = SMBUS_READ_DATA; |
100 | 0ff596d0 | pbrook | } |
101 | 0ff596d0 | pbrook | break;
|
102 | 0ff596d0 | pbrook | default:
|
103 | 0ff596d0 | pbrook | BADF("Unexpected recv start condition in state %d\n", dev->mode);
|
104 | 0ff596d0 | pbrook | dev->mode = SMBUS_CONFUSED; |
105 | 0ff596d0 | pbrook | break;
|
106 | 0ff596d0 | pbrook | } |
107 | 0ff596d0 | pbrook | break;
|
108 | 0ff596d0 | pbrook | |
109 | 0ff596d0 | pbrook | case I2C_FINISH:
|
110 | 0ff596d0 | pbrook | switch (dev->mode) {
|
111 | 0ff596d0 | pbrook | case SMBUS_WRITE_DATA:
|
112 | 0ff596d0 | pbrook | smbus_do_write(dev); |
113 | 0ff596d0 | pbrook | break;
|
114 | 0ff596d0 | pbrook | case SMBUS_RECV_BYTE:
|
115 | 0ff596d0 | pbrook | smbus_do_quick_cmd(dev, 1);
|
116 | 0ff596d0 | pbrook | break;
|
117 | 0ff596d0 | pbrook | case SMBUS_READ_DATA:
|
118 | 0ff596d0 | pbrook | BADF("Unexpected stop during receive\n");
|
119 | 0ff596d0 | pbrook | break;
|
120 | 0ff596d0 | pbrook | default:
|
121 | 0ff596d0 | pbrook | /* Nothing to do. */
|
122 | 0ff596d0 | pbrook | break;
|
123 | 0ff596d0 | pbrook | } |
124 | 0ff596d0 | pbrook | dev->mode = SMBUS_IDLE; |
125 | 0ff596d0 | pbrook | dev->data_len = 0;
|
126 | 0ff596d0 | pbrook | break;
|
127 | 0ff596d0 | pbrook | |
128 | 0ff596d0 | pbrook | case I2C_NACK:
|
129 | 0ff596d0 | pbrook | switch (dev->mode) {
|
130 | 0ff596d0 | pbrook | case SMBUS_DONE:
|
131 | 0ff596d0 | pbrook | /* Nothing to do. */
|
132 | 0ff596d0 | pbrook | break;
|
133 | 0ff596d0 | pbrook | case SMBUS_READ_DATA:
|
134 | 0ff596d0 | pbrook | dev->mode = SMBUS_DONE; |
135 | 0ff596d0 | pbrook | break;
|
136 | 0ff596d0 | pbrook | default:
|
137 | 0ff596d0 | pbrook | BADF("Unexpected NACK in state %d\n", dev->mode);
|
138 | 0ff596d0 | pbrook | dev->mode = SMBUS_CONFUSED; |
139 | 0ff596d0 | pbrook | break;
|
140 | 0ff596d0 | pbrook | } |
141 | 0ff596d0 | pbrook | } |
142 | 0ff596d0 | pbrook | } |
143 | 0ff596d0 | pbrook | |
144 | 0ff596d0 | pbrook | static int smbus_i2c_recv(i2c_slave *s) |
145 | 0ff596d0 | pbrook | { |
146 | 0ff596d0 | pbrook | SMBusDevice *dev = (SMBusDevice *)s; |
147 | 0ff596d0 | pbrook | int ret;
|
148 | 0ff596d0 | pbrook | |
149 | 0ff596d0 | pbrook | switch (dev->mode) {
|
150 | 0ff596d0 | pbrook | case SMBUS_RECV_BYTE:
|
151 | 0ff596d0 | pbrook | if (dev->receive_byte) {
|
152 | 0ff596d0 | pbrook | ret = dev->receive_byte(dev); |
153 | 0ff596d0 | pbrook | } else {
|
154 | 0ff596d0 | pbrook | ret = 0;
|
155 | 0ff596d0 | pbrook | } |
156 | 0ff596d0 | pbrook | DPRINTF("Receive Byte %02x\n", ret);
|
157 | 0ff596d0 | pbrook | dev->mode = SMBUS_DONE; |
158 | 0ff596d0 | pbrook | break;
|
159 | 0ff596d0 | pbrook | case SMBUS_READ_DATA:
|
160 | 0ff596d0 | pbrook | if (dev->read_data) {
|
161 | 0ff596d0 | pbrook | ret = dev->read_data(dev, dev->command, dev->data_len); |
162 | 0ff596d0 | pbrook | dev->data_len++; |
163 | 0ff596d0 | pbrook | } else {
|
164 | 0ff596d0 | pbrook | ret = 0;
|
165 | 0ff596d0 | pbrook | } |
166 | 0ff596d0 | pbrook | DPRINTF("Read data %02x\n", ret);
|
167 | 0ff596d0 | pbrook | break;
|
168 | 0ff596d0 | pbrook | default:
|
169 | 0ff596d0 | pbrook | BADF("Unexpected read in state %d\n", dev->mode);
|
170 | 0ff596d0 | pbrook | dev->mode = SMBUS_CONFUSED; |
171 | 0ff596d0 | pbrook | ret = 0;
|
172 | 0ff596d0 | pbrook | break;
|
173 | 0ff596d0 | pbrook | } |
174 | 0ff596d0 | pbrook | return ret;
|
175 | 0ff596d0 | pbrook | } |
176 | 0ff596d0 | pbrook | |
177 | 0ff596d0 | pbrook | static int smbus_i2c_send(i2c_slave *s, uint8_t data) |
178 | 0ff596d0 | pbrook | { |
179 | 0ff596d0 | pbrook | SMBusDevice *dev = (SMBusDevice *)s; |
180 | 0ff596d0 | pbrook | switch (dev->mode) {
|
181 | 0ff596d0 | pbrook | case SMBUS_WRITE_DATA:
|
182 | 0ff596d0 | pbrook | DPRINTF("Write data %02x\n", data);
|
183 | 0ff596d0 | pbrook | dev->data_buf[dev->data_len++] = data; |
184 | 0ff596d0 | pbrook | break;
|
185 | 0ff596d0 | pbrook | default:
|
186 | 0ff596d0 | pbrook | BADF("Unexpected write in state %d\n", dev->mode);
|
187 | 0ff596d0 | pbrook | break;
|
188 | 0ff596d0 | pbrook | } |
189 | 0ff596d0 | pbrook | return 0; |
190 | 0ff596d0 | pbrook | } |
191 | 0ff596d0 | pbrook | |
192 | 0ff596d0 | pbrook | SMBusDevice *smbus_device_init(i2c_bus *bus, int address, int size) |
193 | 0ff596d0 | pbrook | { |
194 | 0ff596d0 | pbrook | SMBusDevice *dev; |
195 | 0ff596d0 | pbrook | |
196 | 3f582262 | balrog | if (size < sizeof(SMBusDevice)) |
197 | 3f582262 | balrog | cpu_abort(cpu_single_env, "SMBus struct too small");
|
198 | 3f582262 | balrog | |
199 | 0ff596d0 | pbrook | dev = (SMBusDevice *)i2c_slave_init(bus, address, size); |
200 | 0ff596d0 | pbrook | dev->i2c.event = smbus_i2c_event; |
201 | 0ff596d0 | pbrook | dev->i2c.recv = smbus_i2c_recv; |
202 | 0ff596d0 | pbrook | dev->i2c.send = smbus_i2c_send; |
203 | 0ff596d0 | pbrook | |
204 | 0ff596d0 | pbrook | return dev;
|
205 | 0ff596d0 | pbrook | } |
206 | 0ff596d0 | pbrook | |
207 | 0ff596d0 | pbrook | /* Master device commands. */
|
208 | 0ff596d0 | pbrook | void smbus_quick_command(i2c_bus *bus, int addr, int read) |
209 | 0ff596d0 | pbrook | { |
210 | 0ff596d0 | pbrook | i2c_start_transfer(bus, addr, read); |
211 | 0ff596d0 | pbrook | i2c_end_transfer(bus); |
212 | 0ff596d0 | pbrook | } |
213 | 0ff596d0 | pbrook | |
214 | 0ff596d0 | pbrook | uint8_t smbus_receive_byte(i2c_bus *bus, int addr)
|
215 | 0ff596d0 | pbrook | { |
216 | 0ff596d0 | pbrook | uint8_t data; |
217 | 0ff596d0 | pbrook | |
218 | 0ff596d0 | pbrook | i2c_start_transfer(bus, addr, 1);
|
219 | 0ff596d0 | pbrook | data = i2c_recv(bus); |
220 | 0ff596d0 | pbrook | i2c_nack(bus); |
221 | 0ff596d0 | pbrook | i2c_end_transfer(bus); |
222 | 0ff596d0 | pbrook | return data;
|
223 | 0ff596d0 | pbrook | } |
224 | 0ff596d0 | pbrook | |
225 | 0ff596d0 | pbrook | void smbus_send_byte(i2c_bus *bus, int addr, uint8_t data) |
226 | 0ff596d0 | pbrook | { |
227 | 0ff596d0 | pbrook | i2c_start_transfer(bus, addr, 0);
|
228 | 0ff596d0 | pbrook | i2c_send(bus, data); |
229 | 0ff596d0 | pbrook | i2c_end_transfer(bus); |
230 | 0ff596d0 | pbrook | } |
231 | 0ff596d0 | pbrook | |
232 | 0ff596d0 | pbrook | uint8_t smbus_read_byte(i2c_bus *bus, int addr, uint8_t command)
|
233 | 0ff596d0 | pbrook | { |
234 | 0ff596d0 | pbrook | uint8_t data; |
235 | 0ff596d0 | pbrook | i2c_start_transfer(bus, addr, 0);
|
236 | 0ff596d0 | pbrook | i2c_send(bus, command); |
237 | 0ff596d0 | pbrook | i2c_start_transfer(bus, addr, 1);
|
238 | 0ff596d0 | pbrook | data = i2c_recv(bus); |
239 | 0ff596d0 | pbrook | i2c_nack(bus); |
240 | 0ff596d0 | pbrook | i2c_end_transfer(bus); |
241 | 0ff596d0 | pbrook | return data;
|
242 | 0ff596d0 | pbrook | } |
243 | 0ff596d0 | pbrook | |
244 | 0ff596d0 | pbrook | void smbus_write_byte(i2c_bus *bus, int addr, uint8_t command, uint8_t data) |
245 | 0ff596d0 | pbrook | { |
246 | 0ff596d0 | pbrook | i2c_start_transfer(bus, addr, 0);
|
247 | 0ff596d0 | pbrook | i2c_send(bus, command); |
248 | 0ff596d0 | pbrook | i2c_send(bus, data); |
249 | 0ff596d0 | pbrook | i2c_end_transfer(bus); |
250 | 0ff596d0 | pbrook | } |
251 | 0ff596d0 | pbrook | |
252 | 0ff596d0 | pbrook | uint16_t smbus_read_word(i2c_bus *bus, int addr, uint8_t command)
|
253 | 0ff596d0 | pbrook | { |
254 | 0ff596d0 | pbrook | uint16_t data; |
255 | 0ff596d0 | pbrook | i2c_start_transfer(bus, addr, 0);
|
256 | 0ff596d0 | pbrook | i2c_send(bus, command); |
257 | 0ff596d0 | pbrook | i2c_start_transfer(bus, addr, 1);
|
258 | 0ff596d0 | pbrook | data = i2c_recv(bus); |
259 | 0ff596d0 | pbrook | data |= i2c_recv(bus) << 8;
|
260 | 0ff596d0 | pbrook | i2c_nack(bus); |
261 | 0ff596d0 | pbrook | i2c_end_transfer(bus); |
262 | 0ff596d0 | pbrook | return data;
|
263 | 0ff596d0 | pbrook | } |
264 | 0ff596d0 | pbrook | |
265 | 0ff596d0 | pbrook | void smbus_write_word(i2c_bus *bus, int addr, uint8_t command, uint16_t data) |
266 | 0ff596d0 | pbrook | { |
267 | 0ff596d0 | pbrook | i2c_start_transfer(bus, addr, 0);
|
268 | 0ff596d0 | pbrook | i2c_send(bus, command); |
269 | 0ff596d0 | pbrook | i2c_send(bus, data & 0xff);
|
270 | 0ff596d0 | pbrook | i2c_send(bus, data >> 8);
|
271 | 0ff596d0 | pbrook | i2c_end_transfer(bus); |
272 | 0ff596d0 | pbrook | } |
273 | 0ff596d0 | pbrook | |
274 | 0ff596d0 | pbrook | int smbus_read_block(i2c_bus *bus, int addr, uint8_t command, uint8_t *data) |
275 | 0ff596d0 | pbrook | { |
276 | 0ff596d0 | pbrook | int len;
|
277 | 0ff596d0 | pbrook | int i;
|
278 | 0ff596d0 | pbrook | |
279 | 0ff596d0 | pbrook | i2c_start_transfer(bus, addr, 0);
|
280 | 0ff596d0 | pbrook | i2c_send(bus, command); |
281 | 0ff596d0 | pbrook | i2c_start_transfer(bus, addr, 1);
|
282 | 0ff596d0 | pbrook | len = i2c_recv(bus); |
283 | 0ff596d0 | pbrook | if (len > 32) |
284 | 0ff596d0 | pbrook | len = 0;
|
285 | 0ff596d0 | pbrook | for (i = 0; i < len; i++) |
286 | 0ff596d0 | pbrook | data[i] = i2c_recv(bus); |
287 | 0ff596d0 | pbrook | i2c_nack(bus); |
288 | 0ff596d0 | pbrook | i2c_end_transfer(bus); |
289 | 0ff596d0 | pbrook | return len;
|
290 | 0ff596d0 | pbrook | } |
291 | 0ff596d0 | pbrook | |
292 | 0ff596d0 | pbrook | void smbus_write_block(i2c_bus *bus, int addr, uint8_t command, uint8_t *data, |
293 | 0ff596d0 | pbrook | int len)
|
294 | 0ff596d0 | pbrook | { |
295 | 0ff596d0 | pbrook | int i;
|
296 | 0ff596d0 | pbrook | |
297 | 0ff596d0 | pbrook | if (len > 32) |
298 | 0ff596d0 | pbrook | len = 32;
|
299 | 0ff596d0 | pbrook | |
300 | 0ff596d0 | pbrook | i2c_start_transfer(bus, addr, 0);
|
301 | 0ff596d0 | pbrook | i2c_send(bus, command); |
302 | 0ff596d0 | pbrook | i2c_send(bus, len); |
303 | 0ff596d0 | pbrook | for (i = 0; i < len; i++) |
304 | 0ff596d0 | pbrook | i2c_send(bus, data[i]); |
305 | 0ff596d0 | pbrook | i2c_end_transfer(bus); |
306 | 0ff596d0 | pbrook | } |