[PATCH] i2c-piix4: support multiple PIIX4 SMBus hosts

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Some AMD chipsets have a second PIIX4-compatible host adapter accessible
through a second set of registers (e.g. SP5100). Moved the global base
address variable to an extension of struct i2c_adapter; added logic
to detect chipset known to have this feature. Tested on ASUS KCMA-D8 board.

Signed-off-by: Andrew Armenia <andrew@xxxxxxxxxxxxxxxx>
---
 drivers/i2c/busses/i2c-piix4.c |  242 ++++++++++++++++++++++++++++------------
 1 file changed, 170 insertions(+), 72 deletions(-)

diff --git a/drivers/i2c/busses/i2c-piix4.c b/drivers/i2c/busses/i2c-piix4.c
index c14d48d..a029a47 100644
--- a/drivers/i2c/busses/i2c-piix4.c
+++ b/drivers/i2c/busses/i2c-piix4.c
@@ -43,24 +43,25 @@
 
 
 /* PIIX4 SMBus address offsets */
-#define SMBHSTSTS	(0 + piix4_smba)
-#define SMBHSLVSTS	(1 + piix4_smba)
-#define SMBHSTCNT	(2 + piix4_smba)
-#define SMBHSTCMD	(3 + piix4_smba)
-#define SMBHSTADD	(4 + piix4_smba)
-#define SMBHSTDAT0	(5 + piix4_smba)
-#define SMBHSTDAT1	(6 + piix4_smba)
-#define SMBBLKDAT	(7 + piix4_smba)
-#define SMBSLVCNT	(8 + piix4_smba)
-#define SMBSHDWCMD	(9 + piix4_smba)
-#define SMBSLVEVT	(0xA + piix4_smba)
-#define SMBSLVDAT	(0xC + piix4_smba)
+#define SMBHSTSTS	(0 + piix4_adap->smba)
+#define SMBHSLVSTS	(1 + piix4_adap->smba)
+#define SMBHSTCNT	(2 + piix4_adap->smba)
+#define SMBHSTCMD	(3 + piix4_adap->smba)
+#define SMBHSTADD	(4 + piix4_adap->smba)
+#define SMBHSTDAT0	(5 + piix4_adap->smba)
+#define SMBHSTDAT1	(6 + piix4_adap->smba)
+#define SMBBLKDAT	(7 + piix4_adap->smba)
+#define SMBSLVCNT	(8 + piix4_adap->smba)
+#define SMBSHDWCMD	(9 + piix4_adap->smba)
+#define SMBSLVEVT	(0xA + piix4_adap->smba)
+#define SMBSLVDAT	(0xC + piix4_adap->smba)
 
 /* count for request_region */
 #define SMBIOSIZE	8
 
 /* PCI Address Constants */
 #define SMBBA		0x090
+#define SMBAUXBA        0x058
 #define SMBHSTCFG	0x0D2
 #define SMBSLVC		0x0D3
 #define SMBSHDW1	0x0D4
@@ -94,10 +95,16 @@ MODULE_PARM_DESC(force_addr,
 		 "Forcibly enable the PIIX4 at the given address. "
 		 "EXTREMELY DANGEROUS!");
 
-static unsigned short piix4_smba;
 static int srvrworks_csb5_delay;
 static struct pci_driver piix4_driver;
-static struct i2c_adapter piix4_adapter;
+
+struct piix4_adapter {
+	struct i2c_adapter i2c_adapter;
+	unsigned short smba;
+};
+
+static struct piix4_adapter piix4_main_adapter;
+static struct piix4_adapter piix4_aux_adapter;
 
 static struct dmi_system_id __devinitdata piix4_dmi_blacklist[] = {
 	{
@@ -128,7 +135,9 @@ static struct dmi_system_id __devinitdata piix4_dmi_ibm[] = {
 };
 
 static int __devinit piix4_setup(struct pci_dev *PIIX4_dev,
-				const struct pci_device_id *id)
+				const struct pci_device_id *id,
+				struct piix4_adapter *adp)
+
 {
 	unsigned char temp;
 
@@ -155,12 +164,12 @@ static int __devinit piix4_setup(struct pci_dev *PIIX4_dev,
 
 	/* Determine the address of the SMBus areas */
 	if (force_addr) {
-		piix4_smba = force_addr & 0xfff0;
+		adp->smba = force_addr & 0xfff0;
 		force = 0;
 	} else {
-		pci_read_config_word(PIIX4_dev, SMBBA, &piix4_smba);
-		piix4_smba &= 0xfff0;
-		if(piix4_smba == 0) {
+		pci_read_config_word(PIIX4_dev, SMBBA, &adp->smba);
+		adp->smba &= 0xfff0;
+		if (adp->smba == 0) {
 			dev_err(&PIIX4_dev->dev, "SMBus base address "
 				"uninitialized - upgrade BIOS or use "
 				"force_addr=0xaddr\n");
@@ -168,12 +177,12 @@ static int __devinit piix4_setup(struct pci_dev *PIIX4_dev,
 		}
 	}
 
-	if (acpi_check_region(piix4_smba, SMBIOSIZE, piix4_driver.name))
+	if (acpi_check_region(adp->smba, SMBIOSIZE, piix4_driver.name))
 		return -ENODEV;
 
-	if (!request_region(piix4_smba, SMBIOSIZE, piix4_driver.name)) {
+	if (!request_region(adp->smba, SMBIOSIZE, piix4_driver.name)) {
 		dev_err(&PIIX4_dev->dev, "SMBus region 0x%x already in use!\n",
-			piix4_smba);
+			adp->smba);
 		return -EBUSY;
 	}
 
@@ -183,10 +192,10 @@ static int __devinit piix4_setup(struct pci_dev *PIIX4_dev,
 	   sure, we disable the PIIX4 first. */
 	if (force_addr) {
 		pci_write_config_byte(PIIX4_dev, SMBHSTCFG, temp & 0xfe);
-		pci_write_config_word(PIIX4_dev, SMBBA, piix4_smba);
+		pci_write_config_word(PIIX4_dev, SMBBA, adp->smba);
 		pci_write_config_byte(PIIX4_dev, SMBHSTCFG, temp | 0x01);
 		dev_info(&PIIX4_dev->dev, "WARNING: SMBus interface set to "
-			"new address %04x!\n", piix4_smba);
+			"new address %04x!\n", adp->smba);
 	} else if ((temp & 1) == 0) {
 		if (force) {
 			/* This should never need to be done, but has been
@@ -205,8 +214,8 @@ static int __devinit piix4_setup(struct pci_dev *PIIX4_dev,
 		} else {
 			dev_err(&PIIX4_dev->dev,
 				"Host SMBus controller not enabled!\n");
-			release_region(piix4_smba, SMBIOSIZE);
-			piix4_smba = 0;
+			release_region(adp->smba, SMBIOSIZE);
+			adp->smba = 0;
 			return -ENODEV;
 		}
 	}
@@ -222,13 +231,46 @@ static int __devinit piix4_setup(struct pci_dev *PIIX4_dev,
 	pci_read_config_byte(PIIX4_dev, SMBREV, &temp);
 	dev_info(&PIIX4_dev->dev,
 		 "SMBus Host Controller at 0x%x, revision %d\n",
-		 piix4_smba, temp);
+		 adp->smba, temp);
+
+	return 0;
+}
+
+static int __devinit piix4_setup_aux(struct pci_dev *PIIX4_dev,
+				const struct pci_device_id *id,
+				struct piix4_adapter *adp,
+				unsigned short base_reg_addr)
+{
+	/* Read address of auxiliary SMBus controller */
+	pci_read_config_word(PIIX4_dev, base_reg_addr, &adp->smba);
+	adp->smba &= 0xffe0;
+
+	if (adp->smba == 0) {
+		dev_err(&PIIX4_dev->dev, "Aux SMBus base address "
+			"uninitialized - upgrade BIOS\n");
+		return -ENODEV;
+	}
+
+	if (acpi_check_region(adp->smba, SMBIOSIZE, piix4_driver.name))
+		return -ENODEV;
+
+	if (!request_region(adp->smba, SMBIOSIZE, piix4_driver.name)) {
+		dev_err(&PIIX4_dev->dev, "Aux SMBus region 0x%x already"
+			" in use!\n", adp->smba);
+		return -EBUSY;
+	}
+
+	dev_info(&PIIX4_dev->dev,
+		"Auxiliary SMBus Host Controller at 0x%x\n",
+		adp->smba
+	);
 
 	return 0;
 }
 
 static int __devinit piix4_setup_sb800(struct pci_dev *PIIX4_dev,
-				const struct pci_device_id *id)
+				const struct pci_device_id *id,
+				struct piix4_adapter *adp)
 {
 	unsigned short smba_idx = 0xcd6;
 	u8 smba_en_lo, smba_en_hi, i2ccfg, i2ccfg_offset = 0x10, smb_en = 0x2c;
@@ -258,26 +300,26 @@ static int __devinit piix4_setup_sb800(struct pci_dev *PIIX4_dev,
 		return -ENODEV;
 	}
 
-	piix4_smba = ((smba_en_hi << 8) | smba_en_lo) & 0xffe0;
-	if (acpi_check_region(piix4_smba, SMBIOSIZE, piix4_driver.name))
+	adp->smba = ((smba_en_hi << 8) | smba_en_lo) & 0xffe0;
+	if (acpi_check_region(adp->smba, SMBIOSIZE, piix4_driver.name))
 		return -ENODEV;
 
-	if (!request_region(piix4_smba, SMBIOSIZE, piix4_driver.name)) {
+	if (!request_region(adp->smba, SMBIOSIZE, piix4_driver.name)) {
 		dev_err(&PIIX4_dev->dev, "SMBus region 0x%x already in use!\n",
-			piix4_smba);
+			adp->smba);
 		return -EBUSY;
 	}
 
 	/* Request the SMBus I2C bus config region */
-	if (!request_region(piix4_smba + i2ccfg_offset, 1, "i2ccfg")) {
+	if (!request_region(adp->smba + i2ccfg_offset, 1, "i2ccfg")) {
 		dev_err(&PIIX4_dev->dev, "SMBus I2C bus config region "
-			"0x%x already in use!\n", piix4_smba + i2ccfg_offset);
-		release_region(piix4_smba, SMBIOSIZE);
-		piix4_smba = 0;
+			"0x%x already in use!\n", adp->smba + i2ccfg_offset);
+		release_region(adp->smba, SMBIOSIZE);
+		adp->smba = 0;
 		return -EBUSY;
 	}
-	i2ccfg = inb_p(piix4_smba + i2ccfg_offset);
-	release_region(piix4_smba + i2ccfg_offset, 1);
+	i2ccfg = inb_p(adp->smba + i2ccfg_offset);
+	release_region(adp->smba + i2ccfg_offset, 1);
 
 	if (i2ccfg & 1)
 		dev_dbg(&PIIX4_dev->dev, "Using IRQ for SMBus.\n");
@@ -286,32 +328,35 @@ static int __devinit piix4_setup_sb800(struct pci_dev *PIIX4_dev,
 
 	dev_info(&PIIX4_dev->dev,
 		 "SMBus Host Controller at 0x%x, revision %d\n",
-		 piix4_smba, i2ccfg >> 4);
+		 adp->smba, i2ccfg >> 4);
 
 	return 0;
 }
 
-static int piix4_transaction(void)
+static int piix4_transaction(struct piix4_adapter *piix4_adap)
 {
 	int temp;
 	int result = 0;
 	int timeout = 0;
 
-	dev_dbg(&piix4_adapter.dev, "Transaction (pre): CNT=%02x, CMD=%02x, "
+	dev_dbg(&piix4_adap->i2c_adapter.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(&piix4_adap->i2c_adapter.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(&piix4_adap->i2c_adapter.dev,
+				"Failed! (%02x)\n", temp);
 			return -EBUSY;
 		} else {
-			dev_dbg(&piix4_adapter.dev, "Successful!\n");
+			dev_dbg(&piix4_adap->i2c_adapter.dev,
+				"Successful!\n");
 		}
 	}
 
@@ -330,35 +375,39 @@ 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(&piix4_adap->i2c_adapter.dev, "SMBus Timeout!\n");
 		result = -ETIMEDOUT;
 	}
 
 	if (temp & 0x10) {
 		result = -EIO;
-		dev_err(&piix4_adapter.dev, "Error: Failed bus transaction\n");
+		dev_err(&piix4_adap->i2c_adapter.dev,
+			"Error: Failed bus transaction\n");
 	}
 
 	if (temp & 0x08) {
 		result = -EIO;
-		dev_dbg(&piix4_adapter.dev, "Bus collision! SMBus may be "
+		dev_dbg(&piix4_adap->i2c_adapter.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(&piix4_adap->i2c_adapter.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 "
-			"transaction (%02x)\n", temp);
+		dev_err(&piix4_adap->i2c_adapter.dev,
+			"Failed reset at end of transaction (%02x)\n", temp);
 	}
-	dev_dbg(&piix4_adapter.dev, "Transaction (post): CNT=%02x, CMD=%02x, "
+	dev_dbg(&piix4_adap->i2c_adapter.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));
@@ -373,6 +422,9 @@ static s32 piix4_access(struct i2c_adapter * adap, u16 addr,
 	int i, len;
 	int status;
 
+	struct piix4_adapter *piix4_adap = container_of(adap,
+		struct piix4_adapter, i2c_adapter);
+
 	switch (size) {
 	case I2C_SMBUS_QUICK:
 		outb_p((addr << 1) | read_write,
@@ -426,7 +478,7 @@ static s32 piix4_access(struct i2c_adapter * adap, u16 addr,
 
 	outb_p((size & 0x1C) + (ENABLE_INT9 & 1), SMBHSTCNT);
 
-	status = piix4_transaction();
+	status = piix4_transaction(piix4_adap);
 	if (status)
 		return status;
 
@@ -466,12 +518,6 @@ static const struct i2c_algorithm smbus_algorithm = {
 	.functionality	= piix4_func,
 };
 
-static struct i2c_adapter piix4_adapter = {
-	.owner		= THIS_MODULE,
-	.class		= I2C_CLASS_HWMON | I2C_CLASS_SPD,
-	.algo		= &smbus_algorithm,
-};
-
 static DEFINE_PCI_DEVICE_TABLE(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) },
@@ -496,45 +542,97 @@ static DEFINE_PCI_DEVICE_TABLE(piix4_ids) = {
 
 MODULE_DEVICE_TABLE (pci, piix4_ids);
 
+static void piix4_adapter_init(struct piix4_adapter *adap)
+{
+	memset(adap, 0, sizeof(struct piix4_adapter));
+
+	adap->i2c_adapter.owner = THIS_MODULE;
+	adap->i2c_adapter.class = I2C_CLASS_HWMON | I2C_CLASS_SPD;
+	adap->i2c_adapter.algo = &smbus_algorithm;
+}
+
 static int __devinit piix4_probe(struct pci_dev *dev,
 				const struct pci_device_id *id)
 {
 	int retval;
 
+	piix4_adapter_init(&piix4_main_adapter);
+	piix4_adapter_init(&piix4_aux_adapter);
+
 	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);
+		retval = piix4_setup_sb800(dev, id, &piix4_main_adapter);
 	else
-		retval = piix4_setup(dev, id);
+		retval = piix4_setup(dev, id, &piix4_main_adapter);
 
 	if (retval)
 		return retval;
 
 	/* set up the sysfs linkage to our parent device */
-	piix4_adapter.dev.parent = &dev->dev;
+	piix4_main_adapter.i2c_adapter.dev.parent = &dev->dev;
 
-	snprintf(piix4_adapter.name, sizeof(piix4_adapter.name),
-		"SMBus PIIX4 adapter at %04x", piix4_smba);
+	snprintf(piix4_main_adapter.i2c_adapter.name,
+		sizeof(piix4_main_adapter.i2c_adapter.name),
+		"SMBus PIIX4 adapter at %04x", piix4_main_adapter.smba);
 
-	if ((retval = i2c_add_adapter(&piix4_adapter))) {
+	retval = i2c_add_adapter(&piix4_main_adapter.i2c_adapter);
+	if (retval) {
 		dev_err(&dev->dev, "Couldn't register adapter!\n");
-		release_region(piix4_smba, SMBIOSIZE);
-		piix4_smba = 0;
+		release_region(piix4_main_adapter.smba, SMBIOSIZE);
+		piix4_main_adapter.smba = 0;
+		return retval;
+	}
+
+	if (dev->vendor == PCI_VENDOR_ID_ATI &&
+	    dev->device == PCI_DEVICE_ID_ATI_SBX00_SMBUS &&
+	    dev->revision == 0x3d) {
+		/* Setup aux SMBus port on certain AMD chipsets e.g. SP5100 */
+		retval = piix4_setup_aux(dev, id,
+			&piix4_aux_adapter, SMBAUXBA);
+
+		if (retval) {
+			dev_err(&dev->dev, "Aux SMBus setup failed!\n");
+			release_region(piix4_main_adapter.smba, SMBIOSIZE);
+			piix4_aux_adapter.smba = 0;
+			goto aux_fail;
+		}
+
+		piix4_aux_adapter.i2c_adapter.dev.parent = &dev->dev;
+		snprintf(piix4_aux_adapter.i2c_adapter.name,
+			sizeof(piix4_aux_adapter.i2c_adapter.name),
+			"SMBus PIIX4 adapter (aux) at %04x",
+			piix4_aux_adapter.smba);
+
+		retval = i2c_add_adapter(&piix4_aux_adapter.i2c_adapter);
+		if (retval) {
+			dev_err(&dev->dev,
+				"Couldn't register aux adapter!\n");
+			release_region(piix4_aux_adapter.smba, SMBIOSIZE);
+			piix4_aux_adapter.smba = 0;
+			goto aux_fail;
+		}
 	}
 
-	return retval;
+aux_fail:
+	return 0;
+}
+
+static void piix4_adapter_remove(struct piix4_adapter *adp)
+{
+	if (adp->smba) {
+		i2c_del_adapter(&adp->i2c_adapter);
+		release_region(adp->smba, SMBIOSIZE);
+		adp->smba = 0;
+	}
 }
 
 static void __devexit piix4_remove(struct pci_dev *dev)
 {
-	if (piix4_smba) {
-		i2c_del_adapter(&piix4_adapter);
-		release_region(piix4_smba, SMBIOSIZE);
-		piix4_smba = 0;
-	}
+	piix4_adapter_remove(&piix4_main_adapter);
+	piix4_adapter_remove(&piix4_aux_adapter);
 }
 
 static struct pci_driver piix4_driver = {
-- 
1.7.10

--
To unsubscribe from this list: send the line "unsubscribe linux-i2c" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html


[Index of Archives]     [Linux GPIO]     [Linux SPI]     [Linux Hardward Monitoring]     [LM Sensors]     [Linux USB Devel]     [Linux Media]     [Video for Linux]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]

  Powered by Linux