18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci Copyright (c) 1999-2002 Merlin Hughes <merlin@merlin.org> 48c2ecf20Sopenharmony_ci 58c2ecf20Sopenharmony_ci Shamelessly ripped from i2c-piix4.c: 68c2ecf20Sopenharmony_ci 78c2ecf20Sopenharmony_ci Copyright (c) 1998, 1999 Frodo Looijaard <frodol@dds.nl> and 88c2ecf20Sopenharmony_ci Philip Edelbrock <phil@netroedge.com> 98c2ecf20Sopenharmony_ci 108c2ecf20Sopenharmony_ci*/ 118c2ecf20Sopenharmony_ci 128c2ecf20Sopenharmony_ci/* 138c2ecf20Sopenharmony_ci 2002-04-08: Added nForce support. (Csaba Halasz) 148c2ecf20Sopenharmony_ci 2002-10-03: Fixed nForce PnP I/O port. (Michael Steil) 158c2ecf20Sopenharmony_ci 2002-12-28: Rewritten into something that resembles a Linux driver (hch) 168c2ecf20Sopenharmony_ci 2003-11-29: Added back AMD8111 removed by the previous rewrite. 178c2ecf20Sopenharmony_ci (Philip Pokorny) 188c2ecf20Sopenharmony_ci*/ 198c2ecf20Sopenharmony_ci 208c2ecf20Sopenharmony_ci/* 218c2ecf20Sopenharmony_ci Supports AMD756, AMD766, AMD768, AMD8111 and nVidia nForce 228c2ecf20Sopenharmony_ci Note: we assume there can only be one device, with one SMBus interface. 238c2ecf20Sopenharmony_ci*/ 248c2ecf20Sopenharmony_ci 258c2ecf20Sopenharmony_ci#include <linux/module.h> 268c2ecf20Sopenharmony_ci#include <linux/pci.h> 278c2ecf20Sopenharmony_ci#include <linux/kernel.h> 288c2ecf20Sopenharmony_ci#include <linux/delay.h> 298c2ecf20Sopenharmony_ci#include <linux/stddef.h> 308c2ecf20Sopenharmony_ci#include <linux/ioport.h> 318c2ecf20Sopenharmony_ci#include <linux/i2c.h> 328c2ecf20Sopenharmony_ci#include <linux/acpi.h> 338c2ecf20Sopenharmony_ci#include <linux/io.h> 348c2ecf20Sopenharmony_ci 358c2ecf20Sopenharmony_ci/* AMD756 SMBus address offsets */ 368c2ecf20Sopenharmony_ci#define SMB_ADDR_OFFSET 0xE0 378c2ecf20Sopenharmony_ci#define SMB_IOSIZE 16 388c2ecf20Sopenharmony_ci#define SMB_GLOBAL_STATUS (0x0 + amd756_ioport) 398c2ecf20Sopenharmony_ci#define SMB_GLOBAL_ENABLE (0x2 + amd756_ioport) 408c2ecf20Sopenharmony_ci#define SMB_HOST_ADDRESS (0x4 + amd756_ioport) 418c2ecf20Sopenharmony_ci#define SMB_HOST_DATA (0x6 + amd756_ioport) 428c2ecf20Sopenharmony_ci#define SMB_HOST_COMMAND (0x8 + amd756_ioport) 438c2ecf20Sopenharmony_ci#define SMB_HOST_BLOCK_DATA (0x9 + amd756_ioport) 448c2ecf20Sopenharmony_ci#define SMB_HAS_DATA (0xA + amd756_ioport) 458c2ecf20Sopenharmony_ci#define SMB_HAS_DEVICE_ADDRESS (0xC + amd756_ioport) 468c2ecf20Sopenharmony_ci#define SMB_HAS_HOST_ADDRESS (0xE + amd756_ioport) 478c2ecf20Sopenharmony_ci#define SMB_SNOOP_ADDRESS (0xF + amd756_ioport) 488c2ecf20Sopenharmony_ci 498c2ecf20Sopenharmony_ci/* PCI Address Constants */ 508c2ecf20Sopenharmony_ci 518c2ecf20Sopenharmony_ci/* address of I/O space */ 528c2ecf20Sopenharmony_ci#define SMBBA 0x058 /* mh */ 538c2ecf20Sopenharmony_ci#define SMBBANFORCE 0x014 548c2ecf20Sopenharmony_ci 558c2ecf20Sopenharmony_ci/* general configuration */ 568c2ecf20Sopenharmony_ci#define SMBGCFG 0x041 /* mh */ 578c2ecf20Sopenharmony_ci 588c2ecf20Sopenharmony_ci/* silicon revision code */ 598c2ecf20Sopenharmony_ci#define SMBREV 0x008 608c2ecf20Sopenharmony_ci 618c2ecf20Sopenharmony_ci/* Other settings */ 628c2ecf20Sopenharmony_ci#define MAX_TIMEOUT 500 638c2ecf20Sopenharmony_ci 648c2ecf20Sopenharmony_ci/* AMD756 constants */ 658c2ecf20Sopenharmony_ci#define AMD756_QUICK 0x00 668c2ecf20Sopenharmony_ci#define AMD756_BYTE 0x01 678c2ecf20Sopenharmony_ci#define AMD756_BYTE_DATA 0x02 688c2ecf20Sopenharmony_ci#define AMD756_WORD_DATA 0x03 698c2ecf20Sopenharmony_ci#define AMD756_PROCESS_CALL 0x04 708c2ecf20Sopenharmony_ci#define AMD756_BLOCK_DATA 0x05 718c2ecf20Sopenharmony_ci 728c2ecf20Sopenharmony_cistatic struct pci_driver amd756_driver; 738c2ecf20Sopenharmony_cistatic unsigned short amd756_ioport; 748c2ecf20Sopenharmony_ci 758c2ecf20Sopenharmony_ci/* 768c2ecf20Sopenharmony_ci SMBUS event = I/O 28-29 bit 11 778c2ecf20Sopenharmony_ci see E0 for the status bits and enabled in E2 788c2ecf20Sopenharmony_ci 798c2ecf20Sopenharmony_ci*/ 808c2ecf20Sopenharmony_ci#define GS_ABRT_STS (1 << 0) 818c2ecf20Sopenharmony_ci#define GS_COL_STS (1 << 1) 828c2ecf20Sopenharmony_ci#define GS_PRERR_STS (1 << 2) 838c2ecf20Sopenharmony_ci#define GS_HST_STS (1 << 3) 848c2ecf20Sopenharmony_ci#define GS_HCYC_STS (1 << 4) 858c2ecf20Sopenharmony_ci#define GS_TO_STS (1 << 5) 868c2ecf20Sopenharmony_ci#define GS_SMB_STS (1 << 11) 878c2ecf20Sopenharmony_ci 888c2ecf20Sopenharmony_ci#define GS_CLEAR_STS (GS_ABRT_STS | GS_COL_STS | GS_PRERR_STS | \ 898c2ecf20Sopenharmony_ci GS_HCYC_STS | GS_TO_STS ) 908c2ecf20Sopenharmony_ci 918c2ecf20Sopenharmony_ci#define GE_CYC_TYPE_MASK (7) 928c2ecf20Sopenharmony_ci#define GE_HOST_STC (1 << 3) 938c2ecf20Sopenharmony_ci#define GE_ABORT (1 << 5) 948c2ecf20Sopenharmony_ci 958c2ecf20Sopenharmony_ci 968c2ecf20Sopenharmony_cistatic int amd756_transaction(struct i2c_adapter *adap) 978c2ecf20Sopenharmony_ci{ 988c2ecf20Sopenharmony_ci int temp; 998c2ecf20Sopenharmony_ci int result = 0; 1008c2ecf20Sopenharmony_ci int timeout = 0; 1018c2ecf20Sopenharmony_ci 1028c2ecf20Sopenharmony_ci dev_dbg(&adap->dev, "Transaction (pre): GS=%04x, GE=%04x, ADD=%04x, " 1038c2ecf20Sopenharmony_ci "DAT=%04x\n", inw_p(SMB_GLOBAL_STATUS), 1048c2ecf20Sopenharmony_ci inw_p(SMB_GLOBAL_ENABLE), inw_p(SMB_HOST_ADDRESS), 1058c2ecf20Sopenharmony_ci inb_p(SMB_HOST_DATA)); 1068c2ecf20Sopenharmony_ci 1078c2ecf20Sopenharmony_ci /* Make sure the SMBus host is ready to start transmitting */ 1088c2ecf20Sopenharmony_ci if ((temp = inw_p(SMB_GLOBAL_STATUS)) & (GS_HST_STS | GS_SMB_STS)) { 1098c2ecf20Sopenharmony_ci dev_dbg(&adap->dev, "SMBus busy (%04x). Waiting...\n", temp); 1108c2ecf20Sopenharmony_ci do { 1118c2ecf20Sopenharmony_ci msleep(1); 1128c2ecf20Sopenharmony_ci temp = inw_p(SMB_GLOBAL_STATUS); 1138c2ecf20Sopenharmony_ci } while ((temp & (GS_HST_STS | GS_SMB_STS)) && 1148c2ecf20Sopenharmony_ci (timeout++ < MAX_TIMEOUT)); 1158c2ecf20Sopenharmony_ci /* If the SMBus is still busy, we give up */ 1168c2ecf20Sopenharmony_ci if (timeout > MAX_TIMEOUT) { 1178c2ecf20Sopenharmony_ci dev_dbg(&adap->dev, "Busy wait timeout (%04x)\n", temp); 1188c2ecf20Sopenharmony_ci goto abort; 1198c2ecf20Sopenharmony_ci } 1208c2ecf20Sopenharmony_ci timeout = 0; 1218c2ecf20Sopenharmony_ci } 1228c2ecf20Sopenharmony_ci 1238c2ecf20Sopenharmony_ci /* start the transaction by setting the start bit */ 1248c2ecf20Sopenharmony_ci outw_p(inw(SMB_GLOBAL_ENABLE) | GE_HOST_STC, SMB_GLOBAL_ENABLE); 1258c2ecf20Sopenharmony_ci 1268c2ecf20Sopenharmony_ci /* We will always wait for a fraction of a second! */ 1278c2ecf20Sopenharmony_ci do { 1288c2ecf20Sopenharmony_ci msleep(1); 1298c2ecf20Sopenharmony_ci temp = inw_p(SMB_GLOBAL_STATUS); 1308c2ecf20Sopenharmony_ci } while ((temp & GS_HST_STS) && (timeout++ < MAX_TIMEOUT)); 1318c2ecf20Sopenharmony_ci 1328c2ecf20Sopenharmony_ci /* If the SMBus is still busy, we give up */ 1338c2ecf20Sopenharmony_ci if (timeout > MAX_TIMEOUT) { 1348c2ecf20Sopenharmony_ci dev_dbg(&adap->dev, "Completion timeout!\n"); 1358c2ecf20Sopenharmony_ci goto abort; 1368c2ecf20Sopenharmony_ci } 1378c2ecf20Sopenharmony_ci 1388c2ecf20Sopenharmony_ci if (temp & GS_PRERR_STS) { 1398c2ecf20Sopenharmony_ci result = -ENXIO; 1408c2ecf20Sopenharmony_ci dev_dbg(&adap->dev, "SMBus Protocol error (no response)!\n"); 1418c2ecf20Sopenharmony_ci } 1428c2ecf20Sopenharmony_ci 1438c2ecf20Sopenharmony_ci if (temp & GS_COL_STS) { 1448c2ecf20Sopenharmony_ci result = -EIO; 1458c2ecf20Sopenharmony_ci dev_warn(&adap->dev, "SMBus collision!\n"); 1468c2ecf20Sopenharmony_ci } 1478c2ecf20Sopenharmony_ci 1488c2ecf20Sopenharmony_ci if (temp & GS_TO_STS) { 1498c2ecf20Sopenharmony_ci result = -ETIMEDOUT; 1508c2ecf20Sopenharmony_ci dev_dbg(&adap->dev, "SMBus protocol timeout!\n"); 1518c2ecf20Sopenharmony_ci } 1528c2ecf20Sopenharmony_ci 1538c2ecf20Sopenharmony_ci if (temp & GS_HCYC_STS) 1548c2ecf20Sopenharmony_ci dev_dbg(&adap->dev, "SMBus protocol success!\n"); 1558c2ecf20Sopenharmony_ci 1568c2ecf20Sopenharmony_ci outw_p(GS_CLEAR_STS, SMB_GLOBAL_STATUS); 1578c2ecf20Sopenharmony_ci 1588c2ecf20Sopenharmony_ci#ifdef DEBUG 1598c2ecf20Sopenharmony_ci if (((temp = inw_p(SMB_GLOBAL_STATUS)) & GS_CLEAR_STS) != 0x00) { 1608c2ecf20Sopenharmony_ci dev_dbg(&adap->dev, 1618c2ecf20Sopenharmony_ci "Failed reset at end of transaction (%04x)\n", temp); 1628c2ecf20Sopenharmony_ci } 1638c2ecf20Sopenharmony_ci#endif 1648c2ecf20Sopenharmony_ci 1658c2ecf20Sopenharmony_ci dev_dbg(&adap->dev, 1668c2ecf20Sopenharmony_ci "Transaction (post): GS=%04x, GE=%04x, ADD=%04x, DAT=%04x\n", 1678c2ecf20Sopenharmony_ci inw_p(SMB_GLOBAL_STATUS), inw_p(SMB_GLOBAL_ENABLE), 1688c2ecf20Sopenharmony_ci inw_p(SMB_HOST_ADDRESS), inb_p(SMB_HOST_DATA)); 1698c2ecf20Sopenharmony_ci 1708c2ecf20Sopenharmony_ci return result; 1718c2ecf20Sopenharmony_ci 1728c2ecf20Sopenharmony_ci abort: 1738c2ecf20Sopenharmony_ci dev_warn(&adap->dev, "Sending abort\n"); 1748c2ecf20Sopenharmony_ci outw_p(inw(SMB_GLOBAL_ENABLE) | GE_ABORT, SMB_GLOBAL_ENABLE); 1758c2ecf20Sopenharmony_ci msleep(100); 1768c2ecf20Sopenharmony_ci outw_p(GS_CLEAR_STS, SMB_GLOBAL_STATUS); 1778c2ecf20Sopenharmony_ci return -EIO; 1788c2ecf20Sopenharmony_ci} 1798c2ecf20Sopenharmony_ci 1808c2ecf20Sopenharmony_ci/* Return negative errno on error. */ 1818c2ecf20Sopenharmony_cistatic s32 amd756_access(struct i2c_adapter * adap, u16 addr, 1828c2ecf20Sopenharmony_ci unsigned short flags, char read_write, 1838c2ecf20Sopenharmony_ci u8 command, int size, union i2c_smbus_data * data) 1848c2ecf20Sopenharmony_ci{ 1858c2ecf20Sopenharmony_ci int i, len; 1868c2ecf20Sopenharmony_ci int status; 1878c2ecf20Sopenharmony_ci 1888c2ecf20Sopenharmony_ci switch (size) { 1898c2ecf20Sopenharmony_ci case I2C_SMBUS_QUICK: 1908c2ecf20Sopenharmony_ci outw_p(((addr & 0x7f) << 1) | (read_write & 0x01), 1918c2ecf20Sopenharmony_ci SMB_HOST_ADDRESS); 1928c2ecf20Sopenharmony_ci size = AMD756_QUICK; 1938c2ecf20Sopenharmony_ci break; 1948c2ecf20Sopenharmony_ci case I2C_SMBUS_BYTE: 1958c2ecf20Sopenharmony_ci outw_p(((addr & 0x7f) << 1) | (read_write & 0x01), 1968c2ecf20Sopenharmony_ci SMB_HOST_ADDRESS); 1978c2ecf20Sopenharmony_ci if (read_write == I2C_SMBUS_WRITE) 1988c2ecf20Sopenharmony_ci outb_p(command, SMB_HOST_DATA); 1998c2ecf20Sopenharmony_ci size = AMD756_BYTE; 2008c2ecf20Sopenharmony_ci break; 2018c2ecf20Sopenharmony_ci case I2C_SMBUS_BYTE_DATA: 2028c2ecf20Sopenharmony_ci outw_p(((addr & 0x7f) << 1) | (read_write & 0x01), 2038c2ecf20Sopenharmony_ci SMB_HOST_ADDRESS); 2048c2ecf20Sopenharmony_ci outb_p(command, SMB_HOST_COMMAND); 2058c2ecf20Sopenharmony_ci if (read_write == I2C_SMBUS_WRITE) 2068c2ecf20Sopenharmony_ci outw_p(data->byte, SMB_HOST_DATA); 2078c2ecf20Sopenharmony_ci size = AMD756_BYTE_DATA; 2088c2ecf20Sopenharmony_ci break; 2098c2ecf20Sopenharmony_ci case I2C_SMBUS_WORD_DATA: 2108c2ecf20Sopenharmony_ci outw_p(((addr & 0x7f) << 1) | (read_write & 0x01), 2118c2ecf20Sopenharmony_ci SMB_HOST_ADDRESS); 2128c2ecf20Sopenharmony_ci outb_p(command, SMB_HOST_COMMAND); 2138c2ecf20Sopenharmony_ci if (read_write == I2C_SMBUS_WRITE) 2148c2ecf20Sopenharmony_ci outw_p(data->word, SMB_HOST_DATA); /* TODO: endian???? */ 2158c2ecf20Sopenharmony_ci size = AMD756_WORD_DATA; 2168c2ecf20Sopenharmony_ci break; 2178c2ecf20Sopenharmony_ci case I2C_SMBUS_BLOCK_DATA: 2188c2ecf20Sopenharmony_ci outw_p(((addr & 0x7f) << 1) | (read_write & 0x01), 2198c2ecf20Sopenharmony_ci SMB_HOST_ADDRESS); 2208c2ecf20Sopenharmony_ci outb_p(command, SMB_HOST_COMMAND); 2218c2ecf20Sopenharmony_ci if (read_write == I2C_SMBUS_WRITE) { 2228c2ecf20Sopenharmony_ci len = data->block[0]; 2238c2ecf20Sopenharmony_ci if (len < 0) 2248c2ecf20Sopenharmony_ci len = 0; 2258c2ecf20Sopenharmony_ci if (len > 32) 2268c2ecf20Sopenharmony_ci len = 32; 2278c2ecf20Sopenharmony_ci outw_p(len, SMB_HOST_DATA); 2288c2ecf20Sopenharmony_ci /* i = inw_p(SMBHSTCNT); Reset SMBBLKDAT */ 2298c2ecf20Sopenharmony_ci for (i = 1; i <= len; i++) 2308c2ecf20Sopenharmony_ci outb_p(data->block[i], 2318c2ecf20Sopenharmony_ci SMB_HOST_BLOCK_DATA); 2328c2ecf20Sopenharmony_ci } 2338c2ecf20Sopenharmony_ci size = AMD756_BLOCK_DATA; 2348c2ecf20Sopenharmony_ci break; 2358c2ecf20Sopenharmony_ci default: 2368c2ecf20Sopenharmony_ci dev_warn(&adap->dev, "Unsupported transaction %d\n", size); 2378c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 2388c2ecf20Sopenharmony_ci } 2398c2ecf20Sopenharmony_ci 2408c2ecf20Sopenharmony_ci /* How about enabling interrupts... */ 2418c2ecf20Sopenharmony_ci outw_p(size & GE_CYC_TYPE_MASK, SMB_GLOBAL_ENABLE); 2428c2ecf20Sopenharmony_ci 2438c2ecf20Sopenharmony_ci status = amd756_transaction(adap); 2448c2ecf20Sopenharmony_ci if (status) 2458c2ecf20Sopenharmony_ci return status; 2468c2ecf20Sopenharmony_ci 2478c2ecf20Sopenharmony_ci if ((read_write == I2C_SMBUS_WRITE) || (size == AMD756_QUICK)) 2488c2ecf20Sopenharmony_ci return 0; 2498c2ecf20Sopenharmony_ci 2508c2ecf20Sopenharmony_ci 2518c2ecf20Sopenharmony_ci switch (size) { 2528c2ecf20Sopenharmony_ci case AMD756_BYTE: 2538c2ecf20Sopenharmony_ci data->byte = inw_p(SMB_HOST_DATA); 2548c2ecf20Sopenharmony_ci break; 2558c2ecf20Sopenharmony_ci case AMD756_BYTE_DATA: 2568c2ecf20Sopenharmony_ci data->byte = inw_p(SMB_HOST_DATA); 2578c2ecf20Sopenharmony_ci break; 2588c2ecf20Sopenharmony_ci case AMD756_WORD_DATA: 2598c2ecf20Sopenharmony_ci data->word = inw_p(SMB_HOST_DATA); /* TODO: endian???? */ 2608c2ecf20Sopenharmony_ci break; 2618c2ecf20Sopenharmony_ci case AMD756_BLOCK_DATA: 2628c2ecf20Sopenharmony_ci data->block[0] = inw_p(SMB_HOST_DATA) & 0x3f; 2638c2ecf20Sopenharmony_ci if(data->block[0] > 32) 2648c2ecf20Sopenharmony_ci data->block[0] = 32; 2658c2ecf20Sopenharmony_ci /* i = inw_p(SMBHSTCNT); Reset SMBBLKDAT */ 2668c2ecf20Sopenharmony_ci for (i = 1; i <= data->block[0]; i++) 2678c2ecf20Sopenharmony_ci data->block[i] = inb_p(SMB_HOST_BLOCK_DATA); 2688c2ecf20Sopenharmony_ci break; 2698c2ecf20Sopenharmony_ci } 2708c2ecf20Sopenharmony_ci 2718c2ecf20Sopenharmony_ci return 0; 2728c2ecf20Sopenharmony_ci} 2738c2ecf20Sopenharmony_ci 2748c2ecf20Sopenharmony_cistatic u32 amd756_func(struct i2c_adapter *adapter) 2758c2ecf20Sopenharmony_ci{ 2768c2ecf20Sopenharmony_ci return I2C_FUNC_SMBUS_QUICK | I2C_FUNC_SMBUS_BYTE | 2778c2ecf20Sopenharmony_ci I2C_FUNC_SMBUS_BYTE_DATA | I2C_FUNC_SMBUS_WORD_DATA | 2788c2ecf20Sopenharmony_ci I2C_FUNC_SMBUS_BLOCK_DATA; 2798c2ecf20Sopenharmony_ci} 2808c2ecf20Sopenharmony_ci 2818c2ecf20Sopenharmony_cistatic const struct i2c_algorithm smbus_algorithm = { 2828c2ecf20Sopenharmony_ci .smbus_xfer = amd756_access, 2838c2ecf20Sopenharmony_ci .functionality = amd756_func, 2848c2ecf20Sopenharmony_ci}; 2858c2ecf20Sopenharmony_ci 2868c2ecf20Sopenharmony_cistruct i2c_adapter amd756_smbus = { 2878c2ecf20Sopenharmony_ci .owner = THIS_MODULE, 2888c2ecf20Sopenharmony_ci .class = I2C_CLASS_HWMON | I2C_CLASS_SPD, 2898c2ecf20Sopenharmony_ci .algo = &smbus_algorithm, 2908c2ecf20Sopenharmony_ci}; 2918c2ecf20Sopenharmony_ci 2928c2ecf20Sopenharmony_cienum chiptype { AMD756, AMD766, AMD768, NFORCE, AMD8111 }; 2938c2ecf20Sopenharmony_cistatic const char* chipname[] = { 2948c2ecf20Sopenharmony_ci "AMD756", "AMD766", "AMD768", 2958c2ecf20Sopenharmony_ci "nVidia nForce", "AMD8111", 2968c2ecf20Sopenharmony_ci}; 2978c2ecf20Sopenharmony_ci 2988c2ecf20Sopenharmony_cistatic const struct pci_device_id amd756_ids[] = { 2998c2ecf20Sopenharmony_ci { PCI_DEVICE(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_VIPER_740B), 3008c2ecf20Sopenharmony_ci .driver_data = AMD756 }, 3018c2ecf20Sopenharmony_ci { PCI_DEVICE(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_VIPER_7413), 3028c2ecf20Sopenharmony_ci .driver_data = AMD766 }, 3038c2ecf20Sopenharmony_ci { PCI_DEVICE(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_OPUS_7443), 3048c2ecf20Sopenharmony_ci .driver_data = AMD768 }, 3058c2ecf20Sopenharmony_ci { PCI_DEVICE(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_8111_SMBUS), 3068c2ecf20Sopenharmony_ci .driver_data = AMD8111 }, 3078c2ecf20Sopenharmony_ci { PCI_DEVICE(PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE_SMBUS), 3088c2ecf20Sopenharmony_ci .driver_data = NFORCE }, 3098c2ecf20Sopenharmony_ci { 0, } 3108c2ecf20Sopenharmony_ci}; 3118c2ecf20Sopenharmony_ci 3128c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE (pci, amd756_ids); 3138c2ecf20Sopenharmony_ci 3148c2ecf20Sopenharmony_cistatic int amd756_probe(struct pci_dev *pdev, const struct pci_device_id *id) 3158c2ecf20Sopenharmony_ci{ 3168c2ecf20Sopenharmony_ci int nforce = (id->driver_data == NFORCE); 3178c2ecf20Sopenharmony_ci int error; 3188c2ecf20Sopenharmony_ci u8 temp; 3198c2ecf20Sopenharmony_ci 3208c2ecf20Sopenharmony_ci if (amd756_ioport) { 3218c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "Only one device supported " 3228c2ecf20Sopenharmony_ci "(you have a strange motherboard, btw)\n"); 3238c2ecf20Sopenharmony_ci return -ENODEV; 3248c2ecf20Sopenharmony_ci } 3258c2ecf20Sopenharmony_ci 3268c2ecf20Sopenharmony_ci if (nforce) { 3278c2ecf20Sopenharmony_ci if (PCI_FUNC(pdev->devfn) != 1) 3288c2ecf20Sopenharmony_ci return -ENODEV; 3298c2ecf20Sopenharmony_ci 3308c2ecf20Sopenharmony_ci pci_read_config_word(pdev, SMBBANFORCE, &amd756_ioport); 3318c2ecf20Sopenharmony_ci amd756_ioport &= 0xfffc; 3328c2ecf20Sopenharmony_ci } else { /* amd */ 3338c2ecf20Sopenharmony_ci if (PCI_FUNC(pdev->devfn) != 3) 3348c2ecf20Sopenharmony_ci return -ENODEV; 3358c2ecf20Sopenharmony_ci 3368c2ecf20Sopenharmony_ci pci_read_config_byte(pdev, SMBGCFG, &temp); 3378c2ecf20Sopenharmony_ci if ((temp & 128) == 0) { 3388c2ecf20Sopenharmony_ci dev_err(&pdev->dev, 3398c2ecf20Sopenharmony_ci "Error: SMBus controller I/O not enabled!\n"); 3408c2ecf20Sopenharmony_ci return -ENODEV; 3418c2ecf20Sopenharmony_ci } 3428c2ecf20Sopenharmony_ci 3438c2ecf20Sopenharmony_ci /* Determine the address of the SMBus areas */ 3448c2ecf20Sopenharmony_ci /* Technically it is a dword but... */ 3458c2ecf20Sopenharmony_ci pci_read_config_word(pdev, SMBBA, &amd756_ioport); 3468c2ecf20Sopenharmony_ci amd756_ioport &= 0xff00; 3478c2ecf20Sopenharmony_ci amd756_ioport += SMB_ADDR_OFFSET; 3488c2ecf20Sopenharmony_ci } 3498c2ecf20Sopenharmony_ci 3508c2ecf20Sopenharmony_ci error = acpi_check_region(amd756_ioport, SMB_IOSIZE, 3518c2ecf20Sopenharmony_ci amd756_driver.name); 3528c2ecf20Sopenharmony_ci if (error) 3538c2ecf20Sopenharmony_ci return -ENODEV; 3548c2ecf20Sopenharmony_ci 3558c2ecf20Sopenharmony_ci if (!request_region(amd756_ioport, SMB_IOSIZE, amd756_driver.name)) { 3568c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "SMB region 0x%x already in use!\n", 3578c2ecf20Sopenharmony_ci amd756_ioport); 3588c2ecf20Sopenharmony_ci return -ENODEV; 3598c2ecf20Sopenharmony_ci } 3608c2ecf20Sopenharmony_ci 3618c2ecf20Sopenharmony_ci pci_read_config_byte(pdev, SMBREV, &temp); 3628c2ecf20Sopenharmony_ci dev_dbg(&pdev->dev, "SMBREV = 0x%X\n", temp); 3638c2ecf20Sopenharmony_ci dev_dbg(&pdev->dev, "AMD756_smba = 0x%X\n", amd756_ioport); 3648c2ecf20Sopenharmony_ci 3658c2ecf20Sopenharmony_ci /* set up the sysfs linkage to our parent device */ 3668c2ecf20Sopenharmony_ci amd756_smbus.dev.parent = &pdev->dev; 3678c2ecf20Sopenharmony_ci 3688c2ecf20Sopenharmony_ci snprintf(amd756_smbus.name, sizeof(amd756_smbus.name), 3698c2ecf20Sopenharmony_ci "SMBus %s adapter at %04x", chipname[id->driver_data], 3708c2ecf20Sopenharmony_ci amd756_ioport); 3718c2ecf20Sopenharmony_ci 3728c2ecf20Sopenharmony_ci error = i2c_add_adapter(&amd756_smbus); 3738c2ecf20Sopenharmony_ci if (error) 3748c2ecf20Sopenharmony_ci goto out_err; 3758c2ecf20Sopenharmony_ci 3768c2ecf20Sopenharmony_ci return 0; 3778c2ecf20Sopenharmony_ci 3788c2ecf20Sopenharmony_ci out_err: 3798c2ecf20Sopenharmony_ci release_region(amd756_ioport, SMB_IOSIZE); 3808c2ecf20Sopenharmony_ci return error; 3818c2ecf20Sopenharmony_ci} 3828c2ecf20Sopenharmony_ci 3838c2ecf20Sopenharmony_cistatic void amd756_remove(struct pci_dev *dev) 3848c2ecf20Sopenharmony_ci{ 3858c2ecf20Sopenharmony_ci i2c_del_adapter(&amd756_smbus); 3868c2ecf20Sopenharmony_ci release_region(amd756_ioport, SMB_IOSIZE); 3878c2ecf20Sopenharmony_ci} 3888c2ecf20Sopenharmony_ci 3898c2ecf20Sopenharmony_cistatic struct pci_driver amd756_driver = { 3908c2ecf20Sopenharmony_ci .name = "amd756_smbus", 3918c2ecf20Sopenharmony_ci .id_table = amd756_ids, 3928c2ecf20Sopenharmony_ci .probe = amd756_probe, 3938c2ecf20Sopenharmony_ci .remove = amd756_remove, 3948c2ecf20Sopenharmony_ci}; 3958c2ecf20Sopenharmony_ci 3968c2ecf20Sopenharmony_cimodule_pci_driver(amd756_driver); 3978c2ecf20Sopenharmony_ci 3988c2ecf20Sopenharmony_ciMODULE_AUTHOR("Merlin Hughes <merlin@merlin.org>"); 3998c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("AMD756/766/768/8111 and nVidia nForce SMBus driver"); 4008c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 4018c2ecf20Sopenharmony_ci 4028c2ecf20Sopenharmony_ciEXPORT_SYMBOL(amd756_smbus); 403