18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Copyright (c) 2000 Frodo Looijaard <frodol@dds.nl>, 48c2ecf20Sopenharmony_ci * Philip Edelbrock <phil@netroedge.com>, 58c2ecf20Sopenharmony_ci * Mark D. Studebaker <mdsxyz123@yahoo.com>, 68c2ecf20Sopenharmony_ci * Dan Eaton <dan.eaton@rocketlogix.com> and 78c2ecf20Sopenharmony_ci * Stephen Rousset <stephen.rousset@rocketlogix.com> 88c2ecf20Sopenharmony_ci*/ 98c2ecf20Sopenharmony_ci 108c2ecf20Sopenharmony_ci/* 118c2ecf20Sopenharmony_ci This is the driver for the SMB Host controller on 128c2ecf20Sopenharmony_ci Acer Labs Inc. (ALI) M1535 South Bridge. 138c2ecf20Sopenharmony_ci 148c2ecf20Sopenharmony_ci The M1535 is a South bridge for portable systems. 158c2ecf20Sopenharmony_ci It is very similar to the M15x3 South bridges also produced 168c2ecf20Sopenharmony_ci by Acer Labs Inc. Some of the registers within the part 178c2ecf20Sopenharmony_ci have moved and some have been redefined slightly. Additionally, 188c2ecf20Sopenharmony_ci the sequencing of the SMBus transactions has been modified 198c2ecf20Sopenharmony_ci to be more consistent with the sequencing recommended by 208c2ecf20Sopenharmony_ci the manufacturer and observed through testing. These 218c2ecf20Sopenharmony_ci changes are reflected in this driver and can be identified 228c2ecf20Sopenharmony_ci by comparing this driver to the i2c-ali15x3 driver. 238c2ecf20Sopenharmony_ci For an overview of these chips see http://www.acerlabs.com 248c2ecf20Sopenharmony_ci 258c2ecf20Sopenharmony_ci The SMB controller is part of the 7101 device, which is an 268c2ecf20Sopenharmony_ci ACPI-compliant Power Management Unit (PMU). 278c2ecf20Sopenharmony_ci 288c2ecf20Sopenharmony_ci The whole 7101 device has to be enabled for the SMB to work. 298c2ecf20Sopenharmony_ci You can't just enable the SMB alone. 308c2ecf20Sopenharmony_ci The SMB and the ACPI have separate I/O spaces. 318c2ecf20Sopenharmony_ci We make sure that the SMB is enabled. We leave the ACPI alone. 328c2ecf20Sopenharmony_ci 338c2ecf20Sopenharmony_ci This driver controls the SMB Host only. 348c2ecf20Sopenharmony_ci 358c2ecf20Sopenharmony_ci This driver does not use interrupts. 368c2ecf20Sopenharmony_ci*/ 378c2ecf20Sopenharmony_ci 388c2ecf20Sopenharmony_ci 398c2ecf20Sopenharmony_ci/* Note: we assume there can only be one ALI1535, with one SMBus interface */ 408c2ecf20Sopenharmony_ci 418c2ecf20Sopenharmony_ci#include <linux/module.h> 428c2ecf20Sopenharmony_ci#include <linux/pci.h> 438c2ecf20Sopenharmony_ci#include <linux/kernel.h> 448c2ecf20Sopenharmony_ci#include <linux/stddef.h> 458c2ecf20Sopenharmony_ci#include <linux/delay.h> 468c2ecf20Sopenharmony_ci#include <linux/ioport.h> 478c2ecf20Sopenharmony_ci#include <linux/i2c.h> 488c2ecf20Sopenharmony_ci#include <linux/acpi.h> 498c2ecf20Sopenharmony_ci#include <linux/io.h> 508c2ecf20Sopenharmony_ci 518c2ecf20Sopenharmony_ci 528c2ecf20Sopenharmony_ci/* ALI1535 SMBus address offsets */ 538c2ecf20Sopenharmony_ci#define SMBHSTSTS (0 + ali1535_smba) 548c2ecf20Sopenharmony_ci#define SMBHSTTYP (1 + ali1535_smba) 558c2ecf20Sopenharmony_ci#define SMBHSTPORT (2 + ali1535_smba) 568c2ecf20Sopenharmony_ci#define SMBHSTCMD (7 + ali1535_smba) 578c2ecf20Sopenharmony_ci#define SMBHSTADD (3 + ali1535_smba) 588c2ecf20Sopenharmony_ci#define SMBHSTDAT0 (4 + ali1535_smba) 598c2ecf20Sopenharmony_ci#define SMBHSTDAT1 (5 + ali1535_smba) 608c2ecf20Sopenharmony_ci#define SMBBLKDAT (6 + ali1535_smba) 618c2ecf20Sopenharmony_ci 628c2ecf20Sopenharmony_ci/* PCI Address Constants */ 638c2ecf20Sopenharmony_ci#define SMBCOM 0x004 648c2ecf20Sopenharmony_ci#define SMBREV 0x008 658c2ecf20Sopenharmony_ci#define SMBCFG 0x0D1 668c2ecf20Sopenharmony_ci#define SMBBA 0x0E2 678c2ecf20Sopenharmony_ci#define SMBHSTCFG 0x0F0 688c2ecf20Sopenharmony_ci#define SMBCLK 0x0F2 698c2ecf20Sopenharmony_ci 708c2ecf20Sopenharmony_ci/* Other settings */ 718c2ecf20Sopenharmony_ci#define MAX_TIMEOUT 500 /* times 1/100 sec */ 728c2ecf20Sopenharmony_ci#define ALI1535_SMB_IOSIZE 32 738c2ecf20Sopenharmony_ci 748c2ecf20Sopenharmony_ci#define ALI1535_SMB_DEFAULTBASE 0x8040 758c2ecf20Sopenharmony_ci 768c2ecf20Sopenharmony_ci/* ALI1535 address lock bits */ 778c2ecf20Sopenharmony_ci#define ALI1535_LOCK 0x06 /* dwe */ 788c2ecf20Sopenharmony_ci 798c2ecf20Sopenharmony_ci/* ALI1535 command constants */ 808c2ecf20Sopenharmony_ci#define ALI1535_QUICK 0x00 818c2ecf20Sopenharmony_ci#define ALI1535_BYTE 0x10 828c2ecf20Sopenharmony_ci#define ALI1535_BYTE_DATA 0x20 838c2ecf20Sopenharmony_ci#define ALI1535_WORD_DATA 0x30 848c2ecf20Sopenharmony_ci#define ALI1535_BLOCK_DATA 0x40 858c2ecf20Sopenharmony_ci#define ALI1535_I2C_READ 0x60 868c2ecf20Sopenharmony_ci 878c2ecf20Sopenharmony_ci#define ALI1535_DEV10B_EN 0x80 /* Enable 10-bit addressing in */ 888c2ecf20Sopenharmony_ci /* I2C read */ 898c2ecf20Sopenharmony_ci#define ALI1535_T_OUT 0x08 /* Time-out Command (write) */ 908c2ecf20Sopenharmony_ci#define ALI1535_A_HIGH_BIT9 0x08 /* Bit 9 of 10-bit address in */ 918c2ecf20Sopenharmony_ci /* Alert-Response-Address */ 928c2ecf20Sopenharmony_ci /* (read) */ 938c2ecf20Sopenharmony_ci#define ALI1535_KILL 0x04 /* Kill Command (write) */ 948c2ecf20Sopenharmony_ci#define ALI1535_A_HIGH_BIT8 0x04 /* Bit 8 of 10-bit address in */ 958c2ecf20Sopenharmony_ci /* Alert-Response-Address */ 968c2ecf20Sopenharmony_ci /* (read) */ 978c2ecf20Sopenharmony_ci 988c2ecf20Sopenharmony_ci#define ALI1535_D_HI_MASK 0x03 /* Mask for isolating bits 9-8 */ 998c2ecf20Sopenharmony_ci /* of 10-bit address in I2C */ 1008c2ecf20Sopenharmony_ci /* Read Command */ 1018c2ecf20Sopenharmony_ci 1028c2ecf20Sopenharmony_ci/* ALI1535 status register bits */ 1038c2ecf20Sopenharmony_ci#define ALI1535_STS_IDLE 0x04 1048c2ecf20Sopenharmony_ci#define ALI1535_STS_BUSY 0x08 /* host busy */ 1058c2ecf20Sopenharmony_ci#define ALI1535_STS_DONE 0x10 /* transaction complete */ 1068c2ecf20Sopenharmony_ci#define ALI1535_STS_DEV 0x20 /* device error */ 1078c2ecf20Sopenharmony_ci#define ALI1535_STS_BUSERR 0x40 /* bus error */ 1088c2ecf20Sopenharmony_ci#define ALI1535_STS_FAIL 0x80 /* failed bus transaction */ 1098c2ecf20Sopenharmony_ci#define ALI1535_STS_ERR 0xE0 /* all the bad error bits */ 1108c2ecf20Sopenharmony_ci 1118c2ecf20Sopenharmony_ci#define ALI1535_BLOCK_CLR 0x04 /* reset block data index */ 1128c2ecf20Sopenharmony_ci 1138c2ecf20Sopenharmony_ci/* ALI1535 device address register bits */ 1148c2ecf20Sopenharmony_ci#define ALI1535_RD_ADDR 0x01 /* Read/Write Bit in Device */ 1158c2ecf20Sopenharmony_ci /* Address field */ 1168c2ecf20Sopenharmony_ci /* -> Write = 0 */ 1178c2ecf20Sopenharmony_ci /* -> Read = 1 */ 1188c2ecf20Sopenharmony_ci#define ALI1535_SMBIO_EN 0x04 /* SMB I/O Space enable */ 1198c2ecf20Sopenharmony_ci 1208c2ecf20Sopenharmony_cistatic struct pci_driver ali1535_driver; 1218c2ecf20Sopenharmony_cistatic unsigned long ali1535_smba; 1228c2ecf20Sopenharmony_cistatic unsigned short ali1535_offset; 1238c2ecf20Sopenharmony_ci 1248c2ecf20Sopenharmony_ci/* Detect whether a ALI1535 can be found, and initialize it, where necessary. 1258c2ecf20Sopenharmony_ci Note the differences between kernels with the old PCI BIOS interface and 1268c2ecf20Sopenharmony_ci newer kernels with the real PCI interface. In compat.h some things are 1278c2ecf20Sopenharmony_ci defined to make the transition easier. */ 1288c2ecf20Sopenharmony_cistatic int ali1535_setup(struct pci_dev *dev) 1298c2ecf20Sopenharmony_ci{ 1308c2ecf20Sopenharmony_ci int retval; 1318c2ecf20Sopenharmony_ci unsigned char temp; 1328c2ecf20Sopenharmony_ci 1338c2ecf20Sopenharmony_ci /* Check the following things: 1348c2ecf20Sopenharmony_ci - SMB I/O address is initialized 1358c2ecf20Sopenharmony_ci - Device is enabled 1368c2ecf20Sopenharmony_ci - We can use the addresses 1378c2ecf20Sopenharmony_ci */ 1388c2ecf20Sopenharmony_ci 1398c2ecf20Sopenharmony_ci retval = pci_enable_device(dev); 1408c2ecf20Sopenharmony_ci if (retval) { 1418c2ecf20Sopenharmony_ci dev_err(&dev->dev, "ALI1535_smb can't enable device\n"); 1428c2ecf20Sopenharmony_ci goto exit; 1438c2ecf20Sopenharmony_ci } 1448c2ecf20Sopenharmony_ci 1458c2ecf20Sopenharmony_ci /* Determine the address of the SMBus area */ 1468c2ecf20Sopenharmony_ci pci_read_config_word(dev, SMBBA, &ali1535_offset); 1478c2ecf20Sopenharmony_ci dev_dbg(&dev->dev, "ALI1535_smb is at offset 0x%04x\n", ali1535_offset); 1488c2ecf20Sopenharmony_ci ali1535_offset &= (0xffff & ~(ALI1535_SMB_IOSIZE - 1)); 1498c2ecf20Sopenharmony_ci if (ali1535_offset == 0) { 1508c2ecf20Sopenharmony_ci dev_warn(&dev->dev, 1518c2ecf20Sopenharmony_ci "ALI1535_smb region uninitialized - upgrade BIOS?\n"); 1528c2ecf20Sopenharmony_ci retval = -ENODEV; 1538c2ecf20Sopenharmony_ci goto exit; 1548c2ecf20Sopenharmony_ci } 1558c2ecf20Sopenharmony_ci 1568c2ecf20Sopenharmony_ci if (pci_resource_flags(dev, 0) & IORESOURCE_IO) 1578c2ecf20Sopenharmony_ci ali1535_smba = pci_resource_start(dev, 0) + ali1535_offset; 1588c2ecf20Sopenharmony_ci else 1598c2ecf20Sopenharmony_ci ali1535_smba = ali1535_offset; 1608c2ecf20Sopenharmony_ci 1618c2ecf20Sopenharmony_ci retval = acpi_check_region(ali1535_smba, ALI1535_SMB_IOSIZE, 1628c2ecf20Sopenharmony_ci ali1535_driver.name); 1638c2ecf20Sopenharmony_ci if (retval) 1648c2ecf20Sopenharmony_ci goto exit; 1658c2ecf20Sopenharmony_ci 1668c2ecf20Sopenharmony_ci if (!request_region(ali1535_smba, ALI1535_SMB_IOSIZE, 1678c2ecf20Sopenharmony_ci ali1535_driver.name)) { 1688c2ecf20Sopenharmony_ci dev_err(&dev->dev, "ALI1535_smb region 0x%lx already in use!\n", 1698c2ecf20Sopenharmony_ci ali1535_smba); 1708c2ecf20Sopenharmony_ci retval = -EBUSY; 1718c2ecf20Sopenharmony_ci goto exit; 1728c2ecf20Sopenharmony_ci } 1738c2ecf20Sopenharmony_ci 1748c2ecf20Sopenharmony_ci /* check if whole device is enabled */ 1758c2ecf20Sopenharmony_ci pci_read_config_byte(dev, SMBCFG, &temp); 1768c2ecf20Sopenharmony_ci if ((temp & ALI1535_SMBIO_EN) == 0) { 1778c2ecf20Sopenharmony_ci dev_err(&dev->dev, "SMB device not enabled - upgrade BIOS?\n"); 1788c2ecf20Sopenharmony_ci retval = -ENODEV; 1798c2ecf20Sopenharmony_ci goto exit_free; 1808c2ecf20Sopenharmony_ci } 1818c2ecf20Sopenharmony_ci 1828c2ecf20Sopenharmony_ci /* Is SMB Host controller enabled? */ 1838c2ecf20Sopenharmony_ci pci_read_config_byte(dev, SMBHSTCFG, &temp); 1848c2ecf20Sopenharmony_ci if ((temp & 1) == 0) { 1858c2ecf20Sopenharmony_ci dev_err(&dev->dev, "SMBus controller not enabled - upgrade BIOS?\n"); 1868c2ecf20Sopenharmony_ci retval = -ENODEV; 1878c2ecf20Sopenharmony_ci goto exit_free; 1888c2ecf20Sopenharmony_ci } 1898c2ecf20Sopenharmony_ci 1908c2ecf20Sopenharmony_ci /* set SMB clock to 74KHz as recommended in data sheet */ 1918c2ecf20Sopenharmony_ci pci_write_config_byte(dev, SMBCLK, 0x20); 1928c2ecf20Sopenharmony_ci 1938c2ecf20Sopenharmony_ci /* 1948c2ecf20Sopenharmony_ci The interrupt routing for SMB is set up in register 0x77 in the 1958c2ecf20Sopenharmony_ci 1533 ISA Bridge device, NOT in the 7101 device. 1968c2ecf20Sopenharmony_ci Don't bother with finding the 1533 device and reading the register. 1978c2ecf20Sopenharmony_ci if ((....... & 0x0F) == 1) 1988c2ecf20Sopenharmony_ci dev_dbg(&dev->dev, "ALI1535 using Interrupt 9 for SMBus.\n"); 1998c2ecf20Sopenharmony_ci */ 2008c2ecf20Sopenharmony_ci pci_read_config_byte(dev, SMBREV, &temp); 2018c2ecf20Sopenharmony_ci dev_dbg(&dev->dev, "SMBREV = 0x%X\n", temp); 2028c2ecf20Sopenharmony_ci dev_dbg(&dev->dev, "ALI1535_smba = 0x%lx\n", ali1535_smba); 2038c2ecf20Sopenharmony_ci 2048c2ecf20Sopenharmony_ci return 0; 2058c2ecf20Sopenharmony_ci 2068c2ecf20Sopenharmony_ciexit_free: 2078c2ecf20Sopenharmony_ci release_region(ali1535_smba, ALI1535_SMB_IOSIZE); 2088c2ecf20Sopenharmony_ciexit: 2098c2ecf20Sopenharmony_ci return retval; 2108c2ecf20Sopenharmony_ci} 2118c2ecf20Sopenharmony_ci 2128c2ecf20Sopenharmony_cistatic int ali1535_transaction(struct i2c_adapter *adap) 2138c2ecf20Sopenharmony_ci{ 2148c2ecf20Sopenharmony_ci int temp; 2158c2ecf20Sopenharmony_ci int result = 0; 2168c2ecf20Sopenharmony_ci int timeout = 0; 2178c2ecf20Sopenharmony_ci 2188c2ecf20Sopenharmony_ci dev_dbg(&adap->dev, "Transaction (pre): STS=%02x, TYP=%02x, " 2198c2ecf20Sopenharmony_ci "CMD=%02x, ADD=%02x, DAT0=%02x, DAT1=%02x\n", 2208c2ecf20Sopenharmony_ci inb_p(SMBHSTSTS), inb_p(SMBHSTTYP), inb_p(SMBHSTCMD), 2218c2ecf20Sopenharmony_ci inb_p(SMBHSTADD), inb_p(SMBHSTDAT0), inb_p(SMBHSTDAT1)); 2228c2ecf20Sopenharmony_ci 2238c2ecf20Sopenharmony_ci /* get status */ 2248c2ecf20Sopenharmony_ci temp = inb_p(SMBHSTSTS); 2258c2ecf20Sopenharmony_ci 2268c2ecf20Sopenharmony_ci /* Make sure the SMBus host is ready to start transmitting */ 2278c2ecf20Sopenharmony_ci /* Check the busy bit first */ 2288c2ecf20Sopenharmony_ci if (temp & ALI1535_STS_BUSY) { 2298c2ecf20Sopenharmony_ci /* If the host controller is still busy, it may have timed out 2308c2ecf20Sopenharmony_ci * in the previous transaction, resulting in a "SMBus Timeout" 2318c2ecf20Sopenharmony_ci * printk. I've tried the following to reset a stuck busy bit. 2328c2ecf20Sopenharmony_ci * 1. Reset the controller with an KILL command. (this 2338c2ecf20Sopenharmony_ci * doesn't seem to clear the controller if an external 2348c2ecf20Sopenharmony_ci * device is hung) 2358c2ecf20Sopenharmony_ci * 2. Reset the controller and the other SMBus devices with a 2368c2ecf20Sopenharmony_ci * T_OUT command. (this clears the host busy bit if an 2378c2ecf20Sopenharmony_ci * external device is hung, but it comes back upon a new 2388c2ecf20Sopenharmony_ci * access to a device) 2398c2ecf20Sopenharmony_ci * 3. Disable and reenable the controller in SMBHSTCFG. Worst 2408c2ecf20Sopenharmony_ci * case, nothing seems to work except power reset. 2418c2ecf20Sopenharmony_ci */ 2428c2ecf20Sopenharmony_ci 2438c2ecf20Sopenharmony_ci /* Try resetting entire SMB bus, including other devices - This 2448c2ecf20Sopenharmony_ci * may not work either - it clears the BUSY bit but then the 2458c2ecf20Sopenharmony_ci * BUSY bit may come back on when you try and use the chip 2468c2ecf20Sopenharmony_ci * again. If that's the case you are stuck. 2478c2ecf20Sopenharmony_ci */ 2488c2ecf20Sopenharmony_ci dev_info(&adap->dev, 2498c2ecf20Sopenharmony_ci "Resetting entire SMB Bus to clear busy condition (%02x)\n", 2508c2ecf20Sopenharmony_ci temp); 2518c2ecf20Sopenharmony_ci outb_p(ALI1535_T_OUT, SMBHSTTYP); 2528c2ecf20Sopenharmony_ci temp = inb_p(SMBHSTSTS); 2538c2ecf20Sopenharmony_ci } 2548c2ecf20Sopenharmony_ci 2558c2ecf20Sopenharmony_ci /* now check the error bits and the busy bit */ 2568c2ecf20Sopenharmony_ci if (temp & (ALI1535_STS_ERR | ALI1535_STS_BUSY)) { 2578c2ecf20Sopenharmony_ci /* do a clear-on-write */ 2588c2ecf20Sopenharmony_ci outb_p(0xFF, SMBHSTSTS); 2598c2ecf20Sopenharmony_ci temp = inb_p(SMBHSTSTS); 2608c2ecf20Sopenharmony_ci if (temp & (ALI1535_STS_ERR | ALI1535_STS_BUSY)) { 2618c2ecf20Sopenharmony_ci /* This is probably going to be correctable only by a 2628c2ecf20Sopenharmony_ci * power reset as one of the bits now appears to be 2638c2ecf20Sopenharmony_ci * stuck */ 2648c2ecf20Sopenharmony_ci /* This may be a bus or device with electrical problems. */ 2658c2ecf20Sopenharmony_ci dev_err(&adap->dev, 2668c2ecf20Sopenharmony_ci "SMBus reset failed! (0x%02x) - controller or " 2678c2ecf20Sopenharmony_ci "device on bus is probably hung\n", temp); 2688c2ecf20Sopenharmony_ci return -EBUSY; 2698c2ecf20Sopenharmony_ci } 2708c2ecf20Sopenharmony_ci } else { 2718c2ecf20Sopenharmony_ci /* check and clear done bit */ 2728c2ecf20Sopenharmony_ci if (temp & ALI1535_STS_DONE) 2738c2ecf20Sopenharmony_ci outb_p(temp, SMBHSTSTS); 2748c2ecf20Sopenharmony_ci } 2758c2ecf20Sopenharmony_ci 2768c2ecf20Sopenharmony_ci /* start the transaction by writing anything to the start register */ 2778c2ecf20Sopenharmony_ci outb_p(0xFF, SMBHSTPORT); 2788c2ecf20Sopenharmony_ci 2798c2ecf20Sopenharmony_ci /* We will always wait for a fraction of a second! */ 2808c2ecf20Sopenharmony_ci timeout = 0; 2818c2ecf20Sopenharmony_ci do { 2828c2ecf20Sopenharmony_ci usleep_range(1000, 2000); 2838c2ecf20Sopenharmony_ci temp = inb_p(SMBHSTSTS); 2848c2ecf20Sopenharmony_ci } while (((temp & ALI1535_STS_BUSY) && !(temp & ALI1535_STS_IDLE)) 2858c2ecf20Sopenharmony_ci && (timeout++ < MAX_TIMEOUT)); 2868c2ecf20Sopenharmony_ci 2878c2ecf20Sopenharmony_ci /* If the SMBus is still busy, we give up */ 2888c2ecf20Sopenharmony_ci if (timeout > MAX_TIMEOUT) { 2898c2ecf20Sopenharmony_ci result = -ETIMEDOUT; 2908c2ecf20Sopenharmony_ci dev_err(&adap->dev, "SMBus Timeout!\n"); 2918c2ecf20Sopenharmony_ci } 2928c2ecf20Sopenharmony_ci 2938c2ecf20Sopenharmony_ci if (temp & ALI1535_STS_FAIL) { 2948c2ecf20Sopenharmony_ci result = -EIO; 2958c2ecf20Sopenharmony_ci dev_dbg(&adap->dev, "Error: Failed bus transaction\n"); 2968c2ecf20Sopenharmony_ci } 2978c2ecf20Sopenharmony_ci 2988c2ecf20Sopenharmony_ci /* Unfortunately the ALI SMB controller maps "no response" and "bus 2998c2ecf20Sopenharmony_ci * collision" into a single bit. No response is the usual case so don't 3008c2ecf20Sopenharmony_ci * do a printk. This means that bus collisions go unreported. 3018c2ecf20Sopenharmony_ci */ 3028c2ecf20Sopenharmony_ci if (temp & ALI1535_STS_BUSERR) { 3038c2ecf20Sopenharmony_ci result = -ENXIO; 3048c2ecf20Sopenharmony_ci dev_dbg(&adap->dev, 3058c2ecf20Sopenharmony_ci "Error: no response or bus collision ADD=%02x\n", 3068c2ecf20Sopenharmony_ci inb_p(SMBHSTADD)); 3078c2ecf20Sopenharmony_ci } 3088c2ecf20Sopenharmony_ci 3098c2ecf20Sopenharmony_ci /* haven't ever seen this */ 3108c2ecf20Sopenharmony_ci if (temp & ALI1535_STS_DEV) { 3118c2ecf20Sopenharmony_ci result = -EIO; 3128c2ecf20Sopenharmony_ci dev_err(&adap->dev, "Error: device error\n"); 3138c2ecf20Sopenharmony_ci } 3148c2ecf20Sopenharmony_ci 3158c2ecf20Sopenharmony_ci /* check to see if the "command complete" indication is set */ 3168c2ecf20Sopenharmony_ci if (!(temp & ALI1535_STS_DONE)) { 3178c2ecf20Sopenharmony_ci result = -ETIMEDOUT; 3188c2ecf20Sopenharmony_ci dev_err(&adap->dev, "Error: command never completed\n"); 3198c2ecf20Sopenharmony_ci } 3208c2ecf20Sopenharmony_ci 3218c2ecf20Sopenharmony_ci dev_dbg(&adap->dev, "Transaction (post): STS=%02x, TYP=%02x, " 3228c2ecf20Sopenharmony_ci "CMD=%02x, ADD=%02x, DAT0=%02x, DAT1=%02x\n", 3238c2ecf20Sopenharmony_ci inb_p(SMBHSTSTS), inb_p(SMBHSTTYP), inb_p(SMBHSTCMD), 3248c2ecf20Sopenharmony_ci inb_p(SMBHSTADD), inb_p(SMBHSTDAT0), inb_p(SMBHSTDAT1)); 3258c2ecf20Sopenharmony_ci 3268c2ecf20Sopenharmony_ci /* take consequent actions for error conditions */ 3278c2ecf20Sopenharmony_ci if (!(temp & ALI1535_STS_DONE)) { 3288c2ecf20Sopenharmony_ci /* issue "kill" to reset host controller */ 3298c2ecf20Sopenharmony_ci outb_p(ALI1535_KILL, SMBHSTTYP); 3308c2ecf20Sopenharmony_ci outb_p(0xFF, SMBHSTSTS); 3318c2ecf20Sopenharmony_ci } else if (temp & ALI1535_STS_ERR) { 3328c2ecf20Sopenharmony_ci /* issue "timeout" to reset all devices on bus */ 3338c2ecf20Sopenharmony_ci outb_p(ALI1535_T_OUT, SMBHSTTYP); 3348c2ecf20Sopenharmony_ci outb_p(0xFF, SMBHSTSTS); 3358c2ecf20Sopenharmony_ci } 3368c2ecf20Sopenharmony_ci 3378c2ecf20Sopenharmony_ci return result; 3388c2ecf20Sopenharmony_ci} 3398c2ecf20Sopenharmony_ci 3408c2ecf20Sopenharmony_ci/* Return negative errno on error. */ 3418c2ecf20Sopenharmony_cistatic s32 ali1535_access(struct i2c_adapter *adap, u16 addr, 3428c2ecf20Sopenharmony_ci unsigned short flags, char read_write, u8 command, 3438c2ecf20Sopenharmony_ci int size, union i2c_smbus_data *data) 3448c2ecf20Sopenharmony_ci{ 3458c2ecf20Sopenharmony_ci int i, len; 3468c2ecf20Sopenharmony_ci int temp; 3478c2ecf20Sopenharmony_ci int timeout; 3488c2ecf20Sopenharmony_ci s32 result = 0; 3498c2ecf20Sopenharmony_ci 3508c2ecf20Sopenharmony_ci /* make sure SMBus is idle */ 3518c2ecf20Sopenharmony_ci temp = inb_p(SMBHSTSTS); 3528c2ecf20Sopenharmony_ci for (timeout = 0; 3538c2ecf20Sopenharmony_ci (timeout < MAX_TIMEOUT) && !(temp & ALI1535_STS_IDLE); 3548c2ecf20Sopenharmony_ci timeout++) { 3558c2ecf20Sopenharmony_ci usleep_range(1000, 2000); 3568c2ecf20Sopenharmony_ci temp = inb_p(SMBHSTSTS); 3578c2ecf20Sopenharmony_ci } 3588c2ecf20Sopenharmony_ci if (timeout >= MAX_TIMEOUT) 3598c2ecf20Sopenharmony_ci dev_warn(&adap->dev, "Idle wait Timeout! STS=0x%02x\n", temp); 3608c2ecf20Sopenharmony_ci 3618c2ecf20Sopenharmony_ci /* clear status register (clear-on-write) */ 3628c2ecf20Sopenharmony_ci outb_p(0xFF, SMBHSTSTS); 3638c2ecf20Sopenharmony_ci 3648c2ecf20Sopenharmony_ci switch (size) { 3658c2ecf20Sopenharmony_ci case I2C_SMBUS_QUICK: 3668c2ecf20Sopenharmony_ci outb_p(((addr & 0x7f) << 1) | (read_write & 0x01), 3678c2ecf20Sopenharmony_ci SMBHSTADD); 3688c2ecf20Sopenharmony_ci size = ALI1535_QUICK; 3698c2ecf20Sopenharmony_ci outb_p(size, SMBHSTTYP); /* output command */ 3708c2ecf20Sopenharmony_ci break; 3718c2ecf20Sopenharmony_ci case I2C_SMBUS_BYTE: 3728c2ecf20Sopenharmony_ci outb_p(((addr & 0x7f) << 1) | (read_write & 0x01), 3738c2ecf20Sopenharmony_ci SMBHSTADD); 3748c2ecf20Sopenharmony_ci size = ALI1535_BYTE; 3758c2ecf20Sopenharmony_ci outb_p(size, SMBHSTTYP); /* output command */ 3768c2ecf20Sopenharmony_ci if (read_write == I2C_SMBUS_WRITE) 3778c2ecf20Sopenharmony_ci outb_p(command, SMBHSTCMD); 3788c2ecf20Sopenharmony_ci break; 3798c2ecf20Sopenharmony_ci case I2C_SMBUS_BYTE_DATA: 3808c2ecf20Sopenharmony_ci outb_p(((addr & 0x7f) << 1) | (read_write & 0x01), 3818c2ecf20Sopenharmony_ci SMBHSTADD); 3828c2ecf20Sopenharmony_ci size = ALI1535_BYTE_DATA; 3838c2ecf20Sopenharmony_ci outb_p(size, SMBHSTTYP); /* output command */ 3848c2ecf20Sopenharmony_ci outb_p(command, SMBHSTCMD); 3858c2ecf20Sopenharmony_ci if (read_write == I2C_SMBUS_WRITE) 3868c2ecf20Sopenharmony_ci outb_p(data->byte, SMBHSTDAT0); 3878c2ecf20Sopenharmony_ci break; 3888c2ecf20Sopenharmony_ci case I2C_SMBUS_WORD_DATA: 3898c2ecf20Sopenharmony_ci outb_p(((addr & 0x7f) << 1) | (read_write & 0x01), 3908c2ecf20Sopenharmony_ci SMBHSTADD); 3918c2ecf20Sopenharmony_ci size = ALI1535_WORD_DATA; 3928c2ecf20Sopenharmony_ci outb_p(size, SMBHSTTYP); /* output command */ 3938c2ecf20Sopenharmony_ci outb_p(command, SMBHSTCMD); 3948c2ecf20Sopenharmony_ci if (read_write == I2C_SMBUS_WRITE) { 3958c2ecf20Sopenharmony_ci outb_p(data->word & 0xff, SMBHSTDAT0); 3968c2ecf20Sopenharmony_ci outb_p((data->word & 0xff00) >> 8, SMBHSTDAT1); 3978c2ecf20Sopenharmony_ci } 3988c2ecf20Sopenharmony_ci break; 3998c2ecf20Sopenharmony_ci case I2C_SMBUS_BLOCK_DATA: 4008c2ecf20Sopenharmony_ci outb_p(((addr & 0x7f) << 1) | (read_write & 0x01), 4018c2ecf20Sopenharmony_ci SMBHSTADD); 4028c2ecf20Sopenharmony_ci size = ALI1535_BLOCK_DATA; 4038c2ecf20Sopenharmony_ci outb_p(size, SMBHSTTYP); /* output command */ 4048c2ecf20Sopenharmony_ci outb_p(command, SMBHSTCMD); 4058c2ecf20Sopenharmony_ci if (read_write == I2C_SMBUS_WRITE) { 4068c2ecf20Sopenharmony_ci len = data->block[0]; 4078c2ecf20Sopenharmony_ci if (len < 0) { 4088c2ecf20Sopenharmony_ci len = 0; 4098c2ecf20Sopenharmony_ci data->block[0] = len; 4108c2ecf20Sopenharmony_ci } 4118c2ecf20Sopenharmony_ci if (len > 32) { 4128c2ecf20Sopenharmony_ci len = 32; 4138c2ecf20Sopenharmony_ci data->block[0] = len; 4148c2ecf20Sopenharmony_ci } 4158c2ecf20Sopenharmony_ci outb_p(len, SMBHSTDAT0); 4168c2ecf20Sopenharmony_ci /* Reset SMBBLKDAT */ 4178c2ecf20Sopenharmony_ci outb_p(inb_p(SMBHSTTYP) | ALI1535_BLOCK_CLR, SMBHSTTYP); 4188c2ecf20Sopenharmony_ci for (i = 1; i <= len; i++) 4198c2ecf20Sopenharmony_ci outb_p(data->block[i], SMBBLKDAT); 4208c2ecf20Sopenharmony_ci } 4218c2ecf20Sopenharmony_ci break; 4228c2ecf20Sopenharmony_ci default: 4238c2ecf20Sopenharmony_ci dev_warn(&adap->dev, "Unsupported transaction %d\n", size); 4248c2ecf20Sopenharmony_ci result = -EOPNOTSUPP; 4258c2ecf20Sopenharmony_ci goto EXIT; 4268c2ecf20Sopenharmony_ci } 4278c2ecf20Sopenharmony_ci 4288c2ecf20Sopenharmony_ci result = ali1535_transaction(adap); 4298c2ecf20Sopenharmony_ci if (result) 4308c2ecf20Sopenharmony_ci goto EXIT; 4318c2ecf20Sopenharmony_ci 4328c2ecf20Sopenharmony_ci if ((read_write == I2C_SMBUS_WRITE) || (size == ALI1535_QUICK)) { 4338c2ecf20Sopenharmony_ci result = 0; 4348c2ecf20Sopenharmony_ci goto EXIT; 4358c2ecf20Sopenharmony_ci } 4368c2ecf20Sopenharmony_ci 4378c2ecf20Sopenharmony_ci switch (size) { 4388c2ecf20Sopenharmony_ci case ALI1535_BYTE: /* Result put in SMBHSTDAT0 */ 4398c2ecf20Sopenharmony_ci data->byte = inb_p(SMBHSTDAT0); 4408c2ecf20Sopenharmony_ci break; 4418c2ecf20Sopenharmony_ci case ALI1535_BYTE_DATA: 4428c2ecf20Sopenharmony_ci data->byte = inb_p(SMBHSTDAT0); 4438c2ecf20Sopenharmony_ci break; 4448c2ecf20Sopenharmony_ci case ALI1535_WORD_DATA: 4458c2ecf20Sopenharmony_ci data->word = inb_p(SMBHSTDAT0) + (inb_p(SMBHSTDAT1) << 8); 4468c2ecf20Sopenharmony_ci break; 4478c2ecf20Sopenharmony_ci case ALI1535_BLOCK_DATA: 4488c2ecf20Sopenharmony_ci len = inb_p(SMBHSTDAT0); 4498c2ecf20Sopenharmony_ci if (len > 32) 4508c2ecf20Sopenharmony_ci len = 32; 4518c2ecf20Sopenharmony_ci data->block[0] = len; 4528c2ecf20Sopenharmony_ci /* Reset SMBBLKDAT */ 4538c2ecf20Sopenharmony_ci outb_p(inb_p(SMBHSTTYP) | ALI1535_BLOCK_CLR, SMBHSTTYP); 4548c2ecf20Sopenharmony_ci for (i = 1; i <= data->block[0]; i++) { 4558c2ecf20Sopenharmony_ci data->block[i] = inb_p(SMBBLKDAT); 4568c2ecf20Sopenharmony_ci dev_dbg(&adap->dev, "Blk: len=%d, i=%d, data=%02x\n", 4578c2ecf20Sopenharmony_ci len, i, data->block[i]); 4588c2ecf20Sopenharmony_ci } 4598c2ecf20Sopenharmony_ci break; 4608c2ecf20Sopenharmony_ci } 4618c2ecf20Sopenharmony_ciEXIT: 4628c2ecf20Sopenharmony_ci return result; 4638c2ecf20Sopenharmony_ci} 4648c2ecf20Sopenharmony_ci 4658c2ecf20Sopenharmony_ci 4668c2ecf20Sopenharmony_cistatic u32 ali1535_func(struct i2c_adapter *adapter) 4678c2ecf20Sopenharmony_ci{ 4688c2ecf20Sopenharmony_ci return I2C_FUNC_SMBUS_QUICK | I2C_FUNC_SMBUS_BYTE | 4698c2ecf20Sopenharmony_ci I2C_FUNC_SMBUS_BYTE_DATA | I2C_FUNC_SMBUS_WORD_DATA | 4708c2ecf20Sopenharmony_ci I2C_FUNC_SMBUS_BLOCK_DATA; 4718c2ecf20Sopenharmony_ci} 4728c2ecf20Sopenharmony_ci 4738c2ecf20Sopenharmony_cistatic const struct i2c_algorithm smbus_algorithm = { 4748c2ecf20Sopenharmony_ci .smbus_xfer = ali1535_access, 4758c2ecf20Sopenharmony_ci .functionality = ali1535_func, 4768c2ecf20Sopenharmony_ci}; 4778c2ecf20Sopenharmony_ci 4788c2ecf20Sopenharmony_cistatic struct i2c_adapter ali1535_adapter = { 4798c2ecf20Sopenharmony_ci .owner = THIS_MODULE, 4808c2ecf20Sopenharmony_ci .class = I2C_CLASS_HWMON | I2C_CLASS_SPD, 4818c2ecf20Sopenharmony_ci .algo = &smbus_algorithm, 4828c2ecf20Sopenharmony_ci}; 4838c2ecf20Sopenharmony_ci 4848c2ecf20Sopenharmony_cistatic const struct pci_device_id ali1535_ids[] = { 4858c2ecf20Sopenharmony_ci { PCI_DEVICE(PCI_VENDOR_ID_AL, PCI_DEVICE_ID_AL_M7101) }, 4868c2ecf20Sopenharmony_ci { }, 4878c2ecf20Sopenharmony_ci}; 4888c2ecf20Sopenharmony_ci 4898c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(pci, ali1535_ids); 4908c2ecf20Sopenharmony_ci 4918c2ecf20Sopenharmony_cistatic int ali1535_probe(struct pci_dev *dev, const struct pci_device_id *id) 4928c2ecf20Sopenharmony_ci{ 4938c2ecf20Sopenharmony_ci if (ali1535_setup(dev)) { 4948c2ecf20Sopenharmony_ci dev_warn(&dev->dev, 4958c2ecf20Sopenharmony_ci "ALI1535 not detected, module not inserted.\n"); 4968c2ecf20Sopenharmony_ci return -ENODEV; 4978c2ecf20Sopenharmony_ci } 4988c2ecf20Sopenharmony_ci 4998c2ecf20Sopenharmony_ci /* set up the sysfs linkage to our parent device */ 5008c2ecf20Sopenharmony_ci ali1535_adapter.dev.parent = &dev->dev; 5018c2ecf20Sopenharmony_ci 5028c2ecf20Sopenharmony_ci snprintf(ali1535_adapter.name, sizeof(ali1535_adapter.name), 5038c2ecf20Sopenharmony_ci "SMBus ALI1535 adapter at %04x", ali1535_offset); 5048c2ecf20Sopenharmony_ci return i2c_add_adapter(&ali1535_adapter); 5058c2ecf20Sopenharmony_ci} 5068c2ecf20Sopenharmony_ci 5078c2ecf20Sopenharmony_cistatic void ali1535_remove(struct pci_dev *dev) 5088c2ecf20Sopenharmony_ci{ 5098c2ecf20Sopenharmony_ci i2c_del_adapter(&ali1535_adapter); 5108c2ecf20Sopenharmony_ci release_region(ali1535_smba, ALI1535_SMB_IOSIZE); 5118c2ecf20Sopenharmony_ci} 5128c2ecf20Sopenharmony_ci 5138c2ecf20Sopenharmony_cistatic struct pci_driver ali1535_driver = { 5148c2ecf20Sopenharmony_ci .name = "ali1535_smbus", 5158c2ecf20Sopenharmony_ci .id_table = ali1535_ids, 5168c2ecf20Sopenharmony_ci .probe = ali1535_probe, 5178c2ecf20Sopenharmony_ci .remove = ali1535_remove, 5188c2ecf20Sopenharmony_ci}; 5198c2ecf20Sopenharmony_ci 5208c2ecf20Sopenharmony_cimodule_pci_driver(ali1535_driver); 5218c2ecf20Sopenharmony_ci 5228c2ecf20Sopenharmony_ciMODULE_AUTHOR("Frodo Looijaard <frodol@dds.nl>"); 5238c2ecf20Sopenharmony_ciMODULE_AUTHOR("Philip Edelbrock <phil@netroedge.com>"); 5248c2ecf20Sopenharmony_ciMODULE_AUTHOR("Mark D. Studebaker <mdsxyz123@yahoo.com>"); 5258c2ecf20Sopenharmony_ciMODULE_AUTHOR("Dan Eaton <dan.eaton@rocketlogix.com>"); 5268c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("ALI1535 SMBus driver"); 5278c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 528