/*
- i2c-i801.c - Part of lm_sensors, Linux kernel modules for hardware
- monitoring
Copyright (c) 1998 - 2002 Frodo Looijaard <frodol@dds.nl>,
Philip Edelbrock <phil@netroedge.com>, and Mark D. Studebaker
<mdsxyz123@yahoo.com>
+ Copyright (C) 2007, 2008 Jean Delvare <khali@linux-fr.org>
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
*/
/*
- SUPPORTED DEVICES PCI ID
- 82801AA 2413
- 82801AB 2423
- 82801BA 2443
- 82801CA/CAM 2483
- 82801DB 24C3 (HW PEC supported)
- 82801EB 24D3 (HW PEC supported)
- 6300ESB 25A4
- ICH6 266A
- ICH7 27DA
- ESB2 269B
- ICH8 283E
- ICH9 2930
- This driver supports several versions of Intel's I/O Controller Hubs (ICH).
- For SMBus support, they are similar to the PIIX4 and are part
- of Intel's '810' and other chipsets.
- See the file Documentation/i2c/busses/i2c-i801 for details.
- I2C Block Read and Process Call are not supported.
+ Supports the following Intel I/O Controller Hubs (ICH):
+
+ I/O Block I2C
+ region SMBus Block proc. block
+ Chip name PCI ID size PEC buffer call read
+ ----------------------------------------------------------------------
+ 82801AA (ICH) 0x2413 16 no no no no
+ 82801AB (ICH0) 0x2423 16 no no no no
+ 82801BA (ICH2) 0x2443 16 no no no no
+ 82801CA (ICH3) 0x2483 32 soft no no no
+ 82801DB (ICH4) 0x24c3 32 hard yes no no
+ 82801E (ICH5) 0x24d3 32 hard yes yes yes
+ 6300ESB 0x25a4 32 hard yes yes yes
+ 82801F (ICH6) 0x266a 32 hard yes yes yes
+ 6310ESB/6320ESB 0x269b 32 hard yes yes yes
+ 82801G (ICH7) 0x27da 32 hard yes yes yes
+ 82801H (ICH8) 0x283e 32 hard yes yes yes
+ 82801I (ICH9) 0x2930 32 hard yes yes yes
+ Tolapai 0x5032 32 hard yes yes yes
+ ICH10 0x3a30 32 hard yes yes yes
+ ICH10 0x3a60 32 hard yes yes yes
+ PCH 0x3b30 32 hard yes yes yes
+
+ Features supported by this driver:
+ Software PEC no
+ Hardware PEC yes
+ Block buffer yes
+ Block process call transaction no
+ I2C block read transaction yes (doesn't use the block buffer)
+
+ See the file Documentation/i2c/busses/i2c-i801 for details.
*/
/* Note: we assume there can only be one I801, with one SMBus interface */
#include <linux/ioport.h>
#include <linux/init.h>
#include <linux/i2c.h>
+#include <linux/acpi.h>
#include <asm/io.h>
/* I801 SMBus address offsets */
#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 */
+#define SMBPEC (8 + i801_smba) /* ICH3 and later */
+#define SMBAUXSTS (12 + i801_smba) /* ICH4 and later */
+#define SMBAUXCTL (13 + i801_smba) /* ICH4 and later */
/* PCI Address Constants */
#define SMBBAR 4
#define I801_BYTE 0x04
#define I801_BYTE_DATA 0x08
#define I801_WORD_DATA 0x0C
-#define I801_PROC_CALL 0x10 /* later chips only, unimplemented */
+#define I801_PROC_CALL 0x10 /* unimplemented */
#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_I2C_BLOCK_LAST 0x38 /* unimplemented */
+#define I801_I2C_BLOCK_LAST 0x38 /* ICH5 and later */
#define I801_START 0x40
-#define I801_PEC_EN 0x80 /* ICH4 only */
+#define I801_PEC_EN 0x80 /* ICH3 and later */
/* I801 Hosts Status register bits */
#define SMBHSTSTS_BYTE_DONE 0x80
#define SMBHSTSTS_INTR 0x02
#define SMBHSTSTS_HOST_BUSY 0x01
+#define STATUS_FLAGS (SMBHSTSTS_BYTE_DONE | SMBHSTSTS_FAILED | \
+ SMBHSTSTS_BUS_ERR | SMBHSTSTS_DEV_ERR | \
+ SMBHSTSTS_INTR)
+
static unsigned long i801_smba;
static unsigned char i801_original_hstcfg;
static struct pci_driver i801_driver;
static struct pci_dev *I801_dev;
-static int isich4;
-static int i801_transaction(int xact)
+#define FEATURE_SMBUS_PEC (1 << 0)
+#define FEATURE_BLOCK_BUFFER (1 << 1)
+#define FEATURE_BLOCK_PROC (1 << 2)
+#define FEATURE_I2C_BLOCK_READ (1 << 3)
+static unsigned int i801_features;
+
+/* Make sure the SMBus host is ready to start transmitting.
+ Return 0 if it is, -EBUSY if it is not. */
+static int i801_check_pre(void)
{
- int temp;
- int result = 0;
- int timeout = 0;
+ int status;
- dev_dbg(&I801_dev->dev, "Transaction (pre): CNT=%02x, CMD=%02x, "
- "ADD=%02x, DAT0=%02x, DAT1=%02x\n", inb_p(SMBHSTCNT),
- inb_p(SMBHSTCMD), inb_p(SMBHSTADD), inb_p(SMBHSTDAT0),
- inb_p(SMBHSTDAT1));
-
- /* Make sure the SMBus host is ready to start transmitting */
- /* 0x1f = Failed, Bus_Err, Dev_Err, Intr, Host_Busy */
- if ((temp = (0x1f & inb_p(SMBHSTSTS))) != 0x00) {
- dev_dbg(&I801_dev->dev, "SMBus busy (%02x). Resetting...\n",
- temp);
- outb_p(temp, SMBHSTSTS);
- if ((temp = (0x1f & inb_p(SMBHSTSTS))) != 0x00) {
- dev_dbg(&I801_dev->dev, "Failed! (%02x)\n", temp);
- return -1;
- } else {
- dev_dbg(&I801_dev->dev, "Successful!\n");
+ status = inb_p(SMBHSTSTS);
+ if (status & SMBHSTSTS_HOST_BUSY) {
+ dev_err(&I801_dev->dev, "SMBus is busy, can't use it!\n");
+ return -EBUSY;
+ }
+
+ status &= STATUS_FLAGS;
+ if (status) {
+ dev_dbg(&I801_dev->dev, "Clearing status flags (%02x)\n",
+ status);
+ outb_p(status, SMBHSTSTS);
+ status = inb_p(SMBHSTSTS) & STATUS_FLAGS;
+ if (status) {
+ dev_err(&I801_dev->dev,
+ "Failed clearing status flags (%02x)\n",
+ status);
+ return -EBUSY;
}
}
- /* the current contents of SMBHSTCNT can be overwritten, since PEC,
- * INTREN, SMBSCMD are passed in xact */
- outb_p(xact | I801_START, SMBHSTCNT);
+ return 0;
+}
- /* We will always wait for a fraction of a second! */
- do {
- msleep(1);
- temp = inb_p(SMBHSTSTS);
- } while ((temp & SMBHSTSTS_HOST_BUSY) && (timeout++ < MAX_TIMEOUT));
+/* Convert the status register to an error code, and clear it. */
+static int i801_check_post(int status, int timeout)
+{
+ int result = 0;
/* If the SMBus is still busy, we give up */
- if (timeout >= MAX_TIMEOUT) {
- dev_dbg(&I801_dev->dev, "SMBus Timeout!\n");
- result = -1;
+ if (timeout) {
+ dev_err(&I801_dev->dev, "Transaction timeout\n");
/* 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 & SMBHSTSTS_FAILED) {
- result = -1;
- dev_dbg(&I801_dev->dev, "Error: Failed bus transaction\n");
+ /* Check if it worked */
+ status = inb_p(SMBHSTSTS);
+ if ((status & SMBHSTSTS_HOST_BUSY) ||
+ !(status & SMBHSTSTS_FAILED))
+ dev_err(&I801_dev->dev,
+ "Failed terminating the transaction\n");
+ outb_p(STATUS_FLAGS, SMBHSTSTS);
+ return -ETIMEDOUT;
}
- 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 (status & SMBHSTSTS_FAILED) {
+ result = -EIO;
+ dev_err(&I801_dev->dev, "Transaction failed\n");
}
-
- if (temp & SMBHSTSTS_DEV_ERR) {
- result = -1;
- dev_dbg(&I801_dev->dev, "Error: no response!\n");
+ if (status & SMBHSTSTS_DEV_ERR) {
+ result = -ENXIO;
+ dev_dbg(&I801_dev->dev, "No response\n");
+ }
+ if (status & SMBHSTSTS_BUS_ERR) {
+ result = -EAGAIN;
+ dev_dbg(&I801_dev->dev, "Lost arbitration\n");
}
- if ((inb_p(SMBHSTSTS) & 0x1f) != 0x00)
- outb_p(inb(SMBHSTSTS), SMBHSTSTS);
-
- if ((temp = (0x1f & inb_p(SMBHSTSTS))) != 0x00) {
- dev_dbg(&I801_dev->dev, "Failed reset at end of transaction "
- "(%02x)\n", temp);
+ if (result) {
+ /* Clear error flags */
+ outb_p(status & STATUS_FLAGS, SMBHSTSTS);
+ status = inb_p(SMBHSTSTS) & STATUS_FLAGS;
+ if (status) {
+ dev_warn(&I801_dev->dev, "Failed clearing status "
+ "flags at end of transaction (%02x)\n",
+ status);
+ }
}
- dev_dbg(&I801_dev->dev, "Transaction (post): CNT=%02x, CMD=%02x, "
- "ADD=%02x, DAT0=%02x, DAT1=%02x\n", inb_p(SMBHSTCNT),
- inb_p(SMBHSTCMD), inb_p(SMBHSTADD), inb_p(SMBHSTDAT0),
- inb_p(SMBHSTDAT1));
+
return result;
}
+static int i801_transaction(int xact)
+{
+ int status;
+ int result;
+ int timeout = 0;
+
+ result = i801_check_pre();
+ if (result < 0)
+ return result;
+
+ /* 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 {
+ msleep(1);
+ status = inb_p(SMBHSTSTS);
+ } while ((status & SMBHSTSTS_HOST_BUSY) && (timeout++ < MAX_TIMEOUT));
+
+ result = i801_check_post(status, timeout >= MAX_TIMEOUT);
+ if (result < 0)
+ return result;
+
+ outb_p(SMBHSTSTS_INTR, SMBHSTSTS);
+ return 0;
+}
+
/* wait for INTR bit as advised by Intel */
static void i801_wait_hwpec(void)
{
int timeout = 0;
- int temp;
+ int status;
do {
msleep(1);
- temp = inb_p(SMBHSTSTS);
- } while ((!(temp & SMBHSTSTS_INTR))
+ status = inb_p(SMBHSTSTS);
+ } while ((!(status & SMBHSTSTS_INTR))
&& (timeout++ < MAX_TIMEOUT));
if (timeout >= MAX_TIMEOUT) {
dev_dbg(&I801_dev->dev, "PEC Timeout!\n");
}
- outb_p(temp, SMBHSTSTS);
+ outb_p(status, SMBHSTSTS);
}
static int i801_block_transaction_by_block(union i2c_smbus_data *data,
char read_write, int hwpec)
{
int i, len;
+ int status;
inb_p(SMBHSTCNT); /* reset the data buffer index */
outb_p(data->block[i+1], SMBBLKDAT);
}
- if (i801_transaction(I801_BLOCK_DATA | ENABLE_INT9 |
- I801_PEC_EN * hwpec))
- return -1;
+ status = i801_transaction(I801_BLOCK_DATA | ENABLE_INT9 |
+ I801_PEC_EN * hwpec);
+ if (status)
+ return status;
if (read_write == I2C_SMBUS_READ) {
len = inb_p(SMBHSTDAT0);
if (len < 1 || len > I2C_SMBUS_BLOCK_MAX)
- return -1;
+ return -EPROTO;
data->block[0] = len;
for (i = 0; i < len; i++)
}
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 smbcmd;
- int temp;
- int result = 0;
+ int status;
+ int result;
int timeout;
- unsigned char errmask;
+
+ result = i801_check_pre();
+ if (result < 0)
+ return result;
len = data->block[0];
}
for (i = 1; i <= len; i++) {
- if (i == len && read_write == I2C_SMBUS_READ)
- smbcmd = I801_BLOCK_LAST;
- else
- smbcmd = I801_BLOCK_DATA;
- outb_p(smbcmd | ENABLE_INT9, SMBHSTCNT);
-
- dev_dbg(&I801_dev->dev, "Block (pre %d): CNT=%02x, CMD=%02x, "
- "ADD=%02x, DAT0=%02x, BLKDAT=%02x\n", i,
- inb_p(SMBHSTCNT), inb_p(SMBHSTCMD), inb_p(SMBHSTADD),
- inb_p(SMBHSTDAT0), inb_p(SMBBLKDAT));
-
- /* Make sure the SMBus host is ready to start transmitting */
- temp = inb_p(SMBHSTSTS);
- if (i == 1) {
- /* Erronenous conditions before transaction:
- * Byte_Done, Failed, Bus_Err, Dev_Err, Intr, Host_Busy */
- errmask = 0x9f;
+ if (i == len && read_write == I2C_SMBUS_READ) {
+ if (command == I2C_SMBUS_I2C_BLOCK_DATA)
+ smbcmd = I801_I2C_BLOCK_LAST;
+ else
+ smbcmd = I801_BLOCK_LAST;
} else {
- /* Erronenous conditions during transaction:
- * Failed, Bus_Err, Dev_Err, Intr */
- errmask = 0x1e;
- }
- if (temp & errmask) {
- dev_dbg(&I801_dev->dev, "SMBus busy (%02x). "
- "Resetting...\n", temp);
- outb_p(temp, SMBHSTSTS);
- if (((temp = inb_p(SMBHSTSTS)) & errmask) != 0x00) {
- dev_err(&I801_dev->dev,
- "Reset failed! (%02x)\n", temp);
- return -1;
- }
- if (i != 1)
- /* if die in middle of block transaction, fail */
- return -1;
+ 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);
if (i == 1)
outb_p(inb(SMBHSTCNT) | I801_START, SMBHSTCNT);
timeout = 0;
do {
msleep(1);
- temp = inb_p(SMBHSTSTS);
+ status = inb_p(SMBHSTSTS);
}
- while ((!(temp & SMBHSTSTS_BYTE_DONE))
+ while ((!(status & 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 & SMBHSTSTS_FAILED) {
- result = -1;
- dev_dbg(&I801_dev->dev,
- "Error: Failed bus transaction\n");
- } else if (temp & SMBHSTSTS_BUS_ERR) {
- result = -1;
- dev_err(&I801_dev->dev, "Bus collision!\n");
- } else if (temp & SMBHSTSTS_DEV_ERR) {
- result = -1;
- dev_dbg(&I801_dev->dev, "Error: no response!\n");
- }
+ result = i801_check_post(status, timeout >= MAX_TIMEOUT);
+ if (result < 0)
+ return result;
- 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);
- if (len < 1 || len > I2C_SMBUS_BLOCK_MAX)
- return -1;
+ if (len < 1 || len > I2C_SMBUS_BLOCK_MAX) {
+ dev_err(&I801_dev->dev,
+ "Illegal SMBus block read size %d\n",
+ len);
+ /* Recover */
+ while (inb_p(SMBHSTSTS) & SMBHSTSTS_HOST_BUSY)
+ outb_p(SMBHSTSTS_BYTE_DONE, SMBHSTSTS);
+ outb_p(SMBHSTSTS_INTR, SMBHSTSTS);
+ return -EPROTO;
+ }
data->block[0] = len;
}
data->block[i] = inb_p(SMBBLKDAT);
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 */
- if ((temp = (0x1e & inb_p(SMBHSTSTS))) != 0x00) {
- dev_dbg(&I801_dev->dev,
- "Bad status (%02x) at end of transaction\n",
- temp);
- }
- dev_dbg(&I801_dev->dev, "Block (post %d): CNT=%02x, CMD=%02x, "
- "ADD=%02x, DAT0=%02x, BLKDAT=%02x\n", i,
- inb_p(SMBHSTCNT), inb_p(SMBHSTCMD), inb_p(SMBHSTADD),
- inb_p(SMBHSTDAT0), inb_p(SMBBLKDAT));
-
- if (result < 0)
- return result;
+ /* signals SMBBLKDAT ready */
+ outb_p(SMBHSTSTS_BYTE_DONE | SMBHSTSTS_INTR, SMBHSTSTS);
}
- return result;
+
+ return 0;
}
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 -EIO;
return 0;
}
pci_read_config_byte(I801_dev, SMBHSTCFG, &hostc);
pci_write_config_byte(I801_dev, SMBHSTCFG,
hostc | SMBHSTCFG_I2C_EN);
- } else {
+ } else if (!(i801_features & FEATURE_I2C_BLOCK_READ)) {
dev_err(&I801_dev->dev,
- "I2C_SMBUS_I2C_BLOCK_READ not DB!\n");
- return -1;
+ "I2C block read is unsupported!\n");
+ return -EOPNOTSUPP;
}
}
- if (read_write == I2C_SMBUS_WRITE) {
+ if (read_write == I2C_SMBUS_WRITE
+ || command == I2C_SMBUS_I2C_BLOCK_DATA) {
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 */
+ data->block[0] = 32; /* max for SMBus block reads */
}
- if (isich4 && i801_set_block_buffer_mode() == 0 )
+ if ((i801_features & FEATURE_BLOCK_BUFFER)
+ && !(command == I2C_SMBUS_I2C_BLOCK_DATA
+ && read_write == I2C_SMBUS_READ)
+ && 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);
+ command, hwpec);
if (result == 0 && 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 */
pci_write_config_byte(I801_dev, SMBHSTCFG, hostc);
}
return result;
}
-/* Return -1 on error. */
+/* Return negative errno on error. */
static s32 i801_access(struct i2c_adapter * adap, u16 addr,
unsigned short flags, char read_write, u8 command,
int size, union i2c_smbus_data * data)
int block = 0;
int ret, xact = 0;
- hwpec = isich4 && (flags & I2C_CLIENT_PEC)
+ hwpec = (i801_features & FEATURE_SMBUS_PEC) && (flags & I2C_CLIENT_PEC)
&& size != I2C_SMBUS_QUICK
&& size != I2C_SMBUS_I2C_BLOCK_DATA;
xact = I801_WORD_DATA;
break;
case I2C_SMBUS_BLOCK_DATA:
- case I2C_SMBUS_I2C_BLOCK_DATA:
outb_p(((addr & 0x7f) << 1) | (read_write & 0x01),
SMBHSTADD);
outb_p(command, SMBHSTCMD);
block = 1;
break;
- case I2C_SMBUS_PROC_CALL:
+ 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;
default:
dev_err(&I801_dev->dev, "Unsupported transaction %d\n", size);
- return -1;
+ return -EOPNOTSUPP;
}
if (hwpec) /* enable/disable hardware PEC */
/* Some BIOSes don't like it when PEC is enabled at reboot or resume
time, so we forcibly disable it after every transaction. Turn off
E32B for the same reason. */
- if (hwpec)
+ if (hwpec || block)
outb_p(inb_p(SMBAUXCTL) & ~(SMBAUXCTL_CRC | SMBAUXCTL_E32B),
SMBAUXCTL);
if(block)
return ret;
if(ret)
- return -1;
+ return ret;
if ((read_write == I2C_SMBUS_WRITE) || (xact == I801_QUICK))
return 0;
static u32 i801_func(struct i2c_adapter *adapter)
{
return I2C_FUNC_SMBUS_QUICK | I2C_FUNC_SMBUS_BYTE |
- I2C_FUNC_SMBUS_BYTE_DATA | I2C_FUNC_SMBUS_WORD_DATA |
- I2C_FUNC_SMBUS_BLOCK_DATA | I2C_FUNC_SMBUS_WRITE_I2C_BLOCK
- | (isich4 ? I2C_FUNC_SMBUS_HWPEC_CALC : 0);
+ I2C_FUNC_SMBUS_BYTE_DATA | I2C_FUNC_SMBUS_WORD_DATA |
+ I2C_FUNC_SMBUS_BLOCK_DATA | I2C_FUNC_SMBUS_WRITE_I2C_BLOCK |
+ ((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 struct i2c_adapter i801_adapter = {
.owner = THIS_MODULE,
.id = I2C_HW_SMBUS_I801,
- .class = I2C_CLASS_HWMON,
+ .class = I2C_CLASS_HWMON | I2C_CLASS_SPD,
.algo = &smbus_algorithm,
};
{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ESB2_17) },
{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ICH8_5) },
{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ICH9_6) },
+ { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_TOLAPAI_1) },
+ { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ICH10_4) },
+ { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ICH10_5) },
+ { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_PCH_SMBUS) },
{ 0, }
};
int err;
I801_dev = dev;
+ i801_features = 0;
switch (dev->device) {
- case PCI_DEVICE_ID_INTEL_82801DB_3:
case PCI_DEVICE_ID_INTEL_82801EB_3:
case PCI_DEVICE_ID_INTEL_ESB_4:
case PCI_DEVICE_ID_INTEL_ICH6_16:
case PCI_DEVICE_ID_INTEL_ESB2_17:
case PCI_DEVICE_ID_INTEL_ICH8_5:
case PCI_DEVICE_ID_INTEL_ICH9_6:
- isich4 = 1;
+ case PCI_DEVICE_ID_INTEL_TOLAPAI_1:
+ case PCI_DEVICE_ID_INTEL_ICH10_4:
+ case PCI_DEVICE_ID_INTEL_ICH10_5:
+ case PCI_DEVICE_ID_INTEL_PCH_SMBUS:
+ i801_features |= FEATURE_I2C_BLOCK_READ;
+ /* fall through */
+ case PCI_DEVICE_ID_INTEL_82801DB_3:
+ i801_features |= FEATURE_SMBUS_PEC;
+ i801_features |= FEATURE_BLOCK_BUFFER;
break;
- default:
- isich4 = 0;
}
err = pci_enable_device(dev);
goto exit;
}
+ err = acpi_check_resource_conflict(&dev->resource[SMBBAR]);
+ if (err)
+ goto exit;
+
err = pci_request_region(dev, SMBBAR, i801_driver.name);
if (err) {
dev_err(&dev->dev, "Failed to request SMBus region "
else
dev_dbg(&dev->dev, "SMBus using PCI Interrupt\n");
+ /* Clear special mode bits */
+ if (i801_features & (FEATURE_SMBUS_PEC | FEATURE_BLOCK_BUFFER))
+ outb_p(inb_p(SMBAUXCTL) & ~(SMBAUXCTL_CRC | SMBAUXCTL_E32B),
+ SMBAUXCTL);
+
/* set up the sysfs linkage to our parent device */
i801_adapter.dev.parent = &dev->dev;
pci_unregister_driver(&i801_driver);
}
-MODULE_AUTHOR ("Frodo Looijaard <frodol@dds.nl>, "
- "Philip Edelbrock <phil@netroedge.com>, "
- "and Mark D. Studebaker <mdsxyz123@yahoo.com>");
+MODULE_AUTHOR("Mark D. Studebaker <mdsxyz123@yahoo.com>, "
+ "Jean Delvare <khali@linux-fr.org>");
MODULE_DESCRIPTION("I801 SMBus driver");
MODULE_LICENSE("GPL");