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; | ... | ... |