Commit 7edcb9ab authored by Oleg Ryjkov's avatar Oleg Ryjkov Committed by Jean Delvare

i2c-i801: Use the internal 32-byte buffer on ICH4+

Add an ability to utilize the internal SRAM buffer on ICH4
and newer host controllers to speed up execution of block operations.

I've split the code so that it is more clear which block transaction is
performed.

First of all the host controller's type is identified. isich4 is set when
we think that the controller has the internal buffer. Then, before every
block transaction, if isich4 is set, we attempt to enable the E32B bit in
SMBAUXCTL register.
Signed-off-by: default avatarOleg Ryjkov <olegr@google.com>
Signed-off-by: default avatarJean Delvare <khali@linux-fr.org>
parent ca8b9e32
...@@ -5,8 +5,8 @@ Supported adapters: ...@@ -5,8 +5,8 @@ Supported adapters:
'810' and '810E' chipsets) '810' and '810E' chipsets)
* Intel 82801BA (ICH2 - part of the '815E' chipset) * Intel 82801BA (ICH2 - part of the '815E' chipset)
* Intel 82801CA/CAM (ICH3) * Intel 82801CA/CAM (ICH3)
* Intel 82801DB (ICH4) (HW PEC supported, 32 byte buffer not supported) * Intel 82801DB (ICH4) (HW PEC supported)
* Intel 82801EB/ER (ICH5) (HW PEC supported, 32 byte buffer not supported) * Intel 82801EB/ER (ICH5) (HW PEC supported)
* Intel 6300ESB * Intel 6300ESB
* Intel 82801FB/FR/FW/FRW (ICH6) * Intel 82801FB/FR/FW/FRW (ICH6)
* Intel 82801G (ICH7) * Intel 82801G (ICH7)
......
...@@ -26,8 +26,8 @@ ...@@ -26,8 +26,8 @@
82801AB 2423 82801AB 2423
82801BA 2443 82801BA 2443
82801CA/CAM 2483 82801CA/CAM 2483
82801DB 24C3 (HW PEC supported, 32 byte buffer not supported) 82801DB 24C3 (HW PEC supported)
82801EB 24D3 (HW PEC supported, 32 byte buffer not supported) 82801EB 24D3 (HW PEC supported)
6300ESB 25A4 6300ESB 25A4
ICH6 266A ICH6 266A
ICH7 27DA ICH7 27DA
...@@ -114,7 +114,7 @@ static struct pci_driver i801_driver; ...@@ -114,7 +114,7 @@ static struct pci_driver i801_driver;
static struct pci_dev *I801_dev; static struct pci_dev *I801_dev;
static int isich4; static int isich4;
static int i801_transaction(void) static int i801_transaction(int xact)
{ {
int temp; int temp;
int result = 0; int result = 0;
...@@ -139,7 +139,9 @@ static int i801_transaction(void) ...@@ -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! */ /* We will always wait for a fraction of a second! */
do { do {
...@@ -207,44 +209,52 @@ static void i801_wait_hwpec(void) ...@@ -207,44 +209,52 @@ static void i801_wait_hwpec(void)
outb_p(temp, SMBHSTSTS); outb_p(temp, SMBHSTSTS);
} }
/* All-inclusive block transaction function */ static int i801_block_transaction_by_block(union i2c_smbus_data *data,
static int i801_block_transaction(union i2c_smbus_data *data, char read_write, char read_write, int hwpec)
int command, 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 i, len;
int smbcmd; int smbcmd;
int temp; int temp;
int result = 0; int result = 0;
int timeout; int timeout;
unsigned char hostc, errmask; unsigned char errmask;
if (command == I2C_SMBUS_I2C_BLOCK_DATA) { len = data->block[0];
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 (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(len, SMBHSTDAT0);
outb_p(data->block[1], SMBBLKDAT); 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++) { for (i = 1; i <= len; i++) {
...@@ -277,14 +287,11 @@ static int i801_block_transaction(union i2c_smbus_data *data, char read_write, ...@@ -277,14 +287,11 @@ static int i801_block_transaction(union i2c_smbus_data *data, char read_write,
if (((temp = inb_p(SMBHSTSTS)) & errmask) != 0x00) { if (((temp = inb_p(SMBHSTSTS)) & errmask) != 0x00) {
dev_err(&I801_dev->dev, dev_err(&I801_dev->dev,
"Reset failed! (%02x)\n", temp); "Reset failed! (%02x)\n", temp);
result = -1; return -1;
goto END;
} }
if (i != 1) { if (i != 1)
/* if die in middle of block transaction, fail */ /* if die in middle of block transaction, fail */
result = -1; return -1;
goto END;
}
} }
if (i == 1) if (i == 1)
...@@ -326,10 +333,8 @@ static int i801_block_transaction(union i2c_smbus_data *data, char read_write, ...@@ -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) { if (i == 1 && read_write == I2C_SMBUS_READ) {
len = inb_p(SMBHSTDAT0); len = inb_p(SMBHSTDAT0);
if (len < 1 || len > I2C_SMBUS_BLOCK_MAX) { if (len < 1 || len > I2C_SMBUS_BLOCK_MAX)
result = -1; return -1;
goto END;
}
data->block[0] = len; data->block[0] = len;
} }
...@@ -352,14 +357,58 @@ static int i801_block_transaction(union i2c_smbus_data *data, char read_write, ...@@ -352,14 +357,58 @@ static int i801_block_transaction(union i2c_smbus_data *data, char read_write,
inb_p(SMBHSTDAT0), inb_p(SMBBLKDAT)); inb_p(SMBHSTDAT0), inb_p(SMBBLKDAT));
if (result < 0) 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(); i801_wait_hwpec();
result = 0;
END:
if (command == I2C_SMBUS_I2C_BLOCK_DATA) { if (command == I2C_SMBUS_I2C_BLOCK_DATA) {
/* restore saved configuration register value */ /* restore saved configuration register value */
pci_write_config_byte(I801_dev, SMBHSTCFG, hostc); pci_write_config_byte(I801_dev, SMBHSTCFG, hostc);
...@@ -431,15 +480,15 @@ static s32 i801_access(struct i2c_adapter * adap, u16 addr, ...@@ -431,15 +480,15 @@ static s32 i801_access(struct i2c_adapter * adap, u16 addr,
if(block) if(block)
ret = i801_block_transaction(data, read_write, size, hwpec); ret = i801_block_transaction(data, read_write, size, hwpec);
else { else
outb_p(xact | ENABLE_INT9, SMBHSTCNT); ret = i801_transaction(xact | ENABLE_INT9);
ret = i801_transaction();
}
/* Some BIOSes don't like it when PEC is enabled at reboot or resume /* 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) if (hwpec)
outb_p(inb_p(SMBAUXCTL) & (~SMBAUXCTL_CRC), SMBAUXCTL); outb_p(inb_p(SMBAUXCTL) & ~(SMBAUXCTL_CRC | SMBAUXCTL_E32B),
SMBAUXCTL);
if(block) if(block)
return ret; return ret;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment