diff options
-rw-r--r-- | Documentation/i2c/busses/i2c-i801 | 4 | ||||
-rw-r--r-- | drivers/i2c/busses/i2c-i801.c | 151 |
2 files changed, 102 insertions, 53 deletions
diff --git a/Documentation/i2c/busses/i2c-i801 b/Documentation/i2c/busses/i2c-i801 index c34f0db78a3..fe6406f2f9a 100644 --- a/Documentation/i2c/busses/i2c-i801 +++ b/Documentation/i2c/busses/i2c-i801 @@ -5,8 +5,8 @@ Supported adapters: '810' and '810E' chipsets) * Intel 82801BA (ICH2 - part of the '815E' chipset) * Intel 82801CA/CAM (ICH3) - * Intel 82801DB (ICH4) (HW PEC supported, 32 byte buffer not supported) - * Intel 82801EB/ER (ICH5) (HW PEC supported, 32 byte buffer not supported) + * Intel 82801DB (ICH4) (HW PEC supported) + * Intel 82801EB/ER (ICH5) (HW PEC supported) * Intel 6300ESB * Intel 82801FB/FR/FW/FRW (ICH6) * Intel 82801G (ICH7) diff --git a/drivers/i2c/busses/i2c-i801.c b/drivers/i2c/busses/i2c-i801.c index d5e12a4f01b..8f5c686123b 100644 --- a/drivers/i2c/busses/i2c-i801.c +++ b/drivers/i2c/busses/i2c-i801.c @@ -26,8 +26,8 @@ 82801AB 2423 82801BA 2443 82801CA/CAM 2483 - 82801DB 24C3 (HW PEC supported, 32 byte buffer not supported) - 82801EB 24D3 (HW PEC supported, 32 byte buffer not supported) + 82801DB 24C3 (HW PEC supported) + 82801EB 24D3 (HW PEC supported) 6300ESB 25A4 ICH6 266A ICH7 27DA @@ -114,7 +114,7 @@ static struct pci_driver i801_driver; static struct pci_dev *I801_dev; static int isich4; -static int i801_transaction(void) +static int i801_transaction(int xact) { int temp; int result = 0; @@ -139,7 +139,9 @@ static int i801_transaction(void) } } - outb_p(inb(SMBHSTCNT) | I801_START, SMBHSTCNT); + /* the current contents of SMBHSTCNT can be overwritten, since PEC, + * INTREN, SMBSCMD are passed in xact */ + outb_p(xact | I801_START, SMBHSTCNT); /* We will always wait for a fraction of a second! */ do { @@ -207,44 +209,52 @@ static void i801_wait_hwpec(void) outb_p(temp, SMBHSTSTS); } -/* All-inclusive block transaction function */ -static int i801_block_transaction(union i2c_smbus_data *data, char read_write, - int command, int hwpec) +static int i801_block_transaction_by_block(union i2c_smbus_data *data, + char read_write, int hwpec) +{ + int i, len; + + inb_p(SMBHSTCNT); /* reset the data buffer index */ + + /* Use 32-byte buffer to process this transaction */ + if (read_write == I2C_SMBUS_WRITE) { + len = data->block[0]; + outb_p(len, SMBHSTDAT0); + for (i = 0; i < len; i++) + outb_p(data->block[i+1], SMBBLKDAT); + } + + if (i801_transaction(I801_BLOCK_DATA | ENABLE_INT9 | + I801_PEC_EN * hwpec)) + return -1; + + if (read_write == I2C_SMBUS_READ) { + len = inb_p(SMBHSTDAT0); + if (len < 1 || len > I2C_SMBUS_BLOCK_MAX) + return -1; + + data->block[0] = len; + for (i = 0; i < len; i++) + data->block[i + 1] = inb_p(SMBBLKDAT); + } + return 0; +} + +static int i801_block_transaction_byte_by_byte(union i2c_smbus_data *data, + char read_write, int hwpec) { int i, len; int smbcmd; int temp; int result = 0; int timeout; - unsigned char hostc, errmask; + unsigned char errmask; - if (command == I2C_SMBUS_I2C_BLOCK_DATA) { - if (read_write == I2C_SMBUS_WRITE) { - /* set I2C_EN bit in configuration register */ - pci_read_config_byte(I801_dev, SMBHSTCFG, &hostc); - pci_write_config_byte(I801_dev, SMBHSTCFG, - hostc | SMBHSTCFG_I2C_EN); - } else { - dev_err(&I801_dev->dev, - "I2C_SMBUS_I2C_BLOCK_READ not DB!\n"); - return -1; - } - } + len = data->block[0]; if (read_write == I2C_SMBUS_WRITE) { - len = data->block[0]; - if (len < 1) - len = 1; - if (len > 32) - len = 32; outb_p(len, SMBHSTDAT0); outb_p(data->block[1], SMBBLKDAT); - } else { - len = 32; /* max for reads */ - } - - if(isich4 && command != I2C_SMBUS_I2C_BLOCK_DATA) { - /* set 32 byte buffer */ } for (i = 1; i <= len; i++) { @@ -277,14 +287,11 @@ static int i801_block_transaction(union i2c_smbus_data *data, char read_write, if (((temp = inb_p(SMBHSTSTS)) & errmask) != 0x00) { dev_err(&I801_dev->dev, "Reset failed! (%02x)\n", temp); - result = -1; - goto END; + return -1; } - if (i != 1) { + if (i != 1) /* if die in middle of block transaction, fail */ - result = -1; - goto END; - } + return -1; } if (i == 1) @@ -326,10 +333,8 @@ static int i801_block_transaction(union i2c_smbus_data *data, char read_write, if (i == 1 && read_write == I2C_SMBUS_READ) { len = inb_p(SMBHSTDAT0); - if (len < 1 || len > I2C_SMBUS_BLOCK_MAX) { - result = -1; - goto END; - } + if (len < 1 || len > I2C_SMBUS_BLOCK_MAX) + return -1; data->block[0] = len; } @@ -352,14 +357,58 @@ static int i801_block_transaction(union i2c_smbus_data *data, char read_write, inb_p(SMBHSTDAT0), inb_p(SMBBLKDAT)); if (result < 0) - goto END; + return result; } + return result; +} - if (hwpec) +static int i801_set_block_buffer_mode(void) +{ + outb_p(inb_p(SMBAUXCTL) | SMBAUXCTL_E32B, SMBAUXCTL); + if ((inb_p(SMBAUXCTL) & SMBAUXCTL_E32B) == 0) + return -1; + return 0; +} + +/* Block transaction function */ +static int i801_block_transaction(union i2c_smbus_data *data, char read_write, + int command, int hwpec) +{ + int result = 0; + unsigned char hostc; + + if (command == I2C_SMBUS_I2C_BLOCK_DATA) { + if (read_write == I2C_SMBUS_WRITE) { + /* set I2C_EN bit in configuration register */ + pci_read_config_byte(I801_dev, SMBHSTCFG, &hostc); + pci_write_config_byte(I801_dev, SMBHSTCFG, + hostc | SMBHSTCFG_I2C_EN); + } else { + dev_err(&I801_dev->dev, + "I2C_SMBUS_I2C_BLOCK_READ not DB!\n"); + return -1; + } + } + + if (read_write == I2C_SMBUS_WRITE) { + if (data->block[0] < 1) + data->block[0] = 1; + if (data->block[0] > I2C_SMBUS_BLOCK_MAX) + data->block[0] = I2C_SMBUS_BLOCK_MAX; + } else { + data->block[0] = 32; /* max for reads */ + } + + if (isich4 && i801_set_block_buffer_mode() == 0 ) + result = i801_block_transaction_by_block(data, read_write, + hwpec); + else + result = i801_block_transaction_byte_by_byte(data, read_write, + hwpec); + + if (result == 0 && hwpec) i801_wait_hwpec(); - result = 0; -END: if (command == I2C_SMBUS_I2C_BLOCK_DATA) { /* restore saved configuration register value */ pci_write_config_byte(I801_dev, SMBHSTCFG, hostc); @@ -431,15 +480,15 @@ static s32 i801_access(struct i2c_adapter * adap, u16 addr, if(block) ret = i801_block_transaction(data, read_write, size, hwpec); - else { - outb_p(xact | ENABLE_INT9, SMBHSTCNT); - ret = i801_transaction(); - } + else + ret = i801_transaction(xact | ENABLE_INT9); /* Some BIOSes don't like it when PEC is enabled at reboot or resume - time, so we forcibly disable it after every transaction. */ + time, so we forcibly disable it after every transaction. Turn off + E32B for the same reason. */ if (hwpec) - outb_p(inb_p(SMBAUXCTL) & (~SMBAUXCTL_CRC), SMBAUXCTL); + outb_p(inb_p(SMBAUXCTL) & ~(SMBAUXCTL_CRC | SMBAUXCTL_E32B), + SMBAUXCTL); if(block) return ret; |