]> pilppa.org Git - linux-2.6-omap-h63xx.git/blobdiff - drivers/i2c/busses/i2c-i801.c
Merge git://git.kernel.org/pub/scm/linux/kernel/git/davem/net-2.6
[linux-2.6-omap-h63xx.git] / drivers / i2c / busses / i2c-i801.c
index 611b57192c9618ac6159267b4371f89835efd75c..dc7ea32b69a8f64c3a09725cd54748fb8c95b9c8 100644 (file)
@@ -1,9 +1,8 @@
 /*
-    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, 32 byte buffer not supported)
-    82801EB            24D3   (HW PEC supported, 32 byte buffer not 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
+
+  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 */
@@ -51,6 +62,7 @@
 #include <linux/ioport.h>
 #include <linux/init.h>
 #include <linux/i2c.h>
+#include <linux/acpi.h>
 #include <asm/io.h>
 
 /* I801 SMBus address offsets */
@@ -61,9 +73,9 @@
 #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 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 */
 #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 */
-
-
-static int i801_transaction(void);
-static int i801_block_transaction(union i2c_smbus_data *data, char read_write,
-                                 int command, int hwpec);
+#define I801_PEC_EN            0x80    /* ICH3 and later */
+
+/* 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
+
+#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(void)
+#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 status;
+
+       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;
+               }
+       }
+
+       return 0;
+}
+
+/* Convert the status register to an error code, and clear it. */
+static int i801_check_post(int status, int timeout)
 {
-       int temp;
        int result = 0;
-       int timeout = 0;
 
-       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");
+       /* If the SMBus is still busy, we give up */
+       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);
+
+               /* 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 (status & SMBHSTSTS_FAILED) {
+               result = -EIO;
+               dev_err(&I801_dev->dev, "Transaction failed\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 (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);
                }
        }
 
-       outb_p(inb(SMBHSTCNT) | I801_START, SMBHSTCNT);
+       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);
-               temp = inb_p(SMBHSTSTS);
-       } while ((temp & 0x01) && (timeout++ < MAX_TIMEOUT));
+               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 status;
+
+       do {
+               msleep(1);
+               status = inb_p(SMBHSTSTS);
+       } while ((!(status & SMBHSTSTS_INTR))
+                && (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;
+               dev_dbg(&I801_dev->dev, "PEC Timeout!\n");
        }
+       outb_p(status, SMBHSTSTS);
+}
 
-       if (temp & 0x10) {
-               result = -1;
-               dev_dbg(&I801_dev->dev, "Error: Failed bus transaction\n");
-       }
+static int i801_block_transaction_by_block(union i2c_smbus_data *data,
+                                          char read_write, int hwpec)
+{
+       int i, len;
+       int status;
 
-       if (temp & 0x08) {
-               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 */
-       }
+       inb_p(SMBHSTCNT); /* reset the data buffer index */
 
-       if (temp & 0x04) {
-               result = -1;
-               dev_dbg(&I801_dev->dev, "Error: no response!\n");
+       /* 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 ((inb_p(SMBHSTSTS) & 0x1f) != 0x00)
-               outb_p(inb(SMBHSTSTS), SMBHSTSTS);
+       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 -EPROTO;
 
-       if ((temp = (0x1f & inb_p(SMBHSTSTS))) != 0x00) {
-               dev_dbg(&I801_dev->dev, "Failed reset at end of transaction "
-                       "(%02x)\n", temp);
+               data->block[0] = len;
+               for (i = 0; i < len; i++)
+                       data->block[i + 1] = inb_p(SMBBLKDAT);
        }
-       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;
+       return 0;
 }
 
-/* All-inclusive block transaction function */
-static int i801_block_transaction(union i2c_smbus_data *data, char read_write,
-                                 int command, int hwpec)
+static int i801_block_transaction_byte_by_byte(union i2c_smbus_data *data,
+                                              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 hostc, errmask;
 
-       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;
-               }
-       }
+       result = i801_check_pre();
+       if (result < 0)
+               return result;
+
+       len = data->block[0];
 
        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(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++) {
-               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);
-                               result = -1;
-                                goto END;
-                       }
-                       if (i != 1) {
-                               /* if die in middle of block transaction, fail */
-                               result = -1;
-                               goto END;
-                       }
+                       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);
@@ -259,35 +337,28 @@ static int i801_block_transaction(union i2c_smbus_data *data, char read_write,
                timeout = 0;
                do {
                        msleep(1);
-                       temp = inb_p(SMBHSTSTS);
-               }
-                   while ((!(temp & 0x80))
-                          && (timeout++ < MAX_TIMEOUT));
-
-               /* If the SMBus is still busy, we give up */
-               if (timeout >= MAX_TIMEOUT) {
-                       result = -1;
-                       dev_dbg(&I801_dev->dev, "SMBus Timeout!\n");
+                       status = inb_p(SMBHSTSTS);
                }
+               while ((!(status & SMBHSTSTS_BYTE_DONE))
+                      && (timeout++ < MAX_TIMEOUT));
 
-               if (temp & 0x10) {
-                       result = -1;
-                       dev_dbg(&I801_dev->dev,
-                               "Error: Failed bus transaction\n");
-               } else if (temp & 0x08) {
-                       result = -1;
-                       dev_err(&I801_dev->dev, "Bus collision!\n");
-               } else if (temp & 0x04) {
-                       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 = 1;
-                       if (len > 32)
-                               len = 32;
+                       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;
                }
 
@@ -296,47 +367,74 @@ static int i801_block_transaction(union i2c_smbus_data *data, char read_write,
                        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)
-                       goto END;
+               /* signals SMBBLKDAT ready */
+               outb_p(SMBHSTSTS_BYTE_DONE | SMBHSTSTS_INTR, SMBHSTSTS);
        }
 
-       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));
+       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 -EIO;
+       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 (timeout >= MAX_TIMEOUT) {
-                       dev_dbg(&I801_dev->dev, "PEC Timeout!\n");
+       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 if (!(i801_features & FEATURE_I2C_BLOCK_READ)) {
+                       dev_err(&I801_dev->dev,
+                               "I2C block read is unsupported!\n");
+                       return -EOPNOTSUPP;
                }
-               outb_p(temp, SMBHSTSTS); 
        }
-       result = 0;
-END:
-       if (command == I2C_SMBUS_I2C_BLOCK_DATA) {
+
+       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 SMBus block reads */
+       }
+
+       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,
+                                                            command, hwpec);
+
+       if (result == 0 && hwpec)
+               i801_wait_hwpec();
+
+       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)
@@ -345,7 +443,7 @@ static s32 i801_access(struct i2c_adapter * adap, u16 addr,
        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;
 
@@ -381,36 +479,49 @@ static s32 i801_access(struct i2c_adapter * adap, u16 addr,
                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;
        }
 
-       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);
-       else {
-               outb_p(xact | ENABLE_INT9, SMBHSTCNT);
-               ret = i801_transaction();
-       }
+       else
+               ret = i801_transaction(xact | ENABLE_INT9);
 
        /* 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);
+          time, so we forcibly disable it after every transaction. Turn off
+          E32B for the same reason. */
+       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;
 
@@ -430,9 +541,11 @@ static s32 i801_access(struct i2c_adapter * adap, u16 addr,
 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 = {
@@ -443,7 +556,7 @@ 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,
 };
 
@@ -460,6 +573,9 @@ static struct pci_device_id i801_ids[] = {
        { 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) },
        { 0, }
 };
 
@@ -471,8 +587,8 @@ static int __devinit i801_probe(struct pci_dev *dev, const struct pci_device_id
        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:
@@ -480,10 +596,15 @@ 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_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:
+               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);
@@ -502,6 +623,10 @@ static int __devinit i801_probe(struct pci_dev *dev, const struct pci_device_id
                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 "
@@ -524,6 +649,11 @@ static int __devinit i801_probe(struct pci_dev *dev, const struct pci_device_id
        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;
 
@@ -592,9 +722,8 @@ static void __exit i2c_i801_exit(void)
        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");