Commit 0ff596d02fd9d876a31d038255a6d4f89da9dfed
1 parent
c6fdf5fc
I2C/SMBus framework.
git-svn-id: svn://svn.savannah.nongnu.org/qemu/trunk@2845 c046a42c-6fe2-441c-8c8c-71466251a162
Showing
9 changed files
with
566 additions
and
70 deletions
Makefile.target
hw/acpi.c
... | ... | @@ -35,7 +35,7 @@ typedef struct PIIX4PMState { |
35 | 35 | uint8_t apms; |
36 | 36 | QEMUTimer *tmr_timer; |
37 | 37 | int64_t tmr_overflow_time; |
38 | - SMBusDevice *smb_dev[128]; | |
38 | + i2c_bus *smbus; | |
39 | 39 | uint8_t smb_stat; |
40 | 40 | uint8_t smb_ctl; |
41 | 41 | uint8_t smb_cmd; |
... | ... | @@ -63,9 +63,6 @@ typedef struct PIIX4PMState { |
63 | 63 | #define SMBHSTDAT1 0x06 |
64 | 64 | #define SMBBLKDAT 0x07 |
65 | 65 | |
66 | -/* Note: only used for piix4_smbus_register_device */ | |
67 | -static PIIX4PMState *piix4_pm_state; | |
68 | - | |
69 | 66 | static uint32_t get_pmtmr(PIIX4PMState *s) |
70 | 67 | { |
71 | 68 | uint32_t d; |
... | ... | @@ -258,59 +255,44 @@ static void smb_transaction(PIIX4PMState *s) |
258 | 255 | uint8_t read = s->smb_addr & 0x01; |
259 | 256 | uint8_t cmd = s->smb_cmd; |
260 | 257 | uint8_t addr = s->smb_addr >> 1; |
261 | - SMBusDevice *dev = s->smb_dev[addr]; | |
258 | + i2c_bus *bus = s->smbus; | |
262 | 259 | |
263 | 260 | #ifdef DEBUG |
264 | 261 | printf("SMBus trans addr=0x%02x prot=0x%02x\n", addr, prot); |
265 | 262 | #endif |
266 | - if (!dev) goto error; | |
267 | - | |
268 | 263 | switch(prot) { |
269 | 264 | case 0x0: |
270 | - if (!dev->quick_cmd) goto error; | |
271 | - (*dev->quick_cmd)(dev, read); | |
265 | + smbus_quick_command(bus, addr, read); | |
272 | 266 | break; |
273 | 267 | case 0x1: |
274 | 268 | if (read) { |
275 | - if (!dev->receive_byte) goto error; | |
276 | - s->smb_data0 = (*dev->receive_byte)(dev); | |
277 | - } | |
278 | - else { | |
279 | - if (!dev->send_byte) goto error; | |
280 | - (*dev->send_byte)(dev, cmd); | |
269 | + s->smb_data0 = smbus_receive_byte(bus, addr); | |
270 | + } else { | |
271 | + smbus_send_byte(bus, addr, cmd); | |
281 | 272 | } |
282 | 273 | break; |
283 | 274 | case 0x2: |
284 | 275 | if (read) { |
285 | - if (!dev->read_byte) goto error; | |
286 | - s->smb_data0 = (*dev->read_byte)(dev, cmd); | |
287 | - } | |
288 | - else { | |
289 | - if (!dev->write_byte) goto error; | |
290 | - (*dev->write_byte)(dev, cmd, s->smb_data0); | |
276 | + s->smb_data0 = smbus_read_byte(bus, addr, cmd); | |
277 | + } else { | |
278 | + smbus_write_byte(bus, addr, cmd, s->smb_data0); | |
291 | 279 | } |
292 | 280 | break; |
293 | 281 | case 0x3: |
294 | 282 | if (read) { |
295 | 283 | uint16_t val; |
296 | - if (!dev->read_word) goto error; | |
297 | - val = (*dev->read_word)(dev, cmd); | |
284 | + val = smbus_read_word(bus, addr, cmd); | |
298 | 285 | s->smb_data0 = val; |
299 | 286 | s->smb_data1 = val >> 8; |
300 | - } | |
301 | - else { | |
302 | - if (!dev->write_word) goto error; | |
303 | - (*dev->write_word)(dev, cmd, (s->smb_data1 << 8) | s->smb_data0); | |
287 | + } else { | |
288 | + smbus_write_word(bus, addr, cmd, (s->smb_data1 << 8) | s->smb_data0); | |
304 | 289 | } |
305 | 290 | break; |
306 | 291 | case 0x5: |
307 | 292 | if (read) { |
308 | - if (!dev->read_block) goto error; | |
309 | - s->smb_data0 = (*dev->read_block)(dev, cmd, s->smb_data); | |
310 | - } | |
311 | - else { | |
312 | - if (!dev->write_block) goto error; | |
313 | - (*dev->write_block)(dev, cmd, s->smb_data0, s->smb_data); | |
293 | + s->smb_data0 = smbus_read_block(bus, addr, cmd, s->smb_data); | |
294 | + } else { | |
295 | + smbus_write_block(bus, addr, cmd, s->smb_data, s->smb_data0); | |
314 | 296 | } |
315 | 297 | break; |
316 | 298 | default: |
... | ... | @@ -469,7 +451,7 @@ static int pm_load(QEMUFile* f,void* opaque,int version_id) |
469 | 451 | return 0; |
470 | 452 | } |
471 | 453 | |
472 | -void piix4_pm_init(PCIBus *bus, int devfn) | |
454 | +i2c_bus *piix4_pm_init(PCIBus *bus, int devfn) | |
473 | 455 | { |
474 | 456 | PIIX4PMState *s; |
475 | 457 | uint8_t *pci_conf; |
... | ... | @@ -514,10 +496,7 @@ void piix4_pm_init(PCIBus *bus, int devfn) |
514 | 496 | s->tmr_timer = qemu_new_timer(vm_clock, pm_tmr_timer, s); |
515 | 497 | |
516 | 498 | register_savevm("piix4_pm", 0, 1, pm_save, pm_load, s); |
517 | - piix4_pm_state = s; | |
518 | -} | |
519 | 499 | |
520 | -void piix4_smbus_register_device(SMBusDevice *dev, uint8_t addr) | |
521 | -{ | |
522 | - piix4_pm_state->smb_dev[addr] = dev; | |
500 | + s->smbus = i2c_init_bus(); | |
501 | + return s->smbus; | |
523 | 502 | } | ... | ... |
hw/i2c.c
0 โ 100644
1 | +/* | |
2 | + * QEMU I2C bus interface. | |
3 | + * | |
4 | + * Copyright (c) 2007 CodeSourcery. | |
5 | + * Written by Paul Brook | |
6 | + * | |
7 | + * This code is licenced under the LGPL. | |
8 | + */ | |
9 | + | |
10 | +#include "vl.h" | |
11 | + | |
12 | +struct i2c_bus | |
13 | +{ | |
14 | + i2c_slave *current_dev; | |
15 | + i2c_slave *dev; | |
16 | +}; | |
17 | + | |
18 | +/* Create a new I2C bus. */ | |
19 | +i2c_bus *i2c_init_bus(void) | |
20 | +{ | |
21 | + i2c_bus *bus; | |
22 | + | |
23 | + bus = (i2c_bus *)qemu_mallocz(sizeof(i2c_bus)); | |
24 | + return bus; | |
25 | +} | |
26 | + | |
27 | +/* Create a new slave device. */ | |
28 | +i2c_slave *i2c_slave_init(i2c_bus *bus, int address, int size) | |
29 | +{ | |
30 | + i2c_slave *dev; | |
31 | + | |
32 | + if (size < sizeof(i2c_slave)) | |
33 | + cpu_abort(cpu_single_env, "I2C struct too small"); | |
34 | + | |
35 | + dev = (i2c_slave *)qemu_mallocz(size); | |
36 | + dev->address = address; | |
37 | + dev->next = bus->dev; | |
38 | + bus->dev = dev; | |
39 | + | |
40 | + return dev; | |
41 | +} | |
42 | + | |
43 | +void i2c_set_slave_address(i2c_slave *dev, int address) | |
44 | +{ | |
45 | + dev->address = address; | |
46 | +} | |
47 | + | |
48 | +/* Return nonzero if bus is busy. */ | |
49 | +int i2c_bus_busy(i2c_bus *bus) | |
50 | +{ | |
51 | + return bus->current_dev != NULL; | |
52 | +} | |
53 | + | |
54 | +/* Returns nonzero if the bus is already busy, or is the address is not | |
55 | + valid. */ | |
56 | +/* TODO: Make this handle multiple masters. */ | |
57 | +int i2c_start_transfer(i2c_bus *bus, int address, int recv) | |
58 | +{ | |
59 | + i2c_slave *dev; | |
60 | + | |
61 | + for (dev = bus->dev; dev; dev = dev->next) { | |
62 | + if (dev->address == address) | |
63 | + break; | |
64 | + } | |
65 | + | |
66 | + if (!dev) | |
67 | + return 1; | |
68 | + | |
69 | + /* If the bus is already busy, assume this is a repeated | |
70 | + start condition. */ | |
71 | + bus->current_dev = dev; | |
72 | + dev->event(dev, recv ? I2C_START_RECV : I2C_START_SEND); | |
73 | + return 0; | |
74 | +} | |
75 | + | |
76 | +void i2c_end_transfer(i2c_bus *bus) | |
77 | +{ | |
78 | + i2c_slave *dev = bus->current_dev; | |
79 | + | |
80 | + if (!dev) | |
81 | + return; | |
82 | + | |
83 | + dev->event(dev, I2C_FINISH); | |
84 | + | |
85 | + bus->current_dev = NULL; | |
86 | +} | |
87 | + | |
88 | +int i2c_send(i2c_bus *bus, uint8_t data) | |
89 | +{ | |
90 | + i2c_slave *dev = bus->current_dev; | |
91 | + | |
92 | + if (!dev) | |
93 | + return -1; | |
94 | + | |
95 | + return dev->send(dev, data); | |
96 | +} | |
97 | + | |
98 | +int i2c_recv(i2c_bus *bus) | |
99 | +{ | |
100 | + i2c_slave *dev = bus->current_dev; | |
101 | + | |
102 | + if (!dev) | |
103 | + return -1; | |
104 | + | |
105 | + return dev->recv(dev); | |
106 | +} | |
107 | + | |
108 | +void i2c_nack(i2c_bus *bus) | |
109 | +{ | |
110 | + i2c_slave *dev = bus->current_dev; | |
111 | + | |
112 | + if (!dev) | |
113 | + return; | |
114 | + | |
115 | + dev->event(dev, I2C_NACK); | |
116 | +} | |
117 | + | ... | ... |
hw/i2c.h
0 โ 100644
1 | +#ifndef QEMU_I2C_H | |
2 | +#define QEMU_I2C_H | |
3 | + | |
4 | +/* The QEMU I2C implementation only supports simple transfers that complete | |
5 | + immediately. It does not support slave devices that need to be able to | |
6 | + defer their response (eg. CPU slave interfaces where the data is supplied | |
7 | + by the device driver in response to an interrupt). */ | |
8 | + | |
9 | +enum i2c_event { | |
10 | + I2C_START_RECV, | |
11 | + I2C_START_SEND, | |
12 | + I2C_FINISH, | |
13 | + I2C_NACK /* Masker NACKed a recieve byte. */ | |
14 | +}; | |
15 | + | |
16 | +typedef struct i2c_slave i2c_slave; | |
17 | + | |
18 | +/* Master to slave. */ | |
19 | +typedef int (*i2c_send_cb)(i2c_slave *s, uint8_t data); | |
20 | +/* Slave to master. */ | |
21 | +typedef int (*i2c_recv_cb)(i2c_slave *s); | |
22 | +/* Notify the slave of a bus state change. */ | |
23 | +typedef void (*i2c_event_cb)(i2c_slave *s, enum i2c_event event); | |
24 | + | |
25 | +struct i2c_slave | |
26 | +{ | |
27 | + /* Callbacks to be set by the device. */ | |
28 | + i2c_event_cb event; | |
29 | + i2c_recv_cb recv; | |
30 | + i2c_send_cb send; | |
31 | + | |
32 | + /* Remaining fields for internal use by the I2C code. */ | |
33 | + int address; | |
34 | + void *next; | |
35 | +}; | |
36 | + | |
37 | +typedef struct i2c_bus i2c_bus; | |
38 | + | |
39 | +i2c_bus *i2c_init_bus(void); | |
40 | +i2c_slave *i2c_slave_init(i2c_bus *bus, int address, int size); | |
41 | +void i2c_set_slave_address(i2c_slave *dev, int address); | |
42 | +int i2c_bus_busy(i2c_bus *bus); | |
43 | +int i2c_start_transfer(i2c_bus *bus, int address, int recv); | |
44 | +void i2c_end_transfer(i2c_bus *bus); | |
45 | +void i2c_nack(i2c_bus *bus); | |
46 | +int i2c_send(i2c_bus *bus, uint8_t data); | |
47 | +int i2c_recv(i2c_bus *bus); | |
48 | + | |
49 | +#endif | ... | ... |
hw/pc.c
... | ... | @@ -897,11 +897,12 @@ static void pc_init1(int ram_size, int vga_ram_size, int boot_device, |
897 | 897 | |
898 | 898 | if (pci_enabled && acpi_enabled) { |
899 | 899 | uint8_t *eeprom_buf = qemu_mallocz(8 * 256); /* XXX: make this persistent */ |
900 | - piix4_pm_init(pci_bus, piix3_devfn + 3); | |
900 | + i2c_bus *smbus; | |
901 | + | |
902 | + /* TODO: Populate SPD eeprom data. */ | |
903 | + smbus = piix4_pm_init(pci_bus, piix3_devfn + 3); | |
901 | 904 | for (i = 0; i < 8; i++) { |
902 | - SMBusDevice *eeprom = smbus_eeprom_device_init(0x50 + i, | |
903 | - eeprom_buf + (i * 256)); | |
904 | - piix4_smbus_register_device(eeprom, 0x50 + i); | |
905 | + smbus_eeprom_device_init(smbus, 0x50 + i, eeprom_buf + (i * 256)); | |
905 | 906 | } |
906 | 907 | } |
907 | 908 | ... | ... |
hw/smbus.c
0 โ 100644
1 | +/* | |
2 | + * QEMU SMBus device emulation. | |
3 | + * | |
4 | + * Copyright (c) 2007 CodeSourcery. | |
5 | + * Written by Paul Brook | |
6 | + * | |
7 | + * This code is licenced under the LGPL. | |
8 | + */ | |
9 | + | |
10 | +/* TODO: Implement PEC. */ | |
11 | + | |
12 | +#include "vl.h" | |
13 | + | |
14 | +//#define DEBUG_SMBUS 1 | |
15 | + | |
16 | +#ifdef DEBUG_SMBUS | |
17 | +#define DPRINTF(fmt, args...) \ | |
18 | +do { printf("smbus(%02x): " fmt , dev->i2c.address, ##args); } while (0) | |
19 | +#define BADF(fmt, args...) \ | |
20 | +do { fprintf(stderr, "smbus: error: " fmt , ##args); exit(1);} while (0) | |
21 | +#else | |
22 | +#define DPRINTF(fmt, args...) do {} while(0) | |
23 | +#define BADF(fmt, args...) \ | |
24 | +do { fprintf(stderr, "smbus: error: " fmt , ##args);} while (0) | |
25 | +#endif | |
26 | + | |
27 | +enum { | |
28 | + SMBUS_IDLE, | |
29 | + SMBUS_WRITE_DATA, | |
30 | + SMBUS_RECV_BYTE, | |
31 | + SMBUS_READ_DATA, | |
32 | + SMBUS_DONE, | |
33 | + SMBUS_CONFUSED = -1 | |
34 | +}; | |
35 | + | |
36 | +static void smbus_do_quick_cmd(SMBusDevice *dev, int recv) | |
37 | +{ | |
38 | + DPRINTF("Quick Command %d\n", recv); | |
39 | + if (dev->quick_cmd) | |
40 | + dev->quick_cmd(dev, recv); | |
41 | +} | |
42 | + | |
43 | +static void smbus_do_write(SMBusDevice *dev) | |
44 | +{ | |
45 | + if (dev->data_len == 0) { | |
46 | + smbus_do_quick_cmd(dev, 0); | |
47 | + } else if (dev->data_len == 1) { | |
48 | + DPRINTF("Send Byte\n"); | |
49 | + if (dev->send_byte) { | |
50 | + dev->send_byte(dev, dev->data_buf[0]); | |
51 | + } | |
52 | + } else { | |
53 | + dev->command = dev->data_buf[0]; | |
54 | + DPRINTF("Command %d len %d\n", dev->command, dev->data_len - 1); | |
55 | + if (dev->write_data) { | |
56 | + dev->write_data(dev, dev->command, dev->data_buf + 1, | |
57 | + dev->data_len - 1); | |
58 | + } | |
59 | + } | |
60 | +} | |
61 | + | |
62 | +void smbus_i2c_event(i2c_slave *s, enum i2c_event event) | |
63 | +{ | |
64 | + SMBusDevice *dev = (SMBusDevice *)s; | |
65 | + switch (event) { | |
66 | + case I2C_START_SEND: | |
67 | + switch (dev->mode) { | |
68 | + case SMBUS_IDLE: | |
69 | + DPRINTF("Incoming data\n"); | |
70 | + dev->mode = SMBUS_WRITE_DATA; | |
71 | + break; | |
72 | + default: | |
73 | + BADF("Unexpected send start condition in state %d\n", dev->mode); | |
74 | + dev->mode = SMBUS_CONFUSED; | |
75 | + break; | |
76 | + } | |
77 | + break; | |
78 | + | |
79 | + case I2C_START_RECV: | |
80 | + switch (dev->mode) { | |
81 | + case SMBUS_IDLE: | |
82 | + DPRINTF("Read mode\n"); | |
83 | + dev->mode = SMBUS_RECV_BYTE; | |
84 | + break; | |
85 | + case SMBUS_WRITE_DATA: | |
86 | + if (dev->data_len == 0) { | |
87 | + BADF("Read after write with no data\n"); | |
88 | + dev->mode = SMBUS_CONFUSED; | |
89 | + } else { | |
90 | + if (dev->data_len > 1) { | |
91 | + smbus_do_write(dev); | |
92 | + } else { | |
93 | + dev->command = dev->data_buf[0]; | |
94 | + DPRINTF("%02x: Command %d\n", dev->i2c.address, | |
95 | + dev->command); | |
96 | + } | |
97 | + DPRINTF("Read mode\n"); | |
98 | + dev->data_len = 0; | |
99 | + dev->mode = SMBUS_READ_DATA; | |
100 | + } | |
101 | + break; | |
102 | + default: | |
103 | + BADF("Unexpected recv start condition in state %d\n", dev->mode); | |
104 | + dev->mode = SMBUS_CONFUSED; | |
105 | + break; | |
106 | + } | |
107 | + break; | |
108 | + | |
109 | + case I2C_FINISH: | |
110 | + switch (dev->mode) { | |
111 | + case SMBUS_WRITE_DATA: | |
112 | + smbus_do_write(dev); | |
113 | + break; | |
114 | + case SMBUS_RECV_BYTE: | |
115 | + smbus_do_quick_cmd(dev, 1); | |
116 | + break; | |
117 | + case SMBUS_READ_DATA: | |
118 | + BADF("Unexpected stop during receive\n"); | |
119 | + break; | |
120 | + default: | |
121 | + /* Nothing to do. */ | |
122 | + break; | |
123 | + } | |
124 | + dev->mode = SMBUS_IDLE; | |
125 | + dev->data_len = 0; | |
126 | + break; | |
127 | + | |
128 | + case I2C_NACK: | |
129 | + switch (dev->mode) { | |
130 | + case SMBUS_DONE: | |
131 | + /* Nothing to do. */ | |
132 | + break; | |
133 | + case SMBUS_READ_DATA: | |
134 | + dev->mode = SMBUS_DONE; | |
135 | + break; | |
136 | + default: | |
137 | + BADF("Unexpected NACK in state %d\n", dev->mode); | |
138 | + dev->mode = SMBUS_CONFUSED; | |
139 | + break; | |
140 | + } | |
141 | + } | |
142 | +} | |
143 | + | |
144 | +static int smbus_i2c_recv(i2c_slave *s) | |
145 | +{ | |
146 | + SMBusDevice *dev = (SMBusDevice *)s; | |
147 | + int ret; | |
148 | + | |
149 | + switch (dev->mode) { | |
150 | + case SMBUS_RECV_BYTE: | |
151 | + if (dev->receive_byte) { | |
152 | + ret = dev->receive_byte(dev); | |
153 | + } else { | |
154 | + ret = 0; | |
155 | + } | |
156 | + DPRINTF("Receive Byte %02x\n", ret); | |
157 | + dev->mode = SMBUS_DONE; | |
158 | + break; | |
159 | + case SMBUS_READ_DATA: | |
160 | + if (dev->read_data) { | |
161 | + ret = dev->read_data(dev, dev->command, dev->data_len); | |
162 | + dev->data_len++; | |
163 | + } else { | |
164 | + ret = 0; | |
165 | + } | |
166 | + DPRINTF("Read data %02x\n", ret); | |
167 | + break; | |
168 | + default: | |
169 | + BADF("Unexpected read in state %d\n", dev->mode); | |
170 | + dev->mode = SMBUS_CONFUSED; | |
171 | + ret = 0; | |
172 | + break; | |
173 | + } | |
174 | + return ret; | |
175 | +} | |
176 | + | |
177 | +static int smbus_i2c_send(i2c_slave *s, uint8_t data) | |
178 | +{ | |
179 | + SMBusDevice *dev = (SMBusDevice *)s; | |
180 | + switch (dev->mode) { | |
181 | + case SMBUS_WRITE_DATA: | |
182 | + DPRINTF("Write data %02x\n", data); | |
183 | + dev->data_buf[dev->data_len++] = data; | |
184 | + break; | |
185 | + default: | |
186 | + BADF("Unexpected write in state %d\n", dev->mode); | |
187 | + break; | |
188 | + } | |
189 | + return 0; | |
190 | +} | |
191 | + | |
192 | +SMBusDevice *smbus_device_init(i2c_bus *bus, int address, int size) | |
193 | +{ | |
194 | + SMBusDevice *dev; | |
195 | + | |
196 | + dev = (SMBusDevice *)i2c_slave_init(bus, address, size); | |
197 | + dev->i2c.event = smbus_i2c_event; | |
198 | + dev->i2c.recv = smbus_i2c_recv; | |
199 | + dev->i2c.send = smbus_i2c_send; | |
200 | + | |
201 | + return dev; | |
202 | +} | |
203 | + | |
204 | +/* Master device commands. */ | |
205 | +void smbus_quick_command(i2c_bus *bus, int addr, int read) | |
206 | +{ | |
207 | + i2c_start_transfer(bus, addr, read); | |
208 | + i2c_end_transfer(bus); | |
209 | +} | |
210 | + | |
211 | +uint8_t smbus_receive_byte(i2c_bus *bus, int addr) | |
212 | +{ | |
213 | + uint8_t data; | |
214 | + | |
215 | + i2c_start_transfer(bus, addr, 1); | |
216 | + data = i2c_recv(bus); | |
217 | + i2c_nack(bus); | |
218 | + i2c_end_transfer(bus); | |
219 | + return data; | |
220 | +} | |
221 | + | |
222 | +void smbus_send_byte(i2c_bus *bus, int addr, uint8_t data) | |
223 | +{ | |
224 | + i2c_start_transfer(bus, addr, 0); | |
225 | + i2c_send(bus, data); | |
226 | + i2c_end_transfer(bus); | |
227 | +} | |
228 | + | |
229 | +uint8_t smbus_read_byte(i2c_bus *bus, int addr, uint8_t command) | |
230 | +{ | |
231 | + uint8_t data; | |
232 | + i2c_start_transfer(bus, addr, 0); | |
233 | + i2c_send(bus, command); | |
234 | + i2c_start_transfer(bus, addr, 1); | |
235 | + data = i2c_recv(bus); | |
236 | + i2c_nack(bus); | |
237 | + i2c_end_transfer(bus); | |
238 | + return data; | |
239 | +} | |
240 | + | |
241 | +void smbus_write_byte(i2c_bus *bus, int addr, uint8_t command, uint8_t data) | |
242 | +{ | |
243 | + i2c_start_transfer(bus, addr, 0); | |
244 | + i2c_send(bus, command); | |
245 | + i2c_send(bus, data); | |
246 | + i2c_end_transfer(bus); | |
247 | +} | |
248 | + | |
249 | +uint16_t smbus_read_word(i2c_bus *bus, int addr, uint8_t command) | |
250 | +{ | |
251 | + uint16_t data; | |
252 | + i2c_start_transfer(bus, addr, 0); | |
253 | + i2c_send(bus, command); | |
254 | + i2c_start_transfer(bus, addr, 1); | |
255 | + data = i2c_recv(bus); | |
256 | + data |= i2c_recv(bus) << 8; | |
257 | + i2c_nack(bus); | |
258 | + i2c_end_transfer(bus); | |
259 | + return data; | |
260 | +} | |
261 | + | |
262 | +void smbus_write_word(i2c_bus *bus, int addr, uint8_t command, uint16_t data) | |
263 | +{ | |
264 | + i2c_start_transfer(bus, addr, 0); | |
265 | + i2c_send(bus, command); | |
266 | + i2c_send(bus, data & 0xff); | |
267 | + i2c_send(bus, data >> 8); | |
268 | + i2c_end_transfer(bus); | |
269 | +} | |
270 | + | |
271 | +int smbus_read_block(i2c_bus *bus, int addr, uint8_t command, uint8_t *data) | |
272 | +{ | |
273 | + int len; | |
274 | + int i; | |
275 | + | |
276 | + i2c_start_transfer(bus, addr, 0); | |
277 | + i2c_send(bus, command); | |
278 | + i2c_start_transfer(bus, addr, 1); | |
279 | + len = i2c_recv(bus); | |
280 | + if (len > 32) | |
281 | + len = 0; | |
282 | + for (i = 0; i < len; i++) | |
283 | + data[i] = i2c_recv(bus); | |
284 | + i2c_nack(bus); | |
285 | + i2c_end_transfer(bus); | |
286 | + return len; | |
287 | +} | |
288 | + | |
289 | +void smbus_write_block(i2c_bus *bus, int addr, uint8_t command, uint8_t *data, | |
290 | + int len) | |
291 | +{ | |
292 | + int i; | |
293 | + | |
294 | + if (len > 32) | |
295 | + len = 32; | |
296 | + | |
297 | + i2c_start_transfer(bus, addr, 0); | |
298 | + i2c_send(bus, command); | |
299 | + i2c_send(bus, len); | |
300 | + for (i = 0; i < len; i++) | |
301 | + i2c_send(bus, data[i]); | |
302 | + i2c_end_transfer(bus); | |
303 | +} | ... | ... |
hw/smbus.h
... | ... | @@ -25,14 +25,46 @@ |
25 | 25 | typedef struct SMBusDevice SMBusDevice; |
26 | 26 | |
27 | 27 | struct SMBusDevice { |
28 | - uint8_t addr; | |
28 | + /* The SMBus protocol is implemented on top of I2C. */ | |
29 | + i2c_slave i2c; | |
30 | + | |
31 | + /* Callbacks set by the device. */ | |
29 | 32 | void (*quick_cmd)(SMBusDevice *dev, uint8_t read); |
30 | 33 | void (*send_byte)(SMBusDevice *dev, uint8_t val); |
31 | 34 | uint8_t (*receive_byte)(SMBusDevice *dev); |
32 | - void (*write_byte)(SMBusDevice *dev, uint8_t cmd, uint8_t val); | |
33 | - uint8_t (*read_byte)(SMBusDevice *dev, uint8_t cmd); | |
34 | - void (*write_word)(SMBusDevice *dev, uint8_t cmd, uint16_t val); | |
35 | - uint16_t (*read_word)(SMBusDevice *dev, uint8_t cmd); | |
36 | - void (*write_block)(SMBusDevice *dev, uint8_t cmd, uint8_t len, uint8_t *buf); | |
37 | - uint8_t (*read_block)(SMBusDevice *dev, uint8_t cmd, uint8_t *buf); | |
35 | + /* We can't distinguish between a word write and a block write with | |
36 | + length 1, so pass the whole data block including the length byte | |
37 | + (if present). The device is responsible figuring out what type of | |
38 | + command this is. */ | |
39 | + void (*write_data)(SMBusDevice *dev, uint8_t cmd, uint8_t *buf, int len); | |
40 | + /* Likewise we can't distinguish between defferent reads, or even know | |
41 | + the length of the read until the read is complete, so read data a | |
42 | + byte at a time. The device is responsible for adding the length | |
43 | + byte on block reads. */ | |
44 | + uint8_t (*read_data)(SMBusDevice *dev, uint8_t cmd, int n); | |
45 | + | |
46 | + /* Remaining fields for internal use only. */ | |
47 | + int mode; | |
48 | + int data_len; | |
49 | + uint8_t data_buf[34]; /* command + len + 32 bytes of data. */ | |
50 | + uint8_t command; | |
38 | 51 | }; |
52 | + | |
53 | +/* Create a slave device. */ | |
54 | +SMBusDevice *smbus_device_init(i2c_bus *bus, int address, int size); | |
55 | + | |
56 | +/* Master device commands. */ | |
57 | +void smbus_quick_command(i2c_bus *bus, int addr, int read); | |
58 | +uint8_t smbus_receive_byte(i2c_bus *bus, int addr); | |
59 | +void smbus_send_byte(i2c_bus *bus, int addr, uint8_t data); | |
60 | +uint8_t smbus_read_byte(i2c_bus *bus, int addr, uint8_t command); | |
61 | +void smbus_write_byte(i2c_bus *bus, int addr, uint8_t command, uint8_t data); | |
62 | +uint16_t smbus_read_word(i2c_bus *bus, int addr, uint8_t command); | |
63 | +void smbus_write_word(i2c_bus *bus, int addr, uint8_t command, uint16_t data); | |
64 | +int smbus_read_block(i2c_bus *bus, int addr, uint8_t command, uint8_t *data); | |
65 | +void smbus_write_block(i2c_bus *bus, int addr, uint8_t command, uint8_t *data, | |
66 | + int len); | |
67 | + | |
68 | +/* smbus_eeprom.c */ | |
69 | +void smbus_eeprom_device_init(i2c_bus *bus, uint8_t addr, uint8_t *buf); | |
70 | + | ... | ... |
hw/smbus_eeprom.c
... | ... | @@ -58,37 +58,51 @@ static uint8_t eeprom_receive_byte(SMBusDevice *dev) |
58 | 58 | return val; |
59 | 59 | } |
60 | 60 | |
61 | -static void eeprom_write_byte(SMBusDevice *dev, uint8_t cmd, uint8_t val) | |
61 | +static void eeprom_write_data(SMBusDevice *dev, uint8_t cmd, uint8_t *buf, int len) | |
62 | 62 | { |
63 | 63 | SMBusEEPROMDevice *eeprom = (SMBusEEPROMDevice *) dev; |
64 | + int n; | |
64 | 65 | #ifdef DEBUG |
65 | 66 | printf("eeprom_write_byte: addr=0x%02x cmd=0x%02x val=0x%02x\n", dev->addr, |
66 | - cmd, val); | |
67 | + cmd, buf[0]); | |
67 | 68 | #endif |
68 | - eeprom->data[cmd] = val; | |
69 | + /* An page write operation is not a valid SMBus command. | |
70 | + It is a block write without a length byte. Fortunately we | |
71 | + get the full block anyway. */ | |
72 | + /* TODO: Should this set the current location? */ | |
73 | + if (cmd + len > 256) | |
74 | + n = 256 - cmd; | |
75 | + else | |
76 | + n = len; | |
77 | + memcpy(eeprom->data + cmd, buf, n); | |
78 | + len -= n; | |
79 | + if (len) | |
80 | + memcpy(eeprom->data, buf + n, len); | |
69 | 81 | } |
70 | 82 | |
71 | -static uint8_t eeprom_read_byte(SMBusDevice *dev, uint8_t cmd) | |
83 | +static uint8_t eeprom_read_data(SMBusDevice *dev, uint8_t cmd, int n) | |
72 | 84 | { |
73 | 85 | SMBusEEPROMDevice *eeprom = (SMBusEEPROMDevice *) dev; |
74 | - uint8_t val = eeprom->data[cmd]; | |
75 | -#ifdef DEBUG | |
76 | - printf("eeprom_read_byte: addr=0x%02x cmd=0x%02x val=0x%02x\n", dev->addr, | |
77 | - cmd, val); | |
78 | -#endif | |
79 | - return val; | |
86 | + /* If this is the first byte then set the current position. */ | |
87 | + if (n == 0) | |
88 | + eeprom->offset = cmd; | |
89 | + /* As with writes, we implement block reads without the | |
90 | + SMBus length byte. */ | |
91 | + return eeprom_receive_byte(dev); | |
80 | 92 | } |
81 | 93 | |
82 | -SMBusDevice *smbus_eeprom_device_init(uint8_t addr, uint8_t *buf) | |
94 | +void smbus_eeprom_device_init(i2c_bus *bus, uint8_t addr, uint8_t *buf) | |
83 | 95 | { |
84 | - SMBusEEPROMDevice *eeprom = qemu_mallocz(sizeof(SMBusEEPROMDevice)); | |
85 | - eeprom->dev.addr = addr; | |
96 | + SMBusEEPROMDevice *eeprom; | |
97 | + | |
98 | + eeprom = (SMBusEEPROMDevice *)smbus_device_init(bus, addr, | |
99 | + sizeof(SMBusEEPROMDevice)); | |
100 | + | |
86 | 101 | eeprom->dev.quick_cmd = eeprom_quick_cmd; |
87 | 102 | eeprom->dev.send_byte = eeprom_send_byte; |
88 | 103 | eeprom->dev.receive_byte = eeprom_receive_byte; |
89 | - eeprom->dev.write_byte = eeprom_write_byte; | |
90 | - eeprom->dev.read_byte = eeprom_read_byte; | |
104 | + eeprom->dev.write_data = eeprom_write_data; | |
105 | + eeprom->dev.read_data = eeprom_read_data; | |
91 | 106 | eeprom->data = buf; |
92 | 107 | eeprom->offset = 0; |
93 | - return (SMBusDevice *) eeprom; | |
94 | 108 | } | ... | ... |
vl.h
... | ... | @@ -1126,17 +1126,16 @@ int pit_get_out(PITState *pit, int channel, int64_t current_time); |
1126 | 1126 | void pcspk_init(PITState *); |
1127 | 1127 | int pcspk_audio_init(AudioState *, qemu_irq *pic); |
1128 | 1128 | |
1129 | +#include "hw/i2c.h" | |
1130 | + | |
1129 | 1131 | #include "hw/smbus.h" |
1130 | 1132 | |
1131 | 1133 | /* acpi.c */ |
1132 | 1134 | extern int acpi_enabled; |
1133 | -void piix4_pm_init(PCIBus *bus, int devfn); | |
1135 | +i2c_bus *piix4_pm_init(PCIBus *bus, int devfn); | |
1134 | 1136 | void piix4_smbus_register_device(SMBusDevice *dev, uint8_t addr); |
1135 | 1137 | void acpi_bios_init(void); |
1136 | 1138 | |
1137 | -/* smbus_eeprom.c */ | |
1138 | -SMBusDevice *smbus_eeprom_device_init(uint8_t addr, uint8_t *buf); | |
1139 | - | |
1140 | 1139 | /* pc.c */ |
1141 | 1140 | extern QEMUMachine pc_machine; |
1142 | 1141 | extern QEMUMachine isapc_machine; | ... | ... |