I2C overhaul part 3: "we finally figured out NCR NVRAM" edition

This commit is contained in:
RichardG867
2020-11-21 01:36:33 -03:00
parent cf2dba5838
commit d5867928d6
8 changed files with 214 additions and 235 deletions

View File

@@ -103,13 +103,16 @@ gl518sm_i2c_read(void *bus, uint8_t addr, void *priv)
uint16_t read = gl518sm_read(dev, dev->addr_register);
uint8_t ret = 0;
if (dev->i2c_state == 0)
dev->i2c_state = 1;
if ((dev->i2c_state == 1) && (dev->addr_register >= 0x07) && (dev->addr_register <= 0x0c)) { /* two-byte registers: read MSB first */
dev->i2c_state = 2;
ret = read >> 8;
} else
} else {
ret = read;
dev->addr_register++;
dev->addr_register++;
}
return ret;
}
@@ -178,6 +181,7 @@ gl518sm_i2c_write(void *bus, uint8_t addr, uint8_t data, void *priv)
break;
default:
dev->i2c_state = 3;
return 0;
}

View File

@@ -35,7 +35,7 @@ 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);
#define ENABLE_LM75_LOG 1
#ifdef ENABLE_LM75_LOG
int lm75_do_log = ENABLE_LM75_LOG;
@@ -86,23 +86,39 @@ lm75_i2c_read(void *bus, uint8_t addr, void *priv)
lm75_t *dev = (lm75_t *) priv;
uint8_t ret = 0;
switch (dev->addr_register & 0x3) {
case 0x0: /* temperature */
ret = lm75_read(dev, (dev->i2c_state == 1) ? 0x1 : 0x0);
break;
if (dev->i2c_state == 0)
dev->i2c_state = 1;
case 0x1: /* configuration */
ret = lm75_read(dev, 0x2);
break;
/* The AS99127F hardware monitor uses the addresses of its LM75 devices
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_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);
} else {
switch (dev->addr_register & 0x3) {
case 0x0: /* temperature */
ret = lm75_read(dev, (dev->i2c_state == 1) ? 0x0 : 0x1);
break;
case 0x2: /* Thyst */
ret = lm75_read(dev, (dev->i2c_state == 1) ? 0x4 : 0x3);
break;
case 0x3: /* Tos */
ret = lm75_read(dev, (dev->i2c_state == 1) ? 0x6 : 0x5);
break;
case 0x1: /* configuration */
ret = lm75_read(dev, 0x2);
break;
case 0x2: /* Thyst */
ret = lm75_read(dev, (dev->i2c_state == 1) ? 0x3 : 0x4);
break;
case 0x3: /* Tos */
ret = lm75_read(dev, (dev->i2c_state == 1) ? 0x5 : 0x6);
break;
}
}
if (++dev->i2c_state > 2)
dev->i2c_state = 2;
return ret;
}
@@ -112,15 +128,7 @@ lm75_read(lm75_t *dev, uint8_t reg)
{
uint8_t ret;
/* The AS99127F hardware monitor uses the addresses of its LM75 devices
to access some of its proprietary registers. Pass this operation on to
the main monitor address through an internal I2C call, if necessary. */
if ((reg > 0x7) && ((reg & 0xf8) != 0x50) && (dev->as99127f_i2c_addr < 0x80)) {
i2c_start(i2c_smbus, dev->as99127f_i2c_addr);
i2c_write(i2c_smbus, dev->as99127f_i2c_addr, reg);
ret = i2c_read(i2c_smbus, dev->as99127f_i2c_addr);
i2c_stop(i2c_smbus, dev->as99127f_i2c_addr);
} else if ((reg & 0x7) == 0x0) /* temperature high byte */
if ((reg & 0x7) == 0x0) /* temperature high byte */
ret = LM75_TEMP_TO_REG(dev->values->temperatures[dev->local >> 8]) >> 8;
else if ((reg & 0x7) == 0x1) /* temperature low byte */
ret = LM75_TEMP_TO_REG(dev->values->temperatures[dev->local >> 8]);
@@ -145,23 +153,37 @@ lm75_i2c_write(void *bus, uint8_t addr, uint8_t data, void *priv)
return 1;
}
switch (dev->addr_register & 0x3) {
case 0x0: /* temperature */
lm75_write(dev, (dev->i2c_state == 1) ? 0x1 : 0x0, data);
break;
/* The AS99127F hardware monitor uses the addresses of its LM75 devices
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_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);
return 1;
} else {
switch (dev->addr_register & 0x3) {
case 0x0: /* temperature */
lm75_write(dev, (dev->i2c_state == 1) ? 0x0 : 0x1, data);
break;
case 0x1: /* configuration */
lm75_write(dev, 0x2, data);
break;
case 0x1: /* configuration */
lm75_write(dev, 0x2, data);
break;
case 0x2: /* Thyst */
lm75_write(dev, (dev->i2c_state == 1) ? 0x4 : 0x3, data);
break;
case 0x3: /* Tos */
lm75_write(dev, (dev->i2c_state == 1) ? 0x6 : 0x5, data);
break;
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)
dev->i2c_state = 2;
return 1;
}
@@ -171,17 +193,6 @@ lm75_write(lm75_t *dev, uint8_t reg, uint8_t val)
{
lm75_log("LM75: write(%02X, %02X)\n", reg, val);
/* The AS99127F hardware monitor uses the addresses of its LM75 devices
to access some of its proprietary registers. Pass this operation on to
the main monitor address through an internal I2C call, if necessary. */
if ((reg > 0x7) && ((reg & 0xf8) != 0x50) && (dev->as99127f_i2c_addr < 0x80)) {
i2c_start(i2c_smbus, dev->as99127f_i2c_addr);
i2c_write(i2c_smbus, dev->as99127f_i2c_addr, reg);
i2c_write(i2c_smbus, dev->as99127f_i2c_addr, val);
i2c_stop(i2c_smbus, dev->as99127f_i2c_addr);
return 1;
}
uint8_t reg_idx = (reg & 0x7);
if ((reg_idx <= 0x1) || (reg_idx == 0x7))

View File

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

View File

@@ -194,6 +194,8 @@ i2c_has_device(void *bus_handle, uint8_t addr)
if (!bus)
return 0;
i2c_log("I2C: has_device(%s, %02X) = %d\n", bus->name, addr, !!bus->devices[addr]);
return(!!bus->devices[addr]);
}

View File

@@ -6,7 +6,7 @@
*
* This file is part of the 86Box distribution.
*
* Emulation of the AT24Cxx series of I2C EEPROMs.
* Emulation of the 24Cxx series of I2C EEPROMs.
*
*
*
@@ -27,22 +27,42 @@
typedef struct {
void *i2c;
uint8_t addr;
uint8_t *data;
uint8_t writable;
uint8_t addr, *data, writable;
uint16_t addr_mask;
uint8_t addr_register;
uint16_t addr_mask, addr_register;
uint8_t i2c_state;
} i2c_eeprom_t;
#define ENABLE_I2C_EEPROM_LOG 1
#ifdef ENABLE_I2C_EEPROM_LOG
int i2c_eeprom_do_log = ENABLE_I2C_EEPROM_LOG;
static void
i2c_eeprom_log(const char *fmt, ...)
{
va_list ap;
if (i2c_eeprom_do_log) {
va_start(ap, fmt);
pclog_ex(fmt, ap);
va_end(ap);
}
}
#else
#define i2c_eeprom_log(fmt, ...)
#endif
void
i2c_eeprom_start(void *bus, uint8_t addr, void *priv)
{
i2c_eeprom_t *dev = (i2c_eeprom_t *) priv;
i2c_eeprom_log("I2C EEPROM: start()\n");
dev->i2c_state = 0;
dev->addr_register = (addr << 8) & dev->addr_mask;
}
@@ -50,8 +70,13 @@ uint8_t
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];
return dev->data[((addr << 8) | dev->addr_register++) & dev->addr_mask];
i2c_eeprom_log("I2C EEPROM: read(%04X) = %02X\n", dev->addr_register, ret);
if (++dev->addr_register > dev->addr_mask) /* roll-over */
dev->addr_register = 0;
return ret;
}
@@ -62,11 +87,16 @@ i2c_eeprom_write(void *bus, uint8_t addr, uint8_t data, void *priv)
if (dev->i2c_state == 0) {
dev->i2c_state = 1;
dev->addr_register = data;
} else if (dev->writable)
dev->data[((addr << 8) | dev->addr_register++) & dev->addr_mask] = data;
else
return 0;
dev->addr_register = ((addr << 8) | data) & dev->addr_mask;
i2c_eeprom_log("I2C EEPROM: write(address, %04X)\n", dev->addr_register);
} else {
i2c_eeprom_log("I2C EEPROM: write(%04X, %02X) = %s\n", dev->addr_register, data, dev->writable ? "accepted" : "blocked");
if (dev->writable)
dev->data[dev->addr_register] = data;
if (++dev->addr_register > dev->addr_mask) /* roll-over */
dev->addr_register = 0;
return dev->writable;
}
return 1;
}
@@ -78,6 +108,8 @@ i2c_eeprom_init(void *i2c, uint8_t addr, uint8_t *data, uint16_t size, uint8_t w
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);
dev->i2c = i2c;
dev->addr = addr;
dev->data = data;
@@ -96,6 +128,8 @@ i2c_eeprom_close(void *dev_handle)
{
i2c_eeprom_t *dev = (i2c_eeprom_t *) dev_handle;
i2c_eeprom_log("I2C EEPROM: close()\n");
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);
free(dev);

View File

@@ -52,19 +52,43 @@ enum {
};
typedef struct {
char *bus_name;
void *i2c;
uint8_t scl, sda, state, slave_state, slave_addr,
slave_rw, last_sda, pos, transmit, byte;
} i2c_gpio_t;
#ifdef ENABLE_I2C_GPIO_LOG
int i2c_gpio_do_log = ENABLE_I2C_GPIO_LOG;
static void
i2c_gpio_log(int level, const char *fmt, ...)
{
va_list ap;
if (i2c_gpio_do_log >= level) {
va_start(ap, fmt);
pclog_ex(fmt, ap);
va_end(ap);
}
}
#else
#define i2c_gpio_log(fmt, ...)
#endif
void *
i2c_gpio_init(char *bus_name)
{
i2c_gpio_t *dev = (i2c_gpio_t *) malloc(sizeof(i2c_gpio_t));
memset(dev, 0, sizeof(i2c_gpio_t));
dev->i2c = i2c_addbus(bus_name);
i2c_gpio_log(1, "I2C GPIO %s: init()\n", bus_name);
dev->bus_name = bus_name;
dev->i2c = i2c_addbus(dev->bus_name);
dev->scl = dev->sda = 1;
dev->slave_addr = 0xff;
@@ -77,6 +101,8 @@ i2c_gpio_close(void *dev_handle)
{
i2c_gpio_t *dev = (i2c_gpio_t *) dev_handle;
i2c_gpio_log(1, "I2C GPIO %s: close()\n", dev->bus_name);
i2c_removebus(dev->i2c);
free(dev);
@@ -87,6 +113,7 @@ 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);
}
@@ -101,6 +128,8 @@ i2c_gpio_write(i2c_gpio_t *dev)
dev->slave_addr = dev->byte >> 1;
dev->slave_rw = 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);
if (!i2c_has_device(dev->i2c, dev->slave_addr)) {
dev->slave_state = SLAVE_INVALID;
break;
@@ -120,11 +149,13 @@ i2c_gpio_write(i2c_gpio_t *dev)
break;
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;
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);
break;
}
@@ -134,7 +165,10 @@ i2c_gpio_write(i2c_gpio_t *dev)
void
i2c_gpio_stop(i2c_gpio_t *dev)
{
i2c_stop(dev->i2c, dev->slave_addr);
i2c_gpio_log(1, "I2C GPIO %s: Stopping transfer\n", dev->bus_name);
if (dev->slave_addr != 0xff)
i2c_stop(dev->i2c, dev->slave_addr);
dev->slave_addr = 0xff;
dev->slave_state = SLAVE_IDLE;
@@ -149,7 +183,9 @@ i2c_gpio_set(void *dev_handle, uint8_t scl, uint8_t sda)
switch (dev->state) {
case I2C_IDLE:
if (dev->scl && scl && dev->last_sda && !sda) { /* start bit */
/* !dev->scl check breaks NCR SDMS. */
if (/*!dev->scl &&*/ 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;
}
@@ -173,9 +209,11 @@ i2c_gpio_set(void *dev_handle, uint8_t scl, uint8_t sda)
}
} 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);
dev->state = I2C_IDLE;
i2c_gpio_stop(dev);
} else if (!sda && dev->last_sda) { /* start bit */
i2c_gpio_log(2, "I2C GPIO %s: Start bit received (from RECEIVE)\n", dev->bus_name);
dev->pos = 0;
dev->slave_state = SLAVE_IDLE;
}
@@ -184,6 +222,7 @@ 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);
sda = 0;
dev->pos = 0;
dev->state = (dev->transmit == TRANSMITTER_MASTER) ? I2C_RECEIVE_WAIT : I2C_TRANSMIT;
@@ -193,12 +232,14 @@ i2c_gpio_set(void *dev_handle, uint8_t scl, uint8_t sda)
case I2C_TRANSACKNOWLEDGE:
if (!dev->scl && scl) {
if (sda) { /* not acknowledged; must be end of transfer */
i2c_gpio_log(2, "I2C GPIO %s: End of transfer\n", dev->bus_name);
dev->state = I2C_IDLE;
i2c_gpio_stop(dev);
} else { /* next byte to transfer */
dev->state = I2C_TRANSMIT_START;
i2c_gpio_next_byte(dev);
dev->pos = 0;
i2c_gpio_log(2, "I2C GPIO %s: Next byte = %02X\n", dev->bus_name, dev->byte);
}
}
break;
@@ -208,8 +249,10 @@ i2c_gpio_set(void *dev_handle, uint8_t scl, uint8_t sda)
if (dev->last_sda && !sda) { /* start bit */
i2c_gpio_next_byte(dev);
dev->pos = 0;
i2c_gpio_log(2, "I2C GPIO %s: Next byte = %02X\n", dev->bus_name, dev->byte);
}
if (!dev->last_sda && sda) { /* stop bit */
i2c_gpio_log(2, "I2C GPIO %s: Stop bit received (from TRANSMIT_WAIT)\n", dev->bus_name);
dev->state = I2C_IDLE;
i2c_gpio_stop(dev);
}
@@ -220,6 +263,7 @@ i2c_gpio_set(void *dev_handle, uint8_t scl, uint8_t sda)
if (!dev->scl && scl)
dev->state = I2C_TRANSMIT;
if (dev->scl && scl && !dev->last_sda && sda) { /* stop bit */
i2c_gpio_log(2, "I2C GPIO %s: Stop bit received (from TRANSMIT_START)\n", dev->bus_name);
dev->state = I2C_IDLE;
i2c_gpio_stop(dev);
}
@@ -228,13 +272,18 @@ i2c_gpio_set(void *dev_handle, uint8_t scl, uint8_t sda)
case I2C_TRANSMIT:
if (!dev->scl && scl) {
dev->scl = scl;
if (!dev->pos)
i2c_gpio_log(2, "I2C GPIO %s: Transmit byte %02X\n", dev->bus_name, dev->byte);
dev->sda = sda = dev->byte & 0x80;
i2c_gpio_log(2, "I2C GPIO %s: Transmit bit %02X %d\n", dev->bus_name, dev->byte, dev->pos);
dev->byte <<= 1;
dev->pos++;
return;
}
if (dev->scl && !scl && (dev->pos == 8))
if (dev->scl && !scl && (dev->pos == 8)) {
dev->state = I2C_TRANSACKNOWLEDGE;
i2c_gpio_log(2, "I2C GPIO %s: Acknowledge mode\n", dev->bus_name);
}
break;
}