18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * SMBus 2.0 driver for AMD-8111 IO-Hub. 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (c) 2002 Vojtech Pavlik 68c2ecf20Sopenharmony_ci */ 78c2ecf20Sopenharmony_ci 88c2ecf20Sopenharmony_ci#include <linux/module.h> 98c2ecf20Sopenharmony_ci#include <linux/pci.h> 108c2ecf20Sopenharmony_ci#include <linux/kernel.h> 118c2ecf20Sopenharmony_ci#include <linux/stddef.h> 128c2ecf20Sopenharmony_ci#include <linux/ioport.h> 138c2ecf20Sopenharmony_ci#include <linux/i2c.h> 148c2ecf20Sopenharmony_ci#include <linux/delay.h> 158c2ecf20Sopenharmony_ci#include <linux/acpi.h> 168c2ecf20Sopenharmony_ci#include <linux/slab.h> 178c2ecf20Sopenharmony_ci#include <linux/io.h> 188c2ecf20Sopenharmony_ci 198c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 208c2ecf20Sopenharmony_ciMODULE_AUTHOR ("Vojtech Pavlik <vojtech@suse.cz>"); 218c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("AMD8111 SMBus 2.0 driver"); 228c2ecf20Sopenharmony_ci 238c2ecf20Sopenharmony_cistruct amd_smbus { 248c2ecf20Sopenharmony_ci struct pci_dev *dev; 258c2ecf20Sopenharmony_ci struct i2c_adapter adapter; 268c2ecf20Sopenharmony_ci int base; 278c2ecf20Sopenharmony_ci int size; 288c2ecf20Sopenharmony_ci}; 298c2ecf20Sopenharmony_ci 308c2ecf20Sopenharmony_cistatic struct pci_driver amd8111_driver; 318c2ecf20Sopenharmony_ci 328c2ecf20Sopenharmony_ci/* 338c2ecf20Sopenharmony_ci * AMD PCI control registers definitions. 348c2ecf20Sopenharmony_ci */ 358c2ecf20Sopenharmony_ci 368c2ecf20Sopenharmony_ci#define AMD_PCI_MISC 0x48 378c2ecf20Sopenharmony_ci 388c2ecf20Sopenharmony_ci#define AMD_PCI_MISC_SCI 0x04 /* deliver SCI */ 398c2ecf20Sopenharmony_ci#define AMD_PCI_MISC_INT 0x02 /* deliver PCI IRQ */ 408c2ecf20Sopenharmony_ci#define AMD_PCI_MISC_SPEEDUP 0x01 /* 16x clock speedup */ 418c2ecf20Sopenharmony_ci 428c2ecf20Sopenharmony_ci/* 438c2ecf20Sopenharmony_ci * ACPI 2.0 chapter 13 PCI interface definitions. 448c2ecf20Sopenharmony_ci */ 458c2ecf20Sopenharmony_ci 468c2ecf20Sopenharmony_ci#define AMD_EC_DATA 0x00 /* data register */ 478c2ecf20Sopenharmony_ci#define AMD_EC_SC 0x04 /* status of controller */ 488c2ecf20Sopenharmony_ci#define AMD_EC_CMD 0x04 /* command register */ 498c2ecf20Sopenharmony_ci#define AMD_EC_ICR 0x08 /* interrupt control register */ 508c2ecf20Sopenharmony_ci 518c2ecf20Sopenharmony_ci#define AMD_EC_SC_SMI 0x04 /* smi event pending */ 528c2ecf20Sopenharmony_ci#define AMD_EC_SC_SCI 0x02 /* sci event pending */ 538c2ecf20Sopenharmony_ci#define AMD_EC_SC_BURST 0x01 /* burst mode enabled */ 548c2ecf20Sopenharmony_ci#define AMD_EC_SC_CMD 0x08 /* byte in data reg is command */ 558c2ecf20Sopenharmony_ci#define AMD_EC_SC_IBF 0x02 /* data ready for embedded controller */ 568c2ecf20Sopenharmony_ci#define AMD_EC_SC_OBF 0x01 /* data ready for host */ 578c2ecf20Sopenharmony_ci 588c2ecf20Sopenharmony_ci#define AMD_EC_CMD_RD 0x80 /* read EC */ 598c2ecf20Sopenharmony_ci#define AMD_EC_CMD_WR 0x81 /* write EC */ 608c2ecf20Sopenharmony_ci#define AMD_EC_CMD_BE 0x82 /* enable burst mode */ 618c2ecf20Sopenharmony_ci#define AMD_EC_CMD_BD 0x83 /* disable burst mode */ 628c2ecf20Sopenharmony_ci#define AMD_EC_CMD_QR 0x84 /* query EC */ 638c2ecf20Sopenharmony_ci 648c2ecf20Sopenharmony_ci/* 658c2ecf20Sopenharmony_ci * ACPI 2.0 chapter 13 access of registers of the EC 668c2ecf20Sopenharmony_ci */ 678c2ecf20Sopenharmony_ci 688c2ecf20Sopenharmony_cistatic int amd_ec_wait_write(struct amd_smbus *smbus) 698c2ecf20Sopenharmony_ci{ 708c2ecf20Sopenharmony_ci int timeout = 500; 718c2ecf20Sopenharmony_ci 728c2ecf20Sopenharmony_ci while ((inb(smbus->base + AMD_EC_SC) & AMD_EC_SC_IBF) && --timeout) 738c2ecf20Sopenharmony_ci udelay(1); 748c2ecf20Sopenharmony_ci 758c2ecf20Sopenharmony_ci if (!timeout) { 768c2ecf20Sopenharmony_ci dev_warn(&smbus->dev->dev, 778c2ecf20Sopenharmony_ci "Timeout while waiting for IBF to clear\n"); 788c2ecf20Sopenharmony_ci return -ETIMEDOUT; 798c2ecf20Sopenharmony_ci } 808c2ecf20Sopenharmony_ci 818c2ecf20Sopenharmony_ci return 0; 828c2ecf20Sopenharmony_ci} 838c2ecf20Sopenharmony_ci 848c2ecf20Sopenharmony_cistatic int amd_ec_wait_read(struct amd_smbus *smbus) 858c2ecf20Sopenharmony_ci{ 868c2ecf20Sopenharmony_ci int timeout = 500; 878c2ecf20Sopenharmony_ci 888c2ecf20Sopenharmony_ci while ((~inb(smbus->base + AMD_EC_SC) & AMD_EC_SC_OBF) && --timeout) 898c2ecf20Sopenharmony_ci udelay(1); 908c2ecf20Sopenharmony_ci 918c2ecf20Sopenharmony_ci if (!timeout) { 928c2ecf20Sopenharmony_ci dev_warn(&smbus->dev->dev, 938c2ecf20Sopenharmony_ci "Timeout while waiting for OBF to set\n"); 948c2ecf20Sopenharmony_ci return -ETIMEDOUT; 958c2ecf20Sopenharmony_ci } 968c2ecf20Sopenharmony_ci 978c2ecf20Sopenharmony_ci return 0; 988c2ecf20Sopenharmony_ci} 998c2ecf20Sopenharmony_ci 1008c2ecf20Sopenharmony_cistatic int amd_ec_read(struct amd_smbus *smbus, unsigned char address, 1018c2ecf20Sopenharmony_ci unsigned char *data) 1028c2ecf20Sopenharmony_ci{ 1038c2ecf20Sopenharmony_ci int status; 1048c2ecf20Sopenharmony_ci 1058c2ecf20Sopenharmony_ci status = amd_ec_wait_write(smbus); 1068c2ecf20Sopenharmony_ci if (status) 1078c2ecf20Sopenharmony_ci return status; 1088c2ecf20Sopenharmony_ci outb(AMD_EC_CMD_RD, smbus->base + AMD_EC_CMD); 1098c2ecf20Sopenharmony_ci 1108c2ecf20Sopenharmony_ci status = amd_ec_wait_write(smbus); 1118c2ecf20Sopenharmony_ci if (status) 1128c2ecf20Sopenharmony_ci return status; 1138c2ecf20Sopenharmony_ci outb(address, smbus->base + AMD_EC_DATA); 1148c2ecf20Sopenharmony_ci 1158c2ecf20Sopenharmony_ci status = amd_ec_wait_read(smbus); 1168c2ecf20Sopenharmony_ci if (status) 1178c2ecf20Sopenharmony_ci return status; 1188c2ecf20Sopenharmony_ci *data = inb(smbus->base + AMD_EC_DATA); 1198c2ecf20Sopenharmony_ci 1208c2ecf20Sopenharmony_ci return 0; 1218c2ecf20Sopenharmony_ci} 1228c2ecf20Sopenharmony_ci 1238c2ecf20Sopenharmony_cistatic int amd_ec_write(struct amd_smbus *smbus, unsigned char address, 1248c2ecf20Sopenharmony_ci unsigned char data) 1258c2ecf20Sopenharmony_ci{ 1268c2ecf20Sopenharmony_ci int status; 1278c2ecf20Sopenharmony_ci 1288c2ecf20Sopenharmony_ci status = amd_ec_wait_write(smbus); 1298c2ecf20Sopenharmony_ci if (status) 1308c2ecf20Sopenharmony_ci return status; 1318c2ecf20Sopenharmony_ci outb(AMD_EC_CMD_WR, smbus->base + AMD_EC_CMD); 1328c2ecf20Sopenharmony_ci 1338c2ecf20Sopenharmony_ci status = amd_ec_wait_write(smbus); 1348c2ecf20Sopenharmony_ci if (status) 1358c2ecf20Sopenharmony_ci return status; 1368c2ecf20Sopenharmony_ci outb(address, smbus->base + AMD_EC_DATA); 1378c2ecf20Sopenharmony_ci 1388c2ecf20Sopenharmony_ci status = amd_ec_wait_write(smbus); 1398c2ecf20Sopenharmony_ci if (status) 1408c2ecf20Sopenharmony_ci return status; 1418c2ecf20Sopenharmony_ci outb(data, smbus->base + AMD_EC_DATA); 1428c2ecf20Sopenharmony_ci 1438c2ecf20Sopenharmony_ci return 0; 1448c2ecf20Sopenharmony_ci} 1458c2ecf20Sopenharmony_ci 1468c2ecf20Sopenharmony_ci/* 1478c2ecf20Sopenharmony_ci * ACPI 2.0 chapter 13 SMBus 2.0 EC register model 1488c2ecf20Sopenharmony_ci */ 1498c2ecf20Sopenharmony_ci 1508c2ecf20Sopenharmony_ci#define AMD_SMB_PRTCL 0x00 /* protocol, PEC */ 1518c2ecf20Sopenharmony_ci#define AMD_SMB_STS 0x01 /* status */ 1528c2ecf20Sopenharmony_ci#define AMD_SMB_ADDR 0x02 /* address */ 1538c2ecf20Sopenharmony_ci#define AMD_SMB_CMD 0x03 /* command */ 1548c2ecf20Sopenharmony_ci#define AMD_SMB_DATA 0x04 /* 32 data registers */ 1558c2ecf20Sopenharmony_ci#define AMD_SMB_BCNT 0x24 /* number of data bytes */ 1568c2ecf20Sopenharmony_ci#define AMD_SMB_ALRM_A 0x25 /* alarm address */ 1578c2ecf20Sopenharmony_ci#define AMD_SMB_ALRM_D 0x26 /* 2 bytes alarm data */ 1588c2ecf20Sopenharmony_ci 1598c2ecf20Sopenharmony_ci#define AMD_SMB_STS_DONE 0x80 1608c2ecf20Sopenharmony_ci#define AMD_SMB_STS_ALRM 0x40 1618c2ecf20Sopenharmony_ci#define AMD_SMB_STS_RES 0x20 1628c2ecf20Sopenharmony_ci#define AMD_SMB_STS_STATUS 0x1f 1638c2ecf20Sopenharmony_ci 1648c2ecf20Sopenharmony_ci#define AMD_SMB_STATUS_OK 0x00 1658c2ecf20Sopenharmony_ci#define AMD_SMB_STATUS_FAIL 0x07 1668c2ecf20Sopenharmony_ci#define AMD_SMB_STATUS_DNAK 0x10 1678c2ecf20Sopenharmony_ci#define AMD_SMB_STATUS_DERR 0x11 1688c2ecf20Sopenharmony_ci#define AMD_SMB_STATUS_CMD_DENY 0x12 1698c2ecf20Sopenharmony_ci#define AMD_SMB_STATUS_UNKNOWN 0x13 1708c2ecf20Sopenharmony_ci#define AMD_SMB_STATUS_ACC_DENY 0x17 1718c2ecf20Sopenharmony_ci#define AMD_SMB_STATUS_TIMEOUT 0x18 1728c2ecf20Sopenharmony_ci#define AMD_SMB_STATUS_NOTSUP 0x19 1738c2ecf20Sopenharmony_ci#define AMD_SMB_STATUS_BUSY 0x1A 1748c2ecf20Sopenharmony_ci#define AMD_SMB_STATUS_PEC 0x1F 1758c2ecf20Sopenharmony_ci 1768c2ecf20Sopenharmony_ci#define AMD_SMB_PRTCL_WRITE 0x00 1778c2ecf20Sopenharmony_ci#define AMD_SMB_PRTCL_READ 0x01 1788c2ecf20Sopenharmony_ci#define AMD_SMB_PRTCL_QUICK 0x02 1798c2ecf20Sopenharmony_ci#define AMD_SMB_PRTCL_BYTE 0x04 1808c2ecf20Sopenharmony_ci#define AMD_SMB_PRTCL_BYTE_DATA 0x06 1818c2ecf20Sopenharmony_ci#define AMD_SMB_PRTCL_WORD_DATA 0x08 1828c2ecf20Sopenharmony_ci#define AMD_SMB_PRTCL_BLOCK_DATA 0x0a 1838c2ecf20Sopenharmony_ci#define AMD_SMB_PRTCL_PROC_CALL 0x0c 1848c2ecf20Sopenharmony_ci#define AMD_SMB_PRTCL_BLOCK_PROC_CALL 0x0d 1858c2ecf20Sopenharmony_ci#define AMD_SMB_PRTCL_I2C_BLOCK_DATA 0x4a 1868c2ecf20Sopenharmony_ci#define AMD_SMB_PRTCL_PEC 0x80 1878c2ecf20Sopenharmony_ci 1888c2ecf20Sopenharmony_ci 1898c2ecf20Sopenharmony_cistatic s32 amd8111_access(struct i2c_adapter * adap, u16 addr, 1908c2ecf20Sopenharmony_ci unsigned short flags, char read_write, u8 command, int size, 1918c2ecf20Sopenharmony_ci union i2c_smbus_data * data) 1928c2ecf20Sopenharmony_ci{ 1938c2ecf20Sopenharmony_ci struct amd_smbus *smbus = adap->algo_data; 1948c2ecf20Sopenharmony_ci unsigned char protocol, len, pec, temp[2]; 1958c2ecf20Sopenharmony_ci int i, status; 1968c2ecf20Sopenharmony_ci 1978c2ecf20Sopenharmony_ci protocol = (read_write == I2C_SMBUS_READ) ? AMD_SMB_PRTCL_READ 1988c2ecf20Sopenharmony_ci : AMD_SMB_PRTCL_WRITE; 1998c2ecf20Sopenharmony_ci pec = (flags & I2C_CLIENT_PEC) ? AMD_SMB_PRTCL_PEC : 0; 2008c2ecf20Sopenharmony_ci 2018c2ecf20Sopenharmony_ci switch (size) { 2028c2ecf20Sopenharmony_ci case I2C_SMBUS_QUICK: 2038c2ecf20Sopenharmony_ci protocol |= AMD_SMB_PRTCL_QUICK; 2048c2ecf20Sopenharmony_ci read_write = I2C_SMBUS_WRITE; 2058c2ecf20Sopenharmony_ci break; 2068c2ecf20Sopenharmony_ci 2078c2ecf20Sopenharmony_ci case I2C_SMBUS_BYTE: 2088c2ecf20Sopenharmony_ci if (read_write == I2C_SMBUS_WRITE) { 2098c2ecf20Sopenharmony_ci status = amd_ec_write(smbus, AMD_SMB_CMD, 2108c2ecf20Sopenharmony_ci command); 2118c2ecf20Sopenharmony_ci if (status) 2128c2ecf20Sopenharmony_ci return status; 2138c2ecf20Sopenharmony_ci } 2148c2ecf20Sopenharmony_ci protocol |= AMD_SMB_PRTCL_BYTE; 2158c2ecf20Sopenharmony_ci break; 2168c2ecf20Sopenharmony_ci 2178c2ecf20Sopenharmony_ci case I2C_SMBUS_BYTE_DATA: 2188c2ecf20Sopenharmony_ci status = amd_ec_write(smbus, AMD_SMB_CMD, command); 2198c2ecf20Sopenharmony_ci if (status) 2208c2ecf20Sopenharmony_ci return status; 2218c2ecf20Sopenharmony_ci if (read_write == I2C_SMBUS_WRITE) { 2228c2ecf20Sopenharmony_ci status = amd_ec_write(smbus, AMD_SMB_DATA, 2238c2ecf20Sopenharmony_ci data->byte); 2248c2ecf20Sopenharmony_ci if (status) 2258c2ecf20Sopenharmony_ci return status; 2268c2ecf20Sopenharmony_ci } 2278c2ecf20Sopenharmony_ci protocol |= AMD_SMB_PRTCL_BYTE_DATA; 2288c2ecf20Sopenharmony_ci break; 2298c2ecf20Sopenharmony_ci 2308c2ecf20Sopenharmony_ci case I2C_SMBUS_WORD_DATA: 2318c2ecf20Sopenharmony_ci status = amd_ec_write(smbus, AMD_SMB_CMD, command); 2328c2ecf20Sopenharmony_ci if (status) 2338c2ecf20Sopenharmony_ci return status; 2348c2ecf20Sopenharmony_ci if (read_write == I2C_SMBUS_WRITE) { 2358c2ecf20Sopenharmony_ci status = amd_ec_write(smbus, AMD_SMB_DATA, 2368c2ecf20Sopenharmony_ci data->word & 0xff); 2378c2ecf20Sopenharmony_ci if (status) 2388c2ecf20Sopenharmony_ci return status; 2398c2ecf20Sopenharmony_ci status = amd_ec_write(smbus, AMD_SMB_DATA + 1, 2408c2ecf20Sopenharmony_ci data->word >> 8); 2418c2ecf20Sopenharmony_ci if (status) 2428c2ecf20Sopenharmony_ci return status; 2438c2ecf20Sopenharmony_ci } 2448c2ecf20Sopenharmony_ci protocol |= AMD_SMB_PRTCL_WORD_DATA | pec; 2458c2ecf20Sopenharmony_ci break; 2468c2ecf20Sopenharmony_ci 2478c2ecf20Sopenharmony_ci case I2C_SMBUS_BLOCK_DATA: 2488c2ecf20Sopenharmony_ci status = amd_ec_write(smbus, AMD_SMB_CMD, command); 2498c2ecf20Sopenharmony_ci if (status) 2508c2ecf20Sopenharmony_ci return status; 2518c2ecf20Sopenharmony_ci if (read_write == I2C_SMBUS_WRITE) { 2528c2ecf20Sopenharmony_ci len = min_t(u8, data->block[0], 2538c2ecf20Sopenharmony_ci I2C_SMBUS_BLOCK_MAX); 2548c2ecf20Sopenharmony_ci status = amd_ec_write(smbus, AMD_SMB_BCNT, len); 2558c2ecf20Sopenharmony_ci if (status) 2568c2ecf20Sopenharmony_ci return status; 2578c2ecf20Sopenharmony_ci for (i = 0; i < len; i++) { 2588c2ecf20Sopenharmony_ci status = 2598c2ecf20Sopenharmony_ci amd_ec_write(smbus, AMD_SMB_DATA + i, 2608c2ecf20Sopenharmony_ci data->block[i + 1]); 2618c2ecf20Sopenharmony_ci if (status) 2628c2ecf20Sopenharmony_ci return status; 2638c2ecf20Sopenharmony_ci } 2648c2ecf20Sopenharmony_ci } 2658c2ecf20Sopenharmony_ci protocol |= AMD_SMB_PRTCL_BLOCK_DATA | pec; 2668c2ecf20Sopenharmony_ci break; 2678c2ecf20Sopenharmony_ci 2688c2ecf20Sopenharmony_ci case I2C_SMBUS_I2C_BLOCK_DATA: 2698c2ecf20Sopenharmony_ci len = min_t(u8, data->block[0], 2708c2ecf20Sopenharmony_ci I2C_SMBUS_BLOCK_MAX); 2718c2ecf20Sopenharmony_ci status = amd_ec_write(smbus, AMD_SMB_CMD, command); 2728c2ecf20Sopenharmony_ci if (status) 2738c2ecf20Sopenharmony_ci return status; 2748c2ecf20Sopenharmony_ci status = amd_ec_write(smbus, AMD_SMB_BCNT, len); 2758c2ecf20Sopenharmony_ci if (status) 2768c2ecf20Sopenharmony_ci return status; 2778c2ecf20Sopenharmony_ci if (read_write == I2C_SMBUS_WRITE) 2788c2ecf20Sopenharmony_ci for (i = 0; i < len; i++) { 2798c2ecf20Sopenharmony_ci status = 2808c2ecf20Sopenharmony_ci amd_ec_write(smbus, AMD_SMB_DATA + i, 2818c2ecf20Sopenharmony_ci data->block[i + 1]); 2828c2ecf20Sopenharmony_ci if (status) 2838c2ecf20Sopenharmony_ci return status; 2848c2ecf20Sopenharmony_ci } 2858c2ecf20Sopenharmony_ci protocol |= AMD_SMB_PRTCL_I2C_BLOCK_DATA; 2868c2ecf20Sopenharmony_ci break; 2878c2ecf20Sopenharmony_ci 2888c2ecf20Sopenharmony_ci case I2C_SMBUS_PROC_CALL: 2898c2ecf20Sopenharmony_ci status = amd_ec_write(smbus, AMD_SMB_CMD, command); 2908c2ecf20Sopenharmony_ci if (status) 2918c2ecf20Sopenharmony_ci return status; 2928c2ecf20Sopenharmony_ci status = amd_ec_write(smbus, AMD_SMB_DATA, 2938c2ecf20Sopenharmony_ci data->word & 0xff); 2948c2ecf20Sopenharmony_ci if (status) 2958c2ecf20Sopenharmony_ci return status; 2968c2ecf20Sopenharmony_ci status = amd_ec_write(smbus, AMD_SMB_DATA + 1, 2978c2ecf20Sopenharmony_ci data->word >> 8); 2988c2ecf20Sopenharmony_ci if (status) 2998c2ecf20Sopenharmony_ci return status; 3008c2ecf20Sopenharmony_ci protocol = AMD_SMB_PRTCL_PROC_CALL | pec; 3018c2ecf20Sopenharmony_ci read_write = I2C_SMBUS_READ; 3028c2ecf20Sopenharmony_ci break; 3038c2ecf20Sopenharmony_ci 3048c2ecf20Sopenharmony_ci case I2C_SMBUS_BLOCK_PROC_CALL: 3058c2ecf20Sopenharmony_ci len = min_t(u8, data->block[0], 3068c2ecf20Sopenharmony_ci I2C_SMBUS_BLOCK_MAX - 1); 3078c2ecf20Sopenharmony_ci status = amd_ec_write(smbus, AMD_SMB_CMD, command); 3088c2ecf20Sopenharmony_ci if (status) 3098c2ecf20Sopenharmony_ci return status; 3108c2ecf20Sopenharmony_ci status = amd_ec_write(smbus, AMD_SMB_BCNT, len); 3118c2ecf20Sopenharmony_ci if (status) 3128c2ecf20Sopenharmony_ci return status; 3138c2ecf20Sopenharmony_ci for (i = 0; i < len; i++) { 3148c2ecf20Sopenharmony_ci status = amd_ec_write(smbus, AMD_SMB_DATA + i, 3158c2ecf20Sopenharmony_ci data->block[i + 1]); 3168c2ecf20Sopenharmony_ci if (status) 3178c2ecf20Sopenharmony_ci return status; 3188c2ecf20Sopenharmony_ci } 3198c2ecf20Sopenharmony_ci protocol = AMD_SMB_PRTCL_BLOCK_PROC_CALL | pec; 3208c2ecf20Sopenharmony_ci read_write = I2C_SMBUS_READ; 3218c2ecf20Sopenharmony_ci break; 3228c2ecf20Sopenharmony_ci 3238c2ecf20Sopenharmony_ci default: 3248c2ecf20Sopenharmony_ci dev_warn(&adap->dev, "Unsupported transaction %d\n", size); 3258c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 3268c2ecf20Sopenharmony_ci } 3278c2ecf20Sopenharmony_ci 3288c2ecf20Sopenharmony_ci status = amd_ec_write(smbus, AMD_SMB_ADDR, addr << 1); 3298c2ecf20Sopenharmony_ci if (status) 3308c2ecf20Sopenharmony_ci return status; 3318c2ecf20Sopenharmony_ci status = amd_ec_write(smbus, AMD_SMB_PRTCL, protocol); 3328c2ecf20Sopenharmony_ci if (status) 3338c2ecf20Sopenharmony_ci return status; 3348c2ecf20Sopenharmony_ci 3358c2ecf20Sopenharmony_ci status = amd_ec_read(smbus, AMD_SMB_STS, temp + 0); 3368c2ecf20Sopenharmony_ci if (status) 3378c2ecf20Sopenharmony_ci return status; 3388c2ecf20Sopenharmony_ci 3398c2ecf20Sopenharmony_ci if (~temp[0] & AMD_SMB_STS_DONE) { 3408c2ecf20Sopenharmony_ci udelay(500); 3418c2ecf20Sopenharmony_ci status = amd_ec_read(smbus, AMD_SMB_STS, temp + 0); 3428c2ecf20Sopenharmony_ci if (status) 3438c2ecf20Sopenharmony_ci return status; 3448c2ecf20Sopenharmony_ci } 3458c2ecf20Sopenharmony_ci 3468c2ecf20Sopenharmony_ci if (~temp[0] & AMD_SMB_STS_DONE) { 3478c2ecf20Sopenharmony_ci msleep(1); 3488c2ecf20Sopenharmony_ci status = amd_ec_read(smbus, AMD_SMB_STS, temp + 0); 3498c2ecf20Sopenharmony_ci if (status) 3508c2ecf20Sopenharmony_ci return status; 3518c2ecf20Sopenharmony_ci } 3528c2ecf20Sopenharmony_ci 3538c2ecf20Sopenharmony_ci if ((~temp[0] & AMD_SMB_STS_DONE) || (temp[0] & AMD_SMB_STS_STATUS)) 3548c2ecf20Sopenharmony_ci return -EIO; 3558c2ecf20Sopenharmony_ci 3568c2ecf20Sopenharmony_ci if (read_write == I2C_SMBUS_WRITE) 3578c2ecf20Sopenharmony_ci return 0; 3588c2ecf20Sopenharmony_ci 3598c2ecf20Sopenharmony_ci switch (size) { 3608c2ecf20Sopenharmony_ci case I2C_SMBUS_BYTE: 3618c2ecf20Sopenharmony_ci case I2C_SMBUS_BYTE_DATA: 3628c2ecf20Sopenharmony_ci status = amd_ec_read(smbus, AMD_SMB_DATA, &data->byte); 3638c2ecf20Sopenharmony_ci if (status) 3648c2ecf20Sopenharmony_ci return status; 3658c2ecf20Sopenharmony_ci break; 3668c2ecf20Sopenharmony_ci 3678c2ecf20Sopenharmony_ci case I2C_SMBUS_WORD_DATA: 3688c2ecf20Sopenharmony_ci case I2C_SMBUS_PROC_CALL: 3698c2ecf20Sopenharmony_ci status = amd_ec_read(smbus, AMD_SMB_DATA, temp + 0); 3708c2ecf20Sopenharmony_ci if (status) 3718c2ecf20Sopenharmony_ci return status; 3728c2ecf20Sopenharmony_ci status = amd_ec_read(smbus, AMD_SMB_DATA + 1, temp + 1); 3738c2ecf20Sopenharmony_ci if (status) 3748c2ecf20Sopenharmony_ci return status; 3758c2ecf20Sopenharmony_ci data->word = (temp[1] << 8) | temp[0]; 3768c2ecf20Sopenharmony_ci break; 3778c2ecf20Sopenharmony_ci 3788c2ecf20Sopenharmony_ci case I2C_SMBUS_BLOCK_DATA: 3798c2ecf20Sopenharmony_ci case I2C_SMBUS_BLOCK_PROC_CALL: 3808c2ecf20Sopenharmony_ci status = amd_ec_read(smbus, AMD_SMB_BCNT, &len); 3818c2ecf20Sopenharmony_ci if (status) 3828c2ecf20Sopenharmony_ci return status; 3838c2ecf20Sopenharmony_ci len = min_t(u8, len, I2C_SMBUS_BLOCK_MAX); 3848c2ecf20Sopenharmony_ci fallthrough; 3858c2ecf20Sopenharmony_ci case I2C_SMBUS_I2C_BLOCK_DATA: 3868c2ecf20Sopenharmony_ci for (i = 0; i < len; i++) { 3878c2ecf20Sopenharmony_ci status = amd_ec_read(smbus, AMD_SMB_DATA + i, 3888c2ecf20Sopenharmony_ci data->block + i + 1); 3898c2ecf20Sopenharmony_ci if (status) 3908c2ecf20Sopenharmony_ci return status; 3918c2ecf20Sopenharmony_ci } 3928c2ecf20Sopenharmony_ci data->block[0] = len; 3938c2ecf20Sopenharmony_ci break; 3948c2ecf20Sopenharmony_ci } 3958c2ecf20Sopenharmony_ci 3968c2ecf20Sopenharmony_ci return 0; 3978c2ecf20Sopenharmony_ci} 3988c2ecf20Sopenharmony_ci 3998c2ecf20Sopenharmony_ci 4008c2ecf20Sopenharmony_cistatic u32 amd8111_func(struct i2c_adapter *adapter) 4018c2ecf20Sopenharmony_ci{ 4028c2ecf20Sopenharmony_ci return I2C_FUNC_SMBUS_QUICK | I2C_FUNC_SMBUS_BYTE | 4038c2ecf20Sopenharmony_ci I2C_FUNC_SMBUS_BYTE_DATA | 4048c2ecf20Sopenharmony_ci I2C_FUNC_SMBUS_WORD_DATA | I2C_FUNC_SMBUS_BLOCK_DATA | 4058c2ecf20Sopenharmony_ci I2C_FUNC_SMBUS_PROC_CALL | I2C_FUNC_SMBUS_BLOCK_PROC_CALL | 4068c2ecf20Sopenharmony_ci I2C_FUNC_SMBUS_I2C_BLOCK | I2C_FUNC_SMBUS_PEC; 4078c2ecf20Sopenharmony_ci} 4088c2ecf20Sopenharmony_ci 4098c2ecf20Sopenharmony_cistatic const struct i2c_algorithm smbus_algorithm = { 4108c2ecf20Sopenharmony_ci .smbus_xfer = amd8111_access, 4118c2ecf20Sopenharmony_ci .functionality = amd8111_func, 4128c2ecf20Sopenharmony_ci}; 4138c2ecf20Sopenharmony_ci 4148c2ecf20Sopenharmony_ci 4158c2ecf20Sopenharmony_cistatic const struct pci_device_id amd8111_ids[] = { 4168c2ecf20Sopenharmony_ci { PCI_DEVICE(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_8111_SMBUS2) }, 4178c2ecf20Sopenharmony_ci { 0, } 4188c2ecf20Sopenharmony_ci}; 4198c2ecf20Sopenharmony_ci 4208c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE (pci, amd8111_ids); 4218c2ecf20Sopenharmony_ci 4228c2ecf20Sopenharmony_cistatic int amd8111_probe(struct pci_dev *dev, const struct pci_device_id *id) 4238c2ecf20Sopenharmony_ci{ 4248c2ecf20Sopenharmony_ci struct amd_smbus *smbus; 4258c2ecf20Sopenharmony_ci int error; 4268c2ecf20Sopenharmony_ci 4278c2ecf20Sopenharmony_ci if (!(pci_resource_flags(dev, 0) & IORESOURCE_IO)) 4288c2ecf20Sopenharmony_ci return -ENODEV; 4298c2ecf20Sopenharmony_ci 4308c2ecf20Sopenharmony_ci smbus = kzalloc(sizeof(struct amd_smbus), GFP_KERNEL); 4318c2ecf20Sopenharmony_ci if (!smbus) 4328c2ecf20Sopenharmony_ci return -ENOMEM; 4338c2ecf20Sopenharmony_ci 4348c2ecf20Sopenharmony_ci smbus->dev = dev; 4358c2ecf20Sopenharmony_ci smbus->base = pci_resource_start(dev, 0); 4368c2ecf20Sopenharmony_ci smbus->size = pci_resource_len(dev, 0); 4378c2ecf20Sopenharmony_ci 4388c2ecf20Sopenharmony_ci error = acpi_check_resource_conflict(&dev->resource[0]); 4398c2ecf20Sopenharmony_ci if (error) { 4408c2ecf20Sopenharmony_ci error = -ENODEV; 4418c2ecf20Sopenharmony_ci goto out_kfree; 4428c2ecf20Sopenharmony_ci } 4438c2ecf20Sopenharmony_ci 4448c2ecf20Sopenharmony_ci if (!request_region(smbus->base, smbus->size, amd8111_driver.name)) { 4458c2ecf20Sopenharmony_ci error = -EBUSY; 4468c2ecf20Sopenharmony_ci goto out_kfree; 4478c2ecf20Sopenharmony_ci } 4488c2ecf20Sopenharmony_ci 4498c2ecf20Sopenharmony_ci smbus->adapter.owner = THIS_MODULE; 4508c2ecf20Sopenharmony_ci snprintf(smbus->adapter.name, sizeof(smbus->adapter.name), 4518c2ecf20Sopenharmony_ci "SMBus2 AMD8111 adapter at %04x", smbus->base); 4528c2ecf20Sopenharmony_ci smbus->adapter.class = I2C_CLASS_HWMON | I2C_CLASS_SPD; 4538c2ecf20Sopenharmony_ci smbus->adapter.algo = &smbus_algorithm; 4548c2ecf20Sopenharmony_ci smbus->adapter.algo_data = smbus; 4558c2ecf20Sopenharmony_ci 4568c2ecf20Sopenharmony_ci /* set up the sysfs linkage to our parent device */ 4578c2ecf20Sopenharmony_ci smbus->adapter.dev.parent = &dev->dev; 4588c2ecf20Sopenharmony_ci 4598c2ecf20Sopenharmony_ci pci_write_config_dword(smbus->dev, AMD_PCI_MISC, 0); 4608c2ecf20Sopenharmony_ci error = i2c_add_adapter(&smbus->adapter); 4618c2ecf20Sopenharmony_ci if (error) 4628c2ecf20Sopenharmony_ci goto out_release_region; 4638c2ecf20Sopenharmony_ci 4648c2ecf20Sopenharmony_ci pci_set_drvdata(dev, smbus); 4658c2ecf20Sopenharmony_ci return 0; 4668c2ecf20Sopenharmony_ci 4678c2ecf20Sopenharmony_ci out_release_region: 4688c2ecf20Sopenharmony_ci release_region(smbus->base, smbus->size); 4698c2ecf20Sopenharmony_ci out_kfree: 4708c2ecf20Sopenharmony_ci kfree(smbus); 4718c2ecf20Sopenharmony_ci return error; 4728c2ecf20Sopenharmony_ci} 4738c2ecf20Sopenharmony_ci 4748c2ecf20Sopenharmony_cistatic void amd8111_remove(struct pci_dev *dev) 4758c2ecf20Sopenharmony_ci{ 4768c2ecf20Sopenharmony_ci struct amd_smbus *smbus = pci_get_drvdata(dev); 4778c2ecf20Sopenharmony_ci 4788c2ecf20Sopenharmony_ci i2c_del_adapter(&smbus->adapter); 4798c2ecf20Sopenharmony_ci release_region(smbus->base, smbus->size); 4808c2ecf20Sopenharmony_ci kfree(smbus); 4818c2ecf20Sopenharmony_ci} 4828c2ecf20Sopenharmony_ci 4838c2ecf20Sopenharmony_cistatic struct pci_driver amd8111_driver = { 4848c2ecf20Sopenharmony_ci .name = "amd8111_smbus2", 4858c2ecf20Sopenharmony_ci .id_table = amd8111_ids, 4868c2ecf20Sopenharmony_ci .probe = amd8111_probe, 4878c2ecf20Sopenharmony_ci .remove = amd8111_remove, 4888c2ecf20Sopenharmony_ci}; 4898c2ecf20Sopenharmony_ci 4908c2ecf20Sopenharmony_cimodule_pci_driver(amd8111_driver); 491