WARNING - OLD ARCHIVES

This is an archived copy of the Xen.org mailing list, which we have preserved to ensure that existing links to archives are not broken. The live archive, which contains the latest emails, can be found at http://lists.xen.org/
   
 
 
Xen 
 
Home Products Support Community News
 
   
 

xen-devel

[Xen-devel] [PATCH 3 of 10] New/Updated drivers: Intel 82801 (ICH) drive

To: xen-devel@xxxxxxxxxxxxxxxxxxx
Subject: [Xen-devel] [PATCH 3 of 10] New/Updated drivers: Intel 82801 (ICH) driver backported from Linux Kernel Ver. 2.6.29.2
From: Daniel Kiper <dkiper@xxxxxxxxxxxx>
Date: Tue, 24 Nov 2009 12:38:52 +0100
Delivery-date: Tue, 24 Nov 2009 03:42:10 -0800
Envelope-to: www-data@xxxxxxxxxxxxxxxxxxx
List-help: <mailto:xen-devel-request@lists.xensource.com?subject=help>
List-id: Xen developer discussion <xen-devel.lists.xensource.com>
List-post: <mailto:xen-devel@lists.xensource.com>
List-subscribe: <http://lists.xensource.com/mailman/listinfo/xen-devel>, <mailto:xen-devel-request@lists.xensource.com?subject=subscribe>
List-unsubscribe: <http://lists.xensource.com/mailman/listinfo/xen-devel>, <mailto:xen-devel-request@lists.xensource.com?subject=unsubscribe>
Sender: xen-devel-bounces@xxxxxxxxxxxxxxxxxxx
User-agent: Mutt/1.3.28i
# HG changeset patch
# User root@xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
# Date 1259015516 -3600
# Node ID 3eaec2bd5066e416e65ec082cd5d783ef36ff6bb
# Parent  1db1bb63824b25f97d127449faeb3a56f1272c97
Intel 82801 (ICH) driver backported
from Linux Kernel Ver. 2.6.29.2
Signed-off-by: Daniel Kiper <dkiper@xxxxxxxxxxxx>

diff -r 1db1bb63824b -r 3eaec2bd5066 Documentation/i2c/dev-interface
--- a/Documentation/i2c/dev-interface   Mon Nov 23 07:32:47 2009 +0000
+++ b/Documentation/i2c/dev-interface   Mon Nov 23 23:31:56 2009 +0100
@@ -4,6 +4,10 @@ the /dev interface. You need to load mod
 
 Each registered i2c adapter gets a number, counting from 0. You can
 examine /sys/class/i2c-dev/ to see what number corresponds to which adapter.
+Alternatively, you can run "i2cdetect -l" to obtain a formated list of all
+i2c adapters present on your system at a given time. i2cdetect is part of
+the i2c-tools package.
+
 I2C device files are character device files with major device number 89
 and a minor device number corresponding to the number assigned as 
 explained above. They should be called "i2c-%d" (i2c-0, i2c-1, ..., 
@@ -17,30 +21,34 @@ first thing to do is "#include <linux/i2
 first thing to do is "#include <linux/i2c-dev.h>". Please note that
 there are two files named "i2c-dev.h" out there, one is distributed
 with the Linux kernel and is meant to be included from kernel
-driver code, the other one is distributed with lm_sensors and is
+driver code, the other one is distributed with i2c-tools and is
 meant to be included from user-space programs. You obviously want
 the second one here.
 
 Now, you have to decide which adapter you want to access. You should
-inspect /sys/class/i2c-dev/ to decide this. Adapter numbers are assigned
-somewhat dynamically, so you can not even assume /dev/i2c-0 is the
-first adapter.
+inspect /sys/class/i2c-dev/ or run "i2cdetect -l" to decide this.
+Adapter numbers are assigned somewhat dynamically, so you can not
+assume much about them. They can even change from one boot to the next.
 
 Next thing, open the device file, as follows:
+
   int file;
   int adapter_nr = 2; /* probably dynamically determined */
   char filename[20];
   
-  sprintf(filename,"/dev/i2c-%d",adapter_nr);
-  if ((file = open(filename,O_RDWR)) < 0) {
+  snprintf(filename, 19, "/dev/i2c-%d", adapter_nr);
+  file = open(filename, O_RDWR);
+  if (file < 0) {
     /* ERROR HANDLING; you can check errno to see what went wrong */
     exit(1);
   }
 
 When you have opened the device, you must specify with what device
 address you want to communicate:
+
   int addr = 0x40; /* The I2C address */
-  if (ioctl(file,I2C_SLAVE,addr) < 0) {
+
+  if (ioctl(file, I2C_SLAVE, addr) < 0) {
     /* ERROR HANDLING; you can check errno to see what went wrong */
     exit(1);
   }
@@ -48,31 +56,41 @@ Well, you are all set up now. You can no
 Well, you are all set up now. You can now use SMBus commands or plain
 I2C to communicate with your device. SMBus commands are preferred if
 the device supports them. Both are illustrated below.
+
   __u8 register = 0x10; /* Device register to access */
   __s32 res;
   char buf[10];
+
   /* Using SMBus commands */
-  res = i2c_smbus_read_word_data(file,register);
+  res = i2c_smbus_read_word_data(file, register);
   if (res < 0) {
     /* ERROR HANDLING: i2c transaction failed */
   } else {
     /* res contains the read word */
   }
+
   /* Using I2C Write, equivalent of 
-           i2c_smbus_write_word_data(file,register,0x6543) */
+     i2c_smbus_write_word_data(file, register, 0x6543) */
   buf[0] = register;
   buf[1] = 0x43;
   buf[2] = 0x65;
-  if ( write(file,buf,3) != 3) {
-    /* ERROR HANDLING: i2c transaction failed */
-  }
+  if (write(file, buf, 3) ! =3) {
+    /* ERROR HANDLING: i2c transaction failed */
+  }
+
   /* Using I2C Read, equivalent of i2c_smbus_read_byte(file) */
-  if (read(file,buf,1) != 1) {
+  if (read(file, buf, 1) != 1) {
     /* ERROR HANDLING: i2c transaction failed */
   } else {
     /* buf[0] contains the read byte */
   }
 
+Note that only a subset of the I2C and SMBus protocols can be achieved by
+the means of read() and write() calls. In particular, so-called combined
+transactions (mixing read and write messages in the same transaction)
+aren't supported. For this reason, this interface is almost never used by
+user-space programs.
+
 IMPORTANT: because of the use of inline functions, you *have* to use
 '-O' or some variation when you compile your program!
 
@@ -80,31 +98,34 @@ Full interface description
 Full interface description
 ==========================
 
-The following IOCTLs are defined and fully supported 
-(see also i2c-dev.h):
-
-ioctl(file,I2C_SLAVE,long addr)
+The following IOCTLs are defined:
+
+ioctl(file, I2C_SLAVE, long addr)
   Change slave address. The address is passed in the 7 lower bits of the
   argument (except for 10 bit addresses, passed in the 10 lower bits in this
   case).
 
-ioctl(file,I2C_TENBIT,long select)
+ioctl(file, I2C_TENBIT, long select)
   Selects ten bit addresses if select not equals 0, selects normal 7 bit
-  addresses if select equals 0. Default 0.
-
-ioctl(file,I2C_PEC,long select)
+  addresses if select equals 0. Default 0.  This request is only valid
+  if the adapter has I2C_FUNC_10BIT_ADDR.
+
+ioctl(file, I2C_PEC, long select)
   Selects SMBus PEC (packet error checking) generation and verification
   if select not equals 0, disables if select equals 0. Default 0.
-  Used only for SMBus transactions.
-
-ioctl(file,I2C_FUNCS,unsigned long *funcs)
+  Used only for SMBus transactions.  This request only has an effect if the
+  the adapter has I2C_FUNC_SMBUS_PEC; it is still safe if not, it just
+  doesn't have any effect.
+
+ioctl(file, I2C_FUNCS, unsigned long *funcs)
   Gets the adapter functionality and puts it in *funcs.
 
-ioctl(file,I2C_RDWR,struct i2c_rdwr_ioctl_data *msgset)
-
+ioctl(file, I2C_RDWR, struct i2c_rdwr_ioctl_data *msgset)
   Do combined read/write transaction without stop in between.
-  The argument is a pointer to a struct i2c_rdwr_ioctl_data {
-
+  Only valid if the adapter has I2C_FUNC_I2C.  The argument is
+  a pointer to a
+
+  struct i2c_rdwr_ioctl_data {
       struct i2c_msg *msgs;  /* ptr to array of simple messages */
       int nmsgs;             /* number of messages to exchange */
   }
@@ -115,10 +136,9 @@ ioctl(file,I2C_RDWR,struct i2c_rdwr_ioct
   The slave address and whether to use ten bit address mode has to be
   set in each message, overriding the values set with the above ioctl's.
 
-
-Other values are NOT supported at this moment, except for I2C_SMBUS,
-which you should never directly call; instead, use the access functions
-below.
+ioctl(file, I2C_SMBUS, struct i2c_smbus_ioctl_data *args)
+  Not meant to be called  directly; instead, use the access functions
+  below.
 
 You can do plain i2c transactions by using read(2) and write(2) calls.
 You do not need to pass the address byte; instead, set it through
@@ -143,7 +163,52 @@ returns the number of values read. The b
 returns the number of values read. The block buffers need not be longer
 than 32 bytes.
 
-The above functions are all macros, that resolve to calls to the
-i2c_smbus_access function, that on its turn calls a specific ioctl
+The above functions are all inline functions, that resolve to calls to
+the i2c_smbus_access function, that on its turn calls a specific ioctl
 with the data in a specific format. Read the source code if you
 want to know what happens behind the screens.
+
+
+Implementation details
+======================
+
+For the interested, here's the code flow which happens inside the kernel
+when you use the /dev interface to I2C:
+
+1* Your program opens /dev/i2c-N and calls ioctl() on it, as described in
+section "C example" above.
+
+2* These open() and ioctl() calls are handled by the i2c-dev kernel
+driver: see i2c-dev.c:i2cdev_open() and i2c-dev.c:i2cdev_ioctl(),
+respectively. You can think of i2c-dev as a generic I2C chip driver
+that can be programmed from user-space.
+
+3* Some ioctl() calls are for administrative tasks and are handled by
+i2c-dev directly. Examples include I2C_SLAVE (set the address of the
+device you want to access) and I2C_PEC (enable or disable SMBus error
+checking on future transactions.)
+
+4* Other ioctl() calls are converted to in-kernel function calls by
+i2c-dev. Examples include I2C_FUNCS, which queries the I2C adapter
+functionality using i2c.h:i2c_get_functionality(), and I2C_SMBUS, which
+performs an SMBus transaction using i2c-core.c:i2c_smbus_xfer().
+
+The i2c-dev driver is responsible for checking all the parameters that
+come from user-space for validity. After this point, there is no
+difference between these calls that came from user-space through i2c-dev
+and calls that would have been performed by kernel I2C chip drivers
+directly. This means that I2C bus drivers don't need to implement
+anything special to support access from user-space.
+
+5* These i2c-core.c/i2c.h functions are wrappers to the actual
+implementation of your I2C bus driver. Each adapter must declare
+callback functions implementing these standard calls.
+i2c.h:i2c_get_functionality() calls i2c_adapter.algo->functionality(),
+while i2c-core.c:i2c_smbus_xfer() calls either
+adapter.algo->smbus_xfer() if it is implemented, or if not,
+i2c-core.c:i2c_smbus_xfer_emulated() which in turn calls
+i2c_adapter.algo->master_xfer().
+
+After your I2C bus driver has processed these requests, execution runs
+up the call chain, with almost no processing done, except by i2c-dev to
+package the returned data, if any, in suitable format for the ioctl.
diff -r 1db1bb63824b -r 3eaec2bd5066 drivers/i2c/busses/Kconfig
--- a/drivers/i2c/busses/Kconfig        Mon Nov 23 07:32:47 2009 +0000
+++ b/drivers/i2c/busses/Kconfig        Mon Nov 23 23:31:56 2009 +0100
@@ -126,8 +126,9 @@ config I2C_I801
            ESB2
            ICH8
            ICH9
+           Tolapai
            ICH10
-            PCH
+           PCH
 
          This driver can also be built as a module.  If so, the module
          will be called i2c-i801.
diff -r 1db1bb63824b -r 3eaec2bd5066 drivers/i2c/busses/i2c-i801.c
--- a/drivers/i2c/busses/i2c-i801.c     Mon Nov 23 07:32:47 2009 +0000
+++ b/drivers/i2c/busses/i2c-i801.c     Mon Nov 23 23:31:56 2009 +0100
@@ -1,9 +1,8 @@
 /*
-    i2c-i801.c - Part of lm_sensors, Linux kernel modules for hardware
-              monitoring
     Copyright (c) 1998 - 2002  Frodo Looijaard <frodol@xxxxxx>,
     Philip Edelbrock <phil@xxxxxxxxxxxxx>, and Mark D. Studebaker
     <mdsxyz123@xxxxxxxxx>
+    Copyright (C) 2007, 2008   Jean Delvare <khali@xxxxxxxxxxxx>
 
     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
@@ -21,27 +20,37 @@
 */
 
 /*
-    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
-    ICH10              3A30
-    ICH10              3A60
-    PCH                        3B30
-    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 */
@@ -51,11 +60,11 @@
 #include <linux/kernel.h>
 #include <linux/stddef.h>
 #include <linux/delay.h>
-#include <linux/sched.h>
 #include <linux/ioport.h>
 #include <linux/init.h>
 #include <linux/i2c.h>
-#include <asm/io.h>
+#include <linux/acpi.h>
+#include <linux/io.h>
 
 /* I801 SMBus address offsets */
 #define SMBHSTSTS      (0 + i801_smba)
@@ -65,9 +74,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
@@ -77,6 +86,13 @@
 #define SMBHSTCFG_HST_EN       1
 #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
@@ -87,104 +103,293 @@
 #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)
-{
-       int temp;
+
+#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 result = 0;
+
+       /* 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);
+               }
+       }
+
+       return result;
+}
+
+static int i801_transaction(int xact)
+{
+       int status;
+       int result;
        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, "Successfull!\n");
-               }
-       }
-
-       outb_p(inb(SMBHSTCNT) | I801_START, SMBHSTCNT);
+       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));
-
-       /* If the SMBus is still busy, we give up */
+               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 (timeout >= MAX_TIMEOUT) {
-               dev_dbg(&I801_dev->dev, "SMBus Timeout!\n");
-               result = -1;
-       }
-
-       if (temp & 0x10) {
-               result = -1;
-               dev_dbg(&I801_dev->dev, "Error: Failed bus transaction\n");
-       }
-
-       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 */
-       }
-
-       if (temp & 0x04) {
-               result = -1;
-               dev_dbg(&I801_dev->dev, "Error: no response!\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);
-       }
-       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;
-}
-
-/* All-inclusive block transaction function */
+               dev_dbg(&I801_dev->dev, "PEC Timeout!\n");
+       }
+       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 */
+
+       /* 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);
+       }
+
+       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;
+
+               data->block[0] = len;
+               for (i = 0; i < len; i++)
+                       data->block[i + 1] = inb_p(SMBBLKDAT);
+       }
+       return 0;
+}
+
+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 status;
+       int result;
+       int timeout;
+
+       result = i801_check_pre();
+       if (result < 0)
+               return result;
+
+       len = data->block[0];
+
+       if (read_write == I2C_SMBUS_WRITE) {
+               outb_p(len, SMBHSTDAT0);
+               outb_p(data->block[1], SMBBLKDAT);
+       }
+
+       for (i = 1; i <= len; i++) {
+               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 {
+                       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);
+
+               /* We will always wait for a fraction of a second! */
+               timeout = 0;
+               do {
+                       msleep(1);
+                       status = inb_p(SMBHSTSTS);
+               }
+               while ((!(status & SMBHSTSTS_BYTE_DONE))
+                      && (timeout++ < MAX_TIMEOUT));
+
+               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) {
+                               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;
+               }
+
+               /* Retrieve/store value in SMBBLKDAT */
+               if (read_write == I2C_SMBUS_READ)
+                       data->block[i] = inb_p(SMBBLKDAT);
+               if (read_write == I2C_SMBUS_WRITE && i+1 <= len)
+                       outb_p(data->block[i+1], SMBBLKDAT);
+
+               /* signals SMBBLKDAT ready */
+               outb_p(SMBHSTSTS_BYTE_DONE | SMBHSTSTS_INTR, SMBHSTSTS);
+       }
+
+       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 i, len;
-       int smbcmd;
-       int temp;
        int result = 0;
-       int timeout;
-       unsigned char hostc, errmask;
+       unsigned char hostc;
 
        if (command == I2C_SMBUS_I2C_BLOCK_DATA) {
                if (read_write == I2C_SMBUS_WRITE) {
@@ -192,154 +397,45 @@ static int i801_block_transaction(union 
                        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) {
-               len = data->block[0];
-               if (len < 1)
-                       len = 1;
-               if (len > 32)
-                       len = 32;
-               outb_p(len, SMBHSTDAT0);
-               outb_p(data->block[1], SMBBLKDAT);
+       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 {
-               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; 
-               } 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 (i == 1)
-                       outb_p(inb(SMBHSTCNT) | I801_START, SMBHSTCNT);
-
-               /* We will always wait for a fraction of a second! */
-               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");
-               }
-
-               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");
-               }
-
-               if (i == 1 && read_write == I2C_SMBUS_READ) {
-                       len = inb_p(SMBHSTDAT0);
-                       if (len < 1)
-                               len = 1;
-                       if (len > 32)
-                               len = 32;
-                       data->block[0] = len;
-               }
-
-               /* Retrieve/store value in SMBBLKDAT */
-               if (read_write == I2C_SMBUS_READ)
-                       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;
-       }
-
-       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));
-
-               if (timeout >= MAX_TIMEOUT) {
-                       dev_dbg(&I801_dev->dev, "PEC Timeout!\n");
-               }
-               outb_p(temp, SMBHSTSTS); 
-       }
-       result = 0;
-END:
-       if (command == I2C_SMBUS_I2C_BLOCK_DATA) {
+               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)
@@ -348,7 +444,7 @@ static s32 i801_access(struct i2c_adapte
        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;
 
@@ -384,36 +480,49 @@ static s32 i801_access(struct i2c_adapte
                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;
-       }
-
-       outb_p(hwpec, SMBAUXCTL);       /* enable/disable hardware PEC */
+               return -EOPNOTSUPP;
+       }
+
+       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;
 
@@ -433,9 +542,11 @@ static u32 i801_func(struct i2c_adapter 
 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 struct i2c_algorithm smbus_algorithm = {
@@ -462,6 +573,7 @@ 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) },
        { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_PCH_SMBUS) },
@@ -470,18 +582,66 @@ static struct pci_device_id i801_ids[] =
 
 MODULE_DEVICE_TABLE (pci, i801_ids);
 
+#if defined CONFIG_INPUT_APANEL || defined CONFIG_INPUT_APANEL_MODULE
+static unsigned char apanel_addr;
+
+/* Scan the system ROM for the signature "FJKEYINF" */
+static __init const void __iomem *bios_signature(const void __iomem *bios)
+{
+       ssize_t offset;
+       const unsigned char signature[] = "FJKEYINF";
+
+       for (offset = 0; offset < 0x10000; offset += 0x10) {
+               if (check_signature(bios + offset, signature,
+                                   sizeof(signature)-1))
+                       return bios + offset;
+       }
+       return NULL;
+}
+
+static void __init input_apanel_init(void)
+{
+       void __iomem *bios;
+       const void __iomem *p;
+
+       bios = ioremap(0xF0000, 0x10000); /* Can't fail */
+       p = bios_signature(bios);
+       if (p) {
+               /* just use the first address */
+               apanel_addr = readb(p + 8 + 3) >> 1;
+       }
+       iounmap(bios);
+}
+#else
+static void __init input_apanel_init(void) {}
+#endif
+
 static int __devinit i801_probe(struct pci_dev *dev, const struct 
pci_device_id *id)
 {
        unsigned char temp;
        int err;
 
        I801_dev = dev;
-       if ((dev->device == PCI_DEVICE_ID_INTEL_82801DB_3) ||
-           (dev->device == PCI_DEVICE_ID_INTEL_82801EB_3) ||
-           (dev->device == PCI_DEVICE_ID_INTEL_ESB_4))
-               isich4 = 1;
-       else
-               isich4 = 0;
+       i801_features = 0;
+       switch (dev->device) {
+       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_ICH7_17:
+       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:
+               i801_features |= FEATURE_SMBUS_PEC;
+               i801_features |= FEATURE_BLOCK_BUFFER;
+               break;
+       }
 
        err = pci_enable_device(dev);
        if (err) {
@@ -508,6 +668,7 @@ static int __devinit i801_probe(struct p
        }
 
        pci_read_config_byte(I801_dev, SMBHSTCFG, &temp);
+       i801_original_hstcfg = temp;
        temp &= ~SMBHSTCFG_I2C_EN;      /* SMBus timing */
        if (!(temp & SMBHSTCFG_HST_EN)) {
                dev_info(&dev->dev, "Enabling SMBus device\n");
@@ -520,16 +681,34 @@ static int __devinit i801_probe(struct p
        else
                dev_dbg(&dev->dev, "SMBus using PCI Interrupt\n");
 
-       /* set up the driverfs linkage to our parent device */
+       /* 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;
 
-       snprintf(i801_adapter.name, I2C_NAME_SIZE,
+       snprintf(i801_adapter.name, sizeof(i801_adapter.name),
                "SMBus I801 adapter at %04lx", i801_smba);
        err = i2c_add_adapter(&i801_adapter);
        if (err) {
                dev_err(&dev->dev, "Failed to add SMBus adapter\n");
                goto exit_release;
        }
+
+       /* Register optional slaves */
+#if defined CONFIG_INPUT_APANEL || defined CONFIG_INPUT_APANEL_MODULE
+       if (apanel_addr) {
+               struct i2c_board_info info;
+
+               memset(&info, 0, sizeof(struct i2c_board_info));
+               info.addr = apanel_addr;
+               strlcpy(info.type, "fujitsu_apanel", I2C_NAME_SIZE);
+               i2c_new_device(&i801_adapter, &info);
+       }
+#endif
+
        return 0;
 
 exit_release:
@@ -541,6 +720,7 @@ static void __devexit i801_remove(struct
 static void __devexit i801_remove(struct pci_dev *dev)
 {
        i2c_del_adapter(&i801_adapter);
+       pci_write_config_byte(I801_dev, SMBHSTCFG, i801_original_hstcfg);
        pci_release_region(dev, SMBBAR);
        /*
         * do not call pci_disable_device(dev) since it can cause hard hangs on
@@ -548,15 +728,38 @@ static void __devexit i801_remove(struct
         */
 }
 
+#ifdef CONFIG_PM
+static int i801_suspend(struct pci_dev *dev, pm_message_t mesg)
+{
+       pci_save_state(dev);
+       pci_write_config_byte(dev, SMBHSTCFG, i801_original_hstcfg);
+       pci_set_power_state(dev, pci_choose_state(dev, mesg));
+       return 0;
+}
+
+static int i801_resume(struct pci_dev *dev)
+{
+       pci_set_power_state(dev, PCI_D0);
+       pci_restore_state(dev);
+       return pci_enable_device(dev);
+}
+#else
+#define i801_suspend NULL
+#define i801_resume NULL
+#endif
+
 static struct pci_driver i801_driver = {
        .name           = "i801_smbus",
        .id_table       = i801_ids,
        .probe          = i801_probe,
        .remove         = __devexit_p(i801_remove),
+       .suspend        = i801_suspend,
+       .resume         = i801_resume,
 };
 
 static int __init i2c_i801_init(void)
 {
+       input_apanel_init();
        return pci_register_driver(&i801_driver);
 }
 
@@ -565,9 +768,8 @@ static void __exit i2c_i801_exit(void)
        pci_unregister_driver(&i801_driver);
 }
 
-MODULE_AUTHOR ("Frodo Looijaard <frodol@xxxxxx>, "
-               "Philip Edelbrock <phil@xxxxxxxxxxxxx>, "
-               "and Mark D. Studebaker <mdsxyz123@xxxxxxxxx>");
+MODULE_AUTHOR("Mark D. Studebaker <mdsxyz123@xxxxxxxxx>, "
+             "Jean Delvare <khali@xxxxxxxxxxxx>");
 MODULE_DESCRIPTION("I801 SMBus driver");
 MODULE_LICENSE("GPL");
 
diff -r 1db1bb63824b -r 3eaec2bd5066 include/linux/i2c.h
--- a/include/linux/i2c.h       Mon Nov 23 07:32:47 2009 +0000
+++ b/include/linux/i2c.h       Mon Nov 23 23:31:56 2009 +0100
@@ -380,6 +380,7 @@ struct i2c_msg {
 #define I2C_FUNC_I2C                   0x00000001
 #define I2C_FUNC_10BIT_ADDR            0x00000002
 #define I2C_FUNC_PROTOCOL_MANGLING     0x00000004 /* 
I2C_M_{REV_DIR_ADDR,NOSTART,..} */
+#define I2C_FUNC_SMBUS_PEC             0x00000008
 #define I2C_FUNC_SMBUS_HWPEC_CALC      0x00000008 /* SMBus 2.0 */
 #define I2C_FUNC_SMBUS_BLOCK_PROC_CALL 0x00008000 /* SMBus 2.0 */
 #define I2C_FUNC_SMBUS_QUICK           0x00010000 
@@ -416,7 +417,8 @@ struct i2c_msg {
                              I2C_FUNC_SMBUS_WORD_DATA | \
                              I2C_FUNC_SMBUS_PROC_CALL | \
                              I2C_FUNC_SMBUS_WRITE_BLOCK_DATA | \
-                             I2C_FUNC_SMBUS_I2C_BLOCK)
+                             I2C_FUNC_SMBUS_I2C_BLOCK | \
+                             I2C_FUNC_SMBUS_PEC)
 
 /* 
  * Data for SMBus Messages 
diff -r 1db1bb63824b -r 3eaec2bd5066 include/linux/pci_ids.h
--- a/include/linux/pci_ids.h   Mon Nov 23 07:32:47 2009 +0000
+++ b/include/linux/pci_ids.h   Mon Nov 23 23:31:56 2009 +0100
@@ -2222,6 +2222,7 @@
 #define PCI_DEVICE_ID_INTEL_PCH_LPC_MIN        0x3b00
 #define PCI_DEVICE_ID_INTEL_PCH_LPC_MAX        0x3b1f
 #define PCI_DEVICE_ID_INTEL_PCH_SMBUS  0x3b30
+#define PCI_DEVICE_ID_INTEL_TOLAPAI_1  0x5032
 #define PCI_DEVICE_ID_INTEL_82371SB_0  0x7000
 #define PCI_DEVICE_ID_INTEL_82371SB_1  0x7010
 #define PCI_DEVICE_ID_INTEL_82371SB_2  0x7020

_______________________________________________
Xen-devel mailing list
Xen-devel@xxxxxxxxxxxxxxxxxxx
http://lists.xensource.com/xen-devel

<Prev in Thread] Current Thread [Next in Thread>
  • [Xen-devel] [PATCH 3 of 10] New/Updated drivers: Intel 82801 (ICH) driver backported from Linux Kernel Ver. 2.6.29.2, Daniel Kiper <=