1*c942fddfSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later 21da177e4SLinus Torvalds /* 31da177e4SLinus Torvalds Copyright (c) 1999-2002 Merlin Hughes <merlin@merlin.org> 41da177e4SLinus Torvalds 51da177e4SLinus Torvalds Shamelessly ripped from i2c-piix4.c: 61da177e4SLinus Torvalds 71da177e4SLinus Torvalds Copyright (c) 1998, 1999 Frodo Looijaard <frodol@dds.nl> and 81da177e4SLinus Torvalds Philip Edelbrock <phil@netroedge.com> 91da177e4SLinus Torvalds 101da177e4SLinus Torvalds */ 111da177e4SLinus Torvalds 121da177e4SLinus Torvalds /* 131da177e4SLinus Torvalds 2002-04-08: Added nForce support. (Csaba Halasz) 141da177e4SLinus Torvalds 2002-10-03: Fixed nForce PnP I/O port. (Michael Steil) 151da177e4SLinus Torvalds 2002-12-28: Rewritten into something that resembles a Linux driver (hch) 161da177e4SLinus Torvalds 2003-11-29: Added back AMD8111 removed by the previous rewrite. 171da177e4SLinus Torvalds (Philip Pokorny) 181da177e4SLinus Torvalds */ 191da177e4SLinus Torvalds 201da177e4SLinus Torvalds /* 211da177e4SLinus Torvalds Supports AMD756, AMD766, AMD768, AMD8111 and nVidia nForce 221da177e4SLinus Torvalds Note: we assume there can only be one device, with one SMBus interface. 231da177e4SLinus Torvalds */ 241da177e4SLinus Torvalds 251da177e4SLinus Torvalds #include <linux/module.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> 3254fb4a05SJean Delvare #include <linux/acpi.h> 3321782180SH Hartley Sweeten #include <linux/io.h> 341da177e4SLinus Torvalds 351da177e4SLinus Torvalds /* AMD756 SMBus address offsets */ 361da177e4SLinus Torvalds #define SMB_ADDR_OFFSET 0xE0 371da177e4SLinus Torvalds #define SMB_IOSIZE 16 381da177e4SLinus Torvalds #define SMB_GLOBAL_STATUS (0x0 + amd756_ioport) 391da177e4SLinus Torvalds #define SMB_GLOBAL_ENABLE (0x2 + amd756_ioport) 401da177e4SLinus Torvalds #define SMB_HOST_ADDRESS (0x4 + amd756_ioport) 411da177e4SLinus Torvalds #define SMB_HOST_DATA (0x6 + amd756_ioport) 421da177e4SLinus Torvalds #define SMB_HOST_COMMAND (0x8 + amd756_ioport) 431da177e4SLinus Torvalds #define SMB_HOST_BLOCK_DATA (0x9 + amd756_ioport) 441da177e4SLinus Torvalds #define SMB_HAS_DATA (0xA + amd756_ioport) 451da177e4SLinus Torvalds #define SMB_HAS_DEVICE_ADDRESS (0xC + amd756_ioport) 461da177e4SLinus Torvalds #define SMB_HAS_HOST_ADDRESS (0xE + amd756_ioport) 471da177e4SLinus Torvalds #define SMB_SNOOP_ADDRESS (0xF + amd756_ioport) 481da177e4SLinus Torvalds 491da177e4SLinus Torvalds /* PCI Address Constants */ 501da177e4SLinus Torvalds 511da177e4SLinus Torvalds /* address of I/O space */ 521da177e4SLinus Torvalds #define SMBBA 0x058 /* mh */ 531da177e4SLinus Torvalds #define SMBBANFORCE 0x014 541da177e4SLinus Torvalds 551da177e4SLinus Torvalds /* general configuration */ 561da177e4SLinus Torvalds #define SMBGCFG 0x041 /* mh */ 571da177e4SLinus Torvalds 581da177e4SLinus Torvalds /* silicon revision code */ 591da177e4SLinus Torvalds #define SMBREV 0x008 601da177e4SLinus Torvalds 611da177e4SLinus Torvalds /* Other settings */ 621da177e4SLinus Torvalds #define MAX_TIMEOUT 500 631da177e4SLinus Torvalds 641da177e4SLinus Torvalds /* AMD756 constants */ 651da177e4SLinus Torvalds #define AMD756_QUICK 0x00 661da177e4SLinus Torvalds #define AMD756_BYTE 0x01 671da177e4SLinus Torvalds #define AMD756_BYTE_DATA 0x02 681da177e4SLinus Torvalds #define AMD756_WORD_DATA 0x03 691da177e4SLinus Torvalds #define AMD756_PROCESS_CALL 0x04 701da177e4SLinus Torvalds #define AMD756_BLOCK_DATA 0x05 711da177e4SLinus Torvalds 72d6072f84SJean Delvare static struct pci_driver amd756_driver; 7360507095SJean Delvare static unsigned short amd756_ioport; 741da177e4SLinus Torvalds 751da177e4SLinus Torvalds /* 761da177e4SLinus Torvalds SMBUS event = I/O 28-29 bit 11 771da177e4SLinus Torvalds see E0 for the status bits and enabled in E2 781da177e4SLinus Torvalds 791da177e4SLinus Torvalds */ 801da177e4SLinus Torvalds #define GS_ABRT_STS (1 << 0) 811da177e4SLinus Torvalds #define GS_COL_STS (1 << 1) 821da177e4SLinus Torvalds #define GS_PRERR_STS (1 << 2) 831da177e4SLinus Torvalds #define GS_HST_STS (1 << 3) 841da177e4SLinus Torvalds #define GS_HCYC_STS (1 << 4) 851da177e4SLinus Torvalds #define GS_TO_STS (1 << 5) 861da177e4SLinus Torvalds #define GS_SMB_STS (1 << 11) 871da177e4SLinus Torvalds 881da177e4SLinus Torvalds #define GS_CLEAR_STS (GS_ABRT_STS | GS_COL_STS | GS_PRERR_STS | \ 891da177e4SLinus Torvalds GS_HCYC_STS | GS_TO_STS ) 901da177e4SLinus Torvalds 911da177e4SLinus Torvalds #define GE_CYC_TYPE_MASK (7) 921da177e4SLinus Torvalds #define GE_HOST_STC (1 << 3) 931da177e4SLinus Torvalds #define GE_ABORT (1 << 5) 941da177e4SLinus Torvalds 951da177e4SLinus Torvalds 961da177e4SLinus Torvalds static int amd756_transaction(struct i2c_adapter *adap) 971da177e4SLinus Torvalds { 981da177e4SLinus Torvalds int temp; 991da177e4SLinus Torvalds int result = 0; 1001da177e4SLinus Torvalds int timeout = 0; 1011da177e4SLinus Torvalds 1021da177e4SLinus Torvalds dev_dbg(&adap->dev, "Transaction (pre): GS=%04x, GE=%04x, ADD=%04x, " 1031da177e4SLinus Torvalds "DAT=%04x\n", inw_p(SMB_GLOBAL_STATUS), 1041da177e4SLinus Torvalds inw_p(SMB_GLOBAL_ENABLE), inw_p(SMB_HOST_ADDRESS), 1051da177e4SLinus Torvalds inb_p(SMB_HOST_DATA)); 1061da177e4SLinus Torvalds 1071da177e4SLinus Torvalds /* Make sure the SMBus host is ready to start transmitting */ 1081da177e4SLinus Torvalds if ((temp = inw_p(SMB_GLOBAL_STATUS)) & (GS_HST_STS | GS_SMB_STS)) { 1091da177e4SLinus Torvalds dev_dbg(&adap->dev, "SMBus busy (%04x). Waiting...\n", temp); 1101da177e4SLinus Torvalds do { 1111da177e4SLinus Torvalds msleep(1); 1121da177e4SLinus Torvalds temp = inw_p(SMB_GLOBAL_STATUS); 1131da177e4SLinus Torvalds } while ((temp & (GS_HST_STS | GS_SMB_STS)) && 1141da177e4SLinus Torvalds (timeout++ < MAX_TIMEOUT)); 1151da177e4SLinus Torvalds /* If the SMBus is still busy, we give up */ 1164ccc28f7SRoel Kluin if (timeout > MAX_TIMEOUT) { 1171da177e4SLinus Torvalds dev_dbg(&adap->dev, "Busy wait timeout (%04x)\n", temp); 1181da177e4SLinus Torvalds goto abort; 1191da177e4SLinus Torvalds } 1201da177e4SLinus Torvalds timeout = 0; 1211da177e4SLinus Torvalds } 1221da177e4SLinus Torvalds 1231da177e4SLinus Torvalds /* start the transaction by setting the start bit */ 1241da177e4SLinus Torvalds outw_p(inw(SMB_GLOBAL_ENABLE) | GE_HOST_STC, SMB_GLOBAL_ENABLE); 1251da177e4SLinus Torvalds 1261da177e4SLinus Torvalds /* We will always wait for a fraction of a second! */ 1271da177e4SLinus Torvalds do { 1281da177e4SLinus Torvalds msleep(1); 1291da177e4SLinus Torvalds temp = inw_p(SMB_GLOBAL_STATUS); 1301da177e4SLinus Torvalds } while ((temp & GS_HST_STS) && (timeout++ < MAX_TIMEOUT)); 1311da177e4SLinus Torvalds 1321da177e4SLinus Torvalds /* If the SMBus is still busy, we give up */ 1334ccc28f7SRoel Kluin if (timeout > MAX_TIMEOUT) { 1341da177e4SLinus Torvalds dev_dbg(&adap->dev, "Completion timeout!\n"); 1351da177e4SLinus Torvalds goto abort; 1361da177e4SLinus Torvalds } 1371da177e4SLinus Torvalds 1381da177e4SLinus Torvalds if (temp & GS_PRERR_STS) { 13997140342SDavid Brownell result = -ENXIO; 1401da177e4SLinus Torvalds dev_dbg(&adap->dev, "SMBus Protocol error (no response)!\n"); 1411da177e4SLinus Torvalds } 1421da177e4SLinus Torvalds 1431da177e4SLinus Torvalds if (temp & GS_COL_STS) { 14497140342SDavid Brownell result = -EIO; 1451da177e4SLinus Torvalds dev_warn(&adap->dev, "SMBus collision!\n"); 1461da177e4SLinus Torvalds } 1471da177e4SLinus Torvalds 1481da177e4SLinus Torvalds if (temp & GS_TO_STS) { 14997140342SDavid Brownell result = -ETIMEDOUT; 1501da177e4SLinus Torvalds dev_dbg(&adap->dev, "SMBus protocol timeout!\n"); 1511da177e4SLinus Torvalds } 1521da177e4SLinus Torvalds 1531da177e4SLinus Torvalds if (temp & GS_HCYC_STS) 1541da177e4SLinus Torvalds dev_dbg(&adap->dev, "SMBus protocol success!\n"); 1551da177e4SLinus Torvalds 1561da177e4SLinus Torvalds outw_p(GS_CLEAR_STS, SMB_GLOBAL_STATUS); 1571da177e4SLinus Torvalds 1581da177e4SLinus Torvalds #ifdef DEBUG 1591da177e4SLinus Torvalds if (((temp = inw_p(SMB_GLOBAL_STATUS)) & GS_CLEAR_STS) != 0x00) { 1601da177e4SLinus Torvalds dev_dbg(&adap->dev, 1611da177e4SLinus Torvalds "Failed reset at end of transaction (%04x)\n", temp); 1621da177e4SLinus Torvalds } 1631da177e4SLinus Torvalds #endif 1641da177e4SLinus Torvalds 1651da177e4SLinus Torvalds dev_dbg(&adap->dev, 1661da177e4SLinus Torvalds "Transaction (post): GS=%04x, GE=%04x, ADD=%04x, DAT=%04x\n", 1671da177e4SLinus Torvalds inw_p(SMB_GLOBAL_STATUS), inw_p(SMB_GLOBAL_ENABLE), 1681da177e4SLinus Torvalds inw_p(SMB_HOST_ADDRESS), inb_p(SMB_HOST_DATA)); 1691da177e4SLinus Torvalds 1701da177e4SLinus Torvalds return result; 1711da177e4SLinus Torvalds 1721da177e4SLinus Torvalds abort: 1731da177e4SLinus Torvalds dev_warn(&adap->dev, "Sending abort\n"); 1741da177e4SLinus Torvalds outw_p(inw(SMB_GLOBAL_ENABLE) | GE_ABORT, SMB_GLOBAL_ENABLE); 1751da177e4SLinus Torvalds msleep(100); 1761da177e4SLinus Torvalds outw_p(GS_CLEAR_STS, SMB_GLOBAL_STATUS); 17797140342SDavid Brownell return -EIO; 1781da177e4SLinus Torvalds } 1791da177e4SLinus Torvalds 18097140342SDavid Brownell /* Return negative errno on error. */ 1811da177e4SLinus Torvalds static s32 amd756_access(struct i2c_adapter * adap, u16 addr, 1821da177e4SLinus Torvalds unsigned short flags, char read_write, 1831da177e4SLinus Torvalds u8 command, int size, union i2c_smbus_data * data) 1841da177e4SLinus Torvalds { 1851da177e4SLinus Torvalds int i, len; 18697140342SDavid Brownell int status; 1871da177e4SLinus Torvalds 1881da177e4SLinus Torvalds switch (size) { 1891da177e4SLinus Torvalds case I2C_SMBUS_QUICK: 1901da177e4SLinus Torvalds outw_p(((addr & 0x7f) << 1) | (read_write & 0x01), 1911da177e4SLinus Torvalds SMB_HOST_ADDRESS); 1921da177e4SLinus Torvalds size = AMD756_QUICK; 1931da177e4SLinus Torvalds break; 1941da177e4SLinus Torvalds case I2C_SMBUS_BYTE: 1951da177e4SLinus Torvalds outw_p(((addr & 0x7f) << 1) | (read_write & 0x01), 1961da177e4SLinus Torvalds SMB_HOST_ADDRESS); 1971da177e4SLinus Torvalds if (read_write == I2C_SMBUS_WRITE) 1981da177e4SLinus Torvalds outb_p(command, SMB_HOST_DATA); 1991da177e4SLinus Torvalds size = AMD756_BYTE; 2001da177e4SLinus Torvalds break; 2011da177e4SLinus Torvalds case I2C_SMBUS_BYTE_DATA: 2021da177e4SLinus Torvalds outw_p(((addr & 0x7f) << 1) | (read_write & 0x01), 2031da177e4SLinus Torvalds SMB_HOST_ADDRESS); 2041da177e4SLinus Torvalds outb_p(command, SMB_HOST_COMMAND); 2051da177e4SLinus Torvalds if (read_write == I2C_SMBUS_WRITE) 2061da177e4SLinus Torvalds outw_p(data->byte, SMB_HOST_DATA); 2071da177e4SLinus Torvalds size = AMD756_BYTE_DATA; 2081da177e4SLinus Torvalds break; 2091da177e4SLinus Torvalds case I2C_SMBUS_WORD_DATA: 2101da177e4SLinus Torvalds outw_p(((addr & 0x7f) << 1) | (read_write & 0x01), 2111da177e4SLinus Torvalds SMB_HOST_ADDRESS); 2121da177e4SLinus Torvalds outb_p(command, SMB_HOST_COMMAND); 2131da177e4SLinus Torvalds if (read_write == I2C_SMBUS_WRITE) 2141da177e4SLinus Torvalds outw_p(data->word, SMB_HOST_DATA); /* TODO: endian???? */ 2151da177e4SLinus Torvalds size = AMD756_WORD_DATA; 2161da177e4SLinus Torvalds break; 2171da177e4SLinus Torvalds case I2C_SMBUS_BLOCK_DATA: 2181da177e4SLinus Torvalds outw_p(((addr & 0x7f) << 1) | (read_write & 0x01), 2191da177e4SLinus Torvalds SMB_HOST_ADDRESS); 2201da177e4SLinus Torvalds outb_p(command, SMB_HOST_COMMAND); 2211da177e4SLinus Torvalds if (read_write == I2C_SMBUS_WRITE) { 2221da177e4SLinus Torvalds len = data->block[0]; 2231da177e4SLinus Torvalds if (len < 0) 2241da177e4SLinus Torvalds len = 0; 2251da177e4SLinus Torvalds if (len > 32) 2261da177e4SLinus Torvalds len = 32; 2271da177e4SLinus Torvalds outw_p(len, SMB_HOST_DATA); 2281da177e4SLinus Torvalds /* i = inw_p(SMBHSTCNT); Reset SMBBLKDAT */ 2291da177e4SLinus Torvalds for (i = 1; i <= len; i++) 2301da177e4SLinus Torvalds outb_p(data->block[i], 2311da177e4SLinus Torvalds SMB_HOST_BLOCK_DATA); 2321da177e4SLinus Torvalds } 2331da177e4SLinus Torvalds size = AMD756_BLOCK_DATA; 2341da177e4SLinus Torvalds break; 235ac7fc4fbSJean Delvare default: 236ac7fc4fbSJean Delvare dev_warn(&adap->dev, "Unsupported transaction %d\n", size); 237ac7fc4fbSJean Delvare return -EOPNOTSUPP; 2381da177e4SLinus Torvalds } 2391da177e4SLinus Torvalds 2401da177e4SLinus Torvalds /* How about enabling interrupts... */ 2411da177e4SLinus Torvalds outw_p(size & GE_CYC_TYPE_MASK, SMB_GLOBAL_ENABLE); 2421da177e4SLinus Torvalds 24397140342SDavid Brownell status = amd756_transaction(adap); 24497140342SDavid Brownell if (status) 24597140342SDavid Brownell return status; 2461da177e4SLinus Torvalds 2471da177e4SLinus Torvalds if ((read_write == I2C_SMBUS_WRITE) || (size == AMD756_QUICK)) 2481da177e4SLinus Torvalds return 0; 2491da177e4SLinus Torvalds 2501da177e4SLinus Torvalds 2511da177e4SLinus Torvalds switch (size) { 2521da177e4SLinus Torvalds case AMD756_BYTE: 2531da177e4SLinus Torvalds data->byte = inw_p(SMB_HOST_DATA); 2541da177e4SLinus Torvalds break; 2551da177e4SLinus Torvalds case AMD756_BYTE_DATA: 2561da177e4SLinus Torvalds data->byte = inw_p(SMB_HOST_DATA); 2571da177e4SLinus Torvalds break; 2581da177e4SLinus Torvalds case AMD756_WORD_DATA: 2591da177e4SLinus Torvalds data->word = inw_p(SMB_HOST_DATA); /* TODO: endian???? */ 2601da177e4SLinus Torvalds break; 2611da177e4SLinus Torvalds case AMD756_BLOCK_DATA: 2621da177e4SLinus Torvalds data->block[0] = inw_p(SMB_HOST_DATA) & 0x3f; 2631da177e4SLinus Torvalds if(data->block[0] > 32) 2641da177e4SLinus Torvalds data->block[0] = 32; 2651da177e4SLinus Torvalds /* i = inw_p(SMBHSTCNT); Reset SMBBLKDAT */ 2661da177e4SLinus Torvalds for (i = 1; i <= data->block[0]; i++) 2671da177e4SLinus Torvalds data->block[i] = inb_p(SMB_HOST_BLOCK_DATA); 2681da177e4SLinus Torvalds break; 2691da177e4SLinus Torvalds } 2701da177e4SLinus Torvalds 2711da177e4SLinus Torvalds return 0; 2721da177e4SLinus Torvalds } 2731da177e4SLinus Torvalds 2741da177e4SLinus Torvalds static u32 amd756_func(struct i2c_adapter *adapter) 2751da177e4SLinus Torvalds { 2761da177e4SLinus Torvalds return I2C_FUNC_SMBUS_QUICK | I2C_FUNC_SMBUS_BYTE | 2771da177e4SLinus Torvalds I2C_FUNC_SMBUS_BYTE_DATA | I2C_FUNC_SMBUS_WORD_DATA | 278875b0a47SJean Delvare I2C_FUNC_SMBUS_BLOCK_DATA; 2791da177e4SLinus Torvalds } 2801da177e4SLinus Torvalds 2818f9082c5SJean Delvare static const struct i2c_algorithm smbus_algorithm = { 2821da177e4SLinus Torvalds .smbus_xfer = amd756_access, 2831da177e4SLinus Torvalds .functionality = amd756_func, 2841da177e4SLinus Torvalds }; 2851da177e4SLinus Torvalds 2861da177e4SLinus Torvalds struct i2c_adapter amd756_smbus = { 2871da177e4SLinus Torvalds .owner = THIS_MODULE, 2883401b2ffSJean Delvare .class = I2C_CLASS_HWMON | I2C_CLASS_SPD, 2891da177e4SLinus Torvalds .algo = &smbus_algorithm, 2901da177e4SLinus Torvalds }; 2911da177e4SLinus Torvalds 2921da177e4SLinus Torvalds enum chiptype { AMD756, AMD766, AMD768, NFORCE, AMD8111 }; 2931da177e4SLinus Torvalds static const char* chipname[] = { 2941da177e4SLinus Torvalds "AMD756", "AMD766", "AMD768", 2951da177e4SLinus Torvalds "nVidia nForce", "AMD8111", 2961da177e4SLinus Torvalds }; 2971da177e4SLinus Torvalds 298392debf1SJingoo Han static const struct pci_device_id amd756_ids[] = { 2991da177e4SLinus Torvalds { PCI_DEVICE(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_VIPER_740B), 3001da177e4SLinus Torvalds .driver_data = AMD756 }, 3011da177e4SLinus Torvalds { PCI_DEVICE(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_VIPER_7413), 3021da177e4SLinus Torvalds .driver_data = AMD766 }, 3031da177e4SLinus Torvalds { PCI_DEVICE(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_OPUS_7443), 3041da177e4SLinus Torvalds .driver_data = AMD768 }, 3051da177e4SLinus Torvalds { PCI_DEVICE(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_8111_SMBUS), 3061da177e4SLinus Torvalds .driver_data = AMD8111 }, 3071da177e4SLinus Torvalds { PCI_DEVICE(PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE_SMBUS), 3081da177e4SLinus Torvalds .driver_data = NFORCE }, 3091da177e4SLinus Torvalds { 0, } 3101da177e4SLinus Torvalds }; 3111da177e4SLinus Torvalds 3121da177e4SLinus Torvalds MODULE_DEVICE_TABLE (pci, amd756_ids); 3131da177e4SLinus Torvalds 3140b255e92SBill Pemberton static int amd756_probe(struct pci_dev *pdev, const struct pci_device_id *id) 3151da177e4SLinus Torvalds { 3161da177e4SLinus Torvalds int nforce = (id->driver_data == NFORCE); 3171da177e4SLinus Torvalds int error; 3181da177e4SLinus Torvalds u8 temp; 3191da177e4SLinus Torvalds 3201da177e4SLinus Torvalds if (amd756_ioport) { 3211da177e4SLinus Torvalds dev_err(&pdev->dev, "Only one device supported " 3221da177e4SLinus Torvalds "(you have a strange motherboard, btw)\n"); 3231da177e4SLinus Torvalds return -ENODEV; 3241da177e4SLinus Torvalds } 3251da177e4SLinus Torvalds 3261da177e4SLinus Torvalds if (nforce) { 3271da177e4SLinus Torvalds if (PCI_FUNC(pdev->devfn) != 1) 3281da177e4SLinus Torvalds return -ENODEV; 3291da177e4SLinus Torvalds 3301da177e4SLinus Torvalds pci_read_config_word(pdev, SMBBANFORCE, &amd756_ioport); 3311da177e4SLinus Torvalds amd756_ioport &= 0xfffc; 3321da177e4SLinus Torvalds } else { /* amd */ 3331da177e4SLinus Torvalds if (PCI_FUNC(pdev->devfn) != 3) 3341da177e4SLinus Torvalds return -ENODEV; 3351da177e4SLinus Torvalds 3361da177e4SLinus Torvalds pci_read_config_byte(pdev, SMBGCFG, &temp); 3371da177e4SLinus Torvalds if ((temp & 128) == 0) { 3381da177e4SLinus Torvalds dev_err(&pdev->dev, 3391da177e4SLinus Torvalds "Error: SMBus controller I/O not enabled!\n"); 3401da177e4SLinus Torvalds return -ENODEV; 3411da177e4SLinus Torvalds } 3421da177e4SLinus Torvalds 3431da177e4SLinus Torvalds /* Determine the address of the SMBus areas */ 3441da177e4SLinus Torvalds /* Technically it is a dword but... */ 3451da177e4SLinus Torvalds pci_read_config_word(pdev, SMBBA, &amd756_ioport); 3461da177e4SLinus Torvalds amd756_ioport &= 0xff00; 3471da177e4SLinus Torvalds amd756_ioport += SMB_ADDR_OFFSET; 3481da177e4SLinus Torvalds } 3491da177e4SLinus Torvalds 35054fb4a05SJean Delvare error = acpi_check_region(amd756_ioport, SMB_IOSIZE, 35154fb4a05SJean Delvare amd756_driver.name); 35254fb4a05SJean Delvare if (error) 35318669eabSJean Delvare return -ENODEV; 35454fb4a05SJean Delvare 355d6072f84SJean Delvare if (!request_region(amd756_ioport, SMB_IOSIZE, amd756_driver.name)) { 3561da177e4SLinus Torvalds dev_err(&pdev->dev, "SMB region 0x%x already in use!\n", 3571da177e4SLinus Torvalds amd756_ioport); 3581da177e4SLinus Torvalds return -ENODEV; 3591da177e4SLinus Torvalds } 3601da177e4SLinus Torvalds 3611da177e4SLinus Torvalds pci_read_config_byte(pdev, SMBREV, &temp); 3621da177e4SLinus Torvalds dev_dbg(&pdev->dev, "SMBREV = 0x%X\n", temp); 3631da177e4SLinus Torvalds dev_dbg(&pdev->dev, "AMD756_smba = 0x%X\n", amd756_ioport); 3641da177e4SLinus Torvalds 365405ae7d3SRobert P. J. Day /* set up the sysfs linkage to our parent device */ 3661da177e4SLinus Torvalds amd756_smbus.dev.parent = &pdev->dev; 3671da177e4SLinus Torvalds 36866c7acf6SJean Delvare snprintf(amd756_smbus.name, sizeof(amd756_smbus.name), 36966c7acf6SJean Delvare "SMBus %s adapter at %04x", chipname[id->driver_data], 37066c7acf6SJean Delvare amd756_ioport); 3711da177e4SLinus Torvalds 3721da177e4SLinus Torvalds error = i2c_add_adapter(&amd756_smbus); 373ea734404SWolfram Sang if (error) 3741da177e4SLinus Torvalds goto out_err; 3751da177e4SLinus Torvalds 3761da177e4SLinus Torvalds return 0; 3771da177e4SLinus Torvalds 3781da177e4SLinus Torvalds out_err: 3791da177e4SLinus Torvalds release_region(amd756_ioport, SMB_IOSIZE); 3801da177e4SLinus Torvalds return error; 3811da177e4SLinus Torvalds } 3821da177e4SLinus Torvalds 3830b255e92SBill Pemberton static void amd756_remove(struct pci_dev *dev) 3841da177e4SLinus Torvalds { 3851da177e4SLinus Torvalds i2c_del_adapter(&amd756_smbus); 3861da177e4SLinus Torvalds release_region(amd756_ioport, SMB_IOSIZE); 3871da177e4SLinus Torvalds } 3881da177e4SLinus Torvalds 3891da177e4SLinus Torvalds static struct pci_driver amd756_driver = { 3901da177e4SLinus Torvalds .name = "amd756_smbus", 3911da177e4SLinus Torvalds .id_table = amd756_ids, 3921da177e4SLinus Torvalds .probe = amd756_probe, 3930b255e92SBill Pemberton .remove = amd756_remove, 3941da177e4SLinus Torvalds }; 3951da177e4SLinus Torvalds 39656f21788SAxel Lin module_pci_driver(amd756_driver); 3971da177e4SLinus Torvalds 3981da177e4SLinus Torvalds MODULE_AUTHOR("Merlin Hughes <merlin@merlin.org>"); 3991da177e4SLinus Torvalds MODULE_DESCRIPTION("AMD756/766/768/8111 and nVidia nForce SMBus driver"); 4001da177e4SLinus Torvalds MODULE_LICENSE("GPL"); 4011da177e4SLinus Torvalds 4021da177e4SLinus Torvalds EXPORT_SYMBOL(amd756_smbus); 403