Here is the modified patch. As this is my first attempt at submitting
a patch while it obviously isn't ready for inclusion given the errors
any feedback on general style would be welcome.

Tom.

--- linux-3.2.1-gentoo-r2/drivers/i2c/busses/i2c-piix4.c        2012-01-05
10:55:44.000000000 +1100
+++ dev/drivers/i2c/busses/i2c-piix4.c  2012-02-25 04:53:04.743198258 +1100
@@ -25,7 +25,10 @@
        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.
+   The device can register multiple i2c_adapters (up to PIIX4_MAX_ADAPTERS).
+   For devices supporting multiple ports the i2c_adapter should provide
+   an i2c_algorithm to access them.
 */

 #include <linux/module.h>
@@ -40,6 +43,7 @@
 #include <linux/dmi.h>
 #include <linux/acpi.h>
 #include <linux/io.h>
+#include <linux/mutex.h>


 /* PIIX4 SMBus address offsets */
@@ -78,6 +82,9 @@
 #define PIIX4_WORD_DATA                0x0C
 #define PIIX4_BLOCK_DATA       0x14

+/* Multi-port constants */
+#define PIIX4_MAX_ADAPTERS 4
+
 /* insmod parameters */

 /* If force is set to anything different from 0, we forcibly enable the
@@ -97,7 +104,15 @@ MODULE_PARM_DESC(force_addr,
 static unsigned short piix4_smba;
 static int srvrworks_csb5_delay;
 static struct pci_driver piix4_driver;
-static struct i2c_adapter piix4_adapter;
+static struct i2c_adapter *piix4_adapters[PIIX4_MAX_ADAPTERS];
+
+/* SB800 globals */
+DEFINE_MUTEX(piix4_mutex_sb800);
+static u8 piix4_adapter_ports_sb800[4];
+static u8 piix4_sel_port_sb800 = -1;
+static const char *piix4_port_names_sb800[4] = {
+       "SDA0", "SDA2", "SDA3", "SDA4"
+};

 static struct dmi_system_id __devinitdata piix4_dmi_blacklist[] = {
        {
@@ -284,6 +299,8 @@ static int __devinit piix4_setup_sb800(s
        else
                dev_dbg(&PIIX4_dev->dev, "Using SMI# for SMBus.\n");

+       mutex_init(&piix4_mutex_sb800);
+
        dev_info(&PIIX4_dev->dev,
                 "SMBus Host Controller at 0x%x, revision %d\n",
                 piix4_smba, i2ccfg >> 4);
@@ -291,27 +308,27 @@ static int __devinit piix4_setup_sb800(s
        return 0;
 }

-static int piix4_transaction(void)
+static int piix4_transaction(struct i2c_adapter *adap)
 {
        int temp;
        int result = 0;
        int timeout = 0;

-       dev_dbg(&piix4_adapter.dev, "Transaction (pre): CNT=%02x, CMD=%02x, "
+       dev_dbg(&adap->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 */
        if ((temp = inb_p(SMBHSTSTS)) != 0x00) {
-               dev_dbg(&piix4_adapter.dev, "SMBus busy (%02x). "
+               dev_dbg(&adap->dev, "SMBus busy (%02x). "
                        "Resetting...\n", temp);
                outb_p(temp, SMBHSTSTS);
                if ((temp = inb_p(SMBHSTSTS)) != 0x00) {
-                       dev_err(&piix4_adapter.dev, "Failed! (%02x)\n", temp);
+                       dev_err(&adap->dev, "Failed! (%02x)\n", temp);
                        return -EBUSY;
                } else {
-                       dev_dbg(&piix4_adapter.dev, "Successful!\n");
+                       dev_dbg(&adap->dev, "Successful!\n");
                }
        }

@@ -330,35 +347,35 @@ static int piix4_transaction(void)

        /* If the SMBus is still busy, we give up */
        if (timeout == MAX_TIMEOUT) {
-               dev_err(&piix4_adapter.dev, "SMBus Timeout!\n");
+               dev_err(&adap->dev, "SMBus Timeout!\n");
                result = -ETIMEDOUT;
        }

        if (temp & 0x10) {
                result = -EIO;
-               dev_err(&piix4_adapter.dev, "Error: Failed bus transaction\n");
+               dev_err(&adap->dev, "Error: Failed bus transaction\n");
        }

        if (temp & 0x08) {
                result = -EIO;
-               dev_dbg(&piix4_adapter.dev, "Bus collision! SMBus may be "
+               dev_dbg(&adap->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 = -ENXIO;
-               dev_dbg(&piix4_adapter.dev, "Error: no response!\n");
+               dev_dbg(&adap->dev, "Error: no response!\n");
        }

        if (inb_p(SMBHSTSTS) != 0x00)
                outb_p(inb(SMBHSTSTS), SMBHSTSTS);

        if ((temp = inb_p(SMBHSTSTS)) != 0x00) {
-               dev_err(&piix4_adapter.dev, "Failed reset at end of "
+               dev_err(&adap->dev, "Failed reset at end of "
                        "transaction (%02x)\n", temp);
        }
-       dev_dbg(&piix4_adapter.dev, "Transaction (post): CNT=%02x, CMD=%02x, "
+       dev_dbg(&adap->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));
@@ -426,7 +443,7 @@ static s32 piix4_access(struct i2c_adapt

        outb_p((size & 0x1C) + (ENABLE_INT9 & 1), SMBHSTCNT);

-       status = piix4_transaction();
+       status = piix4_transaction(adap);
        if (status)
                return status;

@@ -454,6 +471,47 @@ static s32 piix4_access(struct i2c_adapt
        return 0;
 }

+/* Handles access to multiple SMBus ports on the SB800.
+ * The port is selected by bits 2:1 of the smb_en register (0x2C).
+ * Return negative errno on error.
+ */
+static s32 piix4_access_sb800(struct i2c_adapter * adap, u16 addr,
+                unsigned short flags, char read_write,
+                u8 command, int size, union i2c_smbus_data * data)
+{
+       unsigned short smba_idx = 0xcd6;
+       u8 smba_en_lo, smb_en = 0x2c;
+       u8 port;
+       int retval;
+
+       mutex_lock(&piix4_mutex_sb800);
+
+       port = *((u8*)adap->algo_data);
+       if(port != piix4_sel_port_sb800) {
+               if (!request_region(smba_idx, 2, "smba_idx")) {
+                       dev_err(&adap->dev, "SMBus base address index region "
+                               "0x%x already in use!\n", smba_idx);
+                       retval = -EBUSY;
+                       goto ERROR;
+               }
+               outb_p(smb_en, smba_idx);
+               smba_en_lo = inb_p(smba_idx + 1);
+               smba_en_lo = (smba_en_lo & ~6) | (port << 1);
+               outb_p(smba_en_lo, smba_idx + 1);
+               release_region(smba_idx, 2);
+               piix4_sel_port_sb800 = port;
+               dev_dbg(&adap->dev, "SB800 SMBus port set to %s\n",
piix4_port_names_sb800[port]);
+               msleep(50);
+       }
+
+       retval = piix4_access(adap, addr, flags, read_write, command, size, 
data);
+
+       ERROR:
+       mutex_unlock(&piix4_mutex_sb800);
+
+       return retval;
+}
+
 static u32 piix4_func(struct i2c_adapter *adapter)
 {
        return I2C_FUNC_SMBUS_QUICK | I2C_FUNC_SMBUS_BYTE |
@@ -461,17 +519,95 @@ static u32 piix4_func(struct i2c_adapter
            I2C_FUNC_SMBUS_BLOCK_DATA;
 }

-static const struct i2c_algorithm smbus_algorithm = {
+static const struct i2c_algorithm piix4_smbus_algorithm = {
        .smbus_xfer     = piix4_access,
        .functionality  = piix4_func,
 };

-static struct i2c_adapter piix4_adapter = {
-       .owner          = THIS_MODULE,
-       .class          = I2C_CLASS_HWMON | I2C_CLASS_SPD,
-       .algo           = &smbus_algorithm,
+static const struct i2c_algorithm piix4_smbus_algorithm_sb800 = {
+       .smbus_xfer     = piix4_access_sb800,
+       .functionality  = piix4_func,
 };

+static int __devinit piix4_setup_adapters(struct pci_dev *dev)
+{
+       int retval;
+
+       if (!(piix4_adapters[0] = kzalloc(sizeof(**piix4_adapters), 
GFP_KERNEL)))
+               return -ENOMEM;
+
+       piix4_adapters[0]->owner = THIS_MODULE;
+       piix4_adapters[0]->class = I2C_CLASS_HWMON | I2C_CLASS_SPD;
+       piix4_adapters[0]->algo  = &piix4_smbus_algorithm;
+
+       /* set up the sysfs linkage to our parent device */
+       piix4_adapters[0]->dev.parent = &dev->dev;
+
+       snprintf(piix4_adapters[0]->name, sizeof(piix4_adapters[0]->name),
+               "SMBus PIIX4 adapter at %04x", piix4_smba);
+
+       if ((retval = i2c_add_adapter(piix4_adapters[0]))) {
+               dev_err(&dev->dev, "Couldn't register adapter!\n");
+               kfree(piix4_adapters[0]);
+               piix4_adapters[0] = NULL;
+       }
+
+       return retval;
+}
+
+static int __devinit piix4_setup_adapters_sb800(struct pci_dev *dev)
+{
+       int i, retval;
+
+       dev_dbg(&dev->dev, "piix4_setup_adapters_sb800");
+
+       /* Register 4 adapters */
+       for (i = 0; i < 4; i++) {
+               if (!(piix4_adapters[i] = kzalloc(sizeof(**piix4_adapters), 
GFP_KERNEL))) {
+                       dev_err(&dev->dev, "Out of memory!\n");
+                       retval = -ENOMEM;
+                       goto ERROR;
+
+               }
+               piix4_adapters[i]->owner = THIS_MODULE;
+               piix4_adapters[i]->class = I2C_CLASS_HWMON | I2C_CLASS_SPD;
+               piix4_adapters[i]->algo  = &piix4_smbus_algorithm_sb800;
+
+               piix4_adapter_ports_sb800[i] = i;
+               piix4_adapters[i]->algo_data = &(piix4_adapter_ports_sb800[i]);
+
+               /* set up the sysfs linkage to our parent device */
+               piix4_adapters[i]->dev.parent = &dev->dev;
+
+               dev_dbg(&dev->dev, "sizeof(piix4_adapters[%d]->name) = %ld\n", 
i,
sizeof(piix4_adapters[i]->name));
+
+               snprintf(piix4_adapters[i]->name, 
sizeof(piix4_adapters[i]->name),
+                       "SB800 SMBus adapter %s at %04x",
+                       piix4_port_names_sb800[i], piix4_smba);
+               dev_dbg(&dev->dev, "piix4_adapters[%d]->name = \"%s\" (%ld)\n", 
i,
piix4_adapters[i]->name, strlen(piix4_adapters[i]->name));
+
+               if ((retval = i2c_add_adapter(piix4_adapters[i]))) {
+                       dev_err(&dev->dev, "Couldn't register SB800 adapter %s 
(%d)!\n",
+                                       piix4_port_names_sb800[i], retval);
+                       kfree(piix4_adapters[i]);
+                       piix4_adapters[i] = NULL;
+                       goto ERROR;
+               }
+       }
+
+       return retval;
+
+       ERROR:
+       dev_err(&dev->dev,
+                       "Error setting up SB800 adapters. Unregistering all 
adapters!\n");
+       for (i--; i >= 0; i--) {
+               i2c_del_adapter(piix4_adapters[i]);
+               kfree(piix4_adapters[i]);
+               piix4_adapters[i] = NULL;
+       }
+       return retval;
+}
+
 static const struct pci_device_id piix4_ids[] = {
        { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82371AB_3) },
        { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82443MX_3) },
@@ -501,26 +637,25 @@ static int __devinit piix4_probe(struct
 {
        int retval;

+       dev_dbg(&dev->dev, "piix4_probe: vendor: %d; device: %d; Rev: %d",
+                       dev->vendor, dev->device, dev->revision);
+
        if ((dev->vendor == PCI_VENDOR_ID_ATI &&
             dev->device == PCI_DEVICE_ID_ATI_SBX00_SMBUS &&
             dev->revision >= 0x40) ||
-           dev->vendor == PCI_VENDOR_ID_AMD)
-               /* base address location etc changed in SB800 */
-               retval = piix4_setup_sb800(dev, id);
-       else
-               retval = piix4_setup(dev, id);
-
-       if (retval)
-               return retval;
-
-       /* set up the sysfs linkage to our parent device */
-       piix4_adapter.dev.parent = &dev->dev;
-
-       snprintf(piix4_adapter.name, sizeof(piix4_adapter.name),
-               "SMBus PIIX4 adapter at %04x", piix4_smba);
+           dev->vendor == PCI_VENDOR_ID_AMD) {
+               dev_dbg(&dev->dev, "piix4_probe: SB800 detected");
+               /* SB800 uses a different base address and has 4 SMBus ports */
+               if ((retval = piix4_setup_sb800(dev, id)))
+                       return retval;
+               retval = piix4_setup_adapters_sb800(dev);
+       } else {
+               if ((retval = piix4_setup(dev, id)))
+                       return retval;
+               retval = piix4_setup_adapters(dev);
+       }

-       if ((retval = i2c_add_adapter(&piix4_adapter))) {
-               dev_err(&dev->dev, "Couldn't register adapter!\n");
+       if (retval) {
                release_region(piix4_smba, SMBIOSIZE);
                piix4_smba = 0;
        }
@@ -530,8 +665,16 @@ static int __devinit piix4_probe(struct

 static void __devexit piix4_remove(struct pci_dev *dev)
 {
+       int i;
+
+       for (i = 0; i < PIIX4_MAX_ADAPTERS; i++) {
+               if (piix4_adapters[i]) {
+                       i2c_del_adapter(piix4_adapters[i]);
+                       kfree(piix4_adapters[i]);
+                       piix4_adapters[i] = NULL;
+               }
+       }
        if (piix4_smba) {
-               i2c_del_adapter(&piix4_adapter);
                release_region(piix4_smba, SMBIOSIZE);
                piix4_smba = 0;
        }
--
To unsubscribe from this list: send the line "unsubscribe linux-i2c" in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

Reply via email to