18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci i2c-isch.c - Linux kernel driver for Intel SCH chipset SMBus 48c2ecf20Sopenharmony_ci - Based on i2c-piix4.c 58c2ecf20Sopenharmony_ci Copyright (c) 1998 - 2002 Frodo Looijaard <frodol@dds.nl> and 68c2ecf20Sopenharmony_ci Philip Edelbrock <phil@netroedge.com> 78c2ecf20Sopenharmony_ci - Intel SCH support 88c2ecf20Sopenharmony_ci Copyright (c) 2007 - 2008 Jacob Jun Pan <jacob.jun.pan@intel.com> 98c2ecf20Sopenharmony_ci 108c2ecf20Sopenharmony_ci*/ 118c2ecf20Sopenharmony_ci 128c2ecf20Sopenharmony_ci/* 138c2ecf20Sopenharmony_ci Supports: 148c2ecf20Sopenharmony_ci Intel SCH chipsets (AF82US15W, AF82US15L, AF82UL11L) 158c2ecf20Sopenharmony_ci Note: we assume there can only be one device, with one SMBus interface. 168c2ecf20Sopenharmony_ci*/ 178c2ecf20Sopenharmony_ci 188c2ecf20Sopenharmony_ci#include <linux/module.h> 198c2ecf20Sopenharmony_ci#include <linux/platform_device.h> 208c2ecf20Sopenharmony_ci#include <linux/kernel.h> 218c2ecf20Sopenharmony_ci#include <linux/delay.h> 228c2ecf20Sopenharmony_ci#include <linux/stddef.h> 238c2ecf20Sopenharmony_ci#include <linux/ioport.h> 248c2ecf20Sopenharmony_ci#include <linux/i2c.h> 258c2ecf20Sopenharmony_ci#include <linux/io.h> 268c2ecf20Sopenharmony_ci 278c2ecf20Sopenharmony_ci/* SCH SMBus address offsets */ 288c2ecf20Sopenharmony_ci#define SMBHSTCNT (0 + sch_smba) 298c2ecf20Sopenharmony_ci#define SMBHSTSTS (1 + sch_smba) 308c2ecf20Sopenharmony_ci#define SMBHSTCLK (2 + sch_smba) 318c2ecf20Sopenharmony_ci#define SMBHSTADD (4 + sch_smba) /* TSA */ 328c2ecf20Sopenharmony_ci#define SMBHSTCMD (5 + sch_smba) 338c2ecf20Sopenharmony_ci#define SMBHSTDAT0 (6 + sch_smba) 348c2ecf20Sopenharmony_ci#define SMBHSTDAT1 (7 + sch_smba) 358c2ecf20Sopenharmony_ci#define SMBBLKDAT (0x20 + sch_smba) 368c2ecf20Sopenharmony_ci 378c2ecf20Sopenharmony_ci/* Other settings */ 388c2ecf20Sopenharmony_ci#define MAX_RETRIES 5000 398c2ecf20Sopenharmony_ci 408c2ecf20Sopenharmony_ci/* I2C constants */ 418c2ecf20Sopenharmony_ci#define SCH_QUICK 0x00 428c2ecf20Sopenharmony_ci#define SCH_BYTE 0x01 438c2ecf20Sopenharmony_ci#define SCH_BYTE_DATA 0x02 448c2ecf20Sopenharmony_ci#define SCH_WORD_DATA 0x03 458c2ecf20Sopenharmony_ci#define SCH_BLOCK_DATA 0x05 468c2ecf20Sopenharmony_ci 478c2ecf20Sopenharmony_cistatic unsigned short sch_smba; 488c2ecf20Sopenharmony_cistatic struct i2c_adapter sch_adapter; 498c2ecf20Sopenharmony_cistatic int backbone_speed = 33000; /* backbone speed in kHz */ 508c2ecf20Sopenharmony_cimodule_param(backbone_speed, int, S_IRUSR | S_IWUSR); 518c2ecf20Sopenharmony_ciMODULE_PARM_DESC(backbone_speed, "Backbone speed in kHz, (default = 33000)"); 528c2ecf20Sopenharmony_ci 538c2ecf20Sopenharmony_ci/* 548c2ecf20Sopenharmony_ci * Start the i2c transaction -- the i2c_access will prepare the transaction 558c2ecf20Sopenharmony_ci * and this function will execute it. 568c2ecf20Sopenharmony_ci * return 0 for success and others for failure. 578c2ecf20Sopenharmony_ci */ 588c2ecf20Sopenharmony_cistatic int sch_transaction(void) 598c2ecf20Sopenharmony_ci{ 608c2ecf20Sopenharmony_ci int temp; 618c2ecf20Sopenharmony_ci int result = 0; 628c2ecf20Sopenharmony_ci int retries = 0; 638c2ecf20Sopenharmony_ci 648c2ecf20Sopenharmony_ci dev_dbg(&sch_adapter.dev, "Transaction (pre): CNT=%02x, CMD=%02x, " 658c2ecf20Sopenharmony_ci "ADD=%02x, DAT0=%02x, DAT1=%02x\n", inb(SMBHSTCNT), 668c2ecf20Sopenharmony_ci inb(SMBHSTCMD), inb(SMBHSTADD), inb(SMBHSTDAT0), 678c2ecf20Sopenharmony_ci inb(SMBHSTDAT1)); 688c2ecf20Sopenharmony_ci 698c2ecf20Sopenharmony_ci /* Make sure the SMBus host is ready to start transmitting */ 708c2ecf20Sopenharmony_ci temp = inb(SMBHSTSTS) & 0x0f; 718c2ecf20Sopenharmony_ci if (temp) { 728c2ecf20Sopenharmony_ci /* Can not be busy since we checked it in sch_access */ 738c2ecf20Sopenharmony_ci if (temp & 0x01) { 748c2ecf20Sopenharmony_ci dev_dbg(&sch_adapter.dev, "Completion (%02x). " 758c2ecf20Sopenharmony_ci "Clear...\n", temp); 768c2ecf20Sopenharmony_ci } 778c2ecf20Sopenharmony_ci if (temp & 0x06) { 788c2ecf20Sopenharmony_ci dev_dbg(&sch_adapter.dev, "SMBus error (%02x). " 798c2ecf20Sopenharmony_ci "Resetting...\n", temp); 808c2ecf20Sopenharmony_ci } 818c2ecf20Sopenharmony_ci outb(temp, SMBHSTSTS); 828c2ecf20Sopenharmony_ci temp = inb(SMBHSTSTS) & 0x0f; 838c2ecf20Sopenharmony_ci if (temp) { 848c2ecf20Sopenharmony_ci dev_err(&sch_adapter.dev, 858c2ecf20Sopenharmony_ci "SMBus is not ready: (%02x)\n", temp); 868c2ecf20Sopenharmony_ci return -EAGAIN; 878c2ecf20Sopenharmony_ci } 888c2ecf20Sopenharmony_ci } 898c2ecf20Sopenharmony_ci 908c2ecf20Sopenharmony_ci /* start the transaction by setting bit 4 */ 918c2ecf20Sopenharmony_ci outb(inb(SMBHSTCNT) | 0x10, SMBHSTCNT); 928c2ecf20Sopenharmony_ci 938c2ecf20Sopenharmony_ci do { 948c2ecf20Sopenharmony_ci usleep_range(100, 200); 958c2ecf20Sopenharmony_ci temp = inb(SMBHSTSTS) & 0x0f; 968c2ecf20Sopenharmony_ci } while ((temp & 0x08) && (retries++ < MAX_RETRIES)); 978c2ecf20Sopenharmony_ci 988c2ecf20Sopenharmony_ci /* If the SMBus is still busy, we give up */ 998c2ecf20Sopenharmony_ci if (retries > MAX_RETRIES) { 1008c2ecf20Sopenharmony_ci dev_err(&sch_adapter.dev, "SMBus Timeout!\n"); 1018c2ecf20Sopenharmony_ci result = -ETIMEDOUT; 1028c2ecf20Sopenharmony_ci } 1038c2ecf20Sopenharmony_ci if (temp & 0x04) { 1048c2ecf20Sopenharmony_ci result = -EIO; 1058c2ecf20Sopenharmony_ci dev_dbg(&sch_adapter.dev, "Bus collision! SMBus may be " 1068c2ecf20Sopenharmony_ci "locked until next hard reset. (sorry!)\n"); 1078c2ecf20Sopenharmony_ci /* Clock stops and slave is stuck in mid-transmission */ 1088c2ecf20Sopenharmony_ci } else if (temp & 0x02) { 1098c2ecf20Sopenharmony_ci result = -EIO; 1108c2ecf20Sopenharmony_ci dev_err(&sch_adapter.dev, "Error: no response!\n"); 1118c2ecf20Sopenharmony_ci } else if (temp & 0x01) { 1128c2ecf20Sopenharmony_ci dev_dbg(&sch_adapter.dev, "Post complete!\n"); 1138c2ecf20Sopenharmony_ci outb(temp, SMBHSTSTS); 1148c2ecf20Sopenharmony_ci temp = inb(SMBHSTSTS) & 0x07; 1158c2ecf20Sopenharmony_ci if (temp & 0x06) { 1168c2ecf20Sopenharmony_ci /* Completion clear failed */ 1178c2ecf20Sopenharmony_ci dev_dbg(&sch_adapter.dev, "Failed reset at end of " 1188c2ecf20Sopenharmony_ci "transaction (%02x), Bus error!\n", temp); 1198c2ecf20Sopenharmony_ci } 1208c2ecf20Sopenharmony_ci } else { 1218c2ecf20Sopenharmony_ci result = -ENXIO; 1228c2ecf20Sopenharmony_ci dev_dbg(&sch_adapter.dev, "No such address.\n"); 1238c2ecf20Sopenharmony_ci } 1248c2ecf20Sopenharmony_ci dev_dbg(&sch_adapter.dev, "Transaction (post): CNT=%02x, CMD=%02x, " 1258c2ecf20Sopenharmony_ci "ADD=%02x, DAT0=%02x, DAT1=%02x\n", inb(SMBHSTCNT), 1268c2ecf20Sopenharmony_ci inb(SMBHSTCMD), inb(SMBHSTADD), inb(SMBHSTDAT0), 1278c2ecf20Sopenharmony_ci inb(SMBHSTDAT1)); 1288c2ecf20Sopenharmony_ci return result; 1298c2ecf20Sopenharmony_ci} 1308c2ecf20Sopenharmony_ci 1318c2ecf20Sopenharmony_ci/* 1328c2ecf20Sopenharmony_ci * This is the main access entry for i2c-sch access 1338c2ecf20Sopenharmony_ci * adap is i2c_adapter pointer, addr is the i2c device bus address, read_write 1348c2ecf20Sopenharmony_ci * (0 for read and 1 for write), size is i2c transaction type and data is the 1358c2ecf20Sopenharmony_ci * union of transaction for data to be transferred or data read from bus. 1368c2ecf20Sopenharmony_ci * return 0 for success and others for failure. 1378c2ecf20Sopenharmony_ci */ 1388c2ecf20Sopenharmony_cistatic s32 sch_access(struct i2c_adapter *adap, u16 addr, 1398c2ecf20Sopenharmony_ci unsigned short flags, char read_write, 1408c2ecf20Sopenharmony_ci u8 command, int size, union i2c_smbus_data *data) 1418c2ecf20Sopenharmony_ci{ 1428c2ecf20Sopenharmony_ci int i, len, temp, rc; 1438c2ecf20Sopenharmony_ci 1448c2ecf20Sopenharmony_ci /* Make sure the SMBus host is not busy */ 1458c2ecf20Sopenharmony_ci temp = inb(SMBHSTSTS) & 0x0f; 1468c2ecf20Sopenharmony_ci if (temp & 0x08) { 1478c2ecf20Sopenharmony_ci dev_dbg(&sch_adapter.dev, "SMBus busy (%02x)\n", temp); 1488c2ecf20Sopenharmony_ci return -EAGAIN; 1498c2ecf20Sopenharmony_ci } 1508c2ecf20Sopenharmony_ci temp = inw(SMBHSTCLK); 1518c2ecf20Sopenharmony_ci if (!temp) { 1528c2ecf20Sopenharmony_ci /* 1538c2ecf20Sopenharmony_ci * We can't determine if we have 33 or 25 MHz clock for 1548c2ecf20Sopenharmony_ci * SMBus, so expect 33 MHz and calculate a bus clock of 1558c2ecf20Sopenharmony_ci * 100 kHz. If we actually run at 25 MHz the bus will be 1568c2ecf20Sopenharmony_ci * run ~75 kHz instead which should do no harm. 1578c2ecf20Sopenharmony_ci */ 1588c2ecf20Sopenharmony_ci dev_notice(&sch_adapter.dev, 1598c2ecf20Sopenharmony_ci "Clock divider uninitialized. Setting defaults\n"); 1608c2ecf20Sopenharmony_ci outw(backbone_speed / (4 * 100), SMBHSTCLK); 1618c2ecf20Sopenharmony_ci } 1628c2ecf20Sopenharmony_ci 1638c2ecf20Sopenharmony_ci dev_dbg(&sch_adapter.dev, "access size: %d %s\n", size, 1648c2ecf20Sopenharmony_ci (read_write)?"READ":"WRITE"); 1658c2ecf20Sopenharmony_ci switch (size) { 1668c2ecf20Sopenharmony_ci case I2C_SMBUS_QUICK: 1678c2ecf20Sopenharmony_ci outb((addr << 1) | read_write, SMBHSTADD); 1688c2ecf20Sopenharmony_ci size = SCH_QUICK; 1698c2ecf20Sopenharmony_ci break; 1708c2ecf20Sopenharmony_ci case I2C_SMBUS_BYTE: 1718c2ecf20Sopenharmony_ci outb((addr << 1) | read_write, SMBHSTADD); 1728c2ecf20Sopenharmony_ci if (read_write == I2C_SMBUS_WRITE) 1738c2ecf20Sopenharmony_ci outb(command, SMBHSTCMD); 1748c2ecf20Sopenharmony_ci size = SCH_BYTE; 1758c2ecf20Sopenharmony_ci break; 1768c2ecf20Sopenharmony_ci case I2C_SMBUS_BYTE_DATA: 1778c2ecf20Sopenharmony_ci outb((addr << 1) | read_write, SMBHSTADD); 1788c2ecf20Sopenharmony_ci outb(command, SMBHSTCMD); 1798c2ecf20Sopenharmony_ci if (read_write == I2C_SMBUS_WRITE) 1808c2ecf20Sopenharmony_ci outb(data->byte, SMBHSTDAT0); 1818c2ecf20Sopenharmony_ci size = SCH_BYTE_DATA; 1828c2ecf20Sopenharmony_ci break; 1838c2ecf20Sopenharmony_ci case I2C_SMBUS_WORD_DATA: 1848c2ecf20Sopenharmony_ci outb((addr << 1) | read_write, SMBHSTADD); 1858c2ecf20Sopenharmony_ci outb(command, SMBHSTCMD); 1868c2ecf20Sopenharmony_ci if (read_write == I2C_SMBUS_WRITE) { 1878c2ecf20Sopenharmony_ci outb(data->word & 0xff, SMBHSTDAT0); 1888c2ecf20Sopenharmony_ci outb((data->word & 0xff00) >> 8, SMBHSTDAT1); 1898c2ecf20Sopenharmony_ci } 1908c2ecf20Sopenharmony_ci size = SCH_WORD_DATA; 1918c2ecf20Sopenharmony_ci break; 1928c2ecf20Sopenharmony_ci case I2C_SMBUS_BLOCK_DATA: 1938c2ecf20Sopenharmony_ci outb((addr << 1) | read_write, SMBHSTADD); 1948c2ecf20Sopenharmony_ci outb(command, SMBHSTCMD); 1958c2ecf20Sopenharmony_ci if (read_write == I2C_SMBUS_WRITE) { 1968c2ecf20Sopenharmony_ci len = data->block[0]; 1978c2ecf20Sopenharmony_ci if (len == 0 || len > I2C_SMBUS_BLOCK_MAX) 1988c2ecf20Sopenharmony_ci return -EINVAL; 1998c2ecf20Sopenharmony_ci outb(len, SMBHSTDAT0); 2008c2ecf20Sopenharmony_ci for (i = 1; i <= len; i++) 2018c2ecf20Sopenharmony_ci outb(data->block[i], SMBBLKDAT+i-1); 2028c2ecf20Sopenharmony_ci } 2038c2ecf20Sopenharmony_ci size = SCH_BLOCK_DATA; 2048c2ecf20Sopenharmony_ci break; 2058c2ecf20Sopenharmony_ci default: 2068c2ecf20Sopenharmony_ci dev_warn(&adap->dev, "Unsupported transaction %d\n", size); 2078c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 2088c2ecf20Sopenharmony_ci } 2098c2ecf20Sopenharmony_ci dev_dbg(&sch_adapter.dev, "write size %d to 0x%04x\n", size, SMBHSTCNT); 2108c2ecf20Sopenharmony_ci outb((inb(SMBHSTCNT) & 0xb0) | (size & 0x7), SMBHSTCNT); 2118c2ecf20Sopenharmony_ci 2128c2ecf20Sopenharmony_ci rc = sch_transaction(); 2138c2ecf20Sopenharmony_ci if (rc) /* Error in transaction */ 2148c2ecf20Sopenharmony_ci return rc; 2158c2ecf20Sopenharmony_ci 2168c2ecf20Sopenharmony_ci if ((read_write == I2C_SMBUS_WRITE) || (size == SCH_QUICK)) 2178c2ecf20Sopenharmony_ci return 0; 2188c2ecf20Sopenharmony_ci 2198c2ecf20Sopenharmony_ci switch (size) { 2208c2ecf20Sopenharmony_ci case SCH_BYTE: 2218c2ecf20Sopenharmony_ci case SCH_BYTE_DATA: 2228c2ecf20Sopenharmony_ci data->byte = inb(SMBHSTDAT0); 2238c2ecf20Sopenharmony_ci break; 2248c2ecf20Sopenharmony_ci case SCH_WORD_DATA: 2258c2ecf20Sopenharmony_ci data->word = inb(SMBHSTDAT0) + (inb(SMBHSTDAT1) << 8); 2268c2ecf20Sopenharmony_ci break; 2278c2ecf20Sopenharmony_ci case SCH_BLOCK_DATA: 2288c2ecf20Sopenharmony_ci data->block[0] = inb(SMBHSTDAT0); 2298c2ecf20Sopenharmony_ci if (data->block[0] == 0 || data->block[0] > I2C_SMBUS_BLOCK_MAX) 2308c2ecf20Sopenharmony_ci return -EPROTO; 2318c2ecf20Sopenharmony_ci for (i = 1; i <= data->block[0]; i++) 2328c2ecf20Sopenharmony_ci data->block[i] = inb(SMBBLKDAT+i-1); 2338c2ecf20Sopenharmony_ci break; 2348c2ecf20Sopenharmony_ci } 2358c2ecf20Sopenharmony_ci return 0; 2368c2ecf20Sopenharmony_ci} 2378c2ecf20Sopenharmony_ci 2388c2ecf20Sopenharmony_cistatic u32 sch_func(struct i2c_adapter *adapter) 2398c2ecf20Sopenharmony_ci{ 2408c2ecf20Sopenharmony_ci return I2C_FUNC_SMBUS_QUICK | I2C_FUNC_SMBUS_BYTE | 2418c2ecf20Sopenharmony_ci I2C_FUNC_SMBUS_BYTE_DATA | I2C_FUNC_SMBUS_WORD_DATA | 2428c2ecf20Sopenharmony_ci I2C_FUNC_SMBUS_BLOCK_DATA; 2438c2ecf20Sopenharmony_ci} 2448c2ecf20Sopenharmony_ci 2458c2ecf20Sopenharmony_cistatic const struct i2c_algorithm smbus_algorithm = { 2468c2ecf20Sopenharmony_ci .smbus_xfer = sch_access, 2478c2ecf20Sopenharmony_ci .functionality = sch_func, 2488c2ecf20Sopenharmony_ci}; 2498c2ecf20Sopenharmony_ci 2508c2ecf20Sopenharmony_cistatic struct i2c_adapter sch_adapter = { 2518c2ecf20Sopenharmony_ci .owner = THIS_MODULE, 2528c2ecf20Sopenharmony_ci .class = I2C_CLASS_HWMON | I2C_CLASS_SPD, 2538c2ecf20Sopenharmony_ci .algo = &smbus_algorithm, 2548c2ecf20Sopenharmony_ci}; 2558c2ecf20Sopenharmony_ci 2568c2ecf20Sopenharmony_cistatic int smbus_sch_probe(struct platform_device *dev) 2578c2ecf20Sopenharmony_ci{ 2588c2ecf20Sopenharmony_ci struct resource *res; 2598c2ecf20Sopenharmony_ci int retval; 2608c2ecf20Sopenharmony_ci 2618c2ecf20Sopenharmony_ci res = platform_get_resource(dev, IORESOURCE_IO, 0); 2628c2ecf20Sopenharmony_ci if (!res) 2638c2ecf20Sopenharmony_ci return -EBUSY; 2648c2ecf20Sopenharmony_ci 2658c2ecf20Sopenharmony_ci if (!devm_request_region(&dev->dev, res->start, resource_size(res), 2668c2ecf20Sopenharmony_ci dev->name)) { 2678c2ecf20Sopenharmony_ci dev_err(&dev->dev, "SMBus region 0x%x already in use!\n", 2688c2ecf20Sopenharmony_ci sch_smba); 2698c2ecf20Sopenharmony_ci return -EBUSY; 2708c2ecf20Sopenharmony_ci } 2718c2ecf20Sopenharmony_ci 2728c2ecf20Sopenharmony_ci sch_smba = res->start; 2738c2ecf20Sopenharmony_ci 2748c2ecf20Sopenharmony_ci dev_dbg(&dev->dev, "SMBA = 0x%X\n", sch_smba); 2758c2ecf20Sopenharmony_ci 2768c2ecf20Sopenharmony_ci /* set up the sysfs linkage to our parent device */ 2778c2ecf20Sopenharmony_ci sch_adapter.dev.parent = &dev->dev; 2788c2ecf20Sopenharmony_ci 2798c2ecf20Sopenharmony_ci snprintf(sch_adapter.name, sizeof(sch_adapter.name), 2808c2ecf20Sopenharmony_ci "SMBus SCH adapter at %04x", sch_smba); 2818c2ecf20Sopenharmony_ci 2828c2ecf20Sopenharmony_ci retval = i2c_add_adapter(&sch_adapter); 2838c2ecf20Sopenharmony_ci if (retval) 2848c2ecf20Sopenharmony_ci sch_smba = 0; 2858c2ecf20Sopenharmony_ci 2868c2ecf20Sopenharmony_ci return retval; 2878c2ecf20Sopenharmony_ci} 2888c2ecf20Sopenharmony_ci 2898c2ecf20Sopenharmony_cistatic int smbus_sch_remove(struct platform_device *pdev) 2908c2ecf20Sopenharmony_ci{ 2918c2ecf20Sopenharmony_ci if (sch_smba) { 2928c2ecf20Sopenharmony_ci i2c_del_adapter(&sch_adapter); 2938c2ecf20Sopenharmony_ci sch_smba = 0; 2948c2ecf20Sopenharmony_ci } 2958c2ecf20Sopenharmony_ci 2968c2ecf20Sopenharmony_ci return 0; 2978c2ecf20Sopenharmony_ci} 2988c2ecf20Sopenharmony_ci 2998c2ecf20Sopenharmony_cistatic struct platform_driver smbus_sch_driver = { 3008c2ecf20Sopenharmony_ci .driver = { 3018c2ecf20Sopenharmony_ci .name = "isch_smbus", 3028c2ecf20Sopenharmony_ci }, 3038c2ecf20Sopenharmony_ci .probe = smbus_sch_probe, 3048c2ecf20Sopenharmony_ci .remove = smbus_sch_remove, 3058c2ecf20Sopenharmony_ci}; 3068c2ecf20Sopenharmony_ci 3078c2ecf20Sopenharmony_cimodule_platform_driver(smbus_sch_driver); 3088c2ecf20Sopenharmony_ci 3098c2ecf20Sopenharmony_ciMODULE_AUTHOR("Jacob Pan <jacob.jun.pan@intel.com>"); 3108c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Intel SCH SMBus driver"); 3118c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 3128c2ecf20Sopenharmony_ciMODULE_ALIAS("platform:isch_smbus"); 313