i2c-i801: Implement I2C block read support
I2C block read is supported since the ICH5. I couldn't get it to work using the block buffer, so it's using the old-style byte-by-byte mode for now. Note: I'm also updating the driver author... The i2c-i801 driver was really written by Mark Studebaker, even though he based his work on the i2c-piix4 driver which was written by Philip Edelbrock. Signed-off-by: Jean Delvare <khali@linux-fr.org>
This commit is contained in:
committed by
Jean Delvare
parent
a0921b6c07
commit
6342064cad
@@ -17,9 +17,8 @@ Supported adapters:
|
|||||||
Datasheets: Publicly available at the Intel website
|
Datasheets: Publicly available at the Intel website
|
||||||
|
|
||||||
Authors:
|
Authors:
|
||||||
Frodo Looijaard <frodol@dds.nl>,
|
|
||||||
Philip Edelbrock <phil@netroedge.com>,
|
|
||||||
Mark Studebaker <mdsxyz123@yahoo.com>
|
Mark Studebaker <mdsxyz123@yahoo.com>
|
||||||
|
Jean Delvare <khali@linux-fr.org>
|
||||||
|
|
||||||
|
|
||||||
Module Parameters
|
Module Parameters
|
||||||
@@ -62,7 +61,7 @@ Not supported.
|
|||||||
I2C Block Read Support
|
I2C Block Read Support
|
||||||
----------------------
|
----------------------
|
||||||
|
|
||||||
Not supported at the moment.
|
I2C block read is supported on the 82801EB (ICH5) and later chips.
|
||||||
|
|
||||||
|
|
||||||
SMBus 2.0 Support
|
SMBus 2.0 Support
|
||||||
|
@@ -4,6 +4,7 @@
|
|||||||
Copyright (c) 1998 - 2002 Frodo Looijaard <frodol@dds.nl>,
|
Copyright (c) 1998 - 2002 Frodo Looijaard <frodol@dds.nl>,
|
||||||
Philip Edelbrock <phil@netroedge.com>, and Mark D. Studebaker
|
Philip Edelbrock <phil@netroedge.com>, and Mark D. Studebaker
|
||||||
<mdsxyz123@yahoo.com>
|
<mdsxyz123@yahoo.com>
|
||||||
|
Copyright (C) 2007 Jean Delvare <khali@linux-fr.org>
|
||||||
|
|
||||||
This program is free software; you can redistribute it and/or modify
|
This program is free software; you can redistribute it and/or modify
|
||||||
it under the terms of the GNU General Public License as published by
|
it under the terms of the GNU General Public License as published by
|
||||||
@@ -46,7 +47,7 @@
|
|||||||
Hardware PEC yes
|
Hardware PEC yes
|
||||||
Block buffer yes
|
Block buffer yes
|
||||||
Block process call transaction no
|
Block process call transaction no
|
||||||
I2C block read transaction no
|
I2C block read transaction yes (doesn't use the block buffer)
|
||||||
|
|
||||||
See the file Documentation/i2c/busses/i2c-i801 for details.
|
See the file Documentation/i2c/busses/i2c-i801 for details.
|
||||||
*/
|
*/
|
||||||
@@ -102,9 +103,9 @@
|
|||||||
#define I801_WORD_DATA 0x0C
|
#define I801_WORD_DATA 0x0C
|
||||||
#define I801_PROC_CALL 0x10 /* unimplemented */
|
#define I801_PROC_CALL 0x10 /* unimplemented */
|
||||||
#define I801_BLOCK_DATA 0x14
|
#define I801_BLOCK_DATA 0x14
|
||||||
#define I801_I2C_BLOCK_DATA 0x18 /* unimplemented */
|
#define I801_I2C_BLOCK_DATA 0x18 /* ICH5 and later */
|
||||||
#define I801_BLOCK_LAST 0x34
|
#define I801_BLOCK_LAST 0x34
|
||||||
#define I801_I2C_BLOCK_LAST 0x38 /* unimplemented */
|
#define I801_I2C_BLOCK_LAST 0x38 /* ICH5 and later */
|
||||||
#define I801_START 0x40
|
#define I801_START 0x40
|
||||||
#define I801_PEC_EN 0x80 /* ICH3 and later */
|
#define I801_PEC_EN 0x80 /* ICH3 and later */
|
||||||
|
|
||||||
@@ -256,7 +257,8 @@ static int i801_block_transaction_by_block(union i2c_smbus_data *data,
|
|||||||
}
|
}
|
||||||
|
|
||||||
static int i801_block_transaction_byte_by_byte(union i2c_smbus_data *data,
|
static int i801_block_transaction_byte_by_byte(union i2c_smbus_data *data,
|
||||||
char read_write, int hwpec)
|
char read_write, int command,
|
||||||
|
int hwpec)
|
||||||
{
|
{
|
||||||
int i, len;
|
int i, len;
|
||||||
int smbcmd;
|
int smbcmd;
|
||||||
@@ -273,16 +275,24 @@ static int i801_block_transaction_byte_by_byte(union i2c_smbus_data *data,
|
|||||||
}
|
}
|
||||||
|
|
||||||
for (i = 1; i <= len; i++) {
|
for (i = 1; i <= len; i++) {
|
||||||
if (i == len && read_write == I2C_SMBUS_READ)
|
if (i == len && read_write == I2C_SMBUS_READ) {
|
||||||
smbcmd = I801_BLOCK_LAST;
|
if (command == I2C_SMBUS_I2C_BLOCK_DATA)
|
||||||
else
|
smbcmd = I801_I2C_BLOCK_LAST;
|
||||||
smbcmd = I801_BLOCK_DATA;
|
else
|
||||||
|
smbcmd = I801_BLOCK_LAST;
|
||||||
|
} else {
|
||||||
|
if (command == I2C_SMBUS_I2C_BLOCK_DATA
|
||||||
|
&& read_write == I2C_SMBUS_READ)
|
||||||
|
smbcmd = I801_I2C_BLOCK_DATA;
|
||||||
|
else
|
||||||
|
smbcmd = I801_BLOCK_DATA;
|
||||||
|
}
|
||||||
outb_p(smbcmd | ENABLE_INT9, SMBHSTCNT);
|
outb_p(smbcmd | ENABLE_INT9, SMBHSTCNT);
|
||||||
|
|
||||||
dev_dbg(&I801_dev->dev, "Block (pre %d): CNT=%02x, CMD=%02x, "
|
dev_dbg(&I801_dev->dev, "Block (pre %d): CNT=%02x, CMD=%02x, "
|
||||||
"ADD=%02x, DAT0=%02x, BLKDAT=%02x\n", i,
|
"ADD=%02x, DAT0=%02x, DAT1=%02x, BLKDAT=%02x\n", i,
|
||||||
inb_p(SMBHSTCNT), inb_p(SMBHSTCMD), inb_p(SMBHSTADD),
|
inb_p(SMBHSTCNT), inb_p(SMBHSTCMD), inb_p(SMBHSTADD),
|
||||||
inb_p(SMBHSTDAT0), inb_p(SMBBLKDAT));
|
inb_p(SMBHSTDAT0), inb_p(SMBHSTDAT1), inb_p(SMBBLKDAT));
|
||||||
|
|
||||||
/* Make sure the SMBus host is ready to start transmitting */
|
/* Make sure the SMBus host is ready to start transmitting */
|
||||||
temp = inb_p(SMBHSTSTS);
|
temp = inb_p(SMBHSTSTS);
|
||||||
@@ -346,7 +356,8 @@ static int i801_block_transaction_byte_by_byte(union i2c_smbus_data *data,
|
|||||||
dev_dbg(&I801_dev->dev, "Error: no response!\n");
|
dev_dbg(&I801_dev->dev, "Error: no response!\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (i == 1 && read_write == I2C_SMBUS_READ) {
|
if (i == 1 && read_write == I2C_SMBUS_READ
|
||||||
|
&& command != I2C_SMBUS_I2C_BLOCK_DATA) {
|
||||||
len = inb_p(SMBHSTDAT0);
|
len = inb_p(SMBHSTDAT0);
|
||||||
if (len < 1 || len > I2C_SMBUS_BLOCK_MAX)
|
if (len < 1 || len > I2C_SMBUS_BLOCK_MAX)
|
||||||
return -1;
|
return -1;
|
||||||
@@ -367,9 +378,9 @@ static int i801_block_transaction_byte_by_byte(union i2c_smbus_data *data,
|
|||||||
temp);
|
temp);
|
||||||
}
|
}
|
||||||
dev_dbg(&I801_dev->dev, "Block (post %d): CNT=%02x, CMD=%02x, "
|
dev_dbg(&I801_dev->dev, "Block (post %d): CNT=%02x, CMD=%02x, "
|
||||||
"ADD=%02x, DAT0=%02x, BLKDAT=%02x\n", i,
|
"ADD=%02x, DAT0=%02x, DAT1=%02x, BLKDAT=%02x\n", i,
|
||||||
inb_p(SMBHSTCNT), inb_p(SMBHSTCMD), inb_p(SMBHSTADD),
|
inb_p(SMBHSTCNT), inb_p(SMBHSTCMD), inb_p(SMBHSTADD),
|
||||||
inb_p(SMBHSTDAT0), inb_p(SMBBLKDAT));
|
inb_p(SMBHSTDAT0), inb_p(SMBHSTDAT1), inb_p(SMBBLKDAT));
|
||||||
|
|
||||||
if (result < 0)
|
if (result < 0)
|
||||||
return result;
|
return result;
|
||||||
@@ -398,34 +409,38 @@ static int i801_block_transaction(union i2c_smbus_data *data, char read_write,
|
|||||||
pci_read_config_byte(I801_dev, SMBHSTCFG, &hostc);
|
pci_read_config_byte(I801_dev, SMBHSTCFG, &hostc);
|
||||||
pci_write_config_byte(I801_dev, SMBHSTCFG,
|
pci_write_config_byte(I801_dev, SMBHSTCFG,
|
||||||
hostc | SMBHSTCFG_I2C_EN);
|
hostc | SMBHSTCFG_I2C_EN);
|
||||||
} else {
|
} else if (!(i801_features & FEATURE_I2C_BLOCK_READ)) {
|
||||||
dev_err(&I801_dev->dev,
|
dev_err(&I801_dev->dev,
|
||||||
"I2C_SMBUS_I2C_BLOCK_READ not DB!\n");
|
"I2C block read is unsupported!\n");
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (read_write == I2C_SMBUS_WRITE) {
|
if (read_write == I2C_SMBUS_WRITE
|
||||||
|
|| command == I2C_SMBUS_I2C_BLOCK_DATA) {
|
||||||
if (data->block[0] < 1)
|
if (data->block[0] < 1)
|
||||||
data->block[0] = 1;
|
data->block[0] = 1;
|
||||||
if (data->block[0] > I2C_SMBUS_BLOCK_MAX)
|
if (data->block[0] > I2C_SMBUS_BLOCK_MAX)
|
||||||
data->block[0] = I2C_SMBUS_BLOCK_MAX;
|
data->block[0] = I2C_SMBUS_BLOCK_MAX;
|
||||||
} else {
|
} else {
|
||||||
data->block[0] = 32; /* max for reads */
|
data->block[0] = 32; /* max for SMBus block reads */
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((i801_features & FEATURE_BLOCK_BUFFER)
|
if ((i801_features & FEATURE_BLOCK_BUFFER)
|
||||||
|
&& !(command == I2C_SMBUS_I2C_BLOCK_DATA
|
||||||
|
&& read_write == I2C_SMBUS_READ)
|
||||||
&& i801_set_block_buffer_mode() == 0)
|
&& i801_set_block_buffer_mode() == 0)
|
||||||
result = i801_block_transaction_by_block(data, read_write,
|
result = i801_block_transaction_by_block(data, read_write,
|
||||||
hwpec);
|
hwpec);
|
||||||
else
|
else
|
||||||
result = i801_block_transaction_byte_by_byte(data, read_write,
|
result = i801_block_transaction_byte_by_byte(data, read_write,
|
||||||
hwpec);
|
command, hwpec);
|
||||||
|
|
||||||
if (result == 0 && hwpec)
|
if (result == 0 && hwpec)
|
||||||
i801_wait_hwpec();
|
i801_wait_hwpec();
|
||||||
|
|
||||||
if (command == I2C_SMBUS_I2C_BLOCK_DATA) {
|
if (command == I2C_SMBUS_I2C_BLOCK_DATA
|
||||||
|
&& read_write == I2C_SMBUS_WRITE) {
|
||||||
/* 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);
|
||||||
}
|
}
|
||||||
@@ -477,12 +492,23 @@ static s32 i801_access(struct i2c_adapter * adap, u16 addr,
|
|||||||
xact = I801_WORD_DATA;
|
xact = I801_WORD_DATA;
|
||||||
break;
|
break;
|
||||||
case I2C_SMBUS_BLOCK_DATA:
|
case I2C_SMBUS_BLOCK_DATA:
|
||||||
case I2C_SMBUS_I2C_BLOCK_DATA:
|
|
||||||
outb_p(((addr & 0x7f) << 1) | (read_write & 0x01),
|
outb_p(((addr & 0x7f) << 1) | (read_write & 0x01),
|
||||||
SMBHSTADD);
|
SMBHSTADD);
|
||||||
outb_p(command, SMBHSTCMD);
|
outb_p(command, SMBHSTCMD);
|
||||||
block = 1;
|
block = 1;
|
||||||
break;
|
break;
|
||||||
|
case I2C_SMBUS_I2C_BLOCK_DATA:
|
||||||
|
/* NB: page 240 of ICH5 datasheet shows that the R/#W
|
||||||
|
* bit should be cleared here, even when reading */
|
||||||
|
outb_p((addr & 0x7f) << 1, SMBHSTADD);
|
||||||
|
if (read_write == I2C_SMBUS_READ) {
|
||||||
|
/* NB: page 240 of ICH5 datasheet also shows
|
||||||
|
* that DATA1 is the cmd field when reading */
|
||||||
|
outb_p(command, SMBHSTDAT1);
|
||||||
|
} else
|
||||||
|
outb_p(command, SMBHSTCMD);
|
||||||
|
block = 1;
|
||||||
|
break;
|
||||||
case I2C_SMBUS_PROC_CALL:
|
case I2C_SMBUS_PROC_CALL:
|
||||||
default:
|
default:
|
||||||
dev_err(&I801_dev->dev, "Unsupported transaction %d\n", size);
|
dev_err(&I801_dev->dev, "Unsupported transaction %d\n", size);
|
||||||
@@ -531,7 +557,9 @@ static u32 i801_func(struct i2c_adapter *adapter)
|
|||||||
return I2C_FUNC_SMBUS_QUICK | I2C_FUNC_SMBUS_BYTE |
|
return I2C_FUNC_SMBUS_QUICK | I2C_FUNC_SMBUS_BYTE |
|
||||||
I2C_FUNC_SMBUS_BYTE_DATA | I2C_FUNC_SMBUS_WORD_DATA |
|
I2C_FUNC_SMBUS_BYTE_DATA | I2C_FUNC_SMBUS_WORD_DATA |
|
||||||
I2C_FUNC_SMBUS_BLOCK_DATA | I2C_FUNC_SMBUS_WRITE_I2C_BLOCK |
|
I2C_FUNC_SMBUS_BLOCK_DATA | I2C_FUNC_SMBUS_WRITE_I2C_BLOCK |
|
||||||
((i801_features & FEATURE_SMBUS_PEC) ? I2C_FUNC_SMBUS_PEC : 0);
|
((i801_features & FEATURE_SMBUS_PEC) ? I2C_FUNC_SMBUS_PEC : 0) |
|
||||||
|
((i801_features & FEATURE_I2C_BLOCK_READ) ?
|
||||||
|
I2C_FUNC_SMBUS_READ_I2C_BLOCK : 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
static const struct i2c_algorithm smbus_algorithm = {
|
static const struct i2c_algorithm smbus_algorithm = {
|
||||||
@@ -573,7 +601,6 @@ static int __devinit i801_probe(struct pci_dev *dev, const struct pci_device_id
|
|||||||
I801_dev = dev;
|
I801_dev = dev;
|
||||||
i801_features = 0;
|
i801_features = 0;
|
||||||
switch (dev->device) {
|
switch (dev->device) {
|
||||||
case PCI_DEVICE_ID_INTEL_82801DB_3:
|
|
||||||
case PCI_DEVICE_ID_INTEL_82801EB_3:
|
case PCI_DEVICE_ID_INTEL_82801EB_3:
|
||||||
case PCI_DEVICE_ID_INTEL_ESB_4:
|
case PCI_DEVICE_ID_INTEL_ESB_4:
|
||||||
case PCI_DEVICE_ID_INTEL_ICH6_16:
|
case PCI_DEVICE_ID_INTEL_ICH6_16:
|
||||||
@@ -581,6 +608,9 @@ static int __devinit i801_probe(struct pci_dev *dev, const struct pci_device_id
|
|||||||
case PCI_DEVICE_ID_INTEL_ESB2_17:
|
case PCI_DEVICE_ID_INTEL_ESB2_17:
|
||||||
case PCI_DEVICE_ID_INTEL_ICH8_5:
|
case PCI_DEVICE_ID_INTEL_ICH8_5:
|
||||||
case PCI_DEVICE_ID_INTEL_ICH9_6:
|
case PCI_DEVICE_ID_INTEL_ICH9_6:
|
||||||
|
i801_features |= FEATURE_I2C_BLOCK_READ;
|
||||||
|
/* fall through */
|
||||||
|
case PCI_DEVICE_ID_INTEL_82801DB_3:
|
||||||
case PCI_DEVICE_ID_INTEL_TOLAPAI_1:
|
case PCI_DEVICE_ID_INTEL_TOLAPAI_1:
|
||||||
i801_features |= FEATURE_SMBUS_PEC;
|
i801_features |= FEATURE_SMBUS_PEC;
|
||||||
i801_features |= FEATURE_BLOCK_BUFFER;
|
i801_features |= FEATURE_BLOCK_BUFFER;
|
||||||
@@ -698,9 +728,8 @@ static void __exit i2c_i801_exit(void)
|
|||||||
pci_unregister_driver(&i801_driver);
|
pci_unregister_driver(&i801_driver);
|
||||||
}
|
}
|
||||||
|
|
||||||
MODULE_AUTHOR ("Frodo Looijaard <frodol@dds.nl>, "
|
MODULE_AUTHOR("Mark D. Studebaker <mdsxyz123@yahoo.com>, "
|
||||||
"Philip Edelbrock <phil@netroedge.com>, "
|
"Jean Delvare <khali@linux-fr.org>");
|
||||||
"and Mark D. Studebaker <mdsxyz123@yahoo.com>");
|
|
||||||
MODULE_DESCRIPTION("I801 SMBus driver");
|
MODULE_DESCRIPTION("I801 SMBus driver");
|
||||||
MODULE_LICENSE("GPL");
|
MODULE_LICENSE("GPL");
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user