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