i2c: sis630: Add SIS964 support
authorAmaury Decrême <amaury.decreme@gmail.com>
Mon, 28 Jan 2013 21:21:05 +0000 (22:21 +0100)
committerWolfram Sang <wolfram@the-dreams.de>
Mon, 11 Feb 2013 14:59:38 +0000 (15:59 +0100)
Signed-off-by: Amaury Decrême <amaury.decreme@gmail.com>
Reviewed-by: Jean Delvare <khali@linux-fr.org>
Signed-off-by: Wolfram Sang <wolfram@the-dreams.de>
Documentation/i2c/busses/i2c-sis630
drivers/i2c/busses/Kconfig
drivers/i2c/busses/i2c-sis630.c

index 0b96973..ee79436 100644 (file)
@@ -4,9 +4,11 @@ Supported adapters:
   * Silicon Integrated Systems Corp (SiS)
        630 chipset (Datasheet: available at http://www.sfr-fresh.com/linux)
        730 chipset
+       964 chipset
   * Possible other SiS chipsets ?
 
 Author: Alexander Malysh <amalysh@web.de>
+       Amaury Decrême <amaury.decreme@gmail.com> - SiS964 support
 
 Module Parameters
 -----------------
@@ -18,6 +20,7 @@ Module Parameters
 * high_clock = [1|0] Forcibly set Host Master Clock to 56KHz (default,
                        what your BIOS use). DANGEROUS! This should be a bit
                        faster, but freeze some systems (i.e. my Laptop).
+                       SIS630/730 chip only.
 
 
 Description
@@ -36,6 +39,12 @@ or like this:
 00:00.0 Host bridge: Silicon Integrated Systems [SiS] 730 Host (rev 02)
 00:01.0 ISA bridge: Silicon Integrated Systems [SiS] 85C503/5513
 
+or like this:
+
+00:00.0 Host bridge: Silicon Integrated Systems [SiS] 760/M760 Host (rev 02)
+00:02.0 ISA bridge: Silicon Integrated Systems [SiS] SiS964 [MuTIOL Media IO]
+                                                       LPC Controller (rev 36)
+
 in your 'lspci' output , then this driver is for your chipset.
 
 Thank You
index 87df863..74e18bc 100644 (file)
@@ -197,11 +197,11 @@ config I2C_SIS5595
          will be called i2c-sis5595.
 
 config I2C_SIS630
-       tristate "SiS 630/730"
+       tristate "SiS 630/730/964"
        depends on PCI
        help
          If you say yes to this option, support will be included for the
-         SiS630 and SiS730 SMBus (a subset of I2C) interface.
+         SiS630, SiS730 and SiS964 SMBus (a subset of I2C) interface.
 
          This driver can also be built as a module.  If so, the module
          will be called i2c-sis630.
index de6dddb..df8e20a 100644 (file)
    Supports:
        SIS 630
        SIS 730
+       SIS 964
+
+   Notable differences between chips:
+       +------------------------+--------------------+-------------------+
+       |                        |     SIS630/730     |      SIS964       |
+       +------------------------+--------------------+-------------------+
+       | Clock                  | 14kHz/56kHz        | 55.56kHz/27.78kHz |
+       | SMBus registers offset | 0x80               | 0xE0              |
+       | SMB_CNT                | Bit 1 = Slave Busy | Bit 1 = Bus probe |
+       |         (not used yet) | Bit 3 is reserved  | Bit 3 = Last byte |
+       | SMB_PCOUNT             | Offset + 0x06      | Offset + 0x14     |
+       | SMB_COUNT              | 4:0 bits           | 5:0 bits          |
+       +------------------------+--------------------+-------------------+
+       (Other differences don't affect the functions provided by the driver)
 
    Note: we assume there can only be one device, with one SMBus interface.
 */
 #include <linux/acpi.h>
 #include <linux/io.h>
 
-/* SIS630 SMBus registers */
-#define SMB_STS                        0x80    /* status */
-#define SMB_EN                 0x81    /* status enable */
-#define SMB_CNT                        0x82
-#define SMBHOST_CNT            0x83
-#define SMB_ADDR               0x84
-#define SMB_CMD                        0x85
-#define SMB_PCOUNT             0x86    /* processed count */
-#define SMB_COUNT              0x87
-#define SMB_BYTE               0x88    /* ~0x8F data byte field */
-#define SMBDEV_ADDR            0x90
-#define SMB_DB0                        0x91
-#define SMB_DB1                        0x92
-#define SMB_SAA                        0x93
-
-/* register count for request_region */
+/* SIS964 id is defined here as we are the only file using it */
+#define PCI_DEVICE_ID_SI_964   0x0964
+
+/* SIS630/730/964 SMBus registers */
+#define SMB_STS                        0x00    /* status */
+#define SMB_CNT                        0x02    /* control */
+#define SMBHOST_CNT            0x03    /* host control */
+#define SMB_ADDR               0x04    /* address */
+#define SMB_CMD                        0x05    /* command */
+#define SMB_COUNT              0x07    /* byte count */
+#define SMB_BYTE               0x08    /* ~0x8F data byte field */
+
+/* register count for request_region
+ * As we don't use SMB_PCOUNT, 20 is ok for SiS630 and SiS964
+ */
 #define SIS630_SMB_IOREGION    20
 
 /* PCI address constants */
@@ -96,28 +109,30 @@ static struct pci_driver sis630_driver;
 static bool high_clock;
 static bool force;
 module_param(high_clock, bool, 0);
-MODULE_PARM_DESC(high_clock, "Set Host Master Clock to 56KHz (default 14KHz).");
+MODULE_PARM_DESC(high_clock,
+       "Set Host Master Clock to 56KHz (default 14KHz) (SIS630/730 only).");
 module_param(force, bool, 0);
 MODULE_PARM_DESC(force, "Forcibly enable the SIS630. DANGEROUS!");
 
-/* acpi base address */
-static unsigned short acpi_base;
+/* SMBus base adress */
+static unsigned short smbus_base;
 
 /* supported chips */
 static int supported[] = {
        PCI_DEVICE_ID_SI_630,
        PCI_DEVICE_ID_SI_730,
+       PCI_DEVICE_ID_SI_760,
        0 /* terminates the list */
 };
 
 static inline u8 sis630_read(u8 reg)
 {
-       return inb(acpi_base + reg);
+       return inb(smbus_base + reg);
 }
 
 static inline void sis630_write(u8 reg, u8 data)
 {
-       outb(data, acpi_base + reg);
+       outb(data, smbus_base + reg);
 }
 
 static int sis630_transaction_start(struct i2c_adapter *adap, int size, u8 *oldclock)
@@ -394,6 +409,8 @@ static int sis630_setup(struct pci_dev *sis630_dev)
        unsigned char b;
        struct pci_dev *dummy = NULL;
        int retval, i;
+       /* acpi base address */
+       unsigned short acpi_base;
 
        /* check for supported SiS devices */
        for (i=0; supported[i] > 0 ; i++) {
@@ -438,16 +455,25 @@ static int sis630_setup(struct pci_dev *sis630_dev)
 
        dev_dbg(&sis630_dev->dev, "ACPI base at 0x%04x\n", acpi_base);
 
-       retval = acpi_check_region(acpi_base + SMB_STS, SIS630_SMB_IOREGION,
+       if (supported[i] == PCI_DEVICE_ID_SI_760)
+               smbus_base = acpi_base + 0xE0;
+       else
+               smbus_base = acpi_base + 0x80;
+
+       dev_dbg(&sis630_dev->dev, "SMBus base at 0x%04hx\n", smbus_base);
+
+       retval = acpi_check_region(smbus_base + SMB_STS, SIS630_SMB_IOREGION,
                                   sis630_driver.name);
        if (retval)
                goto exit;
 
        /* Everything is happy, let's grab the memory and set things up. */
-       if (!request_region(acpi_base + SMB_STS, SIS630_SMB_IOREGION,
+       if (!request_region(smbus_base + SMB_STS, SIS630_SMB_IOREGION,
                            sis630_driver.name)) {
-               dev_err(&sis630_dev->dev, "SMBus registers 0x%04x-0x%04x already "
-                       "in use!\n", acpi_base + SMB_STS, acpi_base + SMB_SAA);
+               dev_err(&sis630_dev->dev,
+                       "I/O Region 0x%04hx-0x%04hx for SMBus already in use.\n",
+                       smbus_base + SMB_STS,
+                       smbus_base + SMB_STS + SIS630_SMB_IOREGION - 1);
                retval = -EBUSY;
                goto exit;
        }
@@ -456,7 +482,7 @@ static int sis630_setup(struct pci_dev *sis630_dev)
 
 exit:
        if (retval)
-               acpi_base = 0;
+               smbus_base = 0;
        return retval;
 }
 
@@ -470,11 +496,13 @@ static struct i2c_adapter sis630_adapter = {
        .owner          = THIS_MODULE,
        .class          = I2C_CLASS_HWMON | I2C_CLASS_SPD,
        .algo           = &smbus_algorithm,
+       .retries        = 3
 };
 
 static DEFINE_PCI_DEVICE_TABLE(sis630_ids) = {
        { PCI_DEVICE(PCI_VENDOR_ID_SI, PCI_DEVICE_ID_SI_503) },
        { PCI_DEVICE(PCI_VENDOR_ID_SI, PCI_DEVICE_ID_SI_LPC) },
+       { PCI_DEVICE(PCI_VENDOR_ID_SI, PCI_DEVICE_ID_SI_964) },
        { 0, }
 };
 
@@ -491,17 +519,17 @@ static int sis630_probe(struct pci_dev *dev, const struct pci_device_id *id)
        sis630_adapter.dev.parent = &dev->dev;
 
        snprintf(sis630_adapter.name, sizeof(sis630_adapter.name),
-                "SMBus SIS630 adapter at %04x", acpi_base + SMB_STS);
+                "SMBus SIS630 adapter at %04hx", smbus_base + SMB_STS);
 
        return i2c_add_adapter(&sis630_adapter);
 }
 
 static void sis630_remove(struct pci_dev *dev)
 {
-       if (acpi_base) {
+       if (smbus_base) {
                i2c_del_adapter(&sis630_adapter);
-               release_region(acpi_base + SMB_STS, SIS630_SMB_IOREGION);
-               acpi_base = 0;
+               release_region(smbus_base + SMB_STS, SIS630_SMB_IOREGION);
+               smbus_base = 0;
        }
 }