i2c-i801: Add support for Intel Ibex Peak
[deliverable/linux.git] / drivers / i2c / busses / i2c-i801.c
index aa9157913b9addd98182a0b5f16cd92cd0b80ea3..5123eb69a971b22e267d5de0213276c93eee3a1c 100644 (file)
@@ -1,10 +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         Jean Delvare <khali@linux-fr.org>
+    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
   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     ?       ?
+  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
@@ -62,6 +63,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 */
 #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;
@@ -130,105 +136,137 @@ static struct pci_dev *I801_dev;
 #define FEATURE_I2C_BLOCK_READ (1 << 3)
 static unsigned int i801_features;
 
-static int i801_transaction(int xact)
+/* 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 */
 
@@ -240,14 +278,15 @@ static int i801_block_transaction_by_block(union i2c_smbus_data *data,
                        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++)
@@ -262,10 +301,13 @@ static int i801_block_transaction_byte_by_byte(union i2c_smbus_data *data,
 {
        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];
 
@@ -289,36 +331,6 @@ static int i801_block_transaction_byte_by_byte(union i2c_smbus_data *data,
                }
                outb_p(smbcmd | ENABLE_INT9, SMBHSTCNT);
 
-               dev_dbg(&I801_dev->dev, "Block (pre %d): CNT=%02x, CMD=%02x, "
-                       "ADD=%02x, DAT0=%02x, DAT1=%02x, BLKDAT=%02x\n", i,
-                       inb_p(SMBHSTCNT), inb_p(SMBHSTCMD), inb_p(SMBHSTADD),
-                       inb_p(SMBHSTDAT0), inb_p(SMBHSTDAT1), inb_p(SMBBLKDAT));
-
-               /* Make sure the SMBus host is ready to start transmitting */
-               temp = inb_p(SMBHSTSTS);
-               if (i == 1) {
-                       /* Erroneous conditions before transaction:
-                        * Byte_Done, Failed, Bus_Err, Dev_Err, Intr, Host_Busy */
-                       errmask = 0x9f;
-               } else {
-                       /* Erroneous 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 (i == 1)
                        outb_p(inb(SMBHSTCNT) | I801_START, SMBHSTCNT);
 
@@ -326,41 +338,28 @@ static int i801_block_transaction_byte_by_byte(union i2c_smbus_data *data,
                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
                 && 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;
                }
 
@@ -369,30 +368,19 @@ static int i801_block_transaction_byte_by_byte(union i2c_smbus_data *data,
                        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, DAT1=%02x, BLKDAT=%02x\n", i,
-                       inb_p(SMBHSTCNT), inb_p(SMBHSTCMD), inb_p(SMBHSTADD),
-                       inb_p(SMBHSTDAT0), inb_p(SMBHSTDAT1), 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;
 }
 
@@ -412,7 +400,7 @@ static int i801_block_transaction(union i2c_smbus_data *data, char read_write,
                } else if (!(i801_features & FEATURE_I2C_BLOCK_READ)) {
                        dev_err(&I801_dev->dev,
                                "I2C block read is unsupported!\n");
-                       return -1;
+                       return -EOPNOTSUPP;
                }
        }
 
@@ -447,7 +435,7 @@ static int i801_block_transaction(union i2c_smbus_data *data, char read_write,
        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)
@@ -509,10 +497,9 @@ static s32 i801_access(struct i2c_adapter * adap, u16 addr,
                        outb_p(command, SMBHSTCMD);
                block = 1;
                break;
-       case I2C_SMBUS_PROC_CALL:
        default:
                dev_err(&I801_dev->dev, "Unsupported transaction %d\n", size);
-               return -1;
+               return -EOPNOTSUPP;
        }
 
        if (hwpec)      /* enable/disable hardware PEC */
@@ -535,7 +522,7 @@ static s32 i801_access(struct i2c_adapter * adap, u16 addr,
        if(block)
                return ret;
        if(ret)
-               return -1;
+               return ret;
        if ((read_write == I2C_SMBUS_WRITE) || (xact == I801_QUICK))
                return 0;
 
@@ -570,7 +557,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,
 };
 
@@ -588,6 +575,9 @@ static struct pci_device_id i801_ids[] = {
        { 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, }
 };
 
@@ -608,10 +598,13 @@ 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:
+       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:
-       case PCI_DEVICE_ID_INTEL_TOLAPAI_1:
                i801_features |= FEATURE_SMBUS_PEC;
                i801_features |= FEATURE_BLOCK_BUFFER;
                break;
@@ -633,6 +626,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 "
This page took 0.035682 seconds and 5 git commands to generate.