I2C overhaul part 4: VIA and EEPROM edition

This commit is contained in:
RichardG867
2020-11-22 00:19:13 -03:00
parent d5867928d6
commit 3fb4727483
11 changed files with 313 additions and 129 deletions

View File

@@ -653,7 +653,7 @@ static void
acpi_i2c_set(acpi_t *dev)
{
if (dev->i2c) {
/* Check direction as well due to pull-ups. */
/* Check direction as well to account for the I2C pull-ups. */
i2c_gpio_set(dev->i2c, !(dev->regs.gpio_dir & 0x02) || (dev->regs.gpio_val & 0x02), !(dev->regs.gpio_dir & 0x04) || (dev->regs.gpio_val & 0x04));
}
}

View File

@@ -868,7 +868,9 @@ pipc_init(const device_t *info)
dev->nvr = device_add(&via_nvr_device);
if (dev->local >= VIA_PIPC_596A)
if (dev->local >= VIA_PIPC_686B)
dev->smbus = device_add(&via_smbus_device);
else if (dev->local >= VIA_PIPC_596A)
dev->smbus = device_add(&piix4_smbus_device);
if (dev->local >= VIA_PIPC_596A)

View File

@@ -45,7 +45,7 @@ typedef struct {
} gl518sm_t;
static void gl518sm_i2c_start(void *bus, uint8_t addr, void *priv);
static uint8_t gl518sm_i2c_start(void *bus, uint8_t addr, uint8_t read, void *priv);
static uint8_t gl518sm_i2c_read(void *bus, uint8_t addr, void *priv);
static uint16_t gl518sm_read(gl518sm_t *dev, uint8_t reg);
static uint8_t gl518sm_i2c_write(void *bus, uint8_t addr, uint8_t data, void *priv);
@@ -87,12 +87,14 @@ gl518sm_remap(gl518sm_t *dev, uint8_t addr)
}
static void
gl518sm_i2c_start(void *bus, uint8_t addr, void *priv)
static uint8_t
gl518sm_i2c_start(void *bus, uint8_t addr, uint8_t read, void *priv)
{
gl518sm_t *dev = (gl518sm_t *) priv;
dev->i2c_state = 0;
return 1;
}
@@ -173,11 +175,11 @@ gl518sm_i2c_write(void *bus, uint8_t addr, uint8_t data, void *priv)
break;
case 1:
gl518sm_write(dev, dev->addr_register, data);
gl518sm_write(dev, dev->addr_register, (gl518sm_read(dev, dev->addr_register) & 0xff00) | data);
break;
case 2:
gl518sm_write(dev, dev->addr_register, (data << 8) | gl518sm_read(dev, dev->addr_register));
gl518sm_write(dev, dev->addr_register, (gl518sm_read(dev, dev->addr_register) << 8) | data);
break;
default:

View File

@@ -30,7 +30,7 @@
#define LM75_TEMP_TO_REG(t) ((t) << 8)
static void lm75_i2c_start(void *bus, uint8_t addr, void *priv);
static uint8_t lm75_i2c_start(void *bus, uint8_t addr, uint8_t read, void *priv);
static uint8_t lm75_i2c_read(void *bus, uint8_t addr, void *priv);
static uint8_t lm75_i2c_write(void *bus, uint8_t addr, uint8_t data, void *priv);
static void lm75_reset(lm75_t *dev);
@@ -59,7 +59,7 @@ lm75_log(const char *fmt, ...)
void
lm75_remap(lm75_t *dev, uint8_t addr)
{
lm75_log("LM75: remapping to I2C %02Xh\n", addr);
lm75_log("LM75: remapping to SMBus %02Xh\n", addr);
if (dev->i2c_addr < 0x80)
i2c_removehandler(i2c_smbus, dev->i2c_addr, 1, lm75_i2c_start, lm75_i2c_read, lm75_i2c_write, NULL, dev);
@@ -71,12 +71,14 @@ lm75_remap(lm75_t *dev, uint8_t addr)
}
static void
lm75_i2c_start(void *bus, uint8_t addr, void *priv)
static uint8_t
lm75_i2c_start(void *bus, uint8_t addr, uint8_t read, void *priv)
{
lm75_t *dev = (lm75_t *) priv;
dev->i2c_state = 0;
return 1;
}
@@ -93,7 +95,7 @@ lm75_i2c_read(void *bus, uint8_t addr, void *priv)
to access some of its proprietary registers. Pass this operation on to
the main monitor address through an internal I2C call, if necessary. */
if ((dev->addr_register > 0x7) && ((dev->addr_register & 0xf8) != 0x50) && (dev->as99127f_i2c_addr < 0x80)) {
i2c_start(i2c_smbus, dev->as99127f_i2c_addr);
i2c_start(i2c_smbus, dev->as99127f_i2c_addr, 1);
i2c_write(i2c_smbus, dev->as99127f_i2c_addr, dev->addr_register);
ret = i2c_read(i2c_smbus, dev->as99127f_i2c_addr);
i2c_stop(i2c_smbus, dev->as99127f_i2c_addr);
@@ -149,7 +151,10 @@ lm75_i2c_write(void *bus, uint8_t addr, uint8_t data, void *priv)
if ((dev->i2c_state > 2) || ((dev->i2c_state == 2) && ((dev->addr_register & 0x3) == 0x1))) {
return 0;
} else if (dev->i2c_state == 0) {
dev->addr_register = data;
dev->i2c_state = 1;
/* Linux lm75.c driver relies on a 3-bit address register that doesn't change if bit 2 is set. */
if ((dev->as99127f_i2c_addr >= 0x80) && !(data & 0x04))
dev->addr_register = (data & 0x7);
return 1;
}
@@ -157,7 +162,7 @@ lm75_i2c_write(void *bus, uint8_t addr, uint8_t data, void *priv)
to access some of its proprietary registers. Pass this operation on to
the main monitor address through an internal I2C call, if necessary. */
if ((dev->addr_register > 0x7) && ((dev->addr_register & 0xf8) != 0x50) && (dev->as99127f_i2c_addr < 0x80)) {
i2c_start(i2c_smbus, dev->as99127f_i2c_addr);
i2c_start(i2c_smbus, dev->as99127f_i2c_addr, 0);
i2c_write(i2c_smbus, dev->as99127f_i2c_addr, dev->addr_register);
i2c_write(i2c_smbus, dev->as99127f_i2c_addr, data);
i2c_stop(i2c_smbus, dev->as99127f_i2c_addr);
@@ -175,13 +180,14 @@ lm75_i2c_write(void *bus, uint8_t addr, uint8_t data, void *priv)
case 0x2: /* Thyst */
lm75_write(dev, (dev->i2c_state == 1) ? 0x3 : 0x4, data);
break;
case 0x3: /* Tos */
lm75_write(dev, (dev->i2c_state == 1) ? 0x5 : 0x6, data);
break;
}
}
if (++dev->i2c_state > 2)
if (dev->i2c_state == 1)
dev->i2c_state = 2;
return 1;

View File

@@ -60,7 +60,7 @@ typedef struct {
} lm78_t;
static void lm78_i2c_start(void *bus, uint8_t addr, void *priv);
static uint8_t lm78_i2c_start(void *bus, uint8_t addr, uint8_t read, void *priv);
static uint8_t lm78_isa_read(uint16_t port, void *priv);
static uint8_t lm78_i2c_read(void *bus, uint8_t addr, void *priv);
static uint8_t lm78_read(lm78_t *dev, uint8_t reg, uint8_t bank);
@@ -69,7 +69,7 @@ static uint8_t lm78_i2c_write(void *bus, uint8_t addr, uint8_t data, void *priv)
static uint8_t lm78_write(lm78_t *dev, uint8_t reg, uint8_t val, uint8_t bank);
static void lm78_reset(lm78_t *dev, uint8_t initialization);
#define ENABLE_LM78_LOG 1
#ifdef ENABLE_LM78_LOG
int lm78_do_log = ENABLE_LM78_LOG;
@@ -118,12 +118,14 @@ lm78_remap(lm78_t *dev, uint8_t addr)
}
static void
lm78_i2c_start(void *bus, uint8_t addr, void *priv)
static uint8_t
lm78_i2c_start(void *bus, uint8_t addr, uint8_t read, void *priv)
{
lm78_t *dev = (lm78_t *) priv;
dev->i2c_state = 0;
return 1;
}

View File

@@ -30,7 +30,7 @@
typedef struct _i2c_ {
void (*start)(void *bus, uint8_t addr, void *priv);
uint8_t (*start)(void *bus, uint8_t addr, uint8_t read, void *priv);
uint8_t (*read)(void *bus, uint8_t addr, void *priv);
uint8_t (*write)(void *bus, uint8_t addr, uint8_t data, void *priv);
void (*stop)(void *bus, uint8_t addr, void *priv);
@@ -84,16 +84,43 @@ i2c_addbus(char *name)
void
i2c_removebus(void *bus_handle)
{
int c;
i2c_t *p, *q;
i2c_bus_t *bus = (i2c_bus_t *) bus_handle;
if (!bus_handle)
return;
free(bus_handle);
for (c = 0; c < NADDRS; c++) {
p = bus->devices[c];
if (!p)
continue;
while(p) {
q = p->next;
free(p);
p = q;
}
}
free(bus);
}
char *
i2c_getbusname(void *bus_handle)
{
i2c_bus_t *bus = (i2c_bus_t *) bus_handle;
if (!bus_handle)
return;
return(bus->name);
}
void
i2c_sethandler(void *bus_handle, uint8_t base, int size,
void (*start)(void *bus, uint8_t addr, void *priv),
uint8_t (*start)(void *bus, uint8_t addr, uint8_t read, void *priv),
uint8_t (*read)(void *bus, uint8_t addr, void *priv),
uint8_t (*write)(void *bus, uint8_t addr, uint8_t data, void *priv),
void (*stop)(void *bus, uint8_t addr, void *priv),
@@ -103,7 +130,7 @@ i2c_sethandler(void *bus_handle, uint8_t base, int size,
i2c_t *p, *q = NULL;
i2c_bus_t *bus = (i2c_bus_t *) bus_handle;
if (!bus_handle || ((base + size) >= NADDRS))
if (!bus_handle || ((base + size) > NADDRS))
return;
for (c = 0; c < size; c++) {
@@ -133,7 +160,7 @@ i2c_sethandler(void *bus_handle, uint8_t base, int size,
void
i2c_removehandler(void *bus_handle, uint8_t base, int size,
void (*start)(void *bus, uint8_t addr, void *priv),
uint8_t (*start)(void *bus, uint8_t addr, uint8_t read, void *priv),
uint8_t (*read)(void *bus, uint8_t addr, void *priv),
uint8_t (*write)(void *bus, uint8_t addr, uint8_t data, void *priv),
void (*stop)(void *bus, uint8_t addr, void *priv),
@@ -143,7 +170,7 @@ i2c_removehandler(void *bus_handle, uint8_t base, int size,
i2c_t *p, *q;
i2c_bus_t *bus = (i2c_bus_t *) bus_handle;
if (!bus_handle || ((base + size) >= NADDRS))
if (!bus_handle || ((base + size) > NADDRS))
return;
for (c = 0; c < size; c++) {
@@ -152,7 +179,7 @@ i2c_removehandler(void *bus_handle, uint8_t base, int size,
continue;
while(p) {
q = p->next;
if ((p->read == read) && (p->write == write) && (p->priv == priv)) {
if ((p->start == start) && (p->read == read) && (p->write == write) && (p->stop == stop) && (p->priv == priv)) {
if (p->prev)
p->prev->next = p->next;
else
@@ -173,7 +200,7 @@ i2c_removehandler(void *bus_handle, uint8_t base, int size,
void
i2c_handler(int set, void *bus_handle, uint8_t base, int size,
void (*start)(void *bus, uint8_t addr, void *priv),
uint8_t (*start)(void *bus, uint8_t addr, uint8_t read, void *priv),
uint8_t (*read)(void *bus, uint8_t addr, void *priv),
uint8_t (*write)(void *bus, uint8_t addr, uint8_t data, void *priv),
void (*stop)(void *bus, uint8_t addr, void *priv),
@@ -200,26 +227,29 @@ i2c_has_device(void *bus_handle, uint8_t addr)
}
void
i2c_start(void *bus_handle, uint8_t addr)
uint8_t
i2c_start(void *bus_handle, uint8_t addr, uint8_t read)
{
uint8_t ret = 0;
i2c_bus_t *bus = (i2c_bus_t *) bus_handle;
i2c_t *p;
if (!bus)
return;
return(ret);
p = bus->devices[addr];
if (p) {
while(p) {
if (p->start) {
p->start(bus_handle, addr, p->priv);
ret |= p->start(bus_handle, addr, read, p->priv);
}
p = p->next;
}
}
i2c_log("I2C: start(%s, %02X)\n", bus->name, addr);
return(ret);
}
@@ -264,7 +294,7 @@ i2c_write(void *bus_handle, uint8_t addr, uint8_t data)
if (p) {
while(p) {
if (p->write) {
ret = p->write(bus_handle, addr, data, p->priv);
ret |= p->write(bus_handle, addr, data, p->priv);
}
p = p->next;
}

View File

@@ -29,11 +29,11 @@ typedef struct {
void *i2c;
uint8_t addr, *data, writable;
uint16_t addr_mask, addr_register;
uint8_t i2c_state;
uint32_t addr_mask, addr_register;
uint8_t addr_len, addr_pos;
} i2c_eeprom_t;
#define ENABLE_I2C_EEPROM_LOG 1
#ifdef ENABLE_I2C_EEPROM_LOG
int i2c_eeprom_do_log = ENABLE_I2C_EEPROM_LOG;
@@ -54,15 +54,17 @@ i2c_eeprom_log(const char *fmt, ...)
#endif
void
i2c_eeprom_start(void *bus, uint8_t addr, void *priv)
uint8_t
i2c_eeprom_start(void *bus, uint8_t addr, uint8_t read, void *priv)
{
i2c_eeprom_t *dev = (i2c_eeprom_t *) priv;
i2c_eeprom_log("I2C EEPROM: start()\n");
i2c_eeprom_log("I2C EEPROM %s %02X: start()\n", i2c_getbusname(dev->i2c), dev->addr);
dev->i2c_state = 0;
dev->addr_register = (addr << 8) & dev->addr_mask;
dev->addr_pos = 0;
dev->addr_register = (addr << dev->addr_len) & dev->addr_mask;
return 1;
}
@@ -72,7 +74,7 @@ i2c_eeprom_read(void *bus, uint8_t addr, void *priv)
i2c_eeprom_t *dev = (i2c_eeprom_t *) priv;
uint8_t ret = dev->data[dev->addr_register];
i2c_eeprom_log("I2C EEPROM: read(%04X) = %02X\n", dev->addr_register, ret);
i2c_eeprom_log("I2C EEPROM %s %02X: read(%06X) = %02X\n", i2c_getbusname(dev->i2c), dev->addr, dev->addr_register, ret);
if (++dev->addr_register > dev->addr_mask) /* roll-over */
dev->addr_register = 0;
@@ -85,13 +87,17 @@ i2c_eeprom_write(void *bus, uint8_t addr, uint8_t data, void *priv)
{
i2c_eeprom_t *dev = (i2c_eeprom_t *) priv;
if (dev->i2c_state == 0) {
dev->i2c_state = 1;
dev->addr_register = ((addr << 8) | data) & dev->addr_mask;
i2c_eeprom_log("I2C EEPROM: write(address, %04X)\n", dev->addr_register);
if (dev->addr_pos < dev->addr_len) {
dev->addr_register <<= 8;
dev->addr_register |= data;
dev->addr_register &= (1 << dev->addr_len) - 1;
dev->addr_register |= addr << dev->addr_len;
dev->addr_register &= dev->addr_mask;
i2c_eeprom_log("I2C EEPROM %s %02X: write(address, %04X)\n", i2c_getbusname(dev->i2c), dev->addr, dev->addr_register);
dev->addr_pos += 8;
} else {
i2c_eeprom_log("I2C EEPROM: write(%04X, %02X) = %s\n", dev->addr_register, data, dev->writable ? "accepted" : "blocked");
if (dev->writable)
i2c_eeprom_log("I2C EEPROM %s %02X: write(%06X, %02X) = %d\n", i2c_getbusname(dev->i2c), dev->addr, dev->addr_register, data, !!dev->writable);
if (dev->writable)
dev->data[dev->addr_register] = data;
if (++dev->addr_register > dev->addr_mask) /* roll-over */
dev->addr_register = 0;
@@ -103,21 +109,24 @@ i2c_eeprom_write(void *bus, uint8_t addr, uint8_t data, void *priv)
void *
i2c_eeprom_init(void *i2c, uint8_t addr, uint8_t *data, uint16_t size, uint8_t writable)
i2c_eeprom_init(void *i2c, uint8_t addr, uint8_t *data, uint32_t size, uint8_t writable)
{
i2c_eeprom_t *dev = (i2c_eeprom_t *) malloc(sizeof(i2c_eeprom_t));
memset(dev, 0, sizeof(i2c_eeprom_t));
i2c_eeprom_log("I2C EEPROM: init(%02X, %d, %d)\n", addr, size, writable);
size &= 0x7fffff; /* address space limit of 8 MB = 7 bits from I2C address + 16 bits */
i2c_eeprom_log("I2C EEPROM %s %02X: init(%d, %d)\n", i2c_getbusname(i2c), addr, size, writable);
dev->i2c = i2c;
dev->addr = addr;
dev->data = data;
dev->writable = writable;
dev->addr_len = (size >= 4096) ? 16 : 8; /* use 16-bit addresses on 24C32 and above */
dev->addr_mask = size - 1;
i2c_sethandler(i2c, dev->addr & ~(dev->addr_mask >> 8), (dev->addr_mask >> 8) + 1, i2c_eeprom_start, i2c_eeprom_read, i2c_eeprom_write, NULL, dev);
i2c_sethandler(i2c, dev->addr & ~(dev->addr_mask >> dev->addr_len), (dev->addr_mask >> dev->addr_len) + 1, i2c_eeprom_start, i2c_eeprom_read, i2c_eeprom_write, NULL, dev);
return dev;
}
@@ -128,9 +137,9 @@ i2c_eeprom_close(void *dev_handle)
{
i2c_eeprom_t *dev = (i2c_eeprom_t *) dev_handle;
i2c_eeprom_log("I2C EEPROM: close()\n");
i2c_eeprom_log("I2C EEPROM %s %02X: close()\n", i2c_getbusname(dev->i2c), dev->addr);
i2c_removehandler(dev->i2c, dev->addr & ~(dev->addr_mask >> 8), (dev->addr_mask >> 8) + 1, i2c_eeprom_start, i2c_eeprom_read, i2c_eeprom_write, NULL, dev);
i2c_removehandler(dev->i2c, dev->addr & ~(dev->addr_mask >> dev->addr_len), (dev->addr_mask >> dev->addr_len) + 1, i2c_eeprom_start, i2c_eeprom_read, i2c_eeprom_write, NULL, dev);
free(dev);
}

View File

@@ -39,6 +39,7 @@ enum {
I2C_TRANSMIT_START,
I2C_TRANSMIT,
I2C_ACKNOWLEDGE,
I2C_NEGACKNOWLEDGE,
I2C_TRANSACKNOWLEDGE,
I2C_TRANSMIT_WAIT
};
@@ -55,7 +56,7 @@ typedef struct {
char *bus_name;
void *i2c;
uint8_t scl, sda, state, slave_state, slave_addr,
slave_rw, last_sda, pos, transmit, byte;
slave_read, last_sda, pos, transmit, byte;
} i2c_gpio_t;
@@ -113,11 +114,11 @@ void
i2c_gpio_next_byte(i2c_gpio_t *dev)
{
dev->byte = i2c_read(dev->i2c, dev->slave_addr);
i2c_gpio_log(1, "I2C GPIO %s: next_byte() = %02X\n", dev->bus_name, dev->byte);
i2c_gpio_log(1, "I2C GPIO %s: Transmitting data %02X\n", dev->bus_name, dev->byte);
}
void
uint8_t
i2c_gpio_write(i2c_gpio_t *dev)
{
uint8_t i;
@@ -126,19 +127,20 @@ i2c_gpio_write(i2c_gpio_t *dev)
case SLAVE_IDLE:
i = dev->slave_addr;
dev->slave_addr = dev->byte >> 1;
dev->slave_rw = dev->byte & 1;
dev->slave_read = dev->byte & 1;
i2c_gpio_log(1, "I2C GPIO %s: Initiating transfer to address %02X rw %d\n", dev->bus_name, dev->slave_addr, dev->slave_rw);
i2c_gpio_log(1, "I2C GPIO %s: Initiating %s address %02X\n", dev->bus_name, dev->slave_read ? "read from" : "write to", dev->slave_addr);
if (!i2c_has_device(dev->i2c, dev->slave_addr)) {
dev->slave_state = SLAVE_INVALID;
break;
dev->slave_addr = 0xff;
return I2C_NEGACKNOWLEDGE;
}
if (i == 0xff)
i2c_start(dev->i2c, dev->slave_addr);
if (i == 0xff) /* start only once per transfer */
i2c_start(dev->i2c, dev->slave_addr, dev->slave_read);
if (dev->slave_rw) {
if (dev->slave_read) {
dev->slave_state = SLAVE_SENDDATA;
dev->transmit = TRANSMITTER_SLAVE;
dev->byte = i2c_read(dev->i2c, dev->slave_addr);
@@ -150,15 +152,22 @@ i2c_gpio_write(i2c_gpio_t *dev)
case SLAVE_RECEIVEADDR:
i2c_gpio_log(1, "I2C GPIO %s: Receiving address %02X\n", dev->bus_name, dev->byte);
i2c_write(dev->i2c, dev->slave_addr, dev->byte);
dev->slave_state = dev->slave_rw ? SLAVE_SENDDATA : SLAVE_RECEIVEDATA;
dev->slave_state = dev->slave_read ? SLAVE_SENDDATA : SLAVE_RECEIVEDATA;
if (!i2c_write(dev->i2c, dev->slave_addr, dev->byte))
return I2C_NEGACKNOWLEDGE;
break;
case SLAVE_RECEIVEDATA:
i2c_gpio_log(1, "I2C GPIO %s: Receiving data %02X\n", dev->bus_name, dev->byte);
i2c_write(dev->i2c, dev->slave_addr, dev->byte);
if (!i2c_write(dev->i2c, dev->slave_addr, dev->byte))
return I2C_NEGACKNOWLEDGE;
break;
case SLAVE_INVALID:
return I2C_NEGACKNOWLEDGE;
}
return I2C_ACKNOWLEDGE;
}
@@ -167,7 +176,7 @@ i2c_gpio_stop(i2c_gpio_t *dev)
{
i2c_gpio_log(1, "I2C GPIO %s: Stopping transfer\n", dev->bus_name);
if (dev->slave_addr != 0xff)
if (dev->slave_addr != 0xff) /* don't stop if no transfer was in progress */
i2c_stop(dev->i2c, dev->slave_addr);
dev->slave_addr = 0xff;
@@ -183,8 +192,8 @@ i2c_gpio_set(void *dev_handle, uint8_t scl, uint8_t sda)
switch (dev->state) {
case I2C_IDLE:
/* !dev->scl check breaks NCR SDMS. */
if (/*!dev->scl &&*/ scl && dev->last_sda && !sda) { /* start bit */
/* dev->scl check breaks NCR SDMS. */
if (scl && dev->last_sda && !sda) { /* start bit */
i2c_gpio_log(2, "I2C GPIO %s: Start bit received (from IDLE)\n", dev->bus_name);
dev->state = I2C_RECEIVE;
dev->pos = 0;
@@ -203,10 +212,8 @@ i2c_gpio_set(void *dev_handle, uint8_t scl, uint8_t sda)
dev->byte |= 1;
else
dev->byte &= 0xfe;
if (++dev->pos == 8) {
i2c_gpio_write(dev);
dev->state = I2C_ACKNOWLEDGE;
}
if (++dev->pos == 8)
dev->state = i2c_gpio_write(dev);
} else if (dev->scl && scl) {
if (sda && !dev->last_sda) { /* stop bit */
i2c_gpio_log(2, "I2C GPIO %s: Stop bit received (from RECEIVE)\n", dev->bus_name);
@@ -222,13 +229,23 @@ i2c_gpio_set(void *dev_handle, uint8_t scl, uint8_t sda)
case I2C_ACKNOWLEDGE:
if (!dev->scl && scl) {
i2c_gpio_log(2, "I2C GPIO %s: Acknowledging transfer\n", dev->bus_name);
i2c_gpio_log(2, "I2C GPIO %s: Acknowledging transfer to %02X\n", dev->bus_name, dev->slave_addr);
sda = 0;
dev->pos = 0;
dev->state = (dev->transmit == TRANSMITTER_MASTER) ? I2C_RECEIVE_WAIT : I2C_TRANSMIT;
}
break;
case I2C_NEGACKNOWLEDGE:
if (!dev->scl && scl) {
i2c_gpio_log(2, "I2C GPIO %s: Nacking transfer\n", dev->bus_name);
sda = 1;
dev->pos = 0;
dev->state = I2C_IDLE;
dev->slave_state = SLAVE_IDLE;
}
break;
case I2C_TRANSACKNOWLEDGE:
if (!dev->scl && scl) {
if (sda) { /* not acknowledged; must be end of transfer */
@@ -306,12 +323,17 @@ uint8_t
i2c_gpio_get_sda(void *dev_handle)
{
i2c_gpio_t *dev = (i2c_gpio_t *) dev_handle;
if ((dev->state == I2C_TRANSMIT) || (dev->state == I2C_ACKNOWLEDGE))
return dev->sda;
else if (dev->state == I2C_RECEIVE_WAIT)
return 0; /* ack */
else
return 1;
switch (dev->state) {
case I2C_TRANSMIT:
case I2C_ACKNOWLEDGE:
return dev->sda;
case I2C_RECEIVE_WAIT:
return 0; /* ack */
default:
return 1;
}
}

View File

@@ -98,95 +98,189 @@ static void
smbus_piix4_write(uint16_t addr, uint8_t val, void *priv)
{
smbus_piix4_t *dev = (smbus_piix4_t *) priv;
uint8_t smbus_addr, smbus_read, prev_stat;
uint8_t smbus_addr, cmd, read, block_len, prev_stat, timer_bytes = 0;
smbus_piix4_log("SMBus PIIX4: write(%02X, %02X)\n", addr, val);
prev_stat = dev->next_stat;
dev->next_stat = 0;
dev->next_stat = 0x00;
switch (addr - dev->io_base) {
case 0x00:
/* some status bits are reset by writing 1 to them */
for (smbus_addr = 0x02; smbus_addr <= 0x10; smbus_addr <<= 1) {
for (smbus_addr = 0x02; smbus_addr <= 0x10; smbus_addr <<= 1) { /* handle clearable bits */
if (val & smbus_addr)
dev->stat &= ~smbus_addr;
}
break;
case 0x02:
dev->ctl = val & ~(0x40); /* START always reads 0 */
dev->ctl = val & ((dev->local == SMBUS_VIA) ? 0x3f : 0x1f);
if (val & 0x02) { /* cancel an in-progress command if KILL is set */
/* cancel only if a command is in progress */
if (prev_stat) {
dev->stat = 0x10; /* raise FAILED */
if (prev_stat) { /* cancel only if a command is in progress */
timer_disable(&dev->response_timer);
dev->stat = 0x10; /* raise FAILED */
}
}
if (val & 0x40) { /* dispatch command if START is set */
timer_bytes++; /* address */
smbus_addr = (dev->addr >> 1);
if (!i2c_has_device(i2c_smbus, smbus_addr)) {
/* raise DEV_ERR if no device is at this address */
dev->next_stat = 0x4;
read = dev->addr & 0x01;
/* Raise DEV_ERR if no device is at this address, or if the device returned NAK when starting the transfer. */
if (!i2c_has_device(i2c_smbus, smbus_addr) || !i2c_start(i2c_smbus, smbus_addr, read)) {
dev->next_stat = 0x04;
break;
}
smbus_read = dev->addr & 0x01;
/* start transaction */
i2c_start(i2c_smbus, smbus_addr);
dev->next_stat = 0x02; /* raise INTER (command completed) by default */
/* decode the 3-bit command protocol */
dev->next_stat = 0x2; /* raise INTER (command completed) by default */
switch ((val >> 2) & 0x7) {
/* Decode the command protocol.
VIA-specific modes (0x4 and [0x6:0xf]) are undocumented and required real hardware research. */
cmd = (val >> 2) & 0xf;
smbus_piix4_log("SMBus PIIX4: protocol=%X cmd=%02X data0=%02X data1=%02X\n", cmd, dev->cmd, dev->data0, dev->data1);
switch (cmd) {
case 0x0: /* quick R/W */
break;
case 0x1: /* byte R/W */
if (smbus_read)
if (read) /* byte read */
dev->data0 = i2c_read(i2c_smbus, smbus_addr);
else
else /* byte write */
i2c_write(i2c_smbus, smbus_addr, dev->data0);
timer_bytes++;
break;
case 0x2: /* byte data R/W */
/* command write */
i2c_write(i2c_smbus, smbus_addr, dev->cmd);
if (smbus_read)
timer_bytes++;
if (read) /* byte read */
dev->data0 = i2c_read(i2c_smbus, smbus_addr);
else
else /* byte write */
i2c_write(i2c_smbus, smbus_addr, dev->data0);
timer_bytes++;
break;
case 0x3: /* word data R/W */
/* command write */
i2c_write(i2c_smbus, smbus_addr, dev->cmd);
if (smbus_read) {
timer_bytes++;
if (read) { /* word read */
dev->data0 = i2c_read(i2c_smbus, smbus_addr);
dev->data1 = i2c_read(i2c_smbus, smbus_addr);
} else {
} else { /* word write */
i2c_write(i2c_smbus, smbus_addr, dev->data0);
i2c_write(i2c_smbus, smbus_addr, dev->data1);
}
timer_bytes += 2;
break;
case 0x4: /* process call */
if (dev->local != SMBUS_VIA) /* VIA only */
goto unknown_protocol;
if (!read) { /* command write (only when writing) */
i2c_write(i2c_smbus, smbus_addr, dev->cmd);
timer_bytes++;
}
/* fall-through */
case 0xc: /* I2C process call */
if (!read) { /* word write (only when writing) */
i2c_write(i2c_smbus, smbus_addr, dev->data0);
i2c_write(i2c_smbus, smbus_addr, dev->data1);
timer_bytes += 2;
}
/* word read */
dev->data0 = i2c_read(i2c_smbus, smbus_addr);
dev->data1 = i2c_read(i2c_smbus, smbus_addr);
timer_bytes += 2;
break;
case 0x5: /* block R/W */
timer_bytes++; /* account for the SMBus length byte now */
/* fall-through */
case 0xd: /* I2C block R/W */
i2c_write(i2c_smbus, smbus_addr, dev->cmd);
/* SMBus block data is preceded by a length */
if (smbus_read) {
dev->data0 = i2c_read(i2c_smbus, smbus_addr);
for (smbus_read = 0; smbus_read < MIN(SMBUS_PIIX4_BLOCK_DATA_SIZE, dev->data0); smbus_read++)
dev->data[smbus_read] = i2c_read(i2c_smbus, smbus_addr);
timer_bytes++;
if (read) {
/* block read [data0] (I2C) or [first byte] (SMBus) bytes */
block_len = (cmd == 0x5) ? i2c_read(i2c_smbus, smbus_addr) : dev->data0;
for (read = 0; read < block_len; read++)
dev->data[read & SMBUS_PIIX4_BLOCK_DATA_MASK] = i2c_read(i2c_smbus, smbus_addr);
} else {
i2c_write(i2c_smbus, smbus_addr, dev->data0);
for (smbus_read = 0; smbus_read < MIN(SMBUS_PIIX4_BLOCK_DATA_SIZE, dev->data0); smbus_read++)
i2c_write(i2c_smbus, smbus_addr, dev->data[smbus_read]);
block_len = dev->data0;
if (cmd == 0x5) /* send length [data0] as first byte on SMBus */
i2c_write(i2c_smbus, smbus_addr, block_len);
/* block write [data0] bytes */
for (read = 0; read < block_len; read++) {
if (!i2c_write(i2c_smbus, smbus_addr, dev->data[read & SMBUS_PIIX4_BLOCK_DATA_MASK]))
break;
}
}
timer_bytes += read;
break;
default:
/* other command protocols have undefined behavior, but raise DEV_ERR to be safe */
dev->next_stat = 0x4;
case 0x6: /* I2C with 10-bit address */
if (dev->local != SMBUS_VIA) /* VIA only */
goto unknown_protocol;
/* command write */
i2c_write(i2c_smbus, smbus_addr, dev->cmd);
timer_bytes += 1;
/* fall-through */
case 0xe: /* I2C with 7-bit address */
if (!read) { /* word write (only when writing) */
i2c_write(i2c_smbus, smbus_addr, dev->data0);
i2c_write(i2c_smbus, smbus_addr, dev->data1);
timer_bytes += 2;
}
/* block read [first byte] bytes */
block_len = dev->data[0];
for (read = 0; read < block_len; read++)
dev->data[read & SMBUS_PIIX4_BLOCK_DATA_MASK] = i2c_read(i2c_smbus, smbus_addr);
timer_bytes += read;
break;
case 0xf: /* universal */
/* block write [data0] bytes */
for (read = 0; read < dev->data0; read++) {
if (!i2c_write(i2c_smbus, smbus_addr, dev->data[read & SMBUS_PIIX4_BLOCK_DATA_MASK]))
break;
}
timer_bytes += read;
/* block read [data1] bytes */
for (read = 0; read < dev->data1; read++)
dev->data[read & SMBUS_PIIX4_BLOCK_DATA_MASK] = i2c_read(i2c_smbus, smbus_addr);
timer_bytes += read;
break;
default: /* unknown */
unknown_protocol:
dev->next_stat = 0x04; /* raise DEV_ERR */
timer_bytes = 0;
break;
}
/* stop transaction */
/* Finish transfer. */
i2c_stop(i2c_smbus, smbus_addr);
}
break;
@@ -214,11 +308,11 @@ smbus_piix4_write(uint16_t addr, uint8_t val, void *priv)
break;
}
/* if a status register update was given, dispatch it after 10us to ensure nothing breaks */
if (dev->next_stat) {
dev->stat = 0x1; /* raise HOST_BUSY while waiting */
if (dev->next_stat) { /* schedule dispatch of any pending status register update */
dev->stat = 0x01; /* raise HOST_BUSY while waiting */
timer_disable(&dev->response_timer);
timer_set_delay_u64(&dev->response_timer, 10 * TIMER_USEC);
/* delay = ((half clock for start + half clock for stop) + (bytes * (8 bits + ack))) * 60us period measured on real VIA 686B */
timer_set_delay_u64(&dev->response_timer, (1 + (timer_bytes * 9)) * 60 * TIMER_USEC);
}
}
@@ -228,7 +322,7 @@ smbus_piix4_response(void *priv)
{
smbus_piix4_t *dev = (smbus_piix4_t *) priv;
/* dispatch the status register update */
/* Dispatch the status register update. */
dev->stat = dev->next_stat;
}
@@ -253,7 +347,8 @@ smbus_piix4_init(const device_t *info)
smbus_piix4_t *dev = (smbus_piix4_t *) malloc(sizeof(smbus_piix4_t));
memset(dev, 0, sizeof(smbus_piix4_t));
i2c_smbus = dev->i2c = i2c_addbus("smbus_piix4");
dev->local = info->local;
i2c_smbus = dev->i2c = i2c_addbus((dev->local == SMBUS_VIA) ? "smbus_vt82c686b" : "smbus_piix4");
timer_add(&dev->response_timer, smbus_piix4_response, dev, 0);
@@ -277,7 +372,16 @@ smbus_piix4_close(void *priv)
const device_t piix4_smbus_device = {
"PIIX4-compatible SMBus Host Controller",
DEVICE_AT,
0,
SMBUS_PIIX4,
smbus_piix4_init, smbus_piix4_close, NULL,
{ NULL }, NULL, NULL,
NULL
};
const device_t via_smbus_device = {
"VIA VT82C686B SMBus Host Controller",
DEVICE_AT,
SMBUS_VIA,
smbus_piix4_init, smbus_piix4_close, NULL,
{ NULL }, NULL, NULL,
NULL

View File

@@ -25,36 +25,37 @@ extern void *i2c_smbus;
/* i2c.c */
extern void *i2c_addbus(char *name);
extern void i2c_removebus(void *bus_handle);
extern char *i2c_getbusname(void *bus_handle);
extern void i2c_sethandler(void *bus_handle, uint8_t base, int size,
void (*start)(void *bus, uint8_t addr, void *priv),
uint8_t (*start)(void *bus, uint8_t addr, uint8_t read, void *priv),
uint8_t (*read)(void *bus, uint8_t addr, void *priv),
uint8_t (*write)(void *bus, uint8_t addr, uint8_t data, void *priv),
void (*stop)(void *bus, uint8_t addr, void *priv),
void *priv);
extern void i2c_removehandler(void *bus_handle, uint8_t base, int size,
void (*start)(void *bus, uint8_t addr, void *priv),
uint8_t (*start)(void *bus, uint8_t addr, uint8_t read, void *priv),
uint8_t (*read)(void *bus, uint8_t addr, void *priv),
uint8_t (*write)(void *bus, uint8_t addr, uint8_t data, void *priv),
void (*stop)(void *bus, uint8_t addr, void *priv),
void *priv);
extern void i2c_handler(int set, void *bus_handle, uint8_t base, int size,
void (*start)(void *bus, uint8_t addr, void *priv),
uint8_t (*start)(void *bus, uint8_t addr, uint8_t read, void *priv),
uint8_t (*read)(void *bus, uint8_t addr, void *priv),
uint8_t (*write)(void *bus, uint8_t addr, uint8_t data, void *priv),
void (*stop)(void *bus, uint8_t addr, void *priv),
void *priv);
extern uint8_t i2c_has_device(void *bus_handle, uint8_t addr);
extern void i2c_start(void *bus_handle, uint8_t addr);
extern uint8_t i2c_start(void *bus_handle, uint8_t addr, uint8_t read);
extern uint8_t i2c_read(void *bus_handle, uint8_t addr);
extern uint8_t i2c_write(void *bus_handle, uint8_t addr, uint8_t data);
extern void i2c_stop(void *bus_handle, uint8_t addr);
/* i2c_eeprom.c */
extern void *i2c_eeprom_init(void *i2c, uint8_t addr, uint8_t *data, uint16_t size, uint8_t writable);
extern void *i2c_eeprom_init(void *i2c, uint8_t addr, uint8_t *data, uint32_t size, uint8_t writable);
extern void i2c_eeprom_close(void *dev_handle);
/* i2c_gpio.c */

View File

@@ -19,15 +19,20 @@
#define SMBUS_PIIX4_BLOCK_DATA_SIZE 32
#define SMBUS_PIIX4_BLOCK_DATA_MASK (SMBUS_PIIX4_BLOCK_DATA_SIZE - 1)
typedef struct
{
enum {
SMBUS_PIIX4 = 0,
SMBUS_VIA
};
typedef struct {
uint32_t local;
uint16_t io_base;
uint8_t stat, next_stat, ctl, cmd, addr,
data0, data1,
index,
data[SMBUS_PIIX4_BLOCK_DATA_SIZE];
index, data[SMBUS_PIIX4_BLOCK_DATA_SIZE];
pc_timer_t response_timer;
void *i2c;
} smbus_piix4_t;
@@ -38,6 +43,7 @@ extern void smbus_piix4_remap(smbus_piix4_t *dev, uint16_t new_io_base, uint8_t
#ifdef EMU_DEVICE_H
extern const device_t piix4_smbus_device;
extern const device_t via_smbus_device;
#endif