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