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