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 802fee61d2SChristian Fetzer 8188fa2dfbSRicardo Ribalda Delgado #define KERNCZ_IMC_IDX 0x3e 8288fa2dfbSRicardo Ribalda Delgado #define KERNCZ_IMC_DATA 0x3f 8388fa2dfbSRicardo Ribalda Delgado 846befa3fdSJean Delvare /* 856befa3fdSJean Delvare * SB800 port is selected by bits 2:1 of the smb_en register (0x2c) 866befa3fdSJean Delvare * or the smb_sel register (0x2e), depending on bit 0 of register 0x2f. 876befa3fdSJean Delvare * Hudson-2/Bolton port is always selected by bits 2:1 of register 0x2f. 886befa3fdSJean Delvare */ 892fee61d2SChristian Fetzer #define SB800_PIIX4_PORT_IDX 0x2c 906befa3fdSJean Delvare #define SB800_PIIX4_PORT_IDX_ALT 0x2e 916befa3fdSJean Delvare #define SB800_PIIX4_PORT_IDX_SEL 0x2f 922fee61d2SChristian Fetzer #define SB800_PIIX4_PORT_IDX_MASK 0x06 930fe16195SGuenter Roeck #define SB800_PIIX4_PORT_IDX_SHIFT 1 940fe16195SGuenter Roeck 95c7c06a15SAndrew Cooks /* On kerncz and Hudson2, SmBus0Sel is at bit 20:19 of PMx00 DecodeEn */ 960fe16195SGuenter Roeck #define SB800_PIIX4_PORT_IDX_KERNCZ 0x02 970fe16195SGuenter Roeck #define SB800_PIIX4_PORT_IDX_MASK_KERNCZ 0x18 980fe16195SGuenter Roeck #define SB800_PIIX4_PORT_IDX_SHIFT_KERNCZ 3 992fee61d2SChristian Fetzer 1001da177e4SLinus Torvalds /* insmod parameters */ 1011da177e4SLinus Torvalds 1021da177e4SLinus Torvalds /* If force is set to anything different from 0, we forcibly enable the 1031da177e4SLinus Torvalds PIIX4. DANGEROUS! */ 10460507095SJean Delvare static int force; 1051da177e4SLinus Torvalds module_param (force, int, 0); 1061da177e4SLinus Torvalds MODULE_PARM_DESC(force, "Forcibly enable the PIIX4. DANGEROUS!"); 1071da177e4SLinus Torvalds 1081da177e4SLinus Torvalds /* If force_addr is set to anything different from 0, we forcibly enable 1091da177e4SLinus Torvalds the PIIX4 at the given address. VERY DANGEROUS! */ 11060507095SJean Delvare static int force_addr; 111c78babccSDavid Howells module_param_hw(force_addr, int, ioport, 0); 1121da177e4SLinus Torvalds MODULE_PARM_DESC(force_addr, 1131da177e4SLinus Torvalds "Forcibly enable the PIIX4 at the given address. " 1141da177e4SLinus Torvalds "EXTREMELY DANGEROUS!"); 1151da177e4SLinus Torvalds 116b1c1759cSDavid Milburn static int srvrworks_csb5_delay; 117d6072f84SJean Delvare static struct pci_driver piix4_driver; 1181da177e4SLinus Torvalds 1190b255e92SBill Pemberton static const struct dmi_system_id piix4_dmi_blacklist[] = { 120c2fc54fcSJean Delvare { 121c2fc54fcSJean Delvare .ident = "Sapphire AM2RD790", 122c2fc54fcSJean Delvare .matches = { 123c2fc54fcSJean Delvare DMI_MATCH(DMI_BOARD_VENDOR, "SAPPHIRE Inc."), 124c2fc54fcSJean Delvare DMI_MATCH(DMI_BOARD_NAME, "PC-AM2RD790"), 125c2fc54fcSJean Delvare }, 126c2fc54fcSJean Delvare }, 127c2fc54fcSJean Delvare { 128c2fc54fcSJean Delvare .ident = "DFI Lanparty UT 790FX", 129c2fc54fcSJean Delvare .matches = { 130c2fc54fcSJean Delvare DMI_MATCH(DMI_BOARD_VENDOR, "DFI Inc."), 131c2fc54fcSJean Delvare DMI_MATCH(DMI_BOARD_NAME, "LP UT 790FX"), 132c2fc54fcSJean Delvare }, 133c2fc54fcSJean Delvare }, 134c2fc54fcSJean Delvare { } 135c2fc54fcSJean Delvare }; 136c2fc54fcSJean Delvare 137c2fc54fcSJean Delvare /* The IBM entry is in a separate table because we only check it 138c2fc54fcSJean Delvare on Intel-based systems */ 1390b255e92SBill Pemberton static const struct dmi_system_id piix4_dmi_ibm[] = { 1401da177e4SLinus Torvalds { 1411da177e4SLinus Torvalds .ident = "IBM", 1421da177e4SLinus Torvalds .matches = { DMI_MATCH(DMI_SYS_VENDOR, "IBM"), }, 1431da177e4SLinus Torvalds }, 1441da177e4SLinus Torvalds { }, 1451da177e4SLinus Torvalds }; 1461da177e4SLinus Torvalds 1476befa3fdSJean Delvare /* 1486befa3fdSJean Delvare * SB800 globals 1496befa3fdSJean Delvare */ 1506befa3fdSJean Delvare static u8 piix4_port_sel_sb800; 1510fe16195SGuenter Roeck static u8 piix4_port_mask_sb800; 1520fe16195SGuenter Roeck static u8 piix4_port_shift_sb800; 153725d2e3fSChristian Fetzer static const char *piix4_main_port_names_sb800[PIIX4_MAX_ADAPTERS] = { 15452795f6fSJean Delvare " port 0", " port 2", " port 3", " port 4" 155725d2e3fSChristian Fetzer }; 15652795f6fSJean Delvare static const char *piix4_aux_port_name_sb800 = " port 1"; 157725d2e3fSChristian Fetzer 15814a8086dSAndrew Armenia struct i2c_piix4_adapdata { 15914a8086dSAndrew Armenia unsigned short smba; 1602fee61d2SChristian Fetzer 1612fee61d2SChristian Fetzer /* SB800 */ 1622fee61d2SChristian Fetzer bool sb800_main; 16388fa2dfbSRicardo Ribalda Delgado bool notify_imc; 16433f5ccc3SJean Delvare u8 port; /* Port number, shifted */ 16514a8086dSAndrew Armenia }; 16614a8086dSAndrew Armenia 1670b255e92SBill Pemberton static int piix4_setup(struct pci_dev *PIIX4_dev, 1681da177e4SLinus Torvalds const struct pci_device_id *id) 1691da177e4SLinus Torvalds { 1701da177e4SLinus Torvalds unsigned char temp; 17114a8086dSAndrew Armenia unsigned short piix4_smba; 1721da177e4SLinus Torvalds 173b1c1759cSDavid Milburn if ((PIIX4_dev->vendor == PCI_VENDOR_ID_SERVERWORKS) && 174b1c1759cSDavid Milburn (PIIX4_dev->device == PCI_DEVICE_ID_SERVERWORKS_CSB5)) 175b1c1759cSDavid Milburn srvrworks_csb5_delay = 1; 176b1c1759cSDavid Milburn 177c2fc54fcSJean Delvare /* On some motherboards, it was reported that accessing the SMBus 178c2fc54fcSJean Delvare caused severe hardware problems */ 179c2fc54fcSJean Delvare if (dmi_check_system(piix4_dmi_blacklist)) { 180c2fc54fcSJean Delvare dev_err(&PIIX4_dev->dev, 181c2fc54fcSJean Delvare "Accessing the SMBus on this system is unsafe!\n"); 182c2fc54fcSJean Delvare return -EPERM; 183c2fc54fcSJean Delvare } 184c2fc54fcSJean Delvare 1851da177e4SLinus Torvalds /* Don't access SMBus on IBM systems which get corrupted eeproms */ 186c2fc54fcSJean Delvare if (dmi_check_system(piix4_dmi_ibm) && 1871da177e4SLinus Torvalds PIIX4_dev->vendor == PCI_VENDOR_ID_INTEL) { 188f9ba6c04SJean Delvare dev_err(&PIIX4_dev->dev, "IBM system detected; this module " 1891da177e4SLinus Torvalds "may corrupt your serial eeprom! Refusing to load " 1901da177e4SLinus Torvalds "module!\n"); 1911da177e4SLinus Torvalds return -EPERM; 1921da177e4SLinus Torvalds } 1931da177e4SLinus Torvalds 1941da177e4SLinus Torvalds /* Determine the address of the SMBus areas */ 1951da177e4SLinus Torvalds if (force_addr) { 1961da177e4SLinus Torvalds piix4_smba = force_addr & 0xfff0; 1971da177e4SLinus Torvalds force = 0; 1981da177e4SLinus Torvalds } else { 1991da177e4SLinus Torvalds pci_read_config_word(PIIX4_dev, SMBBA, &piix4_smba); 2001da177e4SLinus Torvalds piix4_smba &= 0xfff0; 2011da177e4SLinus Torvalds if(piix4_smba == 0) { 202fa63cd56SJean Delvare dev_err(&PIIX4_dev->dev, "SMBus base address " 2031da177e4SLinus Torvalds "uninitialized - upgrade BIOS or use " 2041da177e4SLinus Torvalds "force_addr=0xaddr\n"); 2051da177e4SLinus Torvalds return -ENODEV; 2061da177e4SLinus Torvalds } 2071da177e4SLinus Torvalds } 2081da177e4SLinus Torvalds 20954fb4a05SJean Delvare if (acpi_check_region(piix4_smba, SMBIOSIZE, piix4_driver.name)) 21018669eabSJean Delvare return -ENODEV; 21154fb4a05SJean Delvare 212d6072f84SJean Delvare if (!request_region(piix4_smba, SMBIOSIZE, piix4_driver.name)) { 213fa63cd56SJean Delvare dev_err(&PIIX4_dev->dev, "SMBus region 0x%x already in use!\n", 2141da177e4SLinus Torvalds piix4_smba); 215fa63cd56SJean Delvare return -EBUSY; 2161da177e4SLinus Torvalds } 2171da177e4SLinus Torvalds 2181da177e4SLinus Torvalds pci_read_config_byte(PIIX4_dev, SMBHSTCFG, &temp); 2191da177e4SLinus Torvalds 2201da177e4SLinus Torvalds /* If force_addr is set, we program the new address here. Just to make 2211da177e4SLinus Torvalds sure, we disable the PIIX4 first. */ 2221da177e4SLinus Torvalds if (force_addr) { 2231da177e4SLinus Torvalds pci_write_config_byte(PIIX4_dev, SMBHSTCFG, temp & 0xfe); 2241da177e4SLinus Torvalds pci_write_config_word(PIIX4_dev, SMBBA, piix4_smba); 2251da177e4SLinus Torvalds pci_write_config_byte(PIIX4_dev, SMBHSTCFG, temp | 0x01); 2261da177e4SLinus Torvalds dev_info(&PIIX4_dev->dev, "WARNING: SMBus interface set to " 2271da177e4SLinus Torvalds "new address %04x!\n", piix4_smba); 2281da177e4SLinus Torvalds } else if ((temp & 1) == 0) { 2291da177e4SLinus Torvalds if (force) { 2301da177e4SLinus Torvalds /* This should never need to be done, but has been 2311da177e4SLinus Torvalds * noted that many Dell machines have the SMBus 2321da177e4SLinus Torvalds * interface on the PIIX4 disabled!? NOTE: This assumes 2331da177e4SLinus Torvalds * I/O space and other allocations WERE done by the 2341da177e4SLinus Torvalds * Bios! Don't complain if your hardware does weird 2351da177e4SLinus Torvalds * things after enabling this. :') Check for Bios 2361da177e4SLinus Torvalds * updates before resorting to this. 2371da177e4SLinus Torvalds */ 2381da177e4SLinus Torvalds pci_write_config_byte(PIIX4_dev, SMBHSTCFG, 2391da177e4SLinus Torvalds temp | 1); 2408117e41eSJoe Perches dev_notice(&PIIX4_dev->dev, 2418117e41eSJoe Perches "WARNING: SMBus interface has been FORCEFULLY ENABLED!\n"); 2421da177e4SLinus Torvalds } else { 2431da177e4SLinus Torvalds dev_err(&PIIX4_dev->dev, 24466f8a8ffSJean Delvare "SMBus Host Controller not enabled!\n"); 2451da177e4SLinus Torvalds release_region(piix4_smba, SMBIOSIZE); 2461da177e4SLinus Torvalds return -ENODEV; 2471da177e4SLinus Torvalds } 2481da177e4SLinus Torvalds } 2491da177e4SLinus Torvalds 25054aaa1caSRudolf Marek if (((temp & 0x0E) == 8) || ((temp & 0x0E) == 2)) 25166f8a8ffSJean Delvare dev_dbg(&PIIX4_dev->dev, "Using IRQ for SMBus\n"); 2521da177e4SLinus Torvalds else if ((temp & 0x0E) == 0) 25366f8a8ffSJean Delvare dev_dbg(&PIIX4_dev->dev, "Using SMI# for SMBus\n"); 2541da177e4SLinus Torvalds else 2551da177e4SLinus Torvalds dev_err(&PIIX4_dev->dev, "Illegal Interrupt configuration " 2561da177e4SLinus Torvalds "(or code out of date)!\n"); 2571da177e4SLinus Torvalds 2581da177e4SLinus Torvalds pci_read_config_byte(PIIX4_dev, SMBREV, &temp); 259fa63cd56SJean Delvare dev_info(&PIIX4_dev->dev, 260fa63cd56SJean Delvare "SMBus Host Controller at 0x%x, revision %d\n", 261fa63cd56SJean Delvare piix4_smba, temp); 2621da177e4SLinus Torvalds 26314a8086dSAndrew Armenia return piix4_smba; 2641da177e4SLinus Torvalds } 2651da177e4SLinus Torvalds 2660b255e92SBill Pemberton static int piix4_setup_sb800(struct pci_dev *PIIX4_dev, 267a94dd00fSRudolf Marek const struct pci_device_id *id, u8 aux) 26887e1960eSShane Huang { 26914a8086dSAndrew Armenia unsigned short piix4_smba; 2706befa3fdSJean Delvare u8 smba_en_lo, smba_en_hi, smb_en, smb_en_status, port_sel; 271032f708bSShane Huang u8 i2ccfg, i2ccfg_offset = 0x10; 27287e1960eSShane Huang 2733806e94bSCrane Cai /* SB800 and later SMBus does not support forcing address */ 27487e1960eSShane Huang if (force || force_addr) { 2753806e94bSCrane Cai dev_err(&PIIX4_dev->dev, "SMBus does not support " 27687e1960eSShane Huang "forcing address!\n"); 27787e1960eSShane Huang return -EINVAL; 27887e1960eSShane Huang } 27987e1960eSShane Huang 28087e1960eSShane Huang /* Determine the address of the SMBus areas */ 281032f708bSShane Huang if ((PIIX4_dev->vendor == PCI_VENDOR_ID_AMD && 282032f708bSShane Huang PIIX4_dev->device == PCI_DEVICE_ID_AMD_HUDSON2_SMBUS && 283032f708bSShane Huang PIIX4_dev->revision >= 0x41) || 284032f708bSShane Huang (PIIX4_dev->vendor == PCI_VENDOR_ID_AMD && 285bcb29994SVincent Wan PIIX4_dev->device == PCI_DEVICE_ID_AMD_KERNCZ_SMBUS && 28624beb83aSPu Wen PIIX4_dev->revision >= 0x49) || 28724beb83aSPu Wen (PIIX4_dev->vendor == PCI_VENDOR_ID_HYGON && 28824beb83aSPu Wen PIIX4_dev->device == PCI_DEVICE_ID_AMD_KERNCZ_SMBUS)) 289032f708bSShane Huang smb_en = 0x00; 290032f708bSShane Huang else 291a94dd00fSRudolf Marek smb_en = (aux) ? 0x28 : 0x2c; 292a94dd00fSRudolf Marek 29304b6fcabSGuenter Roeck if (!request_muxed_region(SB800_PIIX4_SMB_IDX, 2, "sb800_piix4_smb")) { 29404b6fcabSGuenter Roeck dev_err(&PIIX4_dev->dev, 29504b6fcabSGuenter Roeck "SMB base address index region 0x%x already in use.\n", 29604b6fcabSGuenter Roeck SB800_PIIX4_SMB_IDX); 29704b6fcabSGuenter Roeck return -EBUSY; 29804b6fcabSGuenter Roeck } 29904b6fcabSGuenter Roeck 3002fee61d2SChristian Fetzer outb_p(smb_en, SB800_PIIX4_SMB_IDX); 3012fee61d2SChristian Fetzer smba_en_lo = inb_p(SB800_PIIX4_SMB_IDX + 1); 3022fee61d2SChristian Fetzer outb_p(smb_en + 1, SB800_PIIX4_SMB_IDX); 3032fee61d2SChristian Fetzer smba_en_hi = inb_p(SB800_PIIX4_SMB_IDX + 1); 30404b6fcabSGuenter Roeck 30504b6fcabSGuenter Roeck release_region(SB800_PIIX4_SMB_IDX, 2); 30687e1960eSShane Huang 307032f708bSShane Huang if (!smb_en) { 308032f708bSShane Huang smb_en_status = smba_en_lo & 0x10; 309032f708bSShane Huang piix4_smba = smba_en_hi << 8; 310032f708bSShane Huang if (aux) 311032f708bSShane Huang piix4_smba |= 0x20; 312032f708bSShane Huang } else { 313032f708bSShane Huang smb_en_status = smba_en_lo & 0x01; 314032f708bSShane Huang piix4_smba = ((smba_en_hi << 8) | smba_en_lo) & 0xffe0; 315032f708bSShane Huang } 316032f708bSShane Huang 317032f708bSShane Huang if (!smb_en_status) { 31887e1960eSShane Huang dev_err(&PIIX4_dev->dev, 31966f8a8ffSJean Delvare "SMBus Host Controller not enabled!\n"); 32087e1960eSShane Huang return -ENODEV; 32187e1960eSShane Huang } 32287e1960eSShane Huang 32387e1960eSShane Huang if (acpi_check_region(piix4_smba, SMBIOSIZE, piix4_driver.name)) 32418669eabSJean Delvare return -ENODEV; 32587e1960eSShane Huang 32687e1960eSShane Huang if (!request_region(piix4_smba, SMBIOSIZE, piix4_driver.name)) { 32787e1960eSShane Huang dev_err(&PIIX4_dev->dev, "SMBus region 0x%x already in use!\n", 32887e1960eSShane Huang piix4_smba); 32987e1960eSShane Huang return -EBUSY; 33087e1960eSShane Huang } 33187e1960eSShane Huang 332a94dd00fSRudolf Marek /* Aux SMBus does not support IRQ information */ 333a94dd00fSRudolf Marek if (aux) { 334a94dd00fSRudolf Marek dev_info(&PIIX4_dev->dev, 33585fd0fe6SShane Huang "Auxiliary SMBus Host Controller at 0x%x\n", 33685fd0fe6SShane Huang piix4_smba); 337a94dd00fSRudolf Marek return piix4_smba; 338a94dd00fSRudolf Marek } 339a94dd00fSRudolf Marek 34087e1960eSShane Huang /* Request the SMBus I2C bus config region */ 34187e1960eSShane Huang if (!request_region(piix4_smba + i2ccfg_offset, 1, "i2ccfg")) { 34287e1960eSShane Huang dev_err(&PIIX4_dev->dev, "SMBus I2C bus config region " 34387e1960eSShane Huang "0x%x already in use!\n", piix4_smba + i2ccfg_offset); 34487e1960eSShane Huang release_region(piix4_smba, SMBIOSIZE); 34587e1960eSShane Huang return -EBUSY; 34687e1960eSShane Huang } 34787e1960eSShane Huang i2ccfg = inb_p(piix4_smba + i2ccfg_offset); 34887e1960eSShane Huang release_region(piix4_smba + i2ccfg_offset, 1); 34987e1960eSShane Huang 35087e1960eSShane Huang if (i2ccfg & 1) 35166f8a8ffSJean Delvare dev_dbg(&PIIX4_dev->dev, "Using IRQ for SMBus\n"); 35287e1960eSShane Huang else 35366f8a8ffSJean Delvare dev_dbg(&PIIX4_dev->dev, "Using SMI# for SMBus\n"); 35487e1960eSShane Huang 35587e1960eSShane Huang dev_info(&PIIX4_dev->dev, 35687e1960eSShane Huang "SMBus Host Controller at 0x%x, revision %d\n", 35787e1960eSShane Huang piix4_smba, i2ccfg >> 4); 35887e1960eSShane Huang 3596befa3fdSJean Delvare /* Find which register is used for port selection */ 36024beb83aSPu Wen if (PIIX4_dev->vendor == PCI_VENDOR_ID_AMD || 36124beb83aSPu Wen PIIX4_dev->vendor == PCI_VENDOR_ID_HYGON) { 362c7c06a15SAndrew Cooks if (PIIX4_dev->device == PCI_DEVICE_ID_AMD_KERNCZ_SMBUS || 363c7c06a15SAndrew Cooks (PIIX4_dev->device == PCI_DEVICE_ID_AMD_HUDSON2_SMBUS && 364c7c06a15SAndrew Cooks PIIX4_dev->revision >= 0x1F)) { 3650fe16195SGuenter Roeck piix4_port_sel_sb800 = SB800_PIIX4_PORT_IDX_KERNCZ; 3660fe16195SGuenter Roeck piix4_port_mask_sb800 = SB800_PIIX4_PORT_IDX_MASK_KERNCZ; 3670fe16195SGuenter Roeck piix4_port_shift_sb800 = SB800_PIIX4_PORT_IDX_SHIFT_KERNCZ; 368c7c06a15SAndrew Cooks } else { 3696befa3fdSJean Delvare piix4_port_sel_sb800 = SB800_PIIX4_PORT_IDX_ALT; 3700fe16195SGuenter Roeck piix4_port_mask_sb800 = SB800_PIIX4_PORT_IDX_MASK; 3710fe16195SGuenter Roeck piix4_port_shift_sb800 = SB800_PIIX4_PORT_IDX_SHIFT; 3720fe16195SGuenter Roeck } 3736befa3fdSJean Delvare } else { 37404b6fcabSGuenter Roeck if (!request_muxed_region(SB800_PIIX4_SMB_IDX, 2, 37504b6fcabSGuenter Roeck "sb800_piix4_smb")) { 37604b6fcabSGuenter Roeck release_region(piix4_smba, SMBIOSIZE); 37704b6fcabSGuenter Roeck return -EBUSY; 37804b6fcabSGuenter Roeck } 37904b6fcabSGuenter Roeck 3806befa3fdSJean Delvare outb_p(SB800_PIIX4_PORT_IDX_SEL, SB800_PIIX4_SMB_IDX); 3816befa3fdSJean Delvare port_sel = inb_p(SB800_PIIX4_SMB_IDX + 1); 3826befa3fdSJean Delvare piix4_port_sel_sb800 = (port_sel & 0x01) ? 3836befa3fdSJean Delvare SB800_PIIX4_PORT_IDX_ALT : 3846befa3fdSJean Delvare SB800_PIIX4_PORT_IDX; 3850fe16195SGuenter Roeck piix4_port_mask_sb800 = SB800_PIIX4_PORT_IDX_MASK; 3860fe16195SGuenter Roeck piix4_port_shift_sb800 = SB800_PIIX4_PORT_IDX_SHIFT; 38704b6fcabSGuenter Roeck release_region(SB800_PIIX4_SMB_IDX, 2); 3886befa3fdSJean Delvare } 3896befa3fdSJean Delvare 3906befa3fdSJean Delvare dev_info(&PIIX4_dev->dev, 3916befa3fdSJean Delvare "Using register 0x%02x for SMBus port selection\n", 3926befa3fdSJean Delvare (unsigned int)piix4_port_sel_sb800); 3936befa3fdSJean Delvare 39414a8086dSAndrew Armenia return piix4_smba; 39587e1960eSShane Huang } 39687e1960eSShane Huang 3970b255e92SBill Pemberton static int piix4_setup_aux(struct pci_dev *PIIX4_dev, 3982a2f7404SAndrew Armenia const struct pci_device_id *id, 3992a2f7404SAndrew Armenia unsigned short base_reg_addr) 4002a2f7404SAndrew Armenia { 4012a2f7404SAndrew Armenia /* Set up auxiliary SMBus controllers found on some 4022a2f7404SAndrew Armenia * AMD chipsets e.g. SP5100 (SB700 derivative) */ 4032a2f7404SAndrew Armenia 4042a2f7404SAndrew Armenia unsigned short piix4_smba; 4052a2f7404SAndrew Armenia 4062a2f7404SAndrew Armenia /* Read address of auxiliary SMBus controller */ 4072a2f7404SAndrew Armenia pci_read_config_word(PIIX4_dev, base_reg_addr, &piix4_smba); 4082a2f7404SAndrew Armenia if ((piix4_smba & 1) == 0) { 4092a2f7404SAndrew Armenia dev_dbg(&PIIX4_dev->dev, 4102a2f7404SAndrew Armenia "Auxiliary SMBus controller not enabled\n"); 4112a2f7404SAndrew Armenia return -ENODEV; 4122a2f7404SAndrew Armenia } 4132a2f7404SAndrew Armenia 4142a2f7404SAndrew Armenia piix4_smba &= 0xfff0; 4152a2f7404SAndrew Armenia if (piix4_smba == 0) { 4162a2f7404SAndrew Armenia dev_dbg(&PIIX4_dev->dev, 4172a2f7404SAndrew Armenia "Auxiliary SMBus base address uninitialized\n"); 4182a2f7404SAndrew Armenia return -ENODEV; 4192a2f7404SAndrew Armenia } 4202a2f7404SAndrew Armenia 4212a2f7404SAndrew Armenia if (acpi_check_region(piix4_smba, SMBIOSIZE, piix4_driver.name)) 4222a2f7404SAndrew Armenia return -ENODEV; 4232a2f7404SAndrew Armenia 4242a2f7404SAndrew Armenia if (!request_region(piix4_smba, SMBIOSIZE, piix4_driver.name)) { 4252a2f7404SAndrew Armenia dev_err(&PIIX4_dev->dev, "Auxiliary SMBus region 0x%x " 4262a2f7404SAndrew Armenia "already in use!\n", piix4_smba); 4272a2f7404SAndrew Armenia return -EBUSY; 4282a2f7404SAndrew Armenia } 4292a2f7404SAndrew Armenia 4302a2f7404SAndrew Armenia dev_info(&PIIX4_dev->dev, 4312a2f7404SAndrew Armenia "Auxiliary SMBus Host Controller at 0x%x\n", 4322a2f7404SAndrew Armenia piix4_smba); 4332a2f7404SAndrew Armenia 4342a2f7404SAndrew Armenia return piix4_smba; 4352a2f7404SAndrew Armenia } 4362a2f7404SAndrew Armenia 437e154bf6fSAndrew Armenia static int piix4_transaction(struct i2c_adapter *piix4_adapter) 4381da177e4SLinus Torvalds { 439e154bf6fSAndrew Armenia struct i2c_piix4_adapdata *adapdata = i2c_get_adapdata(piix4_adapter); 440e154bf6fSAndrew Armenia unsigned short piix4_smba = adapdata->smba; 4411da177e4SLinus Torvalds int temp; 4421da177e4SLinus Torvalds int result = 0; 4431da177e4SLinus Torvalds int timeout = 0; 4441da177e4SLinus Torvalds 445e154bf6fSAndrew Armenia dev_dbg(&piix4_adapter->dev, "Transaction (pre): CNT=%02x, CMD=%02x, " 4461da177e4SLinus Torvalds "ADD=%02x, DAT0=%02x, DAT1=%02x\n", inb_p(SMBHSTCNT), 4471da177e4SLinus Torvalds inb_p(SMBHSTCMD), inb_p(SMBHSTADD), inb_p(SMBHSTDAT0), 4481da177e4SLinus Torvalds inb_p(SMBHSTDAT1)); 4491da177e4SLinus Torvalds 4501da177e4SLinus Torvalds /* Make sure the SMBus host is ready to start transmitting */ 4511da177e4SLinus Torvalds if ((temp = inb_p(SMBHSTSTS)) != 0x00) { 452e154bf6fSAndrew Armenia dev_dbg(&piix4_adapter->dev, "SMBus busy (%02x). " 4531da177e4SLinus Torvalds "Resetting...\n", temp); 4541da177e4SLinus Torvalds outb_p(temp, SMBHSTSTS); 4551da177e4SLinus Torvalds if ((temp = inb_p(SMBHSTSTS)) != 0x00) { 456e154bf6fSAndrew Armenia dev_err(&piix4_adapter->dev, "Failed! (%02x)\n", temp); 45797140342SDavid Brownell return -EBUSY; 4581da177e4SLinus Torvalds } else { 459e154bf6fSAndrew Armenia dev_dbg(&piix4_adapter->dev, "Successful!\n"); 4601da177e4SLinus Torvalds } 4611da177e4SLinus Torvalds } 4621da177e4SLinus Torvalds 4631da177e4SLinus Torvalds /* start the transaction by setting bit 6 */ 4641da177e4SLinus Torvalds outb_p(inb(SMBHSTCNT) | 0x040, SMBHSTCNT); 4651da177e4SLinus Torvalds 4661da177e4SLinus Torvalds /* We will always wait for a fraction of a second! (See PIIX4 docs errata) */ 467b1c1759cSDavid Milburn if (srvrworks_csb5_delay) /* Extra delay for SERVERWORKS_CSB5 */ 4680e89b2feSGuenter Roeck usleep_range(2000, 2100); 469b1c1759cSDavid Milburn else 4700e89b2feSGuenter Roeck usleep_range(250, 500); 471b1c1759cSDavid Milburn 472b6a31950SRoel Kluin while ((++timeout < MAX_TIMEOUT) && 473b1c1759cSDavid Milburn ((temp = inb_p(SMBHSTSTS)) & 0x01)) 4740e89b2feSGuenter Roeck usleep_range(250, 500); 4751da177e4SLinus Torvalds 4761da177e4SLinus Torvalds /* If the SMBus is still busy, we give up */ 477b6a31950SRoel Kluin if (timeout == MAX_TIMEOUT) { 478e154bf6fSAndrew Armenia dev_err(&piix4_adapter->dev, "SMBus Timeout!\n"); 47997140342SDavid Brownell result = -ETIMEDOUT; 4801da177e4SLinus Torvalds } 4811da177e4SLinus Torvalds 4821da177e4SLinus Torvalds if (temp & 0x10) { 48397140342SDavid Brownell result = -EIO; 484e154bf6fSAndrew Armenia dev_err(&piix4_adapter->dev, "Error: Failed bus transaction\n"); 4851da177e4SLinus Torvalds } 4861da177e4SLinus Torvalds 4871da177e4SLinus Torvalds if (temp & 0x08) { 48897140342SDavid Brownell result = -EIO; 489e154bf6fSAndrew Armenia dev_dbg(&piix4_adapter->dev, "Bus collision! SMBus may be " 4901da177e4SLinus Torvalds "locked until next hard reset. (sorry!)\n"); 4911da177e4SLinus Torvalds /* Clock stops and slave is stuck in mid-transmission */ 4921da177e4SLinus Torvalds } 4931da177e4SLinus Torvalds 4941da177e4SLinus Torvalds if (temp & 0x04) { 49597140342SDavid Brownell result = -ENXIO; 496e154bf6fSAndrew Armenia dev_dbg(&piix4_adapter->dev, "Error: no response!\n"); 4971da177e4SLinus Torvalds } 4981da177e4SLinus Torvalds 4991da177e4SLinus Torvalds if (inb_p(SMBHSTSTS) != 0x00) 5001da177e4SLinus Torvalds outb_p(inb(SMBHSTSTS), SMBHSTSTS); 5011da177e4SLinus Torvalds 5021da177e4SLinus Torvalds if ((temp = inb_p(SMBHSTSTS)) != 0x00) { 503e154bf6fSAndrew Armenia dev_err(&piix4_adapter->dev, "Failed reset at end of " 5041da177e4SLinus Torvalds "transaction (%02x)\n", temp); 5051da177e4SLinus Torvalds } 506e154bf6fSAndrew Armenia dev_dbg(&piix4_adapter->dev, "Transaction (post): CNT=%02x, CMD=%02x, " 5071da177e4SLinus Torvalds "ADD=%02x, DAT0=%02x, DAT1=%02x\n", inb_p(SMBHSTCNT), 5081da177e4SLinus Torvalds inb_p(SMBHSTCMD), inb_p(SMBHSTADD), inb_p(SMBHSTDAT0), 5091da177e4SLinus Torvalds inb_p(SMBHSTDAT1)); 5101da177e4SLinus Torvalds return result; 5111da177e4SLinus Torvalds } 5121da177e4SLinus Torvalds 51397140342SDavid Brownell /* Return negative errno on error. */ 5141da177e4SLinus Torvalds static s32 piix4_access(struct i2c_adapter * adap, u16 addr, 5151da177e4SLinus Torvalds unsigned short flags, char read_write, 5161da177e4SLinus Torvalds u8 command, int size, union i2c_smbus_data * data) 5171da177e4SLinus Torvalds { 51814a8086dSAndrew Armenia struct i2c_piix4_adapdata *adapdata = i2c_get_adapdata(adap); 51914a8086dSAndrew Armenia unsigned short piix4_smba = adapdata->smba; 5201da177e4SLinus Torvalds int i, len; 52197140342SDavid Brownell int status; 5221da177e4SLinus Torvalds 5231da177e4SLinus Torvalds switch (size) { 5241da177e4SLinus Torvalds case I2C_SMBUS_QUICK: 525fa63cd56SJean Delvare outb_p((addr << 1) | read_write, 5261da177e4SLinus Torvalds SMBHSTADD); 5271da177e4SLinus Torvalds size = PIIX4_QUICK; 5281da177e4SLinus Torvalds break; 5291da177e4SLinus Torvalds case I2C_SMBUS_BYTE: 530fa63cd56SJean Delvare outb_p((addr << 1) | read_write, 5311da177e4SLinus Torvalds SMBHSTADD); 5321da177e4SLinus Torvalds if (read_write == I2C_SMBUS_WRITE) 5331da177e4SLinus Torvalds outb_p(command, SMBHSTCMD); 5341da177e4SLinus Torvalds size = PIIX4_BYTE; 5351da177e4SLinus Torvalds break; 5361da177e4SLinus Torvalds case I2C_SMBUS_BYTE_DATA: 537fa63cd56SJean Delvare outb_p((addr << 1) | read_write, 5381da177e4SLinus Torvalds SMBHSTADD); 5391da177e4SLinus Torvalds outb_p(command, SMBHSTCMD); 5401da177e4SLinus Torvalds if (read_write == I2C_SMBUS_WRITE) 5411da177e4SLinus Torvalds outb_p(data->byte, SMBHSTDAT0); 5421da177e4SLinus Torvalds size = PIIX4_BYTE_DATA; 5431da177e4SLinus Torvalds break; 5441da177e4SLinus Torvalds case I2C_SMBUS_WORD_DATA: 545fa63cd56SJean Delvare outb_p((addr << 1) | read_write, 5461da177e4SLinus Torvalds SMBHSTADD); 5471da177e4SLinus Torvalds outb_p(command, SMBHSTCMD); 5481da177e4SLinus Torvalds if (read_write == I2C_SMBUS_WRITE) { 5491da177e4SLinus Torvalds outb_p(data->word & 0xff, SMBHSTDAT0); 5501da177e4SLinus Torvalds outb_p((data->word & 0xff00) >> 8, SMBHSTDAT1); 5511da177e4SLinus Torvalds } 5521da177e4SLinus Torvalds size = PIIX4_WORD_DATA; 5531da177e4SLinus Torvalds break; 5541da177e4SLinus Torvalds case I2C_SMBUS_BLOCK_DATA: 555fa63cd56SJean Delvare outb_p((addr << 1) | read_write, 5561da177e4SLinus Torvalds SMBHSTADD); 5571da177e4SLinus Torvalds outb_p(command, SMBHSTCMD); 5581da177e4SLinus Torvalds if (read_write == I2C_SMBUS_WRITE) { 5591da177e4SLinus Torvalds len = data->block[0]; 560fa63cd56SJean Delvare if (len == 0 || len > I2C_SMBUS_BLOCK_MAX) 561fa63cd56SJean Delvare return -EINVAL; 5621da177e4SLinus Torvalds outb_p(len, SMBHSTDAT0); 563d7a4c763SWolfram Sang inb_p(SMBHSTCNT); /* Reset SMBBLKDAT */ 5641da177e4SLinus Torvalds for (i = 1; i <= len; i++) 5651da177e4SLinus Torvalds outb_p(data->block[i], SMBBLKDAT); 5661da177e4SLinus Torvalds } 5671da177e4SLinus Torvalds size = PIIX4_BLOCK_DATA; 5681da177e4SLinus Torvalds break; 569ac7fc4fbSJean Delvare default: 570ac7fc4fbSJean Delvare dev_warn(&adap->dev, "Unsupported transaction %d\n", size); 571ac7fc4fbSJean Delvare return -EOPNOTSUPP; 5721da177e4SLinus Torvalds } 5731da177e4SLinus Torvalds 5741da177e4SLinus Torvalds outb_p((size & 0x1C) + (ENABLE_INT9 & 1), SMBHSTCNT); 5751da177e4SLinus Torvalds 576e154bf6fSAndrew Armenia status = piix4_transaction(adap); 57797140342SDavid Brownell if (status) 57897140342SDavid Brownell return status; 5791da177e4SLinus Torvalds 5801da177e4SLinus Torvalds if ((read_write == I2C_SMBUS_WRITE) || (size == PIIX4_QUICK)) 5811da177e4SLinus Torvalds return 0; 5821da177e4SLinus Torvalds 5831da177e4SLinus Torvalds 5841da177e4SLinus Torvalds switch (size) { 5853578a075SJean Delvare case PIIX4_BYTE: 5861da177e4SLinus Torvalds case PIIX4_BYTE_DATA: 5871da177e4SLinus Torvalds data->byte = inb_p(SMBHSTDAT0); 5881da177e4SLinus Torvalds break; 5891da177e4SLinus Torvalds case PIIX4_WORD_DATA: 5901da177e4SLinus Torvalds data->word = inb_p(SMBHSTDAT0) + (inb_p(SMBHSTDAT1) << 8); 5911da177e4SLinus Torvalds break; 5921da177e4SLinus Torvalds case PIIX4_BLOCK_DATA: 5931da177e4SLinus Torvalds data->block[0] = inb_p(SMBHSTDAT0); 594fa63cd56SJean Delvare if (data->block[0] == 0 || data->block[0] > I2C_SMBUS_BLOCK_MAX) 595fa63cd56SJean Delvare return -EPROTO; 596d7a4c763SWolfram Sang inb_p(SMBHSTCNT); /* Reset SMBBLKDAT */ 5971da177e4SLinus Torvalds for (i = 1; i <= data->block[0]; i++) 5981da177e4SLinus Torvalds data->block[i] = inb_p(SMBBLKDAT); 5991da177e4SLinus Torvalds break; 6001da177e4SLinus Torvalds } 6011da177e4SLinus Torvalds return 0; 6021da177e4SLinus Torvalds } 6031da177e4SLinus Torvalds 60488fa2dfbSRicardo Ribalda Delgado static uint8_t piix4_imc_read(uint8_t idx) 60588fa2dfbSRicardo Ribalda Delgado { 60688fa2dfbSRicardo Ribalda Delgado outb_p(idx, KERNCZ_IMC_IDX); 60788fa2dfbSRicardo Ribalda Delgado return inb_p(KERNCZ_IMC_DATA); 60888fa2dfbSRicardo Ribalda Delgado } 60988fa2dfbSRicardo Ribalda Delgado 61088fa2dfbSRicardo Ribalda Delgado static void piix4_imc_write(uint8_t idx, uint8_t value) 61188fa2dfbSRicardo Ribalda Delgado { 61288fa2dfbSRicardo Ribalda Delgado outb_p(idx, KERNCZ_IMC_IDX); 61388fa2dfbSRicardo Ribalda Delgado outb_p(value, KERNCZ_IMC_DATA); 61488fa2dfbSRicardo Ribalda Delgado } 61588fa2dfbSRicardo Ribalda Delgado 61688fa2dfbSRicardo Ribalda Delgado static int piix4_imc_sleep(void) 61788fa2dfbSRicardo Ribalda Delgado { 61888fa2dfbSRicardo Ribalda Delgado int timeout = MAX_TIMEOUT; 61988fa2dfbSRicardo Ribalda Delgado 62088fa2dfbSRicardo Ribalda Delgado if (!request_muxed_region(KERNCZ_IMC_IDX, 2, "smbus_kerncz_imc")) 62188fa2dfbSRicardo Ribalda Delgado return -EBUSY; 62288fa2dfbSRicardo Ribalda Delgado 62388fa2dfbSRicardo Ribalda Delgado /* clear response register */ 62488fa2dfbSRicardo Ribalda Delgado piix4_imc_write(0x82, 0x00); 62588fa2dfbSRicardo Ribalda Delgado /* request ownership flag */ 62688fa2dfbSRicardo Ribalda Delgado piix4_imc_write(0x83, 0xB4); 62788fa2dfbSRicardo Ribalda Delgado /* kick off IMC Mailbox command 96 */ 62888fa2dfbSRicardo Ribalda Delgado piix4_imc_write(0x80, 0x96); 62988fa2dfbSRicardo Ribalda Delgado 63088fa2dfbSRicardo Ribalda Delgado while (timeout--) { 63188fa2dfbSRicardo Ribalda Delgado if (piix4_imc_read(0x82) == 0xfa) { 63288fa2dfbSRicardo Ribalda Delgado release_region(KERNCZ_IMC_IDX, 2); 63388fa2dfbSRicardo Ribalda Delgado return 0; 63488fa2dfbSRicardo Ribalda Delgado } 63588fa2dfbSRicardo Ribalda Delgado usleep_range(1000, 2000); 63688fa2dfbSRicardo Ribalda Delgado } 63788fa2dfbSRicardo Ribalda Delgado 63888fa2dfbSRicardo Ribalda Delgado release_region(KERNCZ_IMC_IDX, 2); 63988fa2dfbSRicardo Ribalda Delgado return -ETIMEDOUT; 64088fa2dfbSRicardo Ribalda Delgado } 64188fa2dfbSRicardo Ribalda Delgado 64288fa2dfbSRicardo Ribalda Delgado static void piix4_imc_wakeup(void) 64388fa2dfbSRicardo Ribalda Delgado { 64488fa2dfbSRicardo Ribalda Delgado int timeout = MAX_TIMEOUT; 64588fa2dfbSRicardo Ribalda Delgado 64688fa2dfbSRicardo Ribalda Delgado if (!request_muxed_region(KERNCZ_IMC_IDX, 2, "smbus_kerncz_imc")) 64788fa2dfbSRicardo Ribalda Delgado return; 64888fa2dfbSRicardo Ribalda Delgado 64988fa2dfbSRicardo Ribalda Delgado /* clear response register */ 65088fa2dfbSRicardo Ribalda Delgado piix4_imc_write(0x82, 0x00); 65188fa2dfbSRicardo Ribalda Delgado /* release ownership flag */ 65288fa2dfbSRicardo Ribalda Delgado piix4_imc_write(0x83, 0xB5); 65388fa2dfbSRicardo Ribalda Delgado /* kick off IMC Mailbox command 96 */ 65488fa2dfbSRicardo Ribalda Delgado piix4_imc_write(0x80, 0x96); 65588fa2dfbSRicardo Ribalda Delgado 65688fa2dfbSRicardo Ribalda Delgado while (timeout--) { 65788fa2dfbSRicardo Ribalda Delgado if (piix4_imc_read(0x82) == 0xfa) 65888fa2dfbSRicardo Ribalda Delgado break; 65988fa2dfbSRicardo Ribalda Delgado usleep_range(1000, 2000); 66088fa2dfbSRicardo Ribalda Delgado } 66188fa2dfbSRicardo Ribalda Delgado 66288fa2dfbSRicardo Ribalda Delgado release_region(KERNCZ_IMC_IDX, 2); 66388fa2dfbSRicardo Ribalda Delgado } 66488fa2dfbSRicardo Ribalda Delgado 6652fee61d2SChristian Fetzer /* 6662fee61d2SChristian Fetzer * Handles access to multiple SMBus ports on the SB800. 6672fee61d2SChristian Fetzer * The port is selected by bits 2:1 of the smb_en register (0x2c). 6682fee61d2SChristian Fetzer * Returns negative errno on error. 6692fee61d2SChristian Fetzer * 6702fee61d2SChristian Fetzer * Note: The selected port must be returned to the initial selection to avoid 6712fee61d2SChristian Fetzer * problems on certain systems. 6722fee61d2SChristian Fetzer */ 6732fee61d2SChristian Fetzer static s32 piix4_access_sb800(struct i2c_adapter *adap, u16 addr, 6742fee61d2SChristian Fetzer unsigned short flags, char read_write, 6752fee61d2SChristian Fetzer u8 command, int size, union i2c_smbus_data *data) 6762fee61d2SChristian Fetzer { 6772fee61d2SChristian Fetzer struct i2c_piix4_adapdata *adapdata = i2c_get_adapdata(adap); 678701dc207SRicardo Ribalda unsigned short piix4_smba = adapdata->smba; 679701dc207SRicardo Ribalda int retries = MAX_TIMEOUT; 680701dc207SRicardo Ribalda int smbslvcnt; 6812fee61d2SChristian Fetzer u8 smba_en_lo; 6822fee61d2SChristian Fetzer u8 port; 6832fee61d2SChristian Fetzer int retval; 6842fee61d2SChristian Fetzer 68504b6fcabSGuenter Roeck if (!request_muxed_region(SB800_PIIX4_SMB_IDX, 2, "sb800_piix4_smb")) 68604b6fcabSGuenter Roeck return -EBUSY; 687bbb27fc3SRicardo Ribalda 688701dc207SRicardo Ribalda /* Request the SMBUS semaphore, avoid conflicts with the IMC */ 689701dc207SRicardo Ribalda smbslvcnt = inb_p(SMBSLVCNT); 690701dc207SRicardo Ribalda do { 691701dc207SRicardo Ribalda outb_p(smbslvcnt | 0x10, SMBSLVCNT); 692701dc207SRicardo Ribalda 693701dc207SRicardo Ribalda /* Check the semaphore status */ 694701dc207SRicardo Ribalda smbslvcnt = inb_p(SMBSLVCNT); 695701dc207SRicardo Ribalda if (smbslvcnt & 0x10) 696701dc207SRicardo Ribalda break; 697701dc207SRicardo Ribalda 698701dc207SRicardo Ribalda usleep_range(1000, 2000); 699701dc207SRicardo Ribalda } while (--retries); 700701dc207SRicardo Ribalda /* SMBus is still owned by the IMC, we give up */ 701bbb27fc3SRicardo Ribalda if (!retries) { 70204b6fcabSGuenter Roeck retval = -EBUSY; 70304b6fcabSGuenter Roeck goto release; 704bbb27fc3SRicardo Ribalda } 7052fee61d2SChristian Fetzer 70688fa2dfbSRicardo Ribalda Delgado /* 70788fa2dfbSRicardo Ribalda Delgado * Notify the IMC (Integrated Micro Controller) if required. 70888fa2dfbSRicardo Ribalda Delgado * Among other responsibilities, the IMC is in charge of monitoring 70988fa2dfbSRicardo Ribalda Delgado * the System fans and temperature sensors, and act accordingly. 71088fa2dfbSRicardo Ribalda Delgado * All this is done through SMBus and can/will collide 71188fa2dfbSRicardo Ribalda Delgado * with our transactions if they are long (BLOCK_DATA). 71288fa2dfbSRicardo Ribalda Delgado * Therefore we need to request the ownership flag during those 71388fa2dfbSRicardo Ribalda Delgado * transactions. 71488fa2dfbSRicardo Ribalda Delgado */ 71588fa2dfbSRicardo Ribalda Delgado if ((size == I2C_SMBUS_BLOCK_DATA) && adapdata->notify_imc) { 71688fa2dfbSRicardo Ribalda Delgado int ret; 71788fa2dfbSRicardo Ribalda Delgado 71888fa2dfbSRicardo Ribalda Delgado ret = piix4_imc_sleep(); 71988fa2dfbSRicardo Ribalda Delgado switch (ret) { 72088fa2dfbSRicardo Ribalda Delgado case -EBUSY: 72188fa2dfbSRicardo Ribalda Delgado dev_warn(&adap->dev, 72288fa2dfbSRicardo Ribalda Delgado "IMC base address index region 0x%x already in use.\n", 72388fa2dfbSRicardo Ribalda Delgado KERNCZ_IMC_IDX); 72488fa2dfbSRicardo Ribalda Delgado break; 72588fa2dfbSRicardo Ribalda Delgado case -ETIMEDOUT: 72688fa2dfbSRicardo Ribalda Delgado dev_warn(&adap->dev, 72788fa2dfbSRicardo Ribalda Delgado "Failed to communicate with the IMC.\n"); 72888fa2dfbSRicardo Ribalda Delgado break; 72988fa2dfbSRicardo Ribalda Delgado default: 73088fa2dfbSRicardo Ribalda Delgado break; 73188fa2dfbSRicardo Ribalda Delgado } 73288fa2dfbSRicardo Ribalda Delgado 73388fa2dfbSRicardo Ribalda Delgado /* If IMC communication fails do not retry */ 73488fa2dfbSRicardo Ribalda Delgado if (ret) { 73588fa2dfbSRicardo Ribalda Delgado dev_warn(&adap->dev, 73688fa2dfbSRicardo Ribalda Delgado "Continuing without IMC notification.\n"); 73788fa2dfbSRicardo Ribalda Delgado adapdata->notify_imc = false; 73888fa2dfbSRicardo Ribalda Delgado } 73988fa2dfbSRicardo Ribalda Delgado } 74088fa2dfbSRicardo Ribalda Delgado 7416befa3fdSJean Delvare outb_p(piix4_port_sel_sb800, SB800_PIIX4_SMB_IDX); 7422fee61d2SChristian Fetzer smba_en_lo = inb_p(SB800_PIIX4_SMB_IDX + 1); 7432fee61d2SChristian Fetzer 7442fee61d2SChristian Fetzer port = adapdata->port; 7450fe16195SGuenter Roeck if ((smba_en_lo & piix4_port_mask_sb800) != port) 7460fe16195SGuenter Roeck outb_p((smba_en_lo & ~piix4_port_mask_sb800) | port, 7472fee61d2SChristian Fetzer SB800_PIIX4_SMB_IDX + 1); 7482fee61d2SChristian Fetzer 7492fee61d2SChristian Fetzer retval = piix4_access(adap, addr, flags, read_write, 7502fee61d2SChristian Fetzer command, size, data); 7512fee61d2SChristian Fetzer 7522fee61d2SChristian Fetzer outb_p(smba_en_lo, SB800_PIIX4_SMB_IDX + 1); 7532fee61d2SChristian Fetzer 754701dc207SRicardo Ribalda /* Release the semaphore */ 755701dc207SRicardo Ribalda outb_p(smbslvcnt | 0x20, SMBSLVCNT); 756701dc207SRicardo Ribalda 75788fa2dfbSRicardo Ribalda Delgado if ((size == I2C_SMBUS_BLOCK_DATA) && adapdata->notify_imc) 75888fa2dfbSRicardo Ribalda Delgado piix4_imc_wakeup(); 75988fa2dfbSRicardo Ribalda Delgado 76004b6fcabSGuenter Roeck release: 76104b6fcabSGuenter Roeck release_region(SB800_PIIX4_SMB_IDX, 2); 7622fee61d2SChristian Fetzer return retval; 7632fee61d2SChristian Fetzer } 7642fee61d2SChristian Fetzer 7651da177e4SLinus Torvalds static u32 piix4_func(struct i2c_adapter *adapter) 7661da177e4SLinus Torvalds { 7671da177e4SLinus Torvalds return I2C_FUNC_SMBUS_QUICK | I2C_FUNC_SMBUS_BYTE | 7681da177e4SLinus Torvalds I2C_FUNC_SMBUS_BYTE_DATA | I2C_FUNC_SMBUS_WORD_DATA | 7691da177e4SLinus Torvalds I2C_FUNC_SMBUS_BLOCK_DATA; 7701da177e4SLinus Torvalds } 7711da177e4SLinus Torvalds 7728f9082c5SJean Delvare static const struct i2c_algorithm smbus_algorithm = { 7731da177e4SLinus Torvalds .smbus_xfer = piix4_access, 7741da177e4SLinus Torvalds .functionality = piix4_func, 7751da177e4SLinus Torvalds }; 7761da177e4SLinus Torvalds 7772fee61d2SChristian Fetzer static const struct i2c_algorithm piix4_smbus_algorithm_sb800 = { 7782fee61d2SChristian Fetzer .smbus_xfer = piix4_access_sb800, 7792fee61d2SChristian Fetzer .functionality = piix4_func, 7802fee61d2SChristian Fetzer }; 7812fee61d2SChristian Fetzer 782392debf1SJingoo Han static const struct pci_device_id piix4_ids[] = { 7839b7389c0SJean Delvare { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82371AB_3) }, 7849b7389c0SJean Delvare { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82443MX_3) }, 7859b7389c0SJean Delvare { PCI_DEVICE(PCI_VENDOR_ID_EFAR, PCI_DEVICE_ID_EFAR_SLC90E66_3) }, 7869b7389c0SJean Delvare { PCI_DEVICE(PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_IXP200_SMBUS) }, 7879b7389c0SJean Delvare { PCI_DEVICE(PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_IXP300_SMBUS) }, 7889b7389c0SJean Delvare { PCI_DEVICE(PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_IXP400_SMBUS) }, 7899b7389c0SJean Delvare { PCI_DEVICE(PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_SBX00_SMBUS) }, 7903806e94bSCrane Cai { PCI_DEVICE(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_HUDSON2_SMBUS) }, 791bcb29994SVincent Wan { PCI_DEVICE(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_KERNCZ_SMBUS) }, 79224beb83aSPu Wen { PCI_DEVICE(PCI_VENDOR_ID_HYGON, PCI_DEVICE_ID_AMD_KERNCZ_SMBUS) }, 7939b7389c0SJean Delvare { PCI_DEVICE(PCI_VENDOR_ID_SERVERWORKS, 7949b7389c0SJean Delvare PCI_DEVICE_ID_SERVERWORKS_OSB4) }, 7959b7389c0SJean Delvare { PCI_DEVICE(PCI_VENDOR_ID_SERVERWORKS, 7969b7389c0SJean Delvare PCI_DEVICE_ID_SERVERWORKS_CSB5) }, 7979b7389c0SJean Delvare { PCI_DEVICE(PCI_VENDOR_ID_SERVERWORKS, 7989b7389c0SJean Delvare PCI_DEVICE_ID_SERVERWORKS_CSB6) }, 7999b7389c0SJean Delvare { PCI_DEVICE(PCI_VENDOR_ID_SERVERWORKS, 8009b7389c0SJean Delvare PCI_DEVICE_ID_SERVERWORKS_HT1000SB) }, 801506a8b6cSFlavio Leitner { PCI_DEVICE(PCI_VENDOR_ID_SERVERWORKS, 802506a8b6cSFlavio Leitner PCI_DEVICE_ID_SERVERWORKS_HT1100LD) }, 8031da177e4SLinus Torvalds { 0, } 8041da177e4SLinus Torvalds }; 8051da177e4SLinus Torvalds 8061da177e4SLinus Torvalds MODULE_DEVICE_TABLE (pci, piix4_ids); 8071da177e4SLinus Torvalds 808ca2061e1SChristian Fetzer static struct i2c_adapter *piix4_main_adapters[PIIX4_MAX_ADAPTERS]; 8092a2f7404SAndrew Armenia static struct i2c_adapter *piix4_aux_adapter; 810528d53a1SJean Delvare static int piix4_adapter_count; 811e154bf6fSAndrew Armenia 8120b255e92SBill Pemberton static int piix4_add_adapter(struct pci_dev *dev, unsigned short smba, 81388fa2dfbSRicardo Ribalda Delgado bool sb800_main, u8 port, bool notify_imc, 8140183eb8bSJean Delvare u8 hw_port_nr, const char *name, 8150183eb8bSJean Delvare struct i2c_adapter **padap) 816e154bf6fSAndrew Armenia { 817e154bf6fSAndrew Armenia struct i2c_adapter *adap; 818e154bf6fSAndrew Armenia struct i2c_piix4_adapdata *adapdata; 819e154bf6fSAndrew Armenia int retval; 820e154bf6fSAndrew Armenia 821e154bf6fSAndrew Armenia adap = kzalloc(sizeof(*adap), GFP_KERNEL); 822e154bf6fSAndrew Armenia if (adap == NULL) { 823e154bf6fSAndrew Armenia release_region(smba, SMBIOSIZE); 824e154bf6fSAndrew Armenia return -ENOMEM; 825e154bf6fSAndrew Armenia } 826e154bf6fSAndrew Armenia 827e154bf6fSAndrew Armenia adap->owner = THIS_MODULE; 828e154bf6fSAndrew Armenia adap->class = I2C_CLASS_HWMON | I2C_CLASS_SPD; 82983c60158SJean Delvare adap->algo = sb800_main ? &piix4_smbus_algorithm_sb800 83083c60158SJean Delvare : &smbus_algorithm; 831e154bf6fSAndrew Armenia 832e154bf6fSAndrew Armenia adapdata = kzalloc(sizeof(*adapdata), GFP_KERNEL); 833e154bf6fSAndrew Armenia if (adapdata == NULL) { 834e154bf6fSAndrew Armenia kfree(adap); 835e154bf6fSAndrew Armenia release_region(smba, SMBIOSIZE); 836e154bf6fSAndrew Armenia return -ENOMEM; 837e154bf6fSAndrew Armenia } 838e154bf6fSAndrew Armenia 839e154bf6fSAndrew Armenia adapdata->smba = smba; 84083c60158SJean Delvare adapdata->sb800_main = sb800_main; 8410fe16195SGuenter Roeck adapdata->port = port << piix4_port_shift_sb800; 84288fa2dfbSRicardo Ribalda Delgado adapdata->notify_imc = notify_imc; 843e154bf6fSAndrew Armenia 844e154bf6fSAndrew Armenia /* set up the sysfs linkage to our parent device */ 845e154bf6fSAndrew Armenia adap->dev.parent = &dev->dev; 846e154bf6fSAndrew Armenia 8470183eb8bSJean Delvare if (has_acpi_companion(&dev->dev)) { 8480183eb8bSJean Delvare acpi_preset_companion(&adap->dev, 8490183eb8bSJean Delvare ACPI_COMPANION(&dev->dev), 8500183eb8bSJean Delvare hw_port_nr); 8510183eb8bSJean Delvare } 8520183eb8bSJean Delvare 853e154bf6fSAndrew Armenia snprintf(adap->name, sizeof(adap->name), 854725d2e3fSChristian Fetzer "SMBus PIIX4 adapter%s at %04x", name, smba); 855e154bf6fSAndrew Armenia 856e154bf6fSAndrew Armenia i2c_set_adapdata(adap, adapdata); 857e154bf6fSAndrew Armenia 858e154bf6fSAndrew Armenia retval = i2c_add_adapter(adap); 859e154bf6fSAndrew Armenia if (retval) { 860e154bf6fSAndrew Armenia kfree(adapdata); 861e154bf6fSAndrew Armenia kfree(adap); 862e154bf6fSAndrew Armenia release_region(smba, SMBIOSIZE); 863e154bf6fSAndrew Armenia return retval; 864e154bf6fSAndrew Armenia } 865e154bf6fSAndrew Armenia 866e154bf6fSAndrew Armenia *padap = adap; 867e154bf6fSAndrew Armenia return 0; 868e154bf6fSAndrew Armenia } 869e154bf6fSAndrew Armenia 87088fa2dfbSRicardo Ribalda Delgado static int piix4_add_adapters_sb800(struct pci_dev *dev, unsigned short smba, 87188fa2dfbSRicardo Ribalda Delgado bool notify_imc) 8722fee61d2SChristian Fetzer { 8732fee61d2SChristian Fetzer struct i2c_piix4_adapdata *adapdata; 8742fee61d2SChristian Fetzer int port; 8752fee61d2SChristian Fetzer int retval; 8762fee61d2SChristian Fetzer 877528d53a1SJean Delvare if (dev->device == PCI_DEVICE_ID_AMD_KERNCZ_SMBUS || 878528d53a1SJean Delvare (dev->device == PCI_DEVICE_ID_AMD_HUDSON2_SMBUS && 879528d53a1SJean Delvare dev->revision >= 0x1F)) { 880528d53a1SJean Delvare piix4_adapter_count = HUDSON2_MAIN_PORTS; 881528d53a1SJean Delvare } else { 882528d53a1SJean Delvare piix4_adapter_count = PIIX4_MAX_ADAPTERS; 883528d53a1SJean Delvare } 884528d53a1SJean Delvare 885528d53a1SJean Delvare for (port = 0; port < piix4_adapter_count; port++) { 8860183eb8bSJean Delvare u8 hw_port_nr = port == 0 ? 0 : port + 1; 8870183eb8bSJean Delvare 88888fa2dfbSRicardo Ribalda Delgado retval = piix4_add_adapter(dev, smba, true, port, notify_imc, 8890183eb8bSJean Delvare hw_port_nr, 890725d2e3fSChristian Fetzer piix4_main_port_names_sb800[port], 8912fee61d2SChristian Fetzer &piix4_main_adapters[port]); 8922fee61d2SChristian Fetzer if (retval < 0) 8932fee61d2SChristian Fetzer goto error; 8942fee61d2SChristian Fetzer } 8952fee61d2SChristian Fetzer 8962fee61d2SChristian Fetzer return retval; 8972fee61d2SChristian Fetzer 8982fee61d2SChristian Fetzer error: 8992fee61d2SChristian Fetzer dev_err(&dev->dev, 9002fee61d2SChristian Fetzer "Error setting up SB800 adapters. Unregistering!\n"); 9012fee61d2SChristian Fetzer while (--port >= 0) { 9022fee61d2SChristian Fetzer adapdata = i2c_get_adapdata(piix4_main_adapters[port]); 9032fee61d2SChristian Fetzer if (adapdata->smba) { 9042fee61d2SChristian Fetzer i2c_del_adapter(piix4_main_adapters[port]); 9052fee61d2SChristian Fetzer kfree(adapdata); 9062fee61d2SChristian Fetzer kfree(piix4_main_adapters[port]); 9072fee61d2SChristian Fetzer piix4_main_adapters[port] = NULL; 9082fee61d2SChristian Fetzer } 9092fee61d2SChristian Fetzer } 9102fee61d2SChristian Fetzer 9112fee61d2SChristian Fetzer return retval; 9122fee61d2SChristian Fetzer } 9132fee61d2SChristian Fetzer 9140b255e92SBill Pemberton static int piix4_probe(struct pci_dev *dev, const struct pci_device_id *id) 9151da177e4SLinus Torvalds { 9161da177e4SLinus Torvalds int retval; 91752795f6fSJean Delvare bool is_sb800 = false; 9181da177e4SLinus Torvalds 91976b3e28fSCrane Cai if ((dev->vendor == PCI_VENDOR_ID_ATI && 92076b3e28fSCrane Cai dev->device == PCI_DEVICE_ID_ATI_SBX00_SMBUS && 92176b3e28fSCrane Cai dev->revision >= 0x40) || 92224beb83aSPu Wen dev->vendor == PCI_VENDOR_ID_AMD || 92324beb83aSPu Wen dev->vendor == PCI_VENDOR_ID_HYGON) { 92488fa2dfbSRicardo Ribalda Delgado bool notify_imc = false; 92552795f6fSJean Delvare is_sb800 = true; 92652795f6fSJean Delvare 92724beb83aSPu Wen if ((dev->vendor == PCI_VENDOR_ID_AMD || 92824beb83aSPu Wen dev->vendor == PCI_VENDOR_ID_HYGON) && 92988fa2dfbSRicardo Ribalda Delgado dev->device == PCI_DEVICE_ID_AMD_KERNCZ_SMBUS) { 93088fa2dfbSRicardo Ribalda Delgado u8 imc; 93188fa2dfbSRicardo Ribalda Delgado 93288fa2dfbSRicardo Ribalda Delgado /* 93388fa2dfbSRicardo Ribalda Delgado * Detect if IMC is active or not, this method is 93488fa2dfbSRicardo Ribalda Delgado * described on coreboot's AMD IMC notes 93588fa2dfbSRicardo Ribalda Delgado */ 93688fa2dfbSRicardo Ribalda Delgado pci_bus_read_config_byte(dev->bus, PCI_DEVFN(0x14, 3), 93788fa2dfbSRicardo Ribalda Delgado 0x40, &imc); 93888fa2dfbSRicardo Ribalda Delgado if (imc & 0x80) 93988fa2dfbSRicardo Ribalda Delgado notify_imc = true; 94088fa2dfbSRicardo Ribalda Delgado } 94188fa2dfbSRicardo Ribalda Delgado 94287e1960eSShane Huang /* base address location etc changed in SB800 */ 943a94dd00fSRudolf Marek retval = piix4_setup_sb800(dev, id, 0); 94404b6fcabSGuenter Roeck if (retval < 0) 9452fee61d2SChristian Fetzer return retval; 94687e1960eSShane Huang 9472fee61d2SChristian Fetzer /* 9482fee61d2SChristian Fetzer * Try to register multiplexed main SMBus adapter, 9492fee61d2SChristian Fetzer * give up if we can't 9502fee61d2SChristian Fetzer */ 95188fa2dfbSRicardo Ribalda Delgado retval = piix4_add_adapters_sb800(dev, retval, notify_imc); 95204b6fcabSGuenter Roeck if (retval < 0) 9532fee61d2SChristian Fetzer return retval; 9542fee61d2SChristian Fetzer } else { 9552fee61d2SChristian Fetzer retval = piix4_setup(dev, id); 95614a8086dSAndrew Armenia if (retval < 0) 9571da177e4SLinus Torvalds return retval; 9581da177e4SLinus Torvalds 9592a2f7404SAndrew Armenia /* Try to register main SMBus adapter, give up if we can't */ 9600183eb8bSJean Delvare retval = piix4_add_adapter(dev, retval, false, 0, false, 0, 9610183eb8bSJean Delvare "", &piix4_main_adapters[0]); 9622a2f7404SAndrew Armenia if (retval < 0) 9632a2f7404SAndrew Armenia return retval; 9642fee61d2SChristian Fetzer } 9652a2f7404SAndrew Armenia 9662a2f7404SAndrew Armenia /* Check for auxiliary SMBus on some AMD chipsets */ 967a94dd00fSRudolf Marek retval = -ENODEV; 968a94dd00fSRudolf Marek 9692a2f7404SAndrew Armenia if (dev->vendor == PCI_VENDOR_ID_ATI && 970a94dd00fSRudolf Marek dev->device == PCI_DEVICE_ID_ATI_SBX00_SMBUS) { 971a94dd00fSRudolf Marek if (dev->revision < 0x40) { 9722a2f7404SAndrew Armenia retval = piix4_setup_aux(dev, id, 0x58); 973a94dd00fSRudolf Marek } else { 974a94dd00fSRudolf Marek /* SB800 added aux bus too */ 975a94dd00fSRudolf Marek retval = piix4_setup_sb800(dev, id, 1); 976a94dd00fSRudolf Marek } 977a94dd00fSRudolf Marek } 978a94dd00fSRudolf Marek 979a94dd00fSRudolf Marek if (dev->vendor == PCI_VENDOR_ID_AMD && 980f27237c1SAdam Honse (dev->device == PCI_DEVICE_ID_AMD_HUDSON2_SMBUS || 981f27237c1SAdam Honse dev->device == PCI_DEVICE_ID_AMD_KERNCZ_SMBUS)) { 982a94dd00fSRudolf Marek retval = piix4_setup_sb800(dev, id, 1); 983a94dd00fSRudolf Marek } 984a94dd00fSRudolf Marek 9852a2f7404SAndrew Armenia if (retval > 0) { 9862a2f7404SAndrew Armenia /* Try to add the aux adapter if it exists, 9872a2f7404SAndrew Armenia * piix4_add_adapter will clean up if this fails */ 9880183eb8bSJean Delvare piix4_add_adapter(dev, retval, false, 0, false, 1, 98952795f6fSJean Delvare is_sb800 ? piix4_aux_port_name_sb800 : "", 990725d2e3fSChristian Fetzer &piix4_aux_adapter); 9912a2f7404SAndrew Armenia } 9922a2f7404SAndrew Armenia 9932a2f7404SAndrew Armenia return 0; 9941da177e4SLinus Torvalds } 9951da177e4SLinus Torvalds 9960b255e92SBill Pemberton static void piix4_adap_remove(struct i2c_adapter *adap) 99714a8086dSAndrew Armenia { 99814a8086dSAndrew Armenia struct i2c_piix4_adapdata *adapdata = i2c_get_adapdata(adap); 99914a8086dSAndrew Armenia 100014a8086dSAndrew Armenia if (adapdata->smba) { 100114a8086dSAndrew Armenia i2c_del_adapter(adap); 100204b6fcabSGuenter Roeck if (adapdata->port == (0 << piix4_port_shift_sb800)) 100314a8086dSAndrew Armenia release_region(adapdata->smba, SMBIOSIZE); 1004e154bf6fSAndrew Armenia kfree(adapdata); 1005e154bf6fSAndrew Armenia kfree(adap); 100614a8086dSAndrew Armenia } 100714a8086dSAndrew Armenia } 100814a8086dSAndrew Armenia 10090b255e92SBill Pemberton static void piix4_remove(struct pci_dev *dev) 10101da177e4SLinus Torvalds { 1011528d53a1SJean Delvare int port = piix4_adapter_count; 1012ca2061e1SChristian Fetzer 1013ca2061e1SChristian Fetzer while (--port >= 0) { 1014ca2061e1SChristian Fetzer if (piix4_main_adapters[port]) { 1015ca2061e1SChristian Fetzer piix4_adap_remove(piix4_main_adapters[port]); 1016ca2061e1SChristian Fetzer piix4_main_adapters[port] = NULL; 1017ca2061e1SChristian Fetzer } 1018e154bf6fSAndrew Armenia } 10192a2f7404SAndrew Armenia 10202a2f7404SAndrew Armenia if (piix4_aux_adapter) { 10212a2f7404SAndrew Armenia piix4_adap_remove(piix4_aux_adapter); 10222a2f7404SAndrew Armenia piix4_aux_adapter = NULL; 10232a2f7404SAndrew Armenia } 10241da177e4SLinus Torvalds } 10251da177e4SLinus Torvalds 10261da177e4SLinus Torvalds static struct pci_driver piix4_driver = { 10271da177e4SLinus Torvalds .name = "piix4_smbus", 10281da177e4SLinus Torvalds .id_table = piix4_ids, 10291da177e4SLinus Torvalds .probe = piix4_probe, 10300b255e92SBill Pemberton .remove = piix4_remove, 10311da177e4SLinus Torvalds }; 10321da177e4SLinus Torvalds 103356f21788SAxel Lin module_pci_driver(piix4_driver); 10341da177e4SLinus Torvalds 1035*f80531c8SJarkko Nikula MODULE_AUTHOR("Frodo Looijaard <frodol@dds.nl>"); 1036*f80531c8SJarkko Nikula MODULE_AUTHOR("Philip Edelbrock <phil@netroedge.com>"); 10371da177e4SLinus Torvalds MODULE_DESCRIPTION("PIIX4 SMBus driver"); 10381da177e4SLinus Torvalds MODULE_LICENSE("GPL"); 1039