18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci Copyright (c) 1998, 1999 Frodo Looijaard <frodol@dds.nl> and 48c2ecf20Sopenharmony_ci Philip Edelbrock <phil@netroedge.com> 58c2ecf20Sopenharmony_ci 68c2ecf20Sopenharmony_ci*/ 78c2ecf20Sopenharmony_ci 88c2ecf20Sopenharmony_ci/* Note: we assume there can only be one SIS5595 with one SMBus interface */ 98c2ecf20Sopenharmony_ci 108c2ecf20Sopenharmony_ci/* 118c2ecf20Sopenharmony_ci Note: all have mfr. ID 0x1039. 128c2ecf20Sopenharmony_ci SUPPORTED PCI ID 138c2ecf20Sopenharmony_ci 5595 0008 148c2ecf20Sopenharmony_ci 158c2ecf20Sopenharmony_ci Note: these chips contain a 0008 device which is incompatible with the 168c2ecf20Sopenharmony_ci 5595. We recognize these by the presence of the listed 178c2ecf20Sopenharmony_ci "blacklist" PCI ID and refuse to load. 188c2ecf20Sopenharmony_ci 198c2ecf20Sopenharmony_ci NOT SUPPORTED PCI ID BLACKLIST PCI ID 208c2ecf20Sopenharmony_ci 540 0008 0540 218c2ecf20Sopenharmony_ci 550 0008 0550 228c2ecf20Sopenharmony_ci 5513 0008 5511 238c2ecf20Sopenharmony_ci 5581 0008 5597 248c2ecf20Sopenharmony_ci 5582 0008 5597 258c2ecf20Sopenharmony_ci 5597 0008 5597 268c2ecf20Sopenharmony_ci 5598 0008 5597/5598 278c2ecf20Sopenharmony_ci 630 0008 0630 288c2ecf20Sopenharmony_ci 645 0008 0645 298c2ecf20Sopenharmony_ci 646 0008 0646 308c2ecf20Sopenharmony_ci 648 0008 0648 318c2ecf20Sopenharmony_ci 650 0008 0650 328c2ecf20Sopenharmony_ci 651 0008 0651 338c2ecf20Sopenharmony_ci 730 0008 0730 348c2ecf20Sopenharmony_ci 735 0008 0735 358c2ecf20Sopenharmony_ci 745 0008 0745 368c2ecf20Sopenharmony_ci 746 0008 0746 378c2ecf20Sopenharmony_ci*/ 388c2ecf20Sopenharmony_ci 398c2ecf20Sopenharmony_ci/* TO DO: 408c2ecf20Sopenharmony_ci * Add Block Transfers (ugly, but supported by the adapter) 418c2ecf20Sopenharmony_ci * Add adapter resets 428c2ecf20Sopenharmony_ci */ 438c2ecf20Sopenharmony_ci 448c2ecf20Sopenharmony_ci#include <linux/kernel.h> 458c2ecf20Sopenharmony_ci#include <linux/module.h> 468c2ecf20Sopenharmony_ci#include <linux/delay.h> 478c2ecf20Sopenharmony_ci#include <linux/pci.h> 488c2ecf20Sopenharmony_ci#include <linux/ioport.h> 498c2ecf20Sopenharmony_ci#include <linux/init.h> 508c2ecf20Sopenharmony_ci#include <linux/i2c.h> 518c2ecf20Sopenharmony_ci#include <linux/acpi.h> 528c2ecf20Sopenharmony_ci#include <linux/io.h> 538c2ecf20Sopenharmony_ci 548c2ecf20Sopenharmony_cistatic int blacklist[] = { 558c2ecf20Sopenharmony_ci PCI_DEVICE_ID_SI_540, 568c2ecf20Sopenharmony_ci PCI_DEVICE_ID_SI_550, 578c2ecf20Sopenharmony_ci PCI_DEVICE_ID_SI_630, 588c2ecf20Sopenharmony_ci PCI_DEVICE_ID_SI_645, 598c2ecf20Sopenharmony_ci PCI_DEVICE_ID_SI_646, 608c2ecf20Sopenharmony_ci PCI_DEVICE_ID_SI_648, 618c2ecf20Sopenharmony_ci PCI_DEVICE_ID_SI_650, 628c2ecf20Sopenharmony_ci PCI_DEVICE_ID_SI_651, 638c2ecf20Sopenharmony_ci PCI_DEVICE_ID_SI_730, 648c2ecf20Sopenharmony_ci PCI_DEVICE_ID_SI_735, 658c2ecf20Sopenharmony_ci PCI_DEVICE_ID_SI_745, 668c2ecf20Sopenharmony_ci PCI_DEVICE_ID_SI_746, 678c2ecf20Sopenharmony_ci PCI_DEVICE_ID_SI_5511, /* 5513 chip has the 0008 device but that ID 688c2ecf20Sopenharmony_ci shows up in other chips so we use the 5511 698c2ecf20Sopenharmony_ci ID for recognition */ 708c2ecf20Sopenharmony_ci PCI_DEVICE_ID_SI_5597, 718c2ecf20Sopenharmony_ci PCI_DEVICE_ID_SI_5598, 728c2ecf20Sopenharmony_ci 0, /* terminates the list */ 738c2ecf20Sopenharmony_ci}; 748c2ecf20Sopenharmony_ci 758c2ecf20Sopenharmony_ci/* Length of ISA address segment */ 768c2ecf20Sopenharmony_ci#define SIS5595_EXTENT 8 778c2ecf20Sopenharmony_ci/* SIS5595 SMBus registers */ 788c2ecf20Sopenharmony_ci#define SMB_STS_LO 0x00 798c2ecf20Sopenharmony_ci#define SMB_STS_HI 0x01 808c2ecf20Sopenharmony_ci#define SMB_CTL_LO 0x02 818c2ecf20Sopenharmony_ci#define SMB_CTL_HI 0x03 828c2ecf20Sopenharmony_ci#define SMB_ADDR 0x04 838c2ecf20Sopenharmony_ci#define SMB_CMD 0x05 848c2ecf20Sopenharmony_ci#define SMB_PCNT 0x06 858c2ecf20Sopenharmony_ci#define SMB_CNT 0x07 868c2ecf20Sopenharmony_ci#define SMB_BYTE 0x08 878c2ecf20Sopenharmony_ci#define SMB_DEV 0x10 888c2ecf20Sopenharmony_ci#define SMB_DB0 0x11 898c2ecf20Sopenharmony_ci#define SMB_DB1 0x12 908c2ecf20Sopenharmony_ci#define SMB_HAA 0x13 918c2ecf20Sopenharmony_ci 928c2ecf20Sopenharmony_ci/* PCI Address Constants */ 938c2ecf20Sopenharmony_ci#define SMB_INDEX 0x38 948c2ecf20Sopenharmony_ci#define SMB_DAT 0x39 958c2ecf20Sopenharmony_ci#define SIS5595_ENABLE_REG 0x40 968c2ecf20Sopenharmony_ci#define ACPI_BASE 0x90 978c2ecf20Sopenharmony_ci 988c2ecf20Sopenharmony_ci/* Other settings */ 998c2ecf20Sopenharmony_ci#define MAX_TIMEOUT 500 1008c2ecf20Sopenharmony_ci 1018c2ecf20Sopenharmony_ci/* SIS5595 constants */ 1028c2ecf20Sopenharmony_ci#define SIS5595_QUICK 0x00 1038c2ecf20Sopenharmony_ci#define SIS5595_BYTE 0x02 1048c2ecf20Sopenharmony_ci#define SIS5595_BYTE_DATA 0x04 1058c2ecf20Sopenharmony_ci#define SIS5595_WORD_DATA 0x06 1068c2ecf20Sopenharmony_ci#define SIS5595_PROC_CALL 0x08 1078c2ecf20Sopenharmony_ci#define SIS5595_BLOCK_DATA 0x0A 1088c2ecf20Sopenharmony_ci 1098c2ecf20Sopenharmony_ci/* insmod parameters */ 1108c2ecf20Sopenharmony_ci 1118c2ecf20Sopenharmony_ci/* If force_addr is set to anything different from 0, we forcibly enable 1128c2ecf20Sopenharmony_ci the device at the given address. */ 1138c2ecf20Sopenharmony_cistatic u16 force_addr; 1148c2ecf20Sopenharmony_cimodule_param_hw(force_addr, ushort, ioport, 0); 1158c2ecf20Sopenharmony_ciMODULE_PARM_DESC(force_addr, "Initialize the base address of the i2c controller"); 1168c2ecf20Sopenharmony_ci 1178c2ecf20Sopenharmony_cistatic struct pci_driver sis5595_driver; 1188c2ecf20Sopenharmony_cistatic unsigned short sis5595_base; 1198c2ecf20Sopenharmony_cistatic struct pci_dev *sis5595_pdev; 1208c2ecf20Sopenharmony_ci 1218c2ecf20Sopenharmony_cistatic u8 sis5595_read(u8 reg) 1228c2ecf20Sopenharmony_ci{ 1238c2ecf20Sopenharmony_ci outb(reg, sis5595_base + SMB_INDEX); 1248c2ecf20Sopenharmony_ci return inb(sis5595_base + SMB_DAT); 1258c2ecf20Sopenharmony_ci} 1268c2ecf20Sopenharmony_ci 1278c2ecf20Sopenharmony_cistatic void sis5595_write(u8 reg, u8 data) 1288c2ecf20Sopenharmony_ci{ 1298c2ecf20Sopenharmony_ci outb(reg, sis5595_base + SMB_INDEX); 1308c2ecf20Sopenharmony_ci outb(data, sis5595_base + SMB_DAT); 1318c2ecf20Sopenharmony_ci} 1328c2ecf20Sopenharmony_ci 1338c2ecf20Sopenharmony_cistatic int sis5595_setup(struct pci_dev *SIS5595_dev) 1348c2ecf20Sopenharmony_ci{ 1358c2ecf20Sopenharmony_ci u16 a; 1368c2ecf20Sopenharmony_ci u8 val; 1378c2ecf20Sopenharmony_ci int *i; 1388c2ecf20Sopenharmony_ci int retval; 1398c2ecf20Sopenharmony_ci 1408c2ecf20Sopenharmony_ci /* Look for imposters */ 1418c2ecf20Sopenharmony_ci for (i = blacklist; *i != 0; i++) { 1428c2ecf20Sopenharmony_ci struct pci_dev *dev; 1438c2ecf20Sopenharmony_ci dev = pci_get_device(PCI_VENDOR_ID_SI, *i, NULL); 1448c2ecf20Sopenharmony_ci if (dev) { 1458c2ecf20Sopenharmony_ci dev_err(&SIS5595_dev->dev, "Looked for SIS5595 but found unsupported device %.4x\n", *i); 1468c2ecf20Sopenharmony_ci pci_dev_put(dev); 1478c2ecf20Sopenharmony_ci return -ENODEV; 1488c2ecf20Sopenharmony_ci } 1498c2ecf20Sopenharmony_ci } 1508c2ecf20Sopenharmony_ci 1518c2ecf20Sopenharmony_ci /* Determine the address of the SMBus areas */ 1528c2ecf20Sopenharmony_ci pci_read_config_word(SIS5595_dev, ACPI_BASE, &sis5595_base); 1538c2ecf20Sopenharmony_ci if (sis5595_base == 0 && force_addr == 0) { 1548c2ecf20Sopenharmony_ci dev_err(&SIS5595_dev->dev, "ACPI base address uninitialized - upgrade BIOS or use force_addr=0xaddr\n"); 1558c2ecf20Sopenharmony_ci return -ENODEV; 1568c2ecf20Sopenharmony_ci } 1578c2ecf20Sopenharmony_ci 1588c2ecf20Sopenharmony_ci if (force_addr) 1598c2ecf20Sopenharmony_ci sis5595_base = force_addr & ~(SIS5595_EXTENT - 1); 1608c2ecf20Sopenharmony_ci dev_dbg(&SIS5595_dev->dev, "ACPI Base address: %04x\n", sis5595_base); 1618c2ecf20Sopenharmony_ci 1628c2ecf20Sopenharmony_ci /* NB: We grab just the two SMBus registers here, but this may still 1638c2ecf20Sopenharmony_ci * interfere with ACPI :-( */ 1648c2ecf20Sopenharmony_ci retval = acpi_check_region(sis5595_base + SMB_INDEX, 2, 1658c2ecf20Sopenharmony_ci sis5595_driver.name); 1668c2ecf20Sopenharmony_ci if (retval) 1678c2ecf20Sopenharmony_ci return retval; 1688c2ecf20Sopenharmony_ci 1698c2ecf20Sopenharmony_ci if (!request_region(sis5595_base + SMB_INDEX, 2, 1708c2ecf20Sopenharmony_ci sis5595_driver.name)) { 1718c2ecf20Sopenharmony_ci dev_err(&SIS5595_dev->dev, "SMBus registers 0x%04x-0x%04x already in use!\n", 1728c2ecf20Sopenharmony_ci sis5595_base + SMB_INDEX, sis5595_base + SMB_INDEX + 1); 1738c2ecf20Sopenharmony_ci return -ENODEV; 1748c2ecf20Sopenharmony_ci } 1758c2ecf20Sopenharmony_ci 1768c2ecf20Sopenharmony_ci if (force_addr) { 1778c2ecf20Sopenharmony_ci dev_info(&SIS5595_dev->dev, "forcing ISA address 0x%04X\n", sis5595_base); 1788c2ecf20Sopenharmony_ci if (pci_write_config_word(SIS5595_dev, ACPI_BASE, sis5595_base) 1798c2ecf20Sopenharmony_ci != PCIBIOS_SUCCESSFUL) 1808c2ecf20Sopenharmony_ci goto error; 1818c2ecf20Sopenharmony_ci if (pci_read_config_word(SIS5595_dev, ACPI_BASE, &a) 1828c2ecf20Sopenharmony_ci != PCIBIOS_SUCCESSFUL) 1838c2ecf20Sopenharmony_ci goto error; 1848c2ecf20Sopenharmony_ci if ((a & ~(SIS5595_EXTENT - 1)) != sis5595_base) { 1858c2ecf20Sopenharmony_ci /* doesn't work for some chips! */ 1868c2ecf20Sopenharmony_ci dev_err(&SIS5595_dev->dev, "force address failed - not supported?\n"); 1878c2ecf20Sopenharmony_ci goto error; 1888c2ecf20Sopenharmony_ci } 1898c2ecf20Sopenharmony_ci } 1908c2ecf20Sopenharmony_ci 1918c2ecf20Sopenharmony_ci if (pci_read_config_byte(SIS5595_dev, SIS5595_ENABLE_REG, &val) 1928c2ecf20Sopenharmony_ci != PCIBIOS_SUCCESSFUL) 1938c2ecf20Sopenharmony_ci goto error; 1948c2ecf20Sopenharmony_ci if ((val & 0x80) == 0) { 1958c2ecf20Sopenharmony_ci dev_info(&SIS5595_dev->dev, "enabling ACPI\n"); 1968c2ecf20Sopenharmony_ci if (pci_write_config_byte(SIS5595_dev, SIS5595_ENABLE_REG, val | 0x80) 1978c2ecf20Sopenharmony_ci != PCIBIOS_SUCCESSFUL) 1988c2ecf20Sopenharmony_ci goto error; 1998c2ecf20Sopenharmony_ci if (pci_read_config_byte(SIS5595_dev, SIS5595_ENABLE_REG, &val) 2008c2ecf20Sopenharmony_ci != PCIBIOS_SUCCESSFUL) 2018c2ecf20Sopenharmony_ci goto error; 2028c2ecf20Sopenharmony_ci if ((val & 0x80) == 0) { 2038c2ecf20Sopenharmony_ci /* doesn't work for some chips? */ 2048c2ecf20Sopenharmony_ci dev_err(&SIS5595_dev->dev, "ACPI enable failed - not supported?\n"); 2058c2ecf20Sopenharmony_ci goto error; 2068c2ecf20Sopenharmony_ci } 2078c2ecf20Sopenharmony_ci } 2088c2ecf20Sopenharmony_ci 2098c2ecf20Sopenharmony_ci /* Everything is happy */ 2108c2ecf20Sopenharmony_ci return 0; 2118c2ecf20Sopenharmony_ci 2128c2ecf20Sopenharmony_cierror: 2138c2ecf20Sopenharmony_ci release_region(sis5595_base + SMB_INDEX, 2); 2148c2ecf20Sopenharmony_ci return -ENODEV; 2158c2ecf20Sopenharmony_ci} 2168c2ecf20Sopenharmony_ci 2178c2ecf20Sopenharmony_cistatic int sis5595_transaction(struct i2c_adapter *adap) 2188c2ecf20Sopenharmony_ci{ 2198c2ecf20Sopenharmony_ci int temp; 2208c2ecf20Sopenharmony_ci int result = 0; 2218c2ecf20Sopenharmony_ci int timeout = 0; 2228c2ecf20Sopenharmony_ci 2238c2ecf20Sopenharmony_ci /* Make sure the SMBus host is ready to start transmitting */ 2248c2ecf20Sopenharmony_ci temp = sis5595_read(SMB_STS_LO) + (sis5595_read(SMB_STS_HI) << 8); 2258c2ecf20Sopenharmony_ci if (temp != 0x00) { 2268c2ecf20Sopenharmony_ci dev_dbg(&adap->dev, "SMBus busy (%04x). Resetting...\n", temp); 2278c2ecf20Sopenharmony_ci sis5595_write(SMB_STS_LO, temp & 0xff); 2288c2ecf20Sopenharmony_ci sis5595_write(SMB_STS_HI, temp >> 8); 2298c2ecf20Sopenharmony_ci if ((temp = sis5595_read(SMB_STS_LO) + (sis5595_read(SMB_STS_HI) << 8)) != 0x00) { 2308c2ecf20Sopenharmony_ci dev_dbg(&adap->dev, "Failed! (%02x)\n", temp); 2318c2ecf20Sopenharmony_ci return -EBUSY; 2328c2ecf20Sopenharmony_ci } else { 2338c2ecf20Sopenharmony_ci dev_dbg(&adap->dev, "Successful!\n"); 2348c2ecf20Sopenharmony_ci } 2358c2ecf20Sopenharmony_ci } 2368c2ecf20Sopenharmony_ci 2378c2ecf20Sopenharmony_ci /* start the transaction by setting bit 4 */ 2388c2ecf20Sopenharmony_ci sis5595_write(SMB_CTL_LO, sis5595_read(SMB_CTL_LO) | 0x10); 2398c2ecf20Sopenharmony_ci 2408c2ecf20Sopenharmony_ci /* We will always wait for a fraction of a second! */ 2418c2ecf20Sopenharmony_ci do { 2428c2ecf20Sopenharmony_ci msleep(1); 2438c2ecf20Sopenharmony_ci temp = sis5595_read(SMB_STS_LO); 2448c2ecf20Sopenharmony_ci } while (!(temp & 0x40) && (timeout++ < MAX_TIMEOUT)); 2458c2ecf20Sopenharmony_ci 2468c2ecf20Sopenharmony_ci /* If the SMBus is still busy, we give up */ 2478c2ecf20Sopenharmony_ci if (timeout > MAX_TIMEOUT) { 2488c2ecf20Sopenharmony_ci dev_dbg(&adap->dev, "SMBus Timeout!\n"); 2498c2ecf20Sopenharmony_ci result = -ETIMEDOUT; 2508c2ecf20Sopenharmony_ci } 2518c2ecf20Sopenharmony_ci 2528c2ecf20Sopenharmony_ci if (temp & 0x10) { 2538c2ecf20Sopenharmony_ci dev_dbg(&adap->dev, "Error: Failed bus transaction\n"); 2548c2ecf20Sopenharmony_ci result = -ENXIO; 2558c2ecf20Sopenharmony_ci } 2568c2ecf20Sopenharmony_ci 2578c2ecf20Sopenharmony_ci if (temp & 0x20) { 2588c2ecf20Sopenharmony_ci dev_err(&adap->dev, "Bus collision! SMBus may be locked until " 2598c2ecf20Sopenharmony_ci "next hard reset (or not...)\n"); 2608c2ecf20Sopenharmony_ci /* Clock stops and slave is stuck in mid-transmission */ 2618c2ecf20Sopenharmony_ci result = -EIO; 2628c2ecf20Sopenharmony_ci } 2638c2ecf20Sopenharmony_ci 2648c2ecf20Sopenharmony_ci temp = sis5595_read(SMB_STS_LO) + (sis5595_read(SMB_STS_HI) << 8); 2658c2ecf20Sopenharmony_ci if (temp != 0x00) { 2668c2ecf20Sopenharmony_ci sis5595_write(SMB_STS_LO, temp & 0xff); 2678c2ecf20Sopenharmony_ci sis5595_write(SMB_STS_HI, temp >> 8); 2688c2ecf20Sopenharmony_ci } 2698c2ecf20Sopenharmony_ci 2708c2ecf20Sopenharmony_ci temp = sis5595_read(SMB_STS_LO) + (sis5595_read(SMB_STS_HI) << 8); 2718c2ecf20Sopenharmony_ci if (temp != 0x00) 2728c2ecf20Sopenharmony_ci dev_dbg(&adap->dev, "Failed reset at end of transaction (%02x)\n", temp); 2738c2ecf20Sopenharmony_ci 2748c2ecf20Sopenharmony_ci return result; 2758c2ecf20Sopenharmony_ci} 2768c2ecf20Sopenharmony_ci 2778c2ecf20Sopenharmony_ci/* Return negative errno on error. */ 2788c2ecf20Sopenharmony_cistatic s32 sis5595_access(struct i2c_adapter *adap, u16 addr, 2798c2ecf20Sopenharmony_ci unsigned short flags, char read_write, 2808c2ecf20Sopenharmony_ci u8 command, int size, union i2c_smbus_data *data) 2818c2ecf20Sopenharmony_ci{ 2828c2ecf20Sopenharmony_ci int status; 2838c2ecf20Sopenharmony_ci 2848c2ecf20Sopenharmony_ci switch (size) { 2858c2ecf20Sopenharmony_ci case I2C_SMBUS_QUICK: 2868c2ecf20Sopenharmony_ci sis5595_write(SMB_ADDR, ((addr & 0x7f) << 1) | (read_write & 0x01)); 2878c2ecf20Sopenharmony_ci size = SIS5595_QUICK; 2888c2ecf20Sopenharmony_ci break; 2898c2ecf20Sopenharmony_ci case I2C_SMBUS_BYTE: 2908c2ecf20Sopenharmony_ci sis5595_write(SMB_ADDR, ((addr & 0x7f) << 1) | (read_write & 0x01)); 2918c2ecf20Sopenharmony_ci if (read_write == I2C_SMBUS_WRITE) 2928c2ecf20Sopenharmony_ci sis5595_write(SMB_CMD, command); 2938c2ecf20Sopenharmony_ci size = SIS5595_BYTE; 2948c2ecf20Sopenharmony_ci break; 2958c2ecf20Sopenharmony_ci case I2C_SMBUS_BYTE_DATA: 2968c2ecf20Sopenharmony_ci sis5595_write(SMB_ADDR, ((addr & 0x7f) << 1) | (read_write & 0x01)); 2978c2ecf20Sopenharmony_ci sis5595_write(SMB_CMD, command); 2988c2ecf20Sopenharmony_ci if (read_write == I2C_SMBUS_WRITE) 2998c2ecf20Sopenharmony_ci sis5595_write(SMB_BYTE, data->byte); 3008c2ecf20Sopenharmony_ci size = SIS5595_BYTE_DATA; 3018c2ecf20Sopenharmony_ci break; 3028c2ecf20Sopenharmony_ci case I2C_SMBUS_PROC_CALL: 3038c2ecf20Sopenharmony_ci case I2C_SMBUS_WORD_DATA: 3048c2ecf20Sopenharmony_ci sis5595_write(SMB_ADDR, ((addr & 0x7f) << 1) | (read_write & 0x01)); 3058c2ecf20Sopenharmony_ci sis5595_write(SMB_CMD, command); 3068c2ecf20Sopenharmony_ci if (read_write == I2C_SMBUS_WRITE) { 3078c2ecf20Sopenharmony_ci sis5595_write(SMB_BYTE, data->word & 0xff); 3088c2ecf20Sopenharmony_ci sis5595_write(SMB_BYTE + 1, 3098c2ecf20Sopenharmony_ci (data->word & 0xff00) >> 8); 3108c2ecf20Sopenharmony_ci } 3118c2ecf20Sopenharmony_ci size = (size == I2C_SMBUS_PROC_CALL) ? SIS5595_PROC_CALL : SIS5595_WORD_DATA; 3128c2ecf20Sopenharmony_ci break; 3138c2ecf20Sopenharmony_ci default: 3148c2ecf20Sopenharmony_ci dev_warn(&adap->dev, "Unsupported transaction %d\n", size); 3158c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 3168c2ecf20Sopenharmony_ci } 3178c2ecf20Sopenharmony_ci 3188c2ecf20Sopenharmony_ci sis5595_write(SMB_CTL_LO, ((size & 0x0E))); 3198c2ecf20Sopenharmony_ci 3208c2ecf20Sopenharmony_ci status = sis5595_transaction(adap); 3218c2ecf20Sopenharmony_ci if (status) 3228c2ecf20Sopenharmony_ci return status; 3238c2ecf20Sopenharmony_ci 3248c2ecf20Sopenharmony_ci if ((size != SIS5595_PROC_CALL) && 3258c2ecf20Sopenharmony_ci ((read_write == I2C_SMBUS_WRITE) || (size == SIS5595_QUICK))) 3268c2ecf20Sopenharmony_ci return 0; 3278c2ecf20Sopenharmony_ci 3288c2ecf20Sopenharmony_ci 3298c2ecf20Sopenharmony_ci switch (size) { 3308c2ecf20Sopenharmony_ci case SIS5595_BYTE: 3318c2ecf20Sopenharmony_ci case SIS5595_BYTE_DATA: 3328c2ecf20Sopenharmony_ci data->byte = sis5595_read(SMB_BYTE); 3338c2ecf20Sopenharmony_ci break; 3348c2ecf20Sopenharmony_ci case SIS5595_WORD_DATA: 3358c2ecf20Sopenharmony_ci case SIS5595_PROC_CALL: 3368c2ecf20Sopenharmony_ci data->word = sis5595_read(SMB_BYTE) + (sis5595_read(SMB_BYTE + 1) << 8); 3378c2ecf20Sopenharmony_ci break; 3388c2ecf20Sopenharmony_ci } 3398c2ecf20Sopenharmony_ci return 0; 3408c2ecf20Sopenharmony_ci} 3418c2ecf20Sopenharmony_ci 3428c2ecf20Sopenharmony_cistatic u32 sis5595_func(struct i2c_adapter *adapter) 3438c2ecf20Sopenharmony_ci{ 3448c2ecf20Sopenharmony_ci return I2C_FUNC_SMBUS_QUICK | I2C_FUNC_SMBUS_BYTE | 3458c2ecf20Sopenharmony_ci I2C_FUNC_SMBUS_BYTE_DATA | I2C_FUNC_SMBUS_WORD_DATA | 3468c2ecf20Sopenharmony_ci I2C_FUNC_SMBUS_PROC_CALL; 3478c2ecf20Sopenharmony_ci} 3488c2ecf20Sopenharmony_ci 3498c2ecf20Sopenharmony_cistatic const struct i2c_algorithm smbus_algorithm = { 3508c2ecf20Sopenharmony_ci .smbus_xfer = sis5595_access, 3518c2ecf20Sopenharmony_ci .functionality = sis5595_func, 3528c2ecf20Sopenharmony_ci}; 3538c2ecf20Sopenharmony_ci 3548c2ecf20Sopenharmony_cistatic struct i2c_adapter sis5595_adapter = { 3558c2ecf20Sopenharmony_ci .owner = THIS_MODULE, 3568c2ecf20Sopenharmony_ci .class = I2C_CLASS_HWMON | I2C_CLASS_SPD, 3578c2ecf20Sopenharmony_ci .algo = &smbus_algorithm, 3588c2ecf20Sopenharmony_ci}; 3598c2ecf20Sopenharmony_ci 3608c2ecf20Sopenharmony_cistatic const struct pci_device_id sis5595_ids[] = { 3618c2ecf20Sopenharmony_ci { PCI_DEVICE(PCI_VENDOR_ID_SI, PCI_DEVICE_ID_SI_503) }, 3628c2ecf20Sopenharmony_ci { 0, } 3638c2ecf20Sopenharmony_ci}; 3648c2ecf20Sopenharmony_ci 3658c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE (pci, sis5595_ids); 3668c2ecf20Sopenharmony_ci 3678c2ecf20Sopenharmony_cistatic int sis5595_probe(struct pci_dev *dev, const struct pci_device_id *id) 3688c2ecf20Sopenharmony_ci{ 3698c2ecf20Sopenharmony_ci int err; 3708c2ecf20Sopenharmony_ci 3718c2ecf20Sopenharmony_ci if (sis5595_setup(dev)) { 3728c2ecf20Sopenharmony_ci dev_err(&dev->dev, "SIS5595 not detected, module not inserted.\n"); 3738c2ecf20Sopenharmony_ci return -ENODEV; 3748c2ecf20Sopenharmony_ci } 3758c2ecf20Sopenharmony_ci 3768c2ecf20Sopenharmony_ci /* set up the sysfs linkage to our parent device */ 3778c2ecf20Sopenharmony_ci sis5595_adapter.dev.parent = &dev->dev; 3788c2ecf20Sopenharmony_ci 3798c2ecf20Sopenharmony_ci snprintf(sis5595_adapter.name, sizeof(sis5595_adapter.name), 3808c2ecf20Sopenharmony_ci "SMBus SIS5595 adapter at %04x", sis5595_base + SMB_INDEX); 3818c2ecf20Sopenharmony_ci err = i2c_add_adapter(&sis5595_adapter); 3828c2ecf20Sopenharmony_ci if (err) { 3838c2ecf20Sopenharmony_ci release_region(sis5595_base + SMB_INDEX, 2); 3848c2ecf20Sopenharmony_ci return err; 3858c2ecf20Sopenharmony_ci } 3868c2ecf20Sopenharmony_ci 3878c2ecf20Sopenharmony_ci /* Always return failure here. This is to allow other drivers to bind 3888c2ecf20Sopenharmony_ci * to this pci device. We don't really want to have control over the 3898c2ecf20Sopenharmony_ci * pci device, we only wanted to read as few register values from it. 3908c2ecf20Sopenharmony_ci */ 3918c2ecf20Sopenharmony_ci sis5595_pdev = pci_dev_get(dev); 3928c2ecf20Sopenharmony_ci return -ENODEV; 3938c2ecf20Sopenharmony_ci} 3948c2ecf20Sopenharmony_ci 3958c2ecf20Sopenharmony_cistatic struct pci_driver sis5595_driver = { 3968c2ecf20Sopenharmony_ci .name = "sis5595_smbus", 3978c2ecf20Sopenharmony_ci .id_table = sis5595_ids, 3988c2ecf20Sopenharmony_ci .probe = sis5595_probe, 3998c2ecf20Sopenharmony_ci}; 4008c2ecf20Sopenharmony_ci 4018c2ecf20Sopenharmony_cistatic int __init i2c_sis5595_init(void) 4028c2ecf20Sopenharmony_ci{ 4038c2ecf20Sopenharmony_ci return pci_register_driver(&sis5595_driver); 4048c2ecf20Sopenharmony_ci} 4058c2ecf20Sopenharmony_ci 4068c2ecf20Sopenharmony_cistatic void __exit i2c_sis5595_exit(void) 4078c2ecf20Sopenharmony_ci{ 4088c2ecf20Sopenharmony_ci pci_unregister_driver(&sis5595_driver); 4098c2ecf20Sopenharmony_ci if (sis5595_pdev) { 4108c2ecf20Sopenharmony_ci i2c_del_adapter(&sis5595_adapter); 4118c2ecf20Sopenharmony_ci release_region(sis5595_base + SMB_INDEX, 2); 4128c2ecf20Sopenharmony_ci pci_dev_put(sis5595_pdev); 4138c2ecf20Sopenharmony_ci sis5595_pdev = NULL; 4148c2ecf20Sopenharmony_ci } 4158c2ecf20Sopenharmony_ci} 4168c2ecf20Sopenharmony_ci 4178c2ecf20Sopenharmony_ciMODULE_AUTHOR("Frodo Looijaard <frodol@dds.nl>"); 4188c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("SIS5595 SMBus driver"); 4198c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 4208c2ecf20Sopenharmony_ci 4218c2ecf20Sopenharmony_cimodule_init(i2c_sis5595_init); 4228c2ecf20Sopenharmony_cimodule_exit(i2c_sis5595_exit); 423