i2c-piix4: Support AMD auxiliary SMBus controller
authorAndrew Armenia <andrew@asquaredlabs.com>
Tue, 24 Jul 2012 12:13:57 +0000 (14:13 +0200)
committerJean Delvare <khali@endymion.delvare>
Tue, 24 Jul 2012 12:13:57 +0000 (14:13 +0200)
Some AMD chipsets, such as the SP5100, have an auxiliary SMBus
controller with a second set of registers. This patch adds
support for this auxiliary controller.

Tested on ASUS KCMA-D8 motherboard.

Signed-off-by: Andrew Armenia <andrew@asquaredlabs.com>
Signed-off-by: Jean Delvare <khali@linux-fr.org>
Documentation/i2c/busses/i2c-piix4
drivers/i2c/busses/Kconfig
drivers/i2c/busses/i2c-piix4.c

index 475bb4ae0720545f8501ce574ddb3a3e01876b23..1e6634f54c5019b45b6d663b6fd7cd7db9b23530 100644 (file)
@@ -8,6 +8,11 @@ Supported adapters:
     Datasheet: Only available via NDA from ServerWorks
   * ATI IXP200, IXP300, IXP400, SB600, SB700 and SB800 southbridges
     Datasheet: Not publicly available
+    SB700 register reference available at:
+    http://support.amd.com/us/Embedded_TechDocs/43009_sb7xx_rrg_pub_1.00.pdf
+  * AMD SP5100 (SB700 derivative found on some server mainboards)
+    Datasheet: Publicly available at the AMD website
+    http://support.amd.com/us/Embedded_TechDocs/44413.pdf
   * AMD Hudson-2
     Datasheet: Not publicly available
   * Standard Microsystems (SMSC) SLC90E66 (Victory66) southbridge
@@ -68,6 +73,10 @@ this driver on those mainboards.
 The ServerWorks Southbridges, the Intel 440MX, and the Victory66 are
 identical to the PIIX4 in I2C/SMBus support.
 
+The AMD SB700 and SP5100 chipsets implement two PIIX4-compatible SMBus
+controllers. If your BIOS initializes the secondary controller, it will
+be detected by this driver as an "Auxiliary SMBus Host Controller".
+
 If you own Force CPCI735 motherboard or other OSB4 based systems you may need
 to change the SMBus Interrupt Select register so the SMBus controller uses
 the SMI mode.
index 7244c8be606360dd10455b8984e344479f0d8bb1..2e7530a4e7b8cdb37cc47cedef24a0da3d5cec12 100644 (file)
@@ -133,7 +133,7 @@ config I2C_PIIX4
            ATI IXP300
            ATI IXP400
            ATI SB600
-           ATI SB700
+           ATI SB700/SP5100
            ATI SB800
            AMD Hudson-2
            Serverworks OSB4
@@ -143,6 +143,10 @@ config I2C_PIIX4
            Serverworks HT-1100
            SMSC Victory66
 
+         Some AMD chipsets contain two PIIX4-compatible SMBus
+         controllers. This driver will attempt to use both controllers
+         on the SB700/SP5100, if they have been initialized by the BIOS.
+
          This driver can also be built as a module.  If so, the module
          will be called i2c-piix4.
 
index 42ed0af10efdc6f2e0f325218dcd3a2ff0eb427b..ef511df2c9656528737be5b13f12e496d3722a94 100644 (file)
    Supports:
        Intel PIIX4, 440MX
        Serverworks OSB4, CSB5, CSB6, HT-1000, HT-1100
-       ATI IXP200, IXP300, IXP400, SB600, SB700, SB800
+       ATI IXP200, IXP300, IXP400, SB600, SB700/SP5100, SB800
        AMD Hudson-2
        SMSC Victory66
 
-   Note: we assume there can only be one device, with one SMBus interface.
+   Note: we assume there can only be one device, with one or more
+   SMBus interfaces.
 */
 
 #include <linux/module.h>
@@ -293,6 +294,46 @@ static int __devinit piix4_setup_sb800(struct pci_dev *PIIX4_dev,
        return piix4_smba;
 }
 
+static int __devinit piix4_setup_aux(struct pci_dev *PIIX4_dev,
+                               const struct pci_device_id *id,
+                               unsigned short base_reg_addr)
+{
+       /* Set up auxiliary SMBus controllers found on some
+        * AMD chipsets e.g. SP5100 (SB700 derivative) */
+
+       unsigned short piix4_smba;
+
+       /* Read address of auxiliary SMBus controller */
+       pci_read_config_word(PIIX4_dev, base_reg_addr, &piix4_smba);
+       if ((piix4_smba & 1) == 0) {
+               dev_dbg(&PIIX4_dev->dev,
+                       "Auxiliary SMBus controller not enabled\n");
+               return -ENODEV;
+       }
+
+       piix4_smba &= 0xfff0;
+       if (piix4_smba == 0) {
+               dev_dbg(&PIIX4_dev->dev,
+                       "Auxiliary SMBus base address uninitialized\n");
+               return -ENODEV;
+       }
+
+       if (acpi_check_region(piix4_smba, SMBIOSIZE, piix4_driver.name))
+               return -ENODEV;
+
+       if (!request_region(piix4_smba, SMBIOSIZE, piix4_driver.name)) {
+               dev_err(&PIIX4_dev->dev, "Auxiliary SMBus region 0x%x "
+                       "already in use!\n", piix4_smba);
+               return -EBUSY;
+       }
+
+       dev_info(&PIIX4_dev->dev,
+                "Auxiliary SMBus Host Controller at 0x%x\n",
+                piix4_smba);
+
+       return piix4_smba;
+}
+
 static int piix4_transaction(struct i2c_adapter *piix4_adapter)
 {
        struct i2c_piix4_adapdata *adapdata = i2c_get_adapdata(piix4_adapter);
@@ -497,6 +538,7 @@ static DEFINE_PCI_DEVICE_TABLE(piix4_ids) = {
 MODULE_DEVICE_TABLE (pci, piix4_ids);
 
 static struct i2c_adapter *piix4_main_adapter;
+static struct i2c_adapter *piix4_aux_adapter;
 
 static int __devinit piix4_add_adapter(struct pci_dev *dev,
                                        unsigned short smba,
@@ -560,10 +602,28 @@ static int __devinit piix4_probe(struct pci_dev *dev,
        else
                retval = piix4_setup(dev, id);
 
+       /* If no main SMBus found, give up */
        if (retval < 0)
                return retval;
 
-       return piix4_add_adapter(dev, retval, &piix4_main_adapter);
+       /* Try to register main SMBus adapter, give up if we can't */
+       retval = piix4_add_adapter(dev, retval, &piix4_main_adapter);
+       if (retval < 0)
+               return retval;
+
+       /* Check for auxiliary SMBus on some AMD chipsets */
+       if (dev->vendor == PCI_VENDOR_ID_ATI &&
+           dev->device == PCI_DEVICE_ID_ATI_SBX00_SMBUS &&
+           dev->revision < 0x40) {
+               retval = piix4_setup_aux(dev, id, 0x58);
+               if (retval > 0) {
+                       /* Try to add the aux adapter if it exists,
+                        * piix4_add_adapter will clean up if this fails */
+                       piix4_add_adapter(dev, retval, &piix4_aux_adapter);
+               }
+       }
+
+       return 0;
 }
 
 static void __devexit piix4_adap_remove(struct i2c_adapter *adap)
@@ -584,6 +644,11 @@ static void __devexit piix4_remove(struct pci_dev *dev)
                piix4_adap_remove(piix4_main_adapter);
                piix4_main_adapter = NULL;
        }
+
+       if (piix4_aux_adapter) {
+               piix4_adap_remove(piix4_aux_adapter);
+               piix4_aux_adapter = NULL;
+       }
 }
 
 static struct pci_driver piix4_driver = {