diff options
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/i2c/busses/i2c-i801.c | 116 |
1 files changed, 75 insertions, 41 deletions
diff --git a/drivers/i2c/busses/i2c-i801.c b/drivers/i2c/busses/i2c-i801.c index 611b57192c96..d5e12a4f01b0 100644 --- a/drivers/i2c/busses/i2c-i801.c +++ b/drivers/i2c/busses/i2c-i801.c @@ -22,10 +22,10 @@ /* SUPPORTED DEVICES PCI ID - 82801AA 2413 - 82801AB 2423 - 82801BA 2443 - 82801CA/CAM 2483 + 82801AA 2413 + 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) 6300ESB 25A4 @@ -74,6 +74,13 @@ #define SMBHSTCFG_SMB_SMI_EN 2 #define SMBHSTCFG_I2C_EN 4 +/* Auxillary control register bits, ICH4+ only */ +#define SMBAUXCTL_CRC 1 +#define SMBAUXCTL_E32B 2 + +/* kill bit for SMBHSTCNT */ +#define SMBHSTCNT_KILL 2 + /* Other settings */ #define MAX_TIMEOUT 100 #define ENABLE_INT9 0 /* set to 0x01 to enable - untested */ @@ -91,10 +98,15 @@ #define I801_START 0x40 #define I801_PEC_EN 0x80 /* ICH4 only */ - -static int i801_transaction(void); -static int i801_block_transaction(union i2c_smbus_data *data, char read_write, - int command, int hwpec); +/* I801 Hosts Status register bits */ +#define SMBHSTSTS_BYTE_DONE 0x80 +#define SMBHSTSTS_INUSE_STS 0x40 +#define SMBHSTSTS_SMBALERT_STS 0x20 +#define SMBHSTSTS_FAILED 0x10 +#define SMBHSTSTS_BUS_ERR 0x08 +#define SMBHSTSTS_DEV_ERR 0x04 +#define SMBHSTSTS_INTR 0x02 +#define SMBHSTSTS_HOST_BUSY 0x01 static unsigned long i801_smba; static unsigned char i801_original_hstcfg; @@ -133,27 +145,32 @@ static int i801_transaction(void) do { msleep(1); temp = inb_p(SMBHSTSTS); - } while ((temp & 0x01) && (timeout++ < MAX_TIMEOUT)); + } while ((temp & SMBHSTSTS_HOST_BUSY) && (timeout++ < MAX_TIMEOUT)); /* If the SMBus is still busy, we give up */ if (timeout >= MAX_TIMEOUT) { dev_dbg(&I801_dev->dev, "SMBus Timeout!\n"); result = -1; + /* try to stop the current command */ + dev_dbg(&I801_dev->dev, "Terminating the current operation\n"); + outb_p(inb_p(SMBHSTCNT) | SMBHSTCNT_KILL, SMBHSTCNT); + msleep(1); + outb_p(inb_p(SMBHSTCNT) & (~SMBHSTCNT_KILL), SMBHSTCNT); } - if (temp & 0x10) { + if (temp & SMBHSTSTS_FAILED) { result = -1; dev_dbg(&I801_dev->dev, "Error: Failed bus transaction\n"); } - if (temp & 0x08) { + if (temp & SMBHSTSTS_BUS_ERR) { result = -1; dev_err(&I801_dev->dev, "Bus collision! SMBus may be locked " "until next hard reset. (sorry!)\n"); /* Clock stops and slave is stuck in mid-transmission */ } - if (temp & 0x04) { + if (temp & SMBHSTSTS_DEV_ERR) { result = -1; dev_dbg(&I801_dev->dev, "Error: no response!\n"); } @@ -172,6 +189,24 @@ static int i801_transaction(void) return result; } +/* wait for INTR bit as advised by Intel */ +static void i801_wait_hwpec(void) +{ + int timeout = 0; + int temp; + + do { + msleep(1); + temp = inb_p(SMBHSTSTS); + } while ((!(temp & SMBHSTSTS_INTR)) + && (timeout++ < MAX_TIMEOUT)); + + if (timeout >= MAX_TIMEOUT) { + dev_dbg(&I801_dev->dev, "PEC Timeout!\n"); + } + 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) @@ -227,13 +262,13 @@ static int i801_block_transaction(union i2c_smbus_data *data, char read_write, /* Make sure the SMBus host is ready to start transmitting */ temp = inb_p(SMBHSTSTS); if (i == 1) { - /* Erronenous conditions before transaction: + /* Erronenous conditions before transaction: * Byte_Done, Failed, Bus_Err, Dev_Err, Intr, Host_Busy */ - errmask=0x9f; + errmask = 0x9f; } else { - /* Erronenous conditions during transaction: + /* Erronenous conditions during transaction: * Failed, Bus_Err, Dev_Err, Intr */ - errmask=0x1e; + errmask = 0x1e; } if (temp & errmask) { dev_dbg(&I801_dev->dev, "SMBus busy (%02x). " @@ -243,7 +278,7 @@ static int i801_block_transaction(union i2c_smbus_data *data, char read_write, dev_err(&I801_dev->dev, "Reset failed! (%02x)\n", temp); result = -1; - goto END; + goto END; } if (i != 1) { /* if die in middle of block transaction, fail */ @@ -261,33 +296,40 @@ static int i801_block_transaction(union i2c_smbus_data *data, char read_write, msleep(1); temp = inb_p(SMBHSTSTS); } - while ((!(temp & 0x80)) - && (timeout++ < MAX_TIMEOUT)); + while ((!(temp & SMBHSTSTS_BYTE_DONE)) + && (timeout++ < MAX_TIMEOUT)); /* If the SMBus is still busy, we give up */ if (timeout >= MAX_TIMEOUT) { + /* try to stop the current command */ + dev_dbg(&I801_dev->dev, "Terminating the current " + "operation\n"); + outb_p(inb_p(SMBHSTCNT) | SMBHSTCNT_KILL, SMBHSTCNT); + msleep(1); + outb_p(inb_p(SMBHSTCNT) & (~SMBHSTCNT_KILL), + SMBHSTCNT); result = -1; dev_dbg(&I801_dev->dev, "SMBus Timeout!\n"); } - if (temp & 0x10) { + if (temp & SMBHSTSTS_FAILED) { result = -1; dev_dbg(&I801_dev->dev, "Error: Failed bus transaction\n"); - } else if (temp & 0x08) { + } else if (temp & SMBHSTSTS_BUS_ERR) { result = -1; dev_err(&I801_dev->dev, "Bus collision!\n"); - } else if (temp & 0x04) { + } else if (temp & SMBHSTSTS_DEV_ERR) { result = -1; dev_dbg(&I801_dev->dev, "Error: no response!\n"); } if (i == 1 && read_write == I2C_SMBUS_READ) { len = inb_p(SMBHSTDAT0); - if (len < 1) - len = 1; - if (len > 32) - len = 32; + if (len < 1 || len > I2C_SMBUS_BLOCK_MAX) { + result = -1; + goto END; + } data->block[0] = len; } @@ -313,20 +355,9 @@ static int i801_block_transaction(union i2c_smbus_data *data, char read_write, goto END; } - if (hwpec) { - /* wait for INTR bit as advised by Intel */ - timeout = 0; - do { - msleep(1); - temp = inb_p(SMBHSTSTS); - } while ((!(temp & 0x02)) - && (timeout++ < MAX_TIMEOUT)); + if (hwpec) + i801_wait_hwpec(); - if (timeout >= MAX_TIMEOUT) { - dev_dbg(&I801_dev->dev, "PEC Timeout!\n"); - } - outb_p(temp, SMBHSTSTS); - } result = 0; END: if (command == I2C_SMBUS_I2C_BLOCK_DATA) { @@ -393,7 +424,10 @@ static s32 i801_access(struct i2c_adapter * adap, u16 addr, return -1; } - outb_p(hwpec, SMBAUXCTL); /* enable/disable hardware PEC */ + if (hwpec) /* enable/disable hardware PEC */ + outb_p(inb_p(SMBAUXCTL) | SMBAUXCTL_CRC, SMBAUXCTL); + else + outb_p(inb_p(SMBAUXCTL) & (~SMBAUXCTL_CRC), SMBAUXCTL); if(block) ret = i801_block_transaction(data, read_write, size, hwpec); @@ -405,7 +439,7 @@ static s32 i801_access(struct i2c_adapter * adap, u16 addr, /* Some BIOSes don't like it when PEC is enabled at reboot or resume time, so we forcibly disable it after every transaction. */ if (hwpec) - outb_p(0, SMBAUXCTL); + outb_p(inb_p(SMBAUXCTL) & (~SMBAUXCTL_CRC), SMBAUXCTL); if(block) return ret; |