mirror of
https://github.com/lm-sensors/lm-sensors.git
synced 2026-11-07 16:01:34 +00:00
ich4 software pec support. Still cleanups to be done but enough for ARP.
git-svn-id: http://lm-sensors.org/svn/lm-sensors/trunk@1391 7894878c-1315-0410-8ee3-d5d059ff63e0
This commit is contained in:
1 parent
7cdb5c4439
commit
bf992c322b
2 files changed
+111
-49
No files matched your search
+33
-1
@@ -51,6 +51,38 @@ at least in the SMBus controller.
|
||||
See the file i2c-piix4 for some additional information.
|
||||
|
||||
|
||||
I2C Block Read Support
|
||||
----------------------
|
||||
|
||||
The devices support a special 2-byte address I2C block read.
|
||||
The driver and the kernel i2c protocol stack do not yet
|
||||
support this.
|
||||
|
||||
|
||||
SMBus 2.0 Support
|
||||
-----------------
|
||||
|
||||
The 82801DB (ICH4) supports several SMBus 2.0 features.
|
||||
Kernel and driver support are as follows:
|
||||
|
||||
Feature Kernel Driver
|
||||
Slave mode no no
|
||||
Host notify no no
|
||||
Block PEC yes yes
|
||||
Hardware PEC yes no
|
||||
|
||||
|
||||
Other ICH4 Features
|
||||
-------------------
|
||||
|
||||
The following additional ICH4 features are not yet supported:
|
||||
|
||||
32 Byte buffer
|
||||
|
||||
|
||||
**********************
|
||||
The lm_sensors project gratefully acknowledges the support of
|
||||
Texas Instruments in the development of this driver.
|
||||
Texas Instruments in the initial development of this driver.
|
||||
|
||||
The lm_sensors project gratefully acknowledges the support of
|
||||
Intel in the development of SMBus 2.0 / ICH4 features of this driver.
|
||||
+78
-48
@@ -48,10 +48,15 @@
|
||||
#include <linux/i2c.h>
|
||||
#include "version.h"
|
||||
|
||||
#define DEBUG 1
|
||||
#ifdef MODULE_LICENSE
|
||||
MODULE_LICENSE("GPL");
|
||||
#endif
|
||||
|
||||
#ifdef I2C_FUNC_SMBUS_BLOCK_DATA_PEC
|
||||
#define HAVE_PEC
|
||||
#endif
|
||||
|
||||
#ifndef PCI_DEVICE_ID_INTEL_82801AA_3
|
||||
#define PCI_DEVICE_ID_INTEL_82801AA_3 0x2413
|
||||
#endif
|
||||
@@ -79,6 +84,9 @@ static int supported[] = {PCI_DEVICE_ID_INTEL_82801AA_3,
|
||||
#define SMBHSTDAT0 (5 + i801_smba)
|
||||
#define SMBHSTDAT1 (6 + i801_smba)
|
||||
#define SMBBLKDAT (7 + i801_smba)
|
||||
#define SMBPEC (8 + i801_smba) /* ICH4 only */
|
||||
#define SMBAUXSTS (12 + i801_smba) /* ICH4 only */
|
||||
#define SMBAUXCTL (13 + i801_smba) /* ICH4 only */
|
||||
|
||||
/* PCI Address Constants */
|
||||
#define SMBBA 0x020
|
||||
@@ -91,8 +99,8 @@ static int supported[] = {PCI_DEVICE_ID_INTEL_82801AA_3,
|
||||
#define SMBHSTCFG_I2C_EN 4
|
||||
|
||||
/* Other settings */
|
||||
#define MAX_TIMEOUT 500
|
||||
#define ENABLE_INT9 0
|
||||
#define MAX_TIMEOUT 100
|
||||
#define ENABLE_INT9 0 /* set to 0x01 to enable - untested */
|
||||
|
||||
/* I801 command constants */
|
||||
#define I801_QUICK 0x00
|
||||
@@ -103,6 +111,8 @@ static int supported[] = {PCI_DEVICE_ID_INTEL_82801AA_3,
|
||||
#define I801_I2C_BLOCK_DATA 0x18 /* unimplemented */
|
||||
#define I801_BLOCK_LAST 0x34
|
||||
#define I801_I2C_BLOCK_LAST 0x38 /* unimplemented */
|
||||
#define I801_START 0x40
|
||||
#define I801_PEC_EN 0x80 /* ICH4 only */
|
||||
|
||||
/* insmod parameters */
|
||||
|
||||
@@ -169,7 +179,7 @@ static struct i2c_adapter i801_adapter = {
|
||||
static int __initdata i801_initialized;
|
||||
static unsigned short i801_smba = 0;
|
||||
static struct pci_dev *I801_dev = NULL;
|
||||
|
||||
static int isich4 = 0;
|
||||
|
||||
/* Detect whether a I801 can be found, and initialize it, where necessary.
|
||||
Note the differences between kernels with the old PCI BIOS interface and
|
||||
@@ -207,6 +217,7 @@ int i801_setup(void)
|
||||
error_return = -ENODEV;
|
||||
goto END;
|
||||
}
|
||||
isich4 = *num == PCI_DEVICE_ID_INTEL_82801DB_SMBUS;
|
||||
|
||||
/* Determine the address of the SMBus areas */
|
||||
if (force_addr) {
|
||||
@@ -217,7 +228,7 @@ int i801_setup(void)
|
||||
i801_smba &= 0xfff0;
|
||||
}
|
||||
|
||||
if (check_region(i801_smba, 8)) {
|
||||
if (check_region(i801_smba, (isich4 ? 16 : 8))) {
|
||||
printk
|
||||
(KERN_ERR "i2c-i801.o: I801_smb region 0x%x already in use!\n",
|
||||
i801_smba);
|
||||
@@ -237,9 +248,7 @@ int i801_setup(void)
|
||||
"address %04x!\n", i801_smba);
|
||||
} else if ((temp & 1) == 0) {
|
||||
if (force) {
|
||||
/* This should never need to be done, but has been noted that
|
||||
many Dell machines have the SMBus interface on the PIIX4
|
||||
disabled!? NOTE: This assumes I/O space and other allocations WERE
|
||||
/* NOTE: This assumes I/O space and other allocations WERE
|
||||
done by the Bios! Don't complain if your hardware does weird
|
||||
things after enabling this. :') Check for Bios updates before
|
||||
resorting to this. */
|
||||
@@ -258,8 +267,7 @@ int i801_setup(void)
|
||||
|
||||
/* note: we assumed that the BIOS picked SMBus or I2C Bus timing
|
||||
appropriately (bit 2 in SMBHSTCFG) */
|
||||
/* Everything is happy, let's grab the memory and set things up. */
|
||||
request_region(i801_smba, 8, "i801-smbus");
|
||||
request_region(i801_smba, (isich4 ? 16 : 8), "i801-smbus");
|
||||
|
||||
#ifdef DEBUG
|
||||
if (temp & 0x02)
|
||||
@@ -279,14 +287,12 @@ int i801_setup(void)
|
||||
}
|
||||
|
||||
|
||||
/* Internally used pause function */
|
||||
void i801_do_pause(unsigned int amount)
|
||||
{
|
||||
current->state = TASK_INTERRUPTIBLE;
|
||||
schedule_timeout(amount);
|
||||
}
|
||||
|
||||
/* Another internally used function */
|
||||
int i801_transaction(void)
|
||||
{
|
||||
int temp;
|
||||
@@ -308,7 +314,7 @@ int i801_transaction(void)
|
||||
temp);
|
||||
#endif
|
||||
outb_p(temp, SMBHSTSTS);
|
||||
if ((temp = inb_p(SMBHSTSTS)) != 0x00) {
|
||||
if ((temp = (0x1f & inb_p(SMBHSTSTS))) != 0x00) {
|
||||
#ifdef DEBUG
|
||||
printk(KERN_DEBUG "i2c-i801.o: Failed! (%02x)\n", temp);
|
||||
#endif
|
||||
@@ -320,8 +326,7 @@ int i801_transaction(void)
|
||||
}
|
||||
}
|
||||
|
||||
/* start the transaction by setting bit 6 */
|
||||
outb_p(inb(SMBHSTCNT) | 0x040, SMBHSTCNT);
|
||||
outb_p(inb(SMBHSTCNT) | I801_START, SMBHSTCNT);
|
||||
|
||||
/* We will always wait for a fraction of a second! */
|
||||
do {
|
||||
@@ -380,16 +385,16 @@ int i801_transaction(void)
|
||||
|
||||
/* All-inclusive block transaction function */
|
||||
int i801_block_transaction(union i2c_smbus_data *data, char read_write,
|
||||
int i2c_enable)
|
||||
int command)
|
||||
{
|
||||
int i, len;
|
||||
int smbcmd;
|
||||
int temp;
|
||||
int result = 0;
|
||||
int timeout = 0;
|
||||
int timeout;
|
||||
unsigned char hostc, errmask;
|
||||
|
||||
if (i2c_enable) {
|
||||
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);
|
||||
@@ -419,8 +424,11 @@ int i801_block_transaction(union i2c_smbus_data *data, char read_write,
|
||||
smbcmd = I801_BLOCK_LAST;
|
||||
else
|
||||
smbcmd = I801_BLOCK_DATA;
|
||||
|
||||
outb_p((smbcmd & 0x3C) + (ENABLE_INT9 & 1), SMBHSTCNT);
|
||||
#ifdef HAVE_PEC
|
||||
if(isich4 && command == I2C_SMBUS_BLOCK_DATA_PEC)
|
||||
smbcmd |= I801_PEC_EN;
|
||||
#endif
|
||||
outb_p(smbcmd | ENABLE_INT9, SMBHSTCNT);
|
||||
|
||||
#ifdef DEBUG
|
||||
printk
|
||||
@@ -461,17 +469,23 @@ int i801_block_transaction(union i2c_smbus_data *data, char read_write,
|
||||
}
|
||||
}
|
||||
|
||||
/* start the transaction by setting bit 6 */
|
||||
if (i==1) outb_p(inb(SMBHSTCNT) | 0x040, SMBHSTCNT);
|
||||
if (i == 1) {
|
||||
#ifdef HAVE_PEC
|
||||
if(isich4 && command == I2C_SMBUS_BLOCK_DATA_PEC) {
|
||||
if(read_write == I2C_SMBUS_WRITE)
|
||||
outb_p(data->block[len + 1], SMBPEC);
|
||||
}
|
||||
#endif
|
||||
outb_p(inb(SMBHSTCNT) | I801_START, SMBHSTCNT);
|
||||
}
|
||||
|
||||
/* We will always wait for a fraction of a second! */
|
||||
timeout = 0;
|
||||
do {
|
||||
temp = inb_p(SMBHSTSTS);
|
||||
i801_do_pause(1);
|
||||
}
|
||||
while (
|
||||
(((i >= len) && (temp & 0x01))
|
||||
|| ((i < len) && !(temp & 0x80)))
|
||||
while ((!(temp & 0x80))
|
||||
&& (timeout++ < MAX_TIMEOUT));
|
||||
|
||||
/* If the SMBus is still busy, we give up */
|
||||
@@ -490,10 +504,7 @@ int i801_block_transaction(union i2c_smbus_data *data, char read_write,
|
||||
#endif
|
||||
} else if (temp & 0x08) {
|
||||
result = -1;
|
||||
printk
|
||||
(KERN_ERR "i2c-i801.o: Bus collision! SMBus may be locked until next hard"
|
||||
" reset. (sorry!)\n");
|
||||
/* Clock stops and slave is stuck in mid-transmission */
|
||||
printk(KERN_ERR "i2c-i801.o: Bus collision!\n");
|
||||
} else if (temp & 0x04) {
|
||||
result = -1;
|
||||
#ifdef DEBUG
|
||||
@@ -516,17 +527,14 @@ int i801_block_transaction(union i2c_smbus_data *data, char read_write,
|
||||
if (read_write == I2C_SMBUS_WRITE && i+1 <= len)
|
||||
outb_p(data->block[i+1], SMBBLKDAT);
|
||||
if ((temp & 0x9e) != 0x00)
|
||||
outb_p(temp, SMBHSTSTS); /* signals SMBBLKDAT ready */
|
||||
outb_p(temp, SMBHSTSTS); /* signals SMBBLKDAT ready */
|
||||
|
||||
temp = inb_p(SMBHSTSTS);
|
||||
#ifdef DEBUG
|
||||
if ((temp = (0x1e & inb_p(SMBHSTSTS))) != 0x00) {
|
||||
#ifdef DEBUG
|
||||
printk
|
||||
(KERN_DEBUG "i2c-i801.o: Failed reset at end of transaction (%02x)\n",
|
||||
(KERN_DEBUG "i2c-i801.o: Bad status (%02x) at end of transaction\n",
|
||||
temp);
|
||||
#endif
|
||||
}
|
||||
#ifdef DEBUG
|
||||
printk
|
||||
(KERN_DEBUG "i2c-i801.o: Block (post %d): CNT=%02x, CMD=%02x, ADD=%02x, "
|
||||
"DAT0=%02x, BLKDAT=%02x\n", i, inb_p(SMBHSTCNT),
|
||||
@@ -534,29 +542,48 @@ int i801_block_transaction(union i2c_smbus_data *data, char read_write,
|
||||
inb_p(SMBBLKDAT));
|
||||
#endif
|
||||
|
||||
if (result < 0) {
|
||||
if (result < 0)
|
||||
goto END;
|
||||
}
|
||||
}
|
||||
#ifdef HAVE_PEC
|
||||
if(isich4 && command == I2C_SMBUS_BLOCK_DATA_PEC) {
|
||||
timeout = 0;
|
||||
do {
|
||||
temp = inb_p(SMBHSTSTS);
|
||||
i801_do_pause(1);
|
||||
}
|
||||
while ((!(temp & 0x80))
|
||||
&& (timeout++ < MAX_TIMEOUT));
|
||||
|
||||
/* If the SMBus is still busy, we give up */
|
||||
if (timeout >= MAX_TIMEOUT) {
|
||||
#ifdef DEBUG
|
||||
printk(KERN_DEBUG "i2c-i801.o: PEC Timeout!\n");
|
||||
#endif
|
||||
}
|
||||
if(read_write == I2C_SMBUS_READ) {
|
||||
data->block[len + 1] = inb_p(SMBPEC);
|
||||
}
|
||||
outb_p(temp, SMBHSTSTS);
|
||||
printk("i2c-i801.o: sts = 0x%02x len = %d PEC = 0x%02x\n", temp, len, data->block[len + 1]);
|
||||
}
|
||||
#endif
|
||||
result = 0;
|
||||
END:
|
||||
if (i2c_enable) {
|
||||
if (command == I2C_SMBUS_I2C_BLOCK_DATA) {
|
||||
/* restore saved configuration register value */
|
||||
pci_write_config_byte(I801_dev, SMBHSTCFG, hostc);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/* Return -1 on error. See smbus.h for more information */
|
||||
/* Return -1 on error. */
|
||||
s32 i801_access(struct i2c_adapter * adap, u16 addr, unsigned short flags,
|
||||
char read_write, u8 command, int size,
|
||||
union i2c_smbus_data * data)
|
||||
{
|
||||
|
||||
switch (size) {
|
||||
case I2C_SMBUS_PROC_CALL:
|
||||
printk(KERN_ERR "i2c-i801.o: I2C_SMBUS_PROC_CALL not supported!\n");
|
||||
return -1;
|
||||
case I2C_SMBUS_QUICK:
|
||||
outb_p(((addr & 0x7f) << 1) | (read_write & 0x01),
|
||||
SMBHSTADD);
|
||||
@@ -589,18 +616,21 @@ s32 i801_access(struct i2c_adapter * adap, u16 addr, unsigned short flags,
|
||||
break;
|
||||
case I2C_SMBUS_BLOCK_DATA:
|
||||
case I2C_SMBUS_I2C_BLOCK_DATA:
|
||||
#ifdef HAVE_PEC
|
||||
case I2C_SMBUS_BLOCK_DATA_PEC:
|
||||
#endif
|
||||
outb_p(((addr & 0x7f) << 1) | (read_write & 0x01),
|
||||
SMBHSTADD);
|
||||
outb_p(command, SMBHSTCMD);
|
||||
/* Block transactions are very different from piix4 block
|
||||
and from the other i801 transactions. Handle in the
|
||||
i801_block_transaction() routine. */
|
||||
return i801_block_transaction(data, read_write,
|
||||
size==I2C_SMBUS_I2C_BLOCK_DATA);
|
||||
return i801_block_transaction(data, read_write, size);
|
||||
case I2C_SMBUS_PROC_CALL:
|
||||
default:
|
||||
printk(KERN_ERR "i2c-i801.o: Unsupported transaction %d\n", size);
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* 'size' is really the transaction type */
|
||||
outb_p((size & 0x3C) + (ENABLE_INT9 & 1), SMBHSTCNT);
|
||||
outb_p(size | ENABLE_INT9, SMBHSTCNT);
|
||||
|
||||
if (i801_transaction()) /* Error in transaction */
|
||||
return -1;
|
||||
@@ -685,7 +715,7 @@ int __init i801_cleanup(void)
|
||||
i801_initialized--;
|
||||
}
|
||||
if (i801_initialized >= 1) {
|
||||
release_region(i801_smba, 8);
|
||||
release_region(i801_smba, (isich4 ? 16 : 8));
|
||||
i801_initialized--;
|
||||
}
|
||||
return 0;
|
||||
|
||||
Reference in new issue
Block a user