1c942fddfSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later 21da177e4SLinus Torvalds /* 31da177e4SLinus Torvalds Copyright (c) 1998 - 2002 Frodo Looijaard <frodol@dds.nl> and 41da177e4SLinus Torvalds Philip Edelbrock <phil@netroedge.com> 51da177e4SLinus Torvalds 61da177e4SLinus Torvalds */ 71da177e4SLinus Torvalds 81da177e4SLinus Torvalds /* 91da177e4SLinus Torvalds Supports: 101da177e4SLinus Torvalds Intel PIIX4, 440MX 11506a8b6cSFlavio Leitner Serverworks OSB4, CSB5, CSB6, HT-1000, HT-1100 122a2f7404SAndrew Armenia ATI IXP200, IXP300, IXP400, SB600, SB700/SP5100, SB800 13032f708bSShane Huang AMD Hudson-2, ML, CZ 1424beb83aSPu Wen Hygon CZ 151da177e4SLinus Torvalds SMSC Victory66 161da177e4SLinus Torvalds 172a2f7404SAndrew Armenia Note: we assume there can only be one device, with one or more 182a2f7404SAndrew Armenia SMBus interfaces. 192fee61d2SChristian Fetzer The device can register multiple i2c_adapters (up to PIIX4_MAX_ADAPTERS). 202fee61d2SChristian Fetzer For devices supporting multiple ports the i2c_adapter should provide 212fee61d2SChristian Fetzer an i2c_algorithm to access them. 221da177e4SLinus Torvalds */ 231da177e4SLinus Torvalds 241da177e4SLinus Torvalds #include <linux/module.h> 251da177e4SLinus Torvalds #include <linux/moduleparam.h> 261da177e4SLinus Torvalds #include <linux/pci.h> 271da177e4SLinus Torvalds #include <linux/kernel.h> 281da177e4SLinus Torvalds #include <linux/delay.h> 291da177e4SLinus Torvalds #include <linux/stddef.h> 301da177e4SLinus Torvalds #include <linux/ioport.h> 311da177e4SLinus Torvalds #include <linux/i2c.h> 32c415b303SDaniel J Blueman #include <linux/slab.h> 331da177e4SLinus Torvalds #include <linux/dmi.h> 3454fb4a05SJean Delvare #include <linux/acpi.h> 3521782180SH Hartley Sweeten #include <linux/io.h> 361da177e4SLinus Torvalds 371da177e4SLinus Torvalds 381da177e4SLinus Torvalds /* PIIX4 SMBus address offsets */ 391da177e4SLinus Torvalds #define SMBHSTSTS (0 + piix4_smba) 401da177e4SLinus Torvalds #define SMBHSLVSTS (1 + piix4_smba) 411da177e4SLinus Torvalds #define SMBHSTCNT (2 + piix4_smba) 421da177e4SLinus Torvalds #define SMBHSTCMD (3 + piix4_smba) 431da177e4SLinus Torvalds #define SMBHSTADD (4 + piix4_smba) 441da177e4SLinus Torvalds #define SMBHSTDAT0 (5 + piix4_smba) 451da177e4SLinus Torvalds #define SMBHSTDAT1 (6 + piix4_smba) 461da177e4SLinus Torvalds #define SMBBLKDAT (7 + piix4_smba) 471da177e4SLinus Torvalds #define SMBSLVCNT (8 + piix4_smba) 481da177e4SLinus Torvalds #define SMBSHDWCMD (9 + piix4_smba) 491da177e4SLinus Torvalds #define SMBSLVEVT (0xA + piix4_smba) 501da177e4SLinus Torvalds #define SMBSLVDAT (0xC + piix4_smba) 511da177e4SLinus Torvalds 521da177e4SLinus Torvalds /* count for request_region */ 53f43128c7SRicardo Ribalda #define SMBIOSIZE 9 541da177e4SLinus Torvalds 551da177e4SLinus Torvalds /* PCI Address Constants */ 561da177e4SLinus Torvalds #define SMBBA 0x090 571da177e4SLinus Torvalds #define SMBHSTCFG 0x0D2 581da177e4SLinus Torvalds #define SMBSLVC 0x0D3 591da177e4SLinus Torvalds #define SMBSHDW1 0x0D4 601da177e4SLinus Torvalds #define SMBSHDW2 0x0D5 611da177e4SLinus Torvalds #define SMBREV 0x0D6 621da177e4SLinus Torvalds 631da177e4SLinus Torvalds /* Other settings */ 641da177e4SLinus Torvalds #define MAX_TIMEOUT 500 651da177e4SLinus Torvalds #define ENABLE_INT9 0 661da177e4SLinus Torvalds 671da177e4SLinus Torvalds /* PIIX4 constants */ 681da177e4SLinus Torvalds #define PIIX4_QUICK 0x00 691da177e4SLinus Torvalds #define PIIX4_BYTE 0x04 701da177e4SLinus Torvalds #define PIIX4_BYTE_DATA 0x08 711da177e4SLinus Torvalds #define PIIX4_WORD_DATA 0x0C 721da177e4SLinus Torvalds #define PIIX4_BLOCK_DATA 0x14 731da177e4SLinus Torvalds 74ca2061e1SChristian Fetzer /* Multi-port constants */ 75ca2061e1SChristian Fetzer #define PIIX4_MAX_ADAPTERS 4 76528d53a1SJean Delvare #define HUDSON2_MAIN_PORTS 2 /* HUDSON2, KERNCZ reserves ports 3, 4 */ 77ca2061e1SChristian Fetzer 782fee61d2SChristian Fetzer /* SB800 constants */ 792fee61d2SChristian Fetzer #define SB800_PIIX4_SMB_IDX 0xcd6 8093102cb4STerry Bowman #define SB800_PIIX4_SMB_MAP_SIZE 2 812fee61d2SChristian Fetzer 8288fa2dfbSRicardo Ribalda Delgado #define KERNCZ_IMC_IDX 0x3e 8388fa2dfbSRicardo Ribalda Delgado #define KERNCZ_IMC_DATA 0x3f 8488fa2dfbSRicardo Ribalda Delgado 856befa3fdSJean Delvare /* 866befa3fdSJean Delvare * SB800 port is selected by bits 2:1 of the smb_en register (0x2c) 876befa3fdSJean Delvare * or the smb_sel register (0x2e), depending on bit 0 of register 0x2f. 886befa3fdSJean Delvare * Hudson-2/Bolton port is always selected by bits 2:1 of register 0x2f. 896befa3fdSJean Delvare */ 902fee61d2SChristian Fetzer #define SB800_PIIX4_PORT_IDX 0x2c 916befa3fdSJean Delvare #define SB800_PIIX4_PORT_IDX_ALT 0x2e 926befa3fdSJean Delvare #define SB800_PIIX4_PORT_IDX_SEL 0x2f 932fee61d2SChristian Fetzer #define SB800_PIIX4_PORT_IDX_MASK 0x06 940fe16195SGuenter Roeck #define SB800_PIIX4_PORT_IDX_SHIFT 1 950fe16195SGuenter Roeck 96c7c06a15SAndrew Cooks /* On kerncz and Hudson2, SmBus0Sel is at bit 20:19 of PMx00 DecodeEn */ 970fe16195SGuenter Roeck #define SB800_PIIX4_PORT_IDX_KERNCZ 0x02 980fe16195SGuenter Roeck #define SB800_PIIX4_PORT_IDX_MASK_KERNCZ 0x18 990fe16195SGuenter Roeck #define SB800_PIIX4_PORT_IDX_SHIFT_KERNCZ 3 1002fee61d2SChristian Fetzer 1017c148722STerry Bowman #define SB800_PIIX4_FCH_PM_ADDR 0xFED80300 1027c148722STerry Bowman #define SB800_PIIX4_FCH_PM_SIZE 8 1037c148722STerry Bowman 1041da177e4SLinus Torvalds /* insmod parameters */ 1051da177e4SLinus Torvalds 1061da177e4SLinus Torvalds /* If force is set to anything different from 0, we forcibly enable the 1071da177e4SLinus Torvalds PIIX4. DANGEROUS! */ 10860507095SJean Delvare static int force; 1091da177e4SLinus Torvalds module_param (force, int, 0); 1101da177e4SLinus Torvalds MODULE_PARM_DESC(force, "Forcibly enable the PIIX4. DANGEROUS!"); 1111da177e4SLinus Torvalds 1121da177e4SLinus Torvalds /* If force_addr is set to anything different from 0, we forcibly enable 1131da177e4SLinus Torvalds the PIIX4 at the given address. VERY DANGEROUS! */ 11460507095SJean Delvare static int force_addr; 115c78babccSDavid Howells module_param_hw(force_addr, int, ioport, 0); 1161da177e4SLinus Torvalds MODULE_PARM_DESC(force_addr, 1171da177e4SLinus Torvalds "Forcibly enable the PIIX4 at the given address. " 1181da177e4SLinus Torvalds "EXTREMELY DANGEROUS!"); 1191da177e4SLinus Torvalds 120b1c1759cSDavid Milburn static int srvrworks_csb5_delay; 121d6072f84SJean Delvare static struct pci_driver piix4_driver; 1221da177e4SLinus Torvalds 1230b255e92SBill Pemberton static const struct dmi_system_id piix4_dmi_blacklist[] = { 124c2fc54fcSJean Delvare { 125c2fc54fcSJean Delvare .ident = "Sapphire AM2RD790", 126c2fc54fcSJean Delvare .matches = { 127c2fc54fcSJean Delvare DMI_MATCH(DMI_BOARD_VENDOR, "SAPPHIRE Inc."), 128c2fc54fcSJean Delvare DMI_MATCH(DMI_BOARD_NAME, "PC-AM2RD790"), 129c2fc54fcSJean Delvare }, 130c2fc54fcSJean Delvare }, 131c2fc54fcSJean Delvare { 132c2fc54fcSJean Delvare .ident = "DFI Lanparty UT 790FX", 133c2fc54fcSJean Delvare .matches = { 134c2fc54fcSJean Delvare DMI_MATCH(DMI_BOARD_VENDOR, "DFI Inc."), 135c2fc54fcSJean Delvare DMI_MATCH(DMI_BOARD_NAME, "LP UT 790FX"), 136c2fc54fcSJean Delvare }, 137c2fc54fcSJean Delvare }, 138c2fc54fcSJean Delvare { } 139c2fc54fcSJean Delvare }; 140c2fc54fcSJean Delvare 141c2fc54fcSJean Delvare /* The IBM entry is in a separate table because we only check it 142c2fc54fcSJean Delvare on Intel-based systems */ 1430b255e92SBill Pemberton static const struct dmi_system_id piix4_dmi_ibm[] = { 1441da177e4SLinus Torvalds { 1451da177e4SLinus Torvalds .ident = "IBM", 1461da177e4SLinus Torvalds .matches = { DMI_MATCH(DMI_SYS_VENDOR, "IBM"), }, 1471da177e4SLinus Torvalds }, 1481da177e4SLinus Torvalds { }, 1491da177e4SLinus Torvalds }; 1501da177e4SLinus Torvalds 1516befa3fdSJean Delvare /* 1526befa3fdSJean Delvare * SB800 globals 1536befa3fdSJean Delvare */ 1546befa3fdSJean Delvare static u8 piix4_port_sel_sb800; 1550fe16195SGuenter Roeck static u8 piix4_port_mask_sb800; 1560fe16195SGuenter Roeck static u8 piix4_port_shift_sb800; 157725d2e3fSChristian Fetzer static const char *piix4_main_port_names_sb800[PIIX4_MAX_ADAPTERS] = { 15852795f6fSJean Delvare " port 0", " port 2", " port 3", " port 4" 159725d2e3fSChristian Fetzer }; 16052795f6fSJean Delvare static const char *piix4_aux_port_name_sb800 = " port 1"; 161725d2e3fSChristian Fetzer 1627c148722STerry Bowman struct sb800_mmio_cfg { 1637c148722STerry Bowman void __iomem *addr; 1647c148722STerry Bowman bool use_mmio; 1657c148722STerry Bowman }; 1667c148722STerry Bowman 16714a8086dSAndrew Armenia struct i2c_piix4_adapdata { 16814a8086dSAndrew Armenia unsigned short smba; 1692fee61d2SChristian Fetzer 1702fee61d2SChristian Fetzer /* SB800 */ 1712fee61d2SChristian Fetzer bool sb800_main; 17288fa2dfbSRicardo Ribalda Delgado bool notify_imc; 17333f5ccc3SJean Delvare u8 port; /* Port number, shifted */ 1747c148722STerry Bowman struct sb800_mmio_cfg mmio_cfg; 17514a8086dSAndrew Armenia }; 17614a8086dSAndrew Armenia 1777c148722STerry Bowman static int piix4_sb800_region_request(struct device *dev, 1787c148722STerry Bowman struct sb800_mmio_cfg *mmio_cfg) 179a3325d22STerry Bowman { 1807c148722STerry Bowman if (mmio_cfg->use_mmio) { 1817c148722STerry Bowman void __iomem *addr; 1827c148722STerry Bowman 183*8ad59b39SJean Delvare if (!request_mem_region_muxed(SB800_PIIX4_FCH_PM_ADDR, 1847c148722STerry Bowman SB800_PIIX4_FCH_PM_SIZE, 185*8ad59b39SJean Delvare "sb800_piix4_smb")) { 1867c148722STerry Bowman dev_err(dev, 1877c148722STerry Bowman "SMBus base address memory region 0x%x already in use.\n", 1887c148722STerry Bowman SB800_PIIX4_FCH_PM_ADDR); 1897c148722STerry Bowman return -EBUSY; 1907c148722STerry Bowman } 1917c148722STerry Bowman 1927c148722STerry Bowman addr = ioremap(SB800_PIIX4_FCH_PM_ADDR, 1937c148722STerry Bowman SB800_PIIX4_FCH_PM_SIZE); 1947c148722STerry Bowman if (!addr) { 195*8ad59b39SJean Delvare release_mem_region(SB800_PIIX4_FCH_PM_ADDR, 196*8ad59b39SJean Delvare SB800_PIIX4_FCH_PM_SIZE); 1977c148722STerry Bowman dev_err(dev, "SMBus base address mapping failed.\n"); 1987c148722STerry Bowman return -ENOMEM; 1997c148722STerry Bowman } 2007c148722STerry Bowman 2017c148722STerry Bowman mmio_cfg->addr = addr; 2027c148722STerry Bowman 2037c148722STerry Bowman return 0; 2047c148722STerry Bowman } 2057c148722STerry Bowman 206a3325d22STerry Bowman if (!request_muxed_region(SB800_PIIX4_SMB_IDX, SB800_PIIX4_SMB_MAP_SIZE, 207a3325d22STerry Bowman "sb800_piix4_smb")) { 208a3325d22STerry Bowman dev_err(dev, 209a3325d22STerry Bowman "SMBus base address index region 0x%x already in use.\n", 210a3325d22STerry Bowman SB800_PIIX4_SMB_IDX); 211a3325d22STerry Bowman return -EBUSY; 212a3325d22STerry Bowman } 213a3325d22STerry Bowman 214a3325d22STerry Bowman return 0; 215a3325d22STerry Bowman } 216a3325d22STerry Bowman 2177c148722STerry Bowman static void piix4_sb800_region_release(struct device *dev, 2187c148722STerry Bowman struct sb800_mmio_cfg *mmio_cfg) 219a3325d22STerry Bowman { 2207c148722STerry Bowman if (mmio_cfg->use_mmio) { 2217c148722STerry Bowman iounmap(mmio_cfg->addr); 222*8ad59b39SJean Delvare release_mem_region(SB800_PIIX4_FCH_PM_ADDR, 223*8ad59b39SJean Delvare SB800_PIIX4_FCH_PM_SIZE); 2247c148722STerry Bowman return; 2257c148722STerry Bowman } 2267c148722STerry Bowman 227a3325d22STerry Bowman release_region(SB800_PIIX4_SMB_IDX, SB800_PIIX4_SMB_MAP_SIZE); 228a3325d22STerry Bowman } 229a3325d22STerry Bowman 2306cf72f41STerry Bowman static bool piix4_sb800_use_mmio(struct pci_dev *PIIX4_dev) 2316cf72f41STerry Bowman { 2326cf72f41STerry Bowman /* 2336cf72f41STerry Bowman * cd6h/cd7h port I/O accesses can be disabled on AMD processors 2346cf72f41STerry Bowman * w/ SMBus PCI revision ID 0x51 or greater. MMIO is supported on 2356cf72f41STerry Bowman * the same processors and is the recommended access method. 2366cf72f41STerry Bowman */ 2376cf72f41STerry Bowman return (PIIX4_dev->vendor == PCI_VENDOR_ID_AMD && 2386cf72f41STerry Bowman PIIX4_dev->device == PCI_DEVICE_ID_AMD_KERNCZ_SMBUS && 2396cf72f41STerry Bowman PIIX4_dev->revision >= 0x51); 2406cf72f41STerry Bowman } 2416cf72f41STerry Bowman 2420b255e92SBill Pemberton static int piix4_setup(struct pci_dev *PIIX4_dev, 2431da177e4SLinus Torvalds const struct pci_device_id *id) 2441da177e4SLinus Torvalds { 2451da177e4SLinus Torvalds unsigned char temp; 24614a8086dSAndrew Armenia unsigned short piix4_smba; 2471da177e4SLinus Torvalds 248b1c1759cSDavid Milburn if ((PIIX4_dev->vendor == PCI_VENDOR_ID_SERVERWORKS) && 249b1c1759cSDavid Milburn (PIIX4_dev->device == PCI_DEVICE_ID_SERVERWORKS_CSB5)) 250b1c1759cSDavid Milburn srvrworks_csb5_delay = 1; 251b1c1759cSDavid Milburn 252c2fc54fcSJean Delvare /* On some motherboards, it was reported that accessing the SMBus 253c2fc54fcSJean Delvare caused severe hardware problems */ 254c2fc54fcSJean Delvare if (dmi_check_system(piix4_dmi_blacklist)) { 255c2fc54fcSJean Delvare dev_err(&PIIX4_dev->dev, 256c2fc54fcSJean Delvare "Accessing the SMBus on this system is unsafe!\n"); 257c2fc54fcSJean Delvare return -EPERM; 258c2fc54fcSJean Delvare } 259c2fc54fcSJean Delvare 2601da177e4SLinus Torvalds /* Don't access SMBus on IBM systems which get corrupted eeproms */ 261c2fc54fcSJean Delvare if (dmi_check_system(piix4_dmi_ibm) && 2621da177e4SLinus Torvalds PIIX4_dev->vendor == PCI_VENDOR_ID_INTEL) { 263f9ba6c04SJean Delvare dev_err(&PIIX4_dev->dev, "IBM system detected; this module " 2641da177e4SLinus Torvalds "may corrupt your serial eeprom! Refusing to load " 2651da177e4SLinus Torvalds "module!\n"); 2661da177e4SLinus Torvalds return -EPERM; 2671da177e4SLinus Torvalds } 2681da177e4SLinus Torvalds 2691da177e4SLinus Torvalds /* Determine the address of the SMBus areas */ 2701da177e4SLinus Torvalds if (force_addr) { 2711da177e4SLinus Torvalds piix4_smba = force_addr & 0xfff0; 2721da177e4SLinus Torvalds force = 0; 2731da177e4SLinus Torvalds } else { 2741da177e4SLinus Torvalds pci_read_config_word(PIIX4_dev, SMBBA, &piix4_smba); 2751da177e4SLinus Torvalds piix4_smba &= 0xfff0; 2761da177e4SLinus Torvalds if(piix4_smba == 0) { 277fa63cd56SJean Delvare dev_err(&PIIX4_dev->dev, "SMBus base address " 2781da177e4SLinus Torvalds "uninitialized - upgrade BIOS or use " 2791da177e4SLinus Torvalds "force_addr=0xaddr\n"); 2801da177e4SLinus Torvalds return -ENODEV; 2811da177e4SLinus Torvalds } 2821da177e4SLinus Torvalds } 2831da177e4SLinus Torvalds 28454fb4a05SJean Delvare if (acpi_check_region(piix4_smba, SMBIOSIZE, piix4_driver.name)) 28518669eabSJean Delvare return -ENODEV; 28654fb4a05SJean Delvare 287d6072f84SJean Delvare if (!request_region(piix4_smba, SMBIOSIZE, piix4_driver.name)) { 288fa63cd56SJean Delvare dev_err(&PIIX4_dev->dev, "SMBus region 0x%x already in use!\n", 2891da177e4SLinus Torvalds piix4_smba); 290fa63cd56SJean Delvare return -EBUSY; 2911da177e4SLinus Torvalds } 2921da177e4SLinus Torvalds 2931da177e4SLinus Torvalds pci_read_config_byte(PIIX4_dev, SMBHSTCFG, &temp); 2941da177e4SLinus Torvalds 2951da177e4SLinus Torvalds /* If force_addr is set, we program the new address here. Just to make 2961da177e4SLinus Torvalds sure, we disable the PIIX4 first. */ 2971da177e4SLinus Torvalds if (force_addr) { 2981da177e4SLinus Torvalds pci_write_config_byte(PIIX4_dev, SMBHSTCFG, temp & 0xfe); 2991da177e4SLinus Torvalds pci_write_config_word(PIIX4_dev, SMBBA, piix4_smba); 3001da177e4SLinus Torvalds pci_write_config_byte(PIIX4_dev, SMBHSTCFG, temp | 0x01); 3011da177e4SLinus Torvalds dev_info(&PIIX4_dev->dev, "WARNING: SMBus interface set to " 3021da177e4SLinus Torvalds "new address %04x!\n", piix4_smba); 3031da177e4SLinus Torvalds } else if ((temp & 1) == 0) { 3041da177e4SLinus Torvalds if (force) { 3051da177e4SLinus Torvalds /* This should never need to be done, but has been 3061da177e4SLinus Torvalds * noted that many Dell machines have the SMBus 3071da177e4SLinus Torvalds * interface on the PIIX4 disabled!? NOTE: This assumes 3081da177e4SLinus Torvalds * I/O space and other allocations WERE done by the 3091da177e4SLinus Torvalds * Bios! Don't complain if your hardware does weird 3101da177e4SLinus Torvalds * things after enabling this. :') Check for Bios 3111da177e4SLinus Torvalds * updates before resorting to this. 3121da177e4SLinus Torvalds */ 3131da177e4SLinus Torvalds pci_write_config_byte(PIIX4_dev, SMBHSTCFG, 3141da177e4SLinus Torvalds temp | 1); 3158117e41eSJoe Perches dev_notice(&PIIX4_dev->dev, 3168117e41eSJoe Perches "WARNING: SMBus interface has been FORCEFULLY ENABLED!\n"); 3171da177e4SLinus Torvalds } else { 3181da177e4SLinus Torvalds dev_err(&PIIX4_dev->dev, 31966f8a8ffSJean Delvare "SMBus Host Controller not enabled!\n"); 3201da177e4SLinus Torvalds release_region(piix4_smba, SMBIOSIZE); 3211da177e4SLinus Torvalds return -ENODEV; 3221da177e4SLinus Torvalds } 3231da177e4SLinus Torvalds } 3241da177e4SLinus Torvalds 32554aaa1caSRudolf Marek if (((temp & 0x0E) == 8) || ((temp & 0x0E) == 2)) 32666f8a8ffSJean Delvare dev_dbg(&PIIX4_dev->dev, "Using IRQ for SMBus\n"); 3271da177e4SLinus Torvalds else if ((temp & 0x0E) == 0) 32866f8a8ffSJean Delvare dev_dbg(&PIIX4_dev->dev, "Using SMI# for SMBus\n"); 3291da177e4SLinus Torvalds else 3301da177e4SLinus Torvalds dev_err(&PIIX4_dev->dev, "Illegal Interrupt configuration " 3311da177e4SLinus Torvalds "(or code out of date)!\n"); 3321da177e4SLinus Torvalds 3331da177e4SLinus Torvalds pci_read_config_byte(PIIX4_dev, SMBREV, &temp); 334fa63cd56SJean Delvare dev_info(&PIIX4_dev->dev, 335fa63cd56SJean Delvare "SMBus Host Controller at 0x%x, revision %d\n", 336fa63cd56SJean Delvare piix4_smba, temp); 3371da177e4SLinus Torvalds 33814a8086dSAndrew Armenia return piix4_smba; 3391da177e4SLinus Torvalds } 3401da177e4SLinus Torvalds 3410a59a24eSTerry Bowman static int piix4_setup_sb800_smba(struct pci_dev *PIIX4_dev, 3420a59a24eSTerry Bowman u8 smb_en, 3430a59a24eSTerry Bowman u8 aux, 3440a59a24eSTerry Bowman u8 *smb_en_status, 3450a59a24eSTerry Bowman unsigned short *piix4_smba) 3460a59a24eSTerry Bowman { 3477c148722STerry Bowman struct sb800_mmio_cfg mmio_cfg; 3480a59a24eSTerry Bowman u8 smba_en_lo; 3490a59a24eSTerry Bowman u8 smba_en_hi; 3500a59a24eSTerry Bowman int retval; 3510a59a24eSTerry Bowman 3526cf72f41STerry Bowman mmio_cfg.use_mmio = piix4_sb800_use_mmio(PIIX4_dev); 3537c148722STerry Bowman retval = piix4_sb800_region_request(&PIIX4_dev->dev, &mmio_cfg); 3540a59a24eSTerry Bowman if (retval) 3550a59a24eSTerry Bowman return retval; 3560a59a24eSTerry Bowman 35746967bc1STerry Bowman if (mmio_cfg.use_mmio) { 35846967bc1STerry Bowman smba_en_lo = ioread8(mmio_cfg.addr); 35946967bc1STerry Bowman smba_en_hi = ioread8(mmio_cfg.addr + 1); 36046967bc1STerry Bowman } else { 3610a59a24eSTerry Bowman outb_p(smb_en, SB800_PIIX4_SMB_IDX); 3620a59a24eSTerry Bowman smba_en_lo = inb_p(SB800_PIIX4_SMB_IDX + 1); 3630a59a24eSTerry Bowman outb_p(smb_en + 1, SB800_PIIX4_SMB_IDX); 3640a59a24eSTerry Bowman smba_en_hi = inb_p(SB800_PIIX4_SMB_IDX + 1); 36546967bc1STerry Bowman } 3660a59a24eSTerry Bowman 3677c148722STerry Bowman piix4_sb800_region_release(&PIIX4_dev->dev, &mmio_cfg); 3680a59a24eSTerry Bowman 3690a59a24eSTerry Bowman if (!smb_en) { 3700a59a24eSTerry Bowman *smb_en_status = smba_en_lo & 0x10; 3710a59a24eSTerry Bowman *piix4_smba = smba_en_hi << 8; 3720a59a24eSTerry Bowman if (aux) 3730a59a24eSTerry Bowman *piix4_smba |= 0x20; 3740a59a24eSTerry Bowman } else { 3750a59a24eSTerry Bowman *smb_en_status = smba_en_lo & 0x01; 3760a59a24eSTerry Bowman *piix4_smba = ((smba_en_hi << 8) | smba_en_lo) & 0xffe0; 3770a59a24eSTerry Bowman } 3780a59a24eSTerry Bowman 3790a59a24eSTerry Bowman if (!*smb_en_status) { 3800a59a24eSTerry Bowman dev_err(&PIIX4_dev->dev, 3810a59a24eSTerry Bowman "SMBus Host Controller not enabled!\n"); 3820a59a24eSTerry Bowman return -ENODEV; 3830a59a24eSTerry Bowman } 3840a59a24eSTerry Bowman 3850a59a24eSTerry Bowman return 0; 3860a59a24eSTerry Bowman } 3870a59a24eSTerry Bowman 3880b255e92SBill Pemberton static int piix4_setup_sb800(struct pci_dev *PIIX4_dev, 389a94dd00fSRudolf Marek const struct pci_device_id *id, u8 aux) 39087e1960eSShane Huang { 39114a8086dSAndrew Armenia unsigned short piix4_smba; 3920a59a24eSTerry Bowman u8 smb_en, smb_en_status, port_sel; 393032f708bSShane Huang u8 i2ccfg, i2ccfg_offset = 0x10; 3947c148722STerry Bowman struct sb800_mmio_cfg mmio_cfg; 395a3325d22STerry Bowman int retval; 39687e1960eSShane Huang 3973806e94bSCrane Cai /* SB800 and later SMBus does not support forcing address */ 39887e1960eSShane Huang if (force || force_addr) { 3993806e94bSCrane Cai dev_err(&PIIX4_dev->dev, "SMBus does not support " 40087e1960eSShane Huang "forcing address!\n"); 40187e1960eSShane Huang return -EINVAL; 40287e1960eSShane Huang } 40387e1960eSShane Huang 40487e1960eSShane Huang /* Determine the address of the SMBus areas */ 405032f708bSShane Huang if ((PIIX4_dev->vendor == PCI_VENDOR_ID_AMD && 406032f708bSShane Huang PIIX4_dev->device == PCI_DEVICE_ID_AMD_HUDSON2_SMBUS && 407032f708bSShane Huang PIIX4_dev->revision >= 0x41) || 408032f708bSShane Huang (PIIX4_dev->vendor == PCI_VENDOR_ID_AMD && 409bcb29994SVincent Wan PIIX4_dev->device == PCI_DEVICE_ID_AMD_KERNCZ_SMBUS && 41024beb83aSPu Wen PIIX4_dev->revision >= 0x49) || 41124beb83aSPu Wen (PIIX4_dev->vendor == PCI_VENDOR_ID_HYGON && 41224beb83aSPu Wen PIIX4_dev->device == PCI_DEVICE_ID_AMD_KERNCZ_SMBUS)) 413032f708bSShane Huang smb_en = 0x00; 414032f708bSShane Huang else 415a94dd00fSRudolf Marek smb_en = (aux) ? 0x28 : 0x2c; 416a94dd00fSRudolf Marek 4170a59a24eSTerry Bowman retval = piix4_setup_sb800_smba(PIIX4_dev, smb_en, aux, &smb_en_status, 4180a59a24eSTerry Bowman &piix4_smba); 4190a59a24eSTerry Bowman 420a3325d22STerry Bowman if (retval) 421a3325d22STerry Bowman return retval; 42204b6fcabSGuenter Roeck 42387e1960eSShane Huang if (acpi_check_region(piix4_smba, SMBIOSIZE, piix4_driver.name)) 42418669eabSJean Delvare return -ENODEV; 42587e1960eSShane Huang 42687e1960eSShane Huang if (!request_region(piix4_smba, SMBIOSIZE, piix4_driver.name)) { 42787e1960eSShane Huang dev_err(&PIIX4_dev->dev, "SMBus region 0x%x already in use!\n", 42887e1960eSShane Huang piix4_smba); 42987e1960eSShane Huang return -EBUSY; 43087e1960eSShane Huang } 43187e1960eSShane Huang 432a94dd00fSRudolf Marek /* Aux SMBus does not support IRQ information */ 433a94dd00fSRudolf Marek if (aux) { 434a94dd00fSRudolf Marek dev_info(&PIIX4_dev->dev, 43585fd0fe6SShane Huang "Auxiliary SMBus Host Controller at 0x%x\n", 43685fd0fe6SShane Huang piix4_smba); 437a94dd00fSRudolf Marek return piix4_smba; 438a94dd00fSRudolf Marek } 439a94dd00fSRudolf Marek 44087e1960eSShane Huang /* Request the SMBus I2C bus config region */ 44187e1960eSShane Huang if (!request_region(piix4_smba + i2ccfg_offset, 1, "i2ccfg")) { 44287e1960eSShane Huang dev_err(&PIIX4_dev->dev, "SMBus I2C bus config region " 44387e1960eSShane Huang "0x%x already in use!\n", piix4_smba + i2ccfg_offset); 44487e1960eSShane Huang release_region(piix4_smba, SMBIOSIZE); 44587e1960eSShane Huang return -EBUSY; 44687e1960eSShane Huang } 44787e1960eSShane Huang i2ccfg = inb_p(piix4_smba + i2ccfg_offset); 44887e1960eSShane Huang release_region(piix4_smba + i2ccfg_offset, 1); 44987e1960eSShane Huang 45087e1960eSShane Huang if (i2ccfg & 1) 45166f8a8ffSJean Delvare dev_dbg(&PIIX4_dev->dev, "Using IRQ for SMBus\n"); 45287e1960eSShane Huang else 45366f8a8ffSJean Delvare dev_dbg(&PIIX4_dev->dev, "Using SMI# for SMBus\n"); 45487e1960eSShane Huang 45587e1960eSShane Huang dev_info(&PIIX4_dev->dev, 45687e1960eSShane Huang "SMBus Host Controller at 0x%x, revision %d\n", 45787e1960eSShane Huang piix4_smba, i2ccfg >> 4); 45887e1960eSShane Huang 4596befa3fdSJean Delvare /* Find which register is used for port selection */ 46024beb83aSPu Wen if (PIIX4_dev->vendor == PCI_VENDOR_ID_AMD || 46124beb83aSPu Wen PIIX4_dev->vendor == PCI_VENDOR_ID_HYGON) { 462c7c06a15SAndrew Cooks if (PIIX4_dev->device == PCI_DEVICE_ID_AMD_KERNCZ_SMBUS || 463c7c06a15SAndrew Cooks (PIIX4_dev->device == PCI_DEVICE_ID_AMD_HUDSON2_SMBUS && 464c7c06a15SAndrew Cooks PIIX4_dev->revision >= 0x1F)) { 4650fe16195SGuenter Roeck piix4_port_sel_sb800 = SB800_PIIX4_PORT_IDX_KERNCZ; 4660fe16195SGuenter Roeck piix4_port_mask_sb800 = SB800_PIIX4_PORT_IDX_MASK_KERNCZ; 4670fe16195SGuenter Roeck piix4_port_shift_sb800 = SB800_PIIX4_PORT_IDX_SHIFT_KERNCZ; 468c7c06a15SAndrew Cooks } else { 4696befa3fdSJean Delvare piix4_port_sel_sb800 = SB800_PIIX4_PORT_IDX_ALT; 4700fe16195SGuenter Roeck piix4_port_mask_sb800 = SB800_PIIX4_PORT_IDX_MASK; 4710fe16195SGuenter Roeck piix4_port_shift_sb800 = SB800_PIIX4_PORT_IDX_SHIFT; 4720fe16195SGuenter Roeck } 4736befa3fdSJean Delvare } else { 4746cf72f41STerry Bowman mmio_cfg.use_mmio = piix4_sb800_use_mmio(PIIX4_dev); 4757c148722STerry Bowman retval = piix4_sb800_region_request(&PIIX4_dev->dev, &mmio_cfg); 476a3325d22STerry Bowman if (retval) { 47704b6fcabSGuenter Roeck release_region(piix4_smba, SMBIOSIZE); 478a3325d22STerry Bowman return retval; 47904b6fcabSGuenter Roeck } 48004b6fcabSGuenter Roeck 4816befa3fdSJean Delvare outb_p(SB800_PIIX4_PORT_IDX_SEL, SB800_PIIX4_SMB_IDX); 4826befa3fdSJean Delvare port_sel = inb_p(SB800_PIIX4_SMB_IDX + 1); 4836befa3fdSJean Delvare piix4_port_sel_sb800 = (port_sel & 0x01) ? 4846befa3fdSJean Delvare SB800_PIIX4_PORT_IDX_ALT : 4856befa3fdSJean Delvare SB800_PIIX4_PORT_IDX; 4860fe16195SGuenter Roeck piix4_port_mask_sb800 = SB800_PIIX4_PORT_IDX_MASK; 4870fe16195SGuenter Roeck piix4_port_shift_sb800 = SB800_PIIX4_PORT_IDX_SHIFT; 4887c148722STerry Bowman piix4_sb800_region_release(&PIIX4_dev->dev, &mmio_cfg); 4896befa3fdSJean Delvare } 4906befa3fdSJean Delvare 4916befa3fdSJean Delvare dev_info(&PIIX4_dev->dev, 4926befa3fdSJean Delvare "Using register 0x%02x for SMBus port selection\n", 4936befa3fdSJean Delvare (unsigned int)piix4_port_sel_sb800); 4946befa3fdSJean Delvare 49514a8086dSAndrew Armenia return piix4_smba; 49687e1960eSShane Huang } 49787e1960eSShane Huang 4980b255e92SBill Pemberton static int piix4_setup_aux(struct pci_dev *PIIX4_dev, 4992a2f7404SAndrew Armenia const struct pci_device_id *id, 5002a2f7404SAndrew Armenia unsigned short base_reg_addr) 5012a2f7404SAndrew Armenia { 5022a2f7404SAndrew Armenia /* Set up auxiliary SMBus controllers found on some 5032a2f7404SAndrew Armenia * AMD chipsets e.g. SP5100 (SB700 derivative) */ 5042a2f7404SAndrew Armenia 5052a2f7404SAndrew Armenia unsigned short piix4_smba; 5062a2f7404SAndrew Armenia 5072a2f7404SAndrew Armenia /* Read address of auxiliary SMBus controller */ 5082a2f7404SAndrew Armenia pci_read_config_word(PIIX4_dev, base_reg_addr, &piix4_smba); 5092a2f7404SAndrew Armenia if ((piix4_smba & 1) == 0) { 5102a2f7404SAndrew Armenia dev_dbg(&PIIX4_dev->dev, 5112a2f7404SAndrew Armenia "Auxiliary SMBus controller not enabled\n"); 5122a2f7404SAndrew Armenia return -ENODEV; 5132a2f7404SAndrew Armenia } 5142a2f7404SAndrew Armenia 5152a2f7404SAndrew Armenia piix4_smba &= 0xfff0; 5162a2f7404SAndrew Armenia if (piix4_smba == 0) { 5172a2f7404SAndrew Armenia dev_dbg(&PIIX4_dev->dev, 5182a2f7404SAndrew Armenia "Auxiliary SMBus base address uninitialized\n"); 5192a2f7404SAndrew Armenia return -ENODEV; 5202a2f7404SAndrew Armenia } 5212a2f7404SAndrew Armenia 5222a2f7404SAndrew Armenia if (acpi_check_region(piix4_smba, SMBIOSIZE, piix4_driver.name)) 5232a2f7404SAndrew Armenia return -ENODEV; 5242a2f7404SAndrew Armenia 5252a2f7404SAndrew Armenia if (!request_region(piix4_smba, SMBIOSIZE, piix4_driver.name)) { 5262a2f7404SAndrew Armenia dev_err(&PIIX4_dev->dev, "Auxiliary SMBus region 0x%x " 5272a2f7404SAndrew Armenia "already in use!\n", piix4_smba); 5282a2f7404SAndrew Armenia return -EBUSY; 5292a2f7404SAndrew Armenia } 5302a2f7404SAndrew Armenia 5312a2f7404SAndrew Armenia dev_info(&PIIX4_dev->dev, 5322a2f7404SAndrew Armenia "Auxiliary SMBus Host Controller at 0x%x\n", 5332a2f7404SAndrew Armenia piix4_smba); 5342a2f7404SAndrew Armenia 5352a2f7404SAndrew Armenia return piix4_smba; 5362a2f7404SAndrew Armenia } 5372a2f7404SAndrew Armenia 538e154bf6fSAndrew Armenia static int piix4_transaction(struct i2c_adapter *piix4_adapter) 5391da177e4SLinus Torvalds { 540e154bf6fSAndrew Armenia struct i2c_piix4_adapdata *adapdata = i2c_get_adapdata(piix4_adapter); 541e154bf6fSAndrew Armenia unsigned short piix4_smba = adapdata->smba; 5421da177e4SLinus Torvalds int temp; 5431da177e4SLinus Torvalds int result = 0; 5441da177e4SLinus Torvalds int timeout = 0; 5451da177e4SLinus Torvalds 546e154bf6fSAndrew Armenia dev_dbg(&piix4_adapter->dev, "Transaction (pre): CNT=%02x, CMD=%02x, " 5471da177e4SLinus Torvalds "ADD=%02x, DAT0=%02x, DAT1=%02x\n", inb_p(SMBHSTCNT), 5481da177e4SLinus Torvalds inb_p(SMBHSTCMD), inb_p(SMBHSTADD), inb_p(SMBHSTDAT0), 5491da177e4SLinus Torvalds inb_p(SMBHSTDAT1)); 5501da177e4SLinus Torvalds 5511da177e4SLinus Torvalds /* Make sure the SMBus host is ready to start transmitting */ 5521da177e4SLinus Torvalds if ((temp = inb_p(SMBHSTSTS)) != 0x00) { 553e154bf6fSAndrew Armenia dev_dbg(&piix4_adapter->dev, "SMBus busy (%02x). " 5541da177e4SLinus Torvalds "Resetting...\n", temp); 5551da177e4SLinus Torvalds outb_p(temp, SMBHSTSTS); 5561da177e4SLinus Torvalds if ((temp = inb_p(SMBHSTSTS)) != 0x00) { 557e154bf6fSAndrew Armenia dev_err(&piix4_adapter->dev, "Failed! (%02x)\n", temp); 55897140342SDavid Brownell return -EBUSY; 5591da177e4SLinus Torvalds } else { 560e154bf6fSAndrew Armenia dev_dbg(&piix4_adapter->dev, "Successful!\n"); 5611da177e4SLinus Torvalds } 5621da177e4SLinus Torvalds } 5631da177e4SLinus Torvalds 5641da177e4SLinus Torvalds /* start the transaction by setting bit 6 */ 5651da177e4SLinus Torvalds outb_p(inb(SMBHSTCNT) | 0x040, SMBHSTCNT); 5661da177e4SLinus Torvalds 5671da177e4SLinus Torvalds /* We will always wait for a fraction of a second! (See PIIX4 docs errata) */ 568b1c1759cSDavid Milburn if (srvrworks_csb5_delay) /* Extra delay for SERVERWORKS_CSB5 */ 5690e89b2feSGuenter Roeck usleep_range(2000, 2100); 570b1c1759cSDavid Milburn else 5710e89b2feSGuenter Roeck usleep_range(250, 500); 572b1c1759cSDavid Milburn 573b6a31950SRoel Kluin while ((++timeout < MAX_TIMEOUT) && 574b1c1759cSDavid Milburn ((temp = inb_p(SMBHSTSTS)) & 0x01)) 5750e89b2feSGuenter Roeck usleep_range(250, 500); 5761da177e4SLinus Torvalds 5771da177e4SLinus Torvalds /* If the SMBus is still busy, we give up */ 578b6a31950SRoel Kluin if (timeout == MAX_TIMEOUT) { 579e154bf6fSAndrew Armenia dev_err(&piix4_adapter->dev, "SMBus Timeout!\n"); 58097140342SDavid Brownell result = -ETIMEDOUT; 5811da177e4SLinus Torvalds } 5821da177e4SLinus Torvalds 5831da177e4SLinus Torvalds if (temp & 0x10) { 58497140342SDavid Brownell result = -EIO; 585e154bf6fSAndrew Armenia dev_err(&piix4_adapter->dev, "Error: Failed bus transaction\n"); 5861da177e4SLinus Torvalds } 5871da177e4SLinus Torvalds 5881da177e4SLinus Torvalds if (temp & 0x08) { 58997140342SDavid Brownell result = -EIO; 590e154bf6fSAndrew Armenia dev_dbg(&piix4_adapter->dev, "Bus collision! SMBus may be " 5911da177e4SLinus Torvalds "locked until next hard reset. (sorry!)\n"); 5921da177e4SLinus Torvalds /* Clock stops and slave is stuck in mid-transmission */ 5931da177e4SLinus Torvalds } 5941da177e4SLinus Torvalds 5951da177e4SLinus Torvalds if (temp & 0x04) { 59697140342SDavid Brownell result = -ENXIO; 597e154bf6fSAndrew Armenia dev_dbg(&piix4_adapter->dev, "Error: no response!\n"); 5981da177e4SLinus Torvalds } 5991da177e4SLinus Torvalds 6001da177e4SLinus Torvalds if (inb_p(SMBHSTSTS) != 0x00) 6011da177e4SLinus Torvalds outb_p(inb(SMBHSTSTS), SMBHSTSTS); 6021da177e4SLinus Torvalds 6031da177e4SLinus Torvalds if ((temp = inb_p(SMBHSTSTS)) != 0x00) { 604e154bf6fSAndrew Armenia dev_err(&piix4_adapter->dev, "Failed reset at end of " 6051da177e4SLinus Torvalds "transaction (%02x)\n", temp); 6061da177e4SLinus Torvalds } 607e154bf6fSAndrew Armenia dev_dbg(&piix4_adapter->dev, "Transaction (post): CNT=%02x, CMD=%02x, " 6081da177e4SLinus Torvalds "ADD=%02x, DAT0=%02x, DAT1=%02x\n", inb_p(SMBHSTCNT), 6091da177e4SLinus Torvalds inb_p(SMBHSTCMD), inb_p(SMBHSTADD), inb_p(SMBHSTDAT0), 6101da177e4SLinus Torvalds inb_p(SMBHSTDAT1)); 6111da177e4SLinus Torvalds return result; 6121da177e4SLinus Torvalds } 6131da177e4SLinus Torvalds 61497140342SDavid Brownell /* Return negative errno on error. */ 6151da177e4SLinus Torvalds static s32 piix4_access(struct i2c_adapter * adap, u16 addr, 6161da177e4SLinus Torvalds unsigned short flags, char read_write, 6171da177e4SLinus Torvalds u8 command, int size, union i2c_smbus_data * data) 6181da177e4SLinus Torvalds { 61914a8086dSAndrew Armenia struct i2c_piix4_adapdata *adapdata = i2c_get_adapdata(adap); 62014a8086dSAndrew Armenia unsigned short piix4_smba = adapdata->smba; 6211da177e4SLinus Torvalds int i, len; 62297140342SDavid Brownell int status; 6231da177e4SLinus Torvalds 6241da177e4SLinus Torvalds switch (size) { 6251da177e4SLinus Torvalds case I2C_SMBUS_QUICK: 626fa63cd56SJean Delvare outb_p((addr << 1) | read_write, 6271da177e4SLinus Torvalds SMBHSTADD); 6281da177e4SLinus Torvalds size = PIIX4_QUICK; 6291da177e4SLinus Torvalds break; 6301da177e4SLinus Torvalds case I2C_SMBUS_BYTE: 631fa63cd56SJean Delvare outb_p((addr << 1) | read_write, 6321da177e4SLinus Torvalds SMBHSTADD); 6331da177e4SLinus Torvalds if (read_write == I2C_SMBUS_WRITE) 6341da177e4SLinus Torvalds outb_p(command, SMBHSTCMD); 6351da177e4SLinus Torvalds size = PIIX4_BYTE; 6361da177e4SLinus Torvalds break; 6371da177e4SLinus Torvalds case I2C_SMBUS_BYTE_DATA: 638fa63cd56SJean Delvare outb_p((addr << 1) | read_write, 6391da177e4SLinus Torvalds SMBHSTADD); 6401da177e4SLinus Torvalds outb_p(command, SMBHSTCMD); 6411da177e4SLinus Torvalds if (read_write == I2C_SMBUS_WRITE) 6421da177e4SLinus Torvalds outb_p(data->byte, SMBHSTDAT0); 6431da177e4SLinus Torvalds size = PIIX4_BYTE_DATA; 6441da177e4SLinus Torvalds break; 6451da177e4SLinus Torvalds case I2C_SMBUS_WORD_DATA: 646fa63cd56SJean Delvare outb_p((addr << 1) | read_write, 6471da177e4SLinus Torvalds SMBHSTADD); 6481da177e4SLinus Torvalds outb_p(command, SMBHSTCMD); 6491da177e4SLinus Torvalds if (read_write == I2C_SMBUS_WRITE) { 6501da177e4SLinus Torvalds outb_p(data->word & 0xff, SMBHSTDAT0); 6511da177e4SLinus Torvalds outb_p((data->word & 0xff00) >> 8, SMBHSTDAT1); 6521da177e4SLinus Torvalds } 6531da177e4SLinus Torvalds size = PIIX4_WORD_DATA; 6541da177e4SLinus Torvalds break; 6551da177e4SLinus Torvalds case I2C_SMBUS_BLOCK_DATA: 656fa63cd56SJean Delvare outb_p((addr << 1) | read_write, 6571da177e4SLinus Torvalds SMBHSTADD); 6581da177e4SLinus Torvalds outb_p(command, SMBHSTCMD); 6591da177e4SLinus Torvalds if (read_write == I2C_SMBUS_WRITE) { 6601da177e4SLinus Torvalds len = data->block[0]; 661fa63cd56SJean Delvare if (len == 0 || len > I2C_SMBUS_BLOCK_MAX) 662fa63cd56SJean Delvare return -EINVAL; 6631da177e4SLinus Torvalds outb_p(len, SMBHSTDAT0); 664d7a4c763SWolfram Sang inb_p(SMBHSTCNT); /* Reset SMBBLKDAT */ 6651da177e4SLinus Torvalds for (i = 1; i <= len; i++) 6661da177e4SLinus Torvalds outb_p(data->block[i], SMBBLKDAT); 6671da177e4SLinus Torvalds } 6681da177e4SLinus Torvalds size = PIIX4_BLOCK_DATA; 6691da177e4SLinus Torvalds break; 670ac7fc4fbSJean Delvare default: 671ac7fc4fbSJean Delvare dev_warn(&adap->dev, "Unsupported transaction %d\n", size); 672ac7fc4fbSJean Delvare return -EOPNOTSUPP; 6731da177e4SLinus Torvalds } 6741da177e4SLinus Torvalds 6751da177e4SLinus Torvalds outb_p((size & 0x1C) + (ENABLE_INT9 & 1), SMBHSTCNT); 6761da177e4SLinus Torvalds 677e154bf6fSAndrew Armenia status = piix4_transaction(adap); 67897140342SDavid Brownell if (status) 67997140342SDavid Brownell return status; 6801da177e4SLinus Torvalds 6811da177e4SLinus Torvalds if ((read_write == I2C_SMBUS_WRITE) || (size == PIIX4_QUICK)) 6821da177e4SLinus Torvalds return 0; 6831da177e4SLinus Torvalds 6841da177e4SLinus Torvalds 6851da177e4SLinus Torvalds switch (size) { 6863578a075SJean Delvare case PIIX4_BYTE: 6871da177e4SLinus Torvalds case PIIX4_BYTE_DATA: 6881da177e4SLinus Torvalds data->byte = inb_p(SMBHSTDAT0); 6891da177e4SLinus Torvalds break; 6901da177e4SLinus Torvalds case PIIX4_WORD_DATA: 6911da177e4SLinus Torvalds data->word = inb_p(SMBHSTDAT0) + (inb_p(SMBHSTDAT1) << 8); 6921da177e4SLinus Torvalds break; 6931da177e4SLinus Torvalds case PIIX4_BLOCK_DATA: 6941da177e4SLinus Torvalds data->block[0] = inb_p(SMBHSTDAT0); 695fa63cd56SJean Delvare if (data->block[0] == 0 || data->block[0] > I2C_SMBUS_BLOCK_MAX) 696fa63cd56SJean Delvare return -EPROTO; 697d7a4c763SWolfram Sang inb_p(SMBHSTCNT); /* Reset SMBBLKDAT */ 6981da177e4SLinus Torvalds for (i = 1; i <= data->block[0]; i++) 6991da177e4SLinus Torvalds data->block[i] = inb_p(SMBBLKDAT); 7001da177e4SLinus Torvalds break; 7011da177e4SLinus Torvalds } 7021da177e4SLinus Torvalds return 0; 7031da177e4SLinus Torvalds } 7041da177e4SLinus Torvalds 70588fa2dfbSRicardo Ribalda Delgado static uint8_t piix4_imc_read(uint8_t idx) 70688fa2dfbSRicardo Ribalda Delgado { 70788fa2dfbSRicardo Ribalda Delgado outb_p(idx, KERNCZ_IMC_IDX); 70888fa2dfbSRicardo Ribalda Delgado return inb_p(KERNCZ_IMC_DATA); 70988fa2dfbSRicardo Ribalda Delgado } 71088fa2dfbSRicardo Ribalda Delgado 71188fa2dfbSRicardo Ribalda Delgado static void piix4_imc_write(uint8_t idx, uint8_t value) 71288fa2dfbSRicardo Ribalda Delgado { 71388fa2dfbSRicardo Ribalda Delgado outb_p(idx, KERNCZ_IMC_IDX); 71488fa2dfbSRicardo Ribalda Delgado outb_p(value, KERNCZ_IMC_DATA); 71588fa2dfbSRicardo Ribalda Delgado } 71688fa2dfbSRicardo Ribalda Delgado 71788fa2dfbSRicardo Ribalda Delgado static int piix4_imc_sleep(void) 71888fa2dfbSRicardo Ribalda Delgado { 71988fa2dfbSRicardo Ribalda Delgado int timeout = MAX_TIMEOUT; 72088fa2dfbSRicardo Ribalda Delgado 72188fa2dfbSRicardo Ribalda Delgado if (!request_muxed_region(KERNCZ_IMC_IDX, 2, "smbus_kerncz_imc")) 72288fa2dfbSRicardo Ribalda Delgado return -EBUSY; 72388fa2dfbSRicardo Ribalda Delgado 72488fa2dfbSRicardo Ribalda Delgado /* clear response register */ 72588fa2dfbSRicardo Ribalda Delgado piix4_imc_write(0x82, 0x00); 72688fa2dfbSRicardo Ribalda Delgado /* request ownership flag */ 72788fa2dfbSRicardo Ribalda Delgado piix4_imc_write(0x83, 0xB4); 72888fa2dfbSRicardo Ribalda Delgado /* kick off IMC Mailbox command 96 */ 72988fa2dfbSRicardo Ribalda Delgado piix4_imc_write(0x80, 0x96); 73088fa2dfbSRicardo Ribalda Delgado 73188fa2dfbSRicardo Ribalda Delgado while (timeout--) { 73288fa2dfbSRicardo Ribalda Delgado if (piix4_imc_read(0x82) == 0xfa) { 73388fa2dfbSRicardo Ribalda Delgado release_region(KERNCZ_IMC_IDX, 2); 73488fa2dfbSRicardo Ribalda Delgado return 0; 73588fa2dfbSRicardo Ribalda Delgado } 73688fa2dfbSRicardo Ribalda Delgado usleep_range(1000, 2000); 73788fa2dfbSRicardo Ribalda Delgado } 73888fa2dfbSRicardo Ribalda Delgado 73988fa2dfbSRicardo Ribalda Delgado release_region(KERNCZ_IMC_IDX, 2); 74088fa2dfbSRicardo Ribalda Delgado return -ETIMEDOUT; 74188fa2dfbSRicardo Ribalda Delgado } 74288fa2dfbSRicardo Ribalda Delgado 74388fa2dfbSRicardo Ribalda Delgado static void piix4_imc_wakeup(void) 74488fa2dfbSRicardo Ribalda Delgado { 74588fa2dfbSRicardo Ribalda Delgado int timeout = MAX_TIMEOUT; 74688fa2dfbSRicardo Ribalda Delgado 74788fa2dfbSRicardo Ribalda Delgado if (!request_muxed_region(KERNCZ_IMC_IDX, 2, "smbus_kerncz_imc")) 74888fa2dfbSRicardo Ribalda Delgado return; 74988fa2dfbSRicardo Ribalda Delgado 75088fa2dfbSRicardo Ribalda Delgado /* clear response register */ 75188fa2dfbSRicardo Ribalda Delgado piix4_imc_write(0x82, 0x00); 75288fa2dfbSRicardo Ribalda Delgado /* release ownership flag */ 75388fa2dfbSRicardo Ribalda Delgado piix4_imc_write(0x83, 0xB5); 75488fa2dfbSRicardo Ribalda Delgado /* kick off IMC Mailbox command 96 */ 75588fa2dfbSRicardo Ribalda Delgado piix4_imc_write(0x80, 0x96); 75688fa2dfbSRicardo Ribalda Delgado 75788fa2dfbSRicardo Ribalda Delgado while (timeout--) { 75888fa2dfbSRicardo Ribalda Delgado if (piix4_imc_read(0x82) == 0xfa) 75988fa2dfbSRicardo Ribalda Delgado break; 76088fa2dfbSRicardo Ribalda Delgado usleep_range(1000, 2000); 76188fa2dfbSRicardo Ribalda Delgado } 76288fa2dfbSRicardo Ribalda Delgado 76388fa2dfbSRicardo Ribalda Delgado release_region(KERNCZ_IMC_IDX, 2); 76488fa2dfbSRicardo Ribalda Delgado } 76588fa2dfbSRicardo Ribalda Delgado 766381a3083STerry Bowman static int piix4_sb800_port_sel(u8 port, struct sb800_mmio_cfg *mmio_cfg) 767fbafbd51STerry Bowman { 768fbafbd51STerry Bowman u8 smba_en_lo, val; 769fbafbd51STerry Bowman 770381a3083STerry Bowman if (mmio_cfg->use_mmio) { 771381a3083STerry Bowman smba_en_lo = ioread8(mmio_cfg->addr + piix4_port_sel_sb800); 772381a3083STerry Bowman val = (smba_en_lo & ~piix4_port_mask_sb800) | port; 773381a3083STerry Bowman if (smba_en_lo != val) 774381a3083STerry Bowman iowrite8(val, mmio_cfg->addr + piix4_port_sel_sb800); 775381a3083STerry Bowman 776381a3083STerry Bowman return (smba_en_lo & piix4_port_mask_sb800); 777381a3083STerry Bowman } 778381a3083STerry Bowman 779fbafbd51STerry Bowman outb_p(piix4_port_sel_sb800, SB800_PIIX4_SMB_IDX); 780fbafbd51STerry Bowman smba_en_lo = inb_p(SB800_PIIX4_SMB_IDX + 1); 781fbafbd51STerry Bowman 782fbafbd51STerry Bowman val = (smba_en_lo & ~piix4_port_mask_sb800) | port; 783fbafbd51STerry Bowman if (smba_en_lo != val) 784fbafbd51STerry Bowman outb_p(val, SB800_PIIX4_SMB_IDX + 1); 785fbafbd51STerry Bowman 786fbafbd51STerry Bowman return (smba_en_lo & piix4_port_mask_sb800); 787fbafbd51STerry Bowman } 788fbafbd51STerry Bowman 7892fee61d2SChristian Fetzer /* 7902fee61d2SChristian Fetzer * Handles access to multiple SMBus ports on the SB800. 7912fee61d2SChristian Fetzer * The port is selected by bits 2:1 of the smb_en register (0x2c). 7922fee61d2SChristian Fetzer * Returns negative errno on error. 7932fee61d2SChristian Fetzer * 7942fee61d2SChristian Fetzer * Note: The selected port must be returned to the initial selection to avoid 7952fee61d2SChristian Fetzer * problems on certain systems. 7962fee61d2SChristian Fetzer */ 7972fee61d2SChristian Fetzer static s32 piix4_access_sb800(struct i2c_adapter *adap, u16 addr, 7982fee61d2SChristian Fetzer unsigned short flags, char read_write, 7992fee61d2SChristian Fetzer u8 command, int size, union i2c_smbus_data *data) 8002fee61d2SChristian Fetzer { 8012fee61d2SChristian Fetzer struct i2c_piix4_adapdata *adapdata = i2c_get_adapdata(adap); 802701dc207SRicardo Ribalda unsigned short piix4_smba = adapdata->smba; 803701dc207SRicardo Ribalda int retries = MAX_TIMEOUT; 804701dc207SRicardo Ribalda int smbslvcnt; 805fbafbd51STerry Bowman u8 prev_port; 8062fee61d2SChristian Fetzer int retval; 8072fee61d2SChristian Fetzer 8087c148722STerry Bowman retval = piix4_sb800_region_request(&adap->dev, &adapdata->mmio_cfg); 809a3325d22STerry Bowman if (retval) 810a3325d22STerry Bowman return retval; 811bbb27fc3SRicardo Ribalda 812701dc207SRicardo Ribalda /* Request the SMBUS semaphore, avoid conflicts with the IMC */ 813701dc207SRicardo Ribalda smbslvcnt = inb_p(SMBSLVCNT); 814701dc207SRicardo Ribalda do { 815701dc207SRicardo Ribalda outb_p(smbslvcnt | 0x10, SMBSLVCNT); 816701dc207SRicardo Ribalda 817701dc207SRicardo Ribalda /* Check the semaphore status */ 818701dc207SRicardo Ribalda smbslvcnt = inb_p(SMBSLVCNT); 819701dc207SRicardo Ribalda if (smbslvcnt & 0x10) 820701dc207SRicardo Ribalda break; 821701dc207SRicardo Ribalda 822701dc207SRicardo Ribalda usleep_range(1000, 2000); 823701dc207SRicardo Ribalda } while (--retries); 824701dc207SRicardo Ribalda /* SMBus is still owned by the IMC, we give up */ 825bbb27fc3SRicardo Ribalda if (!retries) { 82604b6fcabSGuenter Roeck retval = -EBUSY; 82704b6fcabSGuenter Roeck goto release; 828bbb27fc3SRicardo Ribalda } 8292fee61d2SChristian Fetzer 83088fa2dfbSRicardo Ribalda Delgado /* 83188fa2dfbSRicardo Ribalda Delgado * Notify the IMC (Integrated Micro Controller) if required. 83288fa2dfbSRicardo Ribalda Delgado * Among other responsibilities, the IMC is in charge of monitoring 83388fa2dfbSRicardo Ribalda Delgado * the System fans and temperature sensors, and act accordingly. 83488fa2dfbSRicardo Ribalda Delgado * All this is done through SMBus and can/will collide 83588fa2dfbSRicardo Ribalda Delgado * with our transactions if they are long (BLOCK_DATA). 83688fa2dfbSRicardo Ribalda Delgado * Therefore we need to request the ownership flag during those 83788fa2dfbSRicardo Ribalda Delgado * transactions. 83888fa2dfbSRicardo Ribalda Delgado */ 83988fa2dfbSRicardo Ribalda Delgado if ((size == I2C_SMBUS_BLOCK_DATA) && adapdata->notify_imc) { 84088fa2dfbSRicardo Ribalda Delgado int ret; 84188fa2dfbSRicardo Ribalda Delgado 84288fa2dfbSRicardo Ribalda Delgado ret = piix4_imc_sleep(); 84388fa2dfbSRicardo Ribalda Delgado switch (ret) { 84488fa2dfbSRicardo Ribalda Delgado case -EBUSY: 84588fa2dfbSRicardo Ribalda Delgado dev_warn(&adap->dev, 84688fa2dfbSRicardo Ribalda Delgado "IMC base address index region 0x%x already in use.\n", 84788fa2dfbSRicardo Ribalda Delgado KERNCZ_IMC_IDX); 84888fa2dfbSRicardo Ribalda Delgado break; 84988fa2dfbSRicardo Ribalda Delgado case -ETIMEDOUT: 85088fa2dfbSRicardo Ribalda Delgado dev_warn(&adap->dev, 85188fa2dfbSRicardo Ribalda Delgado "Failed to communicate with the IMC.\n"); 85288fa2dfbSRicardo Ribalda Delgado break; 85388fa2dfbSRicardo Ribalda Delgado default: 85488fa2dfbSRicardo Ribalda Delgado break; 85588fa2dfbSRicardo Ribalda Delgado } 85688fa2dfbSRicardo Ribalda Delgado 85788fa2dfbSRicardo Ribalda Delgado /* If IMC communication fails do not retry */ 85888fa2dfbSRicardo Ribalda Delgado if (ret) { 85988fa2dfbSRicardo Ribalda Delgado dev_warn(&adap->dev, 86088fa2dfbSRicardo Ribalda Delgado "Continuing without IMC notification.\n"); 86188fa2dfbSRicardo Ribalda Delgado adapdata->notify_imc = false; 86288fa2dfbSRicardo Ribalda Delgado } 86388fa2dfbSRicardo Ribalda Delgado } 86488fa2dfbSRicardo Ribalda Delgado 865381a3083STerry Bowman prev_port = piix4_sb800_port_sel(adapdata->port, &adapdata->mmio_cfg); 8662fee61d2SChristian Fetzer 8672fee61d2SChristian Fetzer retval = piix4_access(adap, addr, flags, read_write, 8682fee61d2SChristian Fetzer command, size, data); 8692fee61d2SChristian Fetzer 870381a3083STerry Bowman piix4_sb800_port_sel(prev_port, &adapdata->mmio_cfg); 8712fee61d2SChristian Fetzer 872701dc207SRicardo Ribalda /* Release the semaphore */ 873701dc207SRicardo Ribalda outb_p(smbslvcnt | 0x20, SMBSLVCNT); 874701dc207SRicardo Ribalda 87588fa2dfbSRicardo Ribalda Delgado if ((size == I2C_SMBUS_BLOCK_DATA) && adapdata->notify_imc) 87688fa2dfbSRicardo Ribalda Delgado piix4_imc_wakeup(); 87788fa2dfbSRicardo Ribalda Delgado 87804b6fcabSGuenter Roeck release: 8797c148722STerry Bowman piix4_sb800_region_release(&adap->dev, &adapdata->mmio_cfg); 8802fee61d2SChristian Fetzer return retval; 8812fee61d2SChristian Fetzer } 8822fee61d2SChristian Fetzer 8831da177e4SLinus Torvalds static u32 piix4_func(struct i2c_adapter *adapter) 8841da177e4SLinus Torvalds { 8851da177e4SLinus Torvalds return I2C_FUNC_SMBUS_QUICK | I2C_FUNC_SMBUS_BYTE | 8861da177e4SLinus Torvalds I2C_FUNC_SMBUS_BYTE_DATA | I2C_FUNC_SMBUS_WORD_DATA | 8871da177e4SLinus Torvalds I2C_FUNC_SMBUS_BLOCK_DATA; 8881da177e4SLinus Torvalds } 8891da177e4SLinus Torvalds 8908f9082c5SJean Delvare static const struct i2c_algorithm smbus_algorithm = { 8911da177e4SLinus Torvalds .smbus_xfer = piix4_access, 8921da177e4SLinus Torvalds .functionality = piix4_func, 8931da177e4SLinus Torvalds }; 8941da177e4SLinus Torvalds 8952fee61d2SChristian Fetzer static const struct i2c_algorithm piix4_smbus_algorithm_sb800 = { 8962fee61d2SChristian Fetzer .smbus_xfer = piix4_access_sb800, 8972fee61d2SChristian Fetzer .functionality = piix4_func, 8982fee61d2SChristian Fetzer }; 8992fee61d2SChristian Fetzer 900392debf1SJingoo Han static const struct pci_device_id piix4_ids[] = { 9019b7389c0SJean Delvare { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82371AB_3) }, 9029b7389c0SJean Delvare { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82443MX_3) }, 9039b7389c0SJean Delvare { PCI_DEVICE(PCI_VENDOR_ID_EFAR, PCI_DEVICE_ID_EFAR_SLC90E66_3) }, 9049b7389c0SJean Delvare { PCI_DEVICE(PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_IXP200_SMBUS) }, 9059b7389c0SJean Delvare { PCI_DEVICE(PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_IXP300_SMBUS) }, 9069b7389c0SJean Delvare { PCI_DEVICE(PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_IXP400_SMBUS) }, 9079b7389c0SJean Delvare { PCI_DEVICE(PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_SBX00_SMBUS) }, 9083806e94bSCrane Cai { PCI_DEVICE(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_HUDSON2_SMBUS) }, 909bcb29994SVincent Wan { PCI_DEVICE(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_KERNCZ_SMBUS) }, 91024beb83aSPu Wen { PCI_DEVICE(PCI_VENDOR_ID_HYGON, PCI_DEVICE_ID_AMD_KERNCZ_SMBUS) }, 9119b7389c0SJean Delvare { PCI_DEVICE(PCI_VENDOR_ID_SERVERWORKS, 9129b7389c0SJean Delvare PCI_DEVICE_ID_SERVERWORKS_OSB4) }, 9139b7389c0SJean Delvare { PCI_DEVICE(PCI_VENDOR_ID_SERVERWORKS, 9149b7389c0SJean Delvare PCI_DEVICE_ID_SERVERWORKS_CSB5) }, 9159b7389c0SJean Delvare { PCI_DEVICE(PCI_VENDOR_ID_SERVERWORKS, 9169b7389c0SJean Delvare PCI_DEVICE_ID_SERVERWORKS_CSB6) }, 9179b7389c0SJean Delvare { PCI_DEVICE(PCI_VENDOR_ID_SERVERWORKS, 9189b7389c0SJean Delvare PCI_DEVICE_ID_SERVERWORKS_HT1000SB) }, 919506a8b6cSFlavio Leitner { PCI_DEVICE(PCI_VENDOR_ID_SERVERWORKS, 920506a8b6cSFlavio Leitner PCI_DEVICE_ID_SERVERWORKS_HT1100LD) }, 9211da177e4SLinus Torvalds { 0, } 9221da177e4SLinus Torvalds }; 9231da177e4SLinus Torvalds 9241da177e4SLinus Torvalds MODULE_DEVICE_TABLE (pci, piix4_ids); 9251da177e4SLinus Torvalds 926ca2061e1SChristian Fetzer static struct i2c_adapter *piix4_main_adapters[PIIX4_MAX_ADAPTERS]; 9272a2f7404SAndrew Armenia static struct i2c_adapter *piix4_aux_adapter; 928528d53a1SJean Delvare static int piix4_adapter_count; 929e154bf6fSAndrew Armenia 9300b255e92SBill Pemberton static int piix4_add_adapter(struct pci_dev *dev, unsigned short smba, 93188fa2dfbSRicardo Ribalda Delgado bool sb800_main, u8 port, bool notify_imc, 9320183eb8bSJean Delvare u8 hw_port_nr, const char *name, 9330183eb8bSJean Delvare struct i2c_adapter **padap) 934e154bf6fSAndrew Armenia { 935e154bf6fSAndrew Armenia struct i2c_adapter *adap; 936e154bf6fSAndrew Armenia struct i2c_piix4_adapdata *adapdata; 937e154bf6fSAndrew Armenia int retval; 938e154bf6fSAndrew Armenia 939e154bf6fSAndrew Armenia adap = kzalloc(sizeof(*adap), GFP_KERNEL); 940e154bf6fSAndrew Armenia if (adap == NULL) { 941e154bf6fSAndrew Armenia release_region(smba, SMBIOSIZE); 942e154bf6fSAndrew Armenia return -ENOMEM; 943e154bf6fSAndrew Armenia } 944e154bf6fSAndrew Armenia 945e154bf6fSAndrew Armenia adap->owner = THIS_MODULE; 946e154bf6fSAndrew Armenia adap->class = I2C_CLASS_HWMON | I2C_CLASS_SPD; 94783c60158SJean Delvare adap->algo = sb800_main ? &piix4_smbus_algorithm_sb800 94883c60158SJean Delvare : &smbus_algorithm; 949e154bf6fSAndrew Armenia 950e154bf6fSAndrew Armenia adapdata = kzalloc(sizeof(*adapdata), GFP_KERNEL); 951e154bf6fSAndrew Armenia if (adapdata == NULL) { 952e154bf6fSAndrew Armenia kfree(adap); 953e154bf6fSAndrew Armenia release_region(smba, SMBIOSIZE); 954e154bf6fSAndrew Armenia return -ENOMEM; 955e154bf6fSAndrew Armenia } 956e154bf6fSAndrew Armenia 9576cf72f41STerry Bowman adapdata->mmio_cfg.use_mmio = piix4_sb800_use_mmio(dev); 958e154bf6fSAndrew Armenia adapdata->smba = smba; 95983c60158SJean Delvare adapdata->sb800_main = sb800_main; 9600fe16195SGuenter Roeck adapdata->port = port << piix4_port_shift_sb800; 96188fa2dfbSRicardo Ribalda Delgado adapdata->notify_imc = notify_imc; 962e154bf6fSAndrew Armenia 963e154bf6fSAndrew Armenia /* set up the sysfs linkage to our parent device */ 964e154bf6fSAndrew Armenia adap->dev.parent = &dev->dev; 965e154bf6fSAndrew Armenia 9660183eb8bSJean Delvare if (has_acpi_companion(&dev->dev)) { 9670183eb8bSJean Delvare acpi_preset_companion(&adap->dev, 9680183eb8bSJean Delvare ACPI_COMPANION(&dev->dev), 9690183eb8bSJean Delvare hw_port_nr); 9700183eb8bSJean Delvare } 9710183eb8bSJean Delvare 972e154bf6fSAndrew Armenia snprintf(adap->name, sizeof(adap->name), 973725d2e3fSChristian Fetzer "SMBus PIIX4 adapter%s at %04x", name, smba); 974e154bf6fSAndrew Armenia 975e154bf6fSAndrew Armenia i2c_set_adapdata(adap, adapdata); 976e154bf6fSAndrew Armenia 977e154bf6fSAndrew Armenia retval = i2c_add_adapter(adap); 978e154bf6fSAndrew Armenia if (retval) { 979e154bf6fSAndrew Armenia kfree(adapdata); 980e154bf6fSAndrew Armenia kfree(adap); 981e154bf6fSAndrew Armenia release_region(smba, SMBIOSIZE); 982e154bf6fSAndrew Armenia return retval; 983e154bf6fSAndrew Armenia } 984e154bf6fSAndrew Armenia 985e154bf6fSAndrew Armenia *padap = adap; 986e154bf6fSAndrew Armenia return 0; 987e154bf6fSAndrew Armenia } 988e154bf6fSAndrew Armenia 98988fa2dfbSRicardo Ribalda Delgado static int piix4_add_adapters_sb800(struct pci_dev *dev, unsigned short smba, 99088fa2dfbSRicardo Ribalda Delgado bool notify_imc) 9912fee61d2SChristian Fetzer { 9922fee61d2SChristian Fetzer struct i2c_piix4_adapdata *adapdata; 9932fee61d2SChristian Fetzer int port; 9942fee61d2SChristian Fetzer int retval; 9952fee61d2SChristian Fetzer 996528d53a1SJean Delvare if (dev->device == PCI_DEVICE_ID_AMD_KERNCZ_SMBUS || 997528d53a1SJean Delvare (dev->device == PCI_DEVICE_ID_AMD_HUDSON2_SMBUS && 998528d53a1SJean Delvare dev->revision >= 0x1F)) { 999528d53a1SJean Delvare piix4_adapter_count = HUDSON2_MAIN_PORTS; 1000528d53a1SJean Delvare } else { 1001528d53a1SJean Delvare piix4_adapter_count = PIIX4_MAX_ADAPTERS; 1002528d53a1SJean Delvare } 1003528d53a1SJean Delvare 1004528d53a1SJean Delvare for (port = 0; port < piix4_adapter_count; port++) { 10050183eb8bSJean Delvare u8 hw_port_nr = port == 0 ? 0 : port + 1; 10060183eb8bSJean Delvare 100788fa2dfbSRicardo Ribalda Delgado retval = piix4_add_adapter(dev, smba, true, port, notify_imc, 10080183eb8bSJean Delvare hw_port_nr, 1009725d2e3fSChristian Fetzer piix4_main_port_names_sb800[port], 10102fee61d2SChristian Fetzer &piix4_main_adapters[port]); 10112fee61d2SChristian Fetzer if (retval < 0) 10122fee61d2SChristian Fetzer goto error; 10132fee61d2SChristian Fetzer } 10142fee61d2SChristian Fetzer 10152fee61d2SChristian Fetzer return retval; 10162fee61d2SChristian Fetzer 10172fee61d2SChristian Fetzer error: 10182fee61d2SChristian Fetzer dev_err(&dev->dev, 10192fee61d2SChristian Fetzer "Error setting up SB800 adapters. Unregistering!\n"); 10202fee61d2SChristian Fetzer while (--port >= 0) { 10212fee61d2SChristian Fetzer adapdata = i2c_get_adapdata(piix4_main_adapters[port]); 10222fee61d2SChristian Fetzer if (adapdata->smba) { 10232fee61d2SChristian Fetzer i2c_del_adapter(piix4_main_adapters[port]); 10242fee61d2SChristian Fetzer kfree(adapdata); 10252fee61d2SChristian Fetzer kfree(piix4_main_adapters[port]); 10262fee61d2SChristian Fetzer piix4_main_adapters[port] = NULL; 10272fee61d2SChristian Fetzer } 10282fee61d2SChristian Fetzer } 10292fee61d2SChristian Fetzer 10302fee61d2SChristian Fetzer return retval; 10312fee61d2SChristian Fetzer } 10322fee61d2SChristian Fetzer 10330b255e92SBill Pemberton static int piix4_probe(struct pci_dev *dev, const struct pci_device_id *id) 10341da177e4SLinus Torvalds { 10351da177e4SLinus Torvalds int retval; 103652795f6fSJean Delvare bool is_sb800 = false; 10371da177e4SLinus Torvalds 103876b3e28fSCrane Cai if ((dev->vendor == PCI_VENDOR_ID_ATI && 103976b3e28fSCrane Cai dev->device == PCI_DEVICE_ID_ATI_SBX00_SMBUS && 104076b3e28fSCrane Cai dev->revision >= 0x40) || 104124beb83aSPu Wen dev->vendor == PCI_VENDOR_ID_AMD || 104224beb83aSPu Wen dev->vendor == PCI_VENDOR_ID_HYGON) { 104388fa2dfbSRicardo Ribalda Delgado bool notify_imc = false; 104452795f6fSJean Delvare is_sb800 = true; 104552795f6fSJean Delvare 104624beb83aSPu Wen if ((dev->vendor == PCI_VENDOR_ID_AMD || 104724beb83aSPu Wen dev->vendor == PCI_VENDOR_ID_HYGON) && 104888fa2dfbSRicardo Ribalda Delgado dev->device == PCI_DEVICE_ID_AMD_KERNCZ_SMBUS) { 104988fa2dfbSRicardo Ribalda Delgado u8 imc; 105088fa2dfbSRicardo Ribalda Delgado 105188fa2dfbSRicardo Ribalda Delgado /* 105288fa2dfbSRicardo Ribalda Delgado * Detect if IMC is active or not, this method is 105388fa2dfbSRicardo Ribalda Delgado * described on coreboot's AMD IMC notes 105488fa2dfbSRicardo Ribalda Delgado */ 105588fa2dfbSRicardo Ribalda Delgado pci_bus_read_config_byte(dev->bus, PCI_DEVFN(0x14, 3), 105688fa2dfbSRicardo Ribalda Delgado 0x40, &imc); 105788fa2dfbSRicardo Ribalda Delgado if (imc & 0x80) 105888fa2dfbSRicardo Ribalda Delgado notify_imc = true; 105988fa2dfbSRicardo Ribalda Delgado } 106088fa2dfbSRicardo Ribalda Delgado 106187e1960eSShane Huang /* base address location etc changed in SB800 */ 1062a94dd00fSRudolf Marek retval = piix4_setup_sb800(dev, id, 0); 106304b6fcabSGuenter Roeck if (retval < 0) 10642fee61d2SChristian Fetzer return retval; 106587e1960eSShane Huang 10662fee61d2SChristian Fetzer /* 10672fee61d2SChristian Fetzer * Try to register multiplexed main SMBus adapter, 10682fee61d2SChristian Fetzer * give up if we can't 10692fee61d2SChristian Fetzer */ 107088fa2dfbSRicardo Ribalda Delgado retval = piix4_add_adapters_sb800(dev, retval, notify_imc); 107104b6fcabSGuenter Roeck if (retval < 0) 10722fee61d2SChristian Fetzer return retval; 10732fee61d2SChristian Fetzer } else { 10742fee61d2SChristian Fetzer retval = piix4_setup(dev, id); 107514a8086dSAndrew Armenia if (retval < 0) 10761da177e4SLinus Torvalds return retval; 10771da177e4SLinus Torvalds 10782a2f7404SAndrew Armenia /* Try to register main SMBus adapter, give up if we can't */ 10790183eb8bSJean Delvare retval = piix4_add_adapter(dev, retval, false, 0, false, 0, 10800183eb8bSJean Delvare "", &piix4_main_adapters[0]); 10812a2f7404SAndrew Armenia if (retval < 0) 10822a2f7404SAndrew Armenia return retval; 10832fee61d2SChristian Fetzer } 10842a2f7404SAndrew Armenia 10852a2f7404SAndrew Armenia /* Check for auxiliary SMBus on some AMD chipsets */ 1086a94dd00fSRudolf Marek retval = -ENODEV; 1087a94dd00fSRudolf Marek 10882a2f7404SAndrew Armenia if (dev->vendor == PCI_VENDOR_ID_ATI && 1089a94dd00fSRudolf Marek dev->device == PCI_DEVICE_ID_ATI_SBX00_SMBUS) { 1090a94dd00fSRudolf Marek if (dev->revision < 0x40) { 10912a2f7404SAndrew Armenia retval = piix4_setup_aux(dev, id, 0x58); 1092a94dd00fSRudolf Marek } else { 1093a94dd00fSRudolf Marek /* SB800 added aux bus too */ 1094a94dd00fSRudolf Marek retval = piix4_setup_sb800(dev, id, 1); 1095a94dd00fSRudolf Marek } 1096a94dd00fSRudolf Marek } 1097a94dd00fSRudolf Marek 1098a94dd00fSRudolf Marek if (dev->vendor == PCI_VENDOR_ID_AMD && 1099f27237c1SAdam Honse (dev->device == PCI_DEVICE_ID_AMD_HUDSON2_SMBUS || 1100f27237c1SAdam Honse dev->device == PCI_DEVICE_ID_AMD_KERNCZ_SMBUS)) { 1101a94dd00fSRudolf Marek retval = piix4_setup_sb800(dev, id, 1); 1102a94dd00fSRudolf Marek } 1103a94dd00fSRudolf Marek 11042a2f7404SAndrew Armenia if (retval > 0) { 11052a2f7404SAndrew Armenia /* Try to add the aux adapter if it exists, 11062a2f7404SAndrew Armenia * piix4_add_adapter will clean up if this fails */ 11070183eb8bSJean Delvare piix4_add_adapter(dev, retval, false, 0, false, 1, 110852795f6fSJean Delvare is_sb800 ? piix4_aux_port_name_sb800 : "", 1109725d2e3fSChristian Fetzer &piix4_aux_adapter); 11102a2f7404SAndrew Armenia } 11112a2f7404SAndrew Armenia 11122a2f7404SAndrew Armenia return 0; 11131da177e4SLinus Torvalds } 11141da177e4SLinus Torvalds 11150b255e92SBill Pemberton static void piix4_adap_remove(struct i2c_adapter *adap) 111614a8086dSAndrew Armenia { 111714a8086dSAndrew Armenia struct i2c_piix4_adapdata *adapdata = i2c_get_adapdata(adap); 111814a8086dSAndrew Armenia 111914a8086dSAndrew Armenia if (adapdata->smba) { 112014a8086dSAndrew Armenia i2c_del_adapter(adap); 112104b6fcabSGuenter Roeck if (adapdata->port == (0 << piix4_port_shift_sb800)) 112214a8086dSAndrew Armenia release_region(adapdata->smba, SMBIOSIZE); 1123e154bf6fSAndrew Armenia kfree(adapdata); 1124e154bf6fSAndrew Armenia kfree(adap); 112514a8086dSAndrew Armenia } 112614a8086dSAndrew Armenia } 112714a8086dSAndrew Armenia 11280b255e92SBill Pemberton static void piix4_remove(struct pci_dev *dev) 11291da177e4SLinus Torvalds { 1130528d53a1SJean Delvare int port = piix4_adapter_count; 1131ca2061e1SChristian Fetzer 1132ca2061e1SChristian Fetzer while (--port >= 0) { 1133ca2061e1SChristian Fetzer if (piix4_main_adapters[port]) { 1134ca2061e1SChristian Fetzer piix4_adap_remove(piix4_main_adapters[port]); 1135ca2061e1SChristian Fetzer piix4_main_adapters[port] = NULL; 1136ca2061e1SChristian Fetzer } 1137e154bf6fSAndrew Armenia } 11382a2f7404SAndrew Armenia 11392a2f7404SAndrew Armenia if (piix4_aux_adapter) { 11402a2f7404SAndrew Armenia piix4_adap_remove(piix4_aux_adapter); 11412a2f7404SAndrew Armenia piix4_aux_adapter = NULL; 11422a2f7404SAndrew Armenia } 11431da177e4SLinus Torvalds } 11441da177e4SLinus Torvalds 11451da177e4SLinus Torvalds static struct pci_driver piix4_driver = { 11461da177e4SLinus Torvalds .name = "piix4_smbus", 11471da177e4SLinus Torvalds .id_table = piix4_ids, 11481da177e4SLinus Torvalds .probe = piix4_probe, 11490b255e92SBill Pemberton .remove = piix4_remove, 11501da177e4SLinus Torvalds }; 11511da177e4SLinus Torvalds 115256f21788SAxel Lin module_pci_driver(piix4_driver); 11531da177e4SLinus Torvalds 1154f80531c8SJarkko Nikula MODULE_AUTHOR("Frodo Looijaard <frodol@dds.nl>"); 1155f80531c8SJarkko Nikula MODULE_AUTHOR("Philip Edelbrock <phil@netroedge.com>"); 11561da177e4SLinus Torvalds MODULE_DESCRIPTION("PIIX4 SMBus driver"); 11571da177e4SLinus Torvalds MODULE_LICENSE("GPL"); 1158