18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci Copyright (c) 1998 - 2002 Frodo Looijaard <frodol@dds.nl>, 48c2ecf20Sopenharmony_ci Philip Edelbrock <phil@netroedge.com>, Kyösti Mälkki <kmalkki@cc.hut.fi>, 58c2ecf20Sopenharmony_ci Mark D. Studebaker <mdsxyz123@yahoo.com> 68c2ecf20Sopenharmony_ci Copyright (C) 2005 - 2008 Jean Delvare <jdelvare@suse.de> 78c2ecf20Sopenharmony_ci 88c2ecf20Sopenharmony_ci*/ 98c2ecf20Sopenharmony_ci 108c2ecf20Sopenharmony_ci/* 118c2ecf20Sopenharmony_ci Supports the following VIA south bridges: 128c2ecf20Sopenharmony_ci 138c2ecf20Sopenharmony_ci Chip name PCI ID REV I2C block 148c2ecf20Sopenharmony_ci VT82C596A 0x3050 no 158c2ecf20Sopenharmony_ci VT82C596B 0x3051 no 168c2ecf20Sopenharmony_ci VT82C686A 0x3057 0x30 no 178c2ecf20Sopenharmony_ci VT82C686B 0x3057 0x40 yes 188c2ecf20Sopenharmony_ci VT8231 0x8235 no? 198c2ecf20Sopenharmony_ci VT8233 0x3074 yes 208c2ecf20Sopenharmony_ci VT8233A 0x3147 yes? 218c2ecf20Sopenharmony_ci VT8235 0x3177 yes 228c2ecf20Sopenharmony_ci VT8237R 0x3227 yes 238c2ecf20Sopenharmony_ci VT8237A 0x3337 yes 248c2ecf20Sopenharmony_ci VT8237S 0x3372 yes 258c2ecf20Sopenharmony_ci VT8251 0x3287 yes 268c2ecf20Sopenharmony_ci CX700 0x8324 yes 278c2ecf20Sopenharmony_ci VX800/VX820 0x8353 yes 288c2ecf20Sopenharmony_ci VX855/VX875 0x8409 yes 298c2ecf20Sopenharmony_ci 308c2ecf20Sopenharmony_ci Note: we assume there can only be one device, with one SMBus interface. 318c2ecf20Sopenharmony_ci*/ 328c2ecf20Sopenharmony_ci 338c2ecf20Sopenharmony_ci#include <linux/module.h> 348c2ecf20Sopenharmony_ci#include <linux/delay.h> 358c2ecf20Sopenharmony_ci#include <linux/pci.h> 368c2ecf20Sopenharmony_ci#include <linux/kernel.h> 378c2ecf20Sopenharmony_ci#include <linux/stddef.h> 388c2ecf20Sopenharmony_ci#include <linux/ioport.h> 398c2ecf20Sopenharmony_ci#include <linux/i2c.h> 408c2ecf20Sopenharmony_ci#include <linux/init.h> 418c2ecf20Sopenharmony_ci#include <linux/acpi.h> 428c2ecf20Sopenharmony_ci#include <linux/io.h> 438c2ecf20Sopenharmony_ci 448c2ecf20Sopenharmony_cistatic struct pci_dev *vt596_pdev; 458c2ecf20Sopenharmony_ci 468c2ecf20Sopenharmony_ci#define SMBBA1 0x90 478c2ecf20Sopenharmony_ci#define SMBBA2 0x80 488c2ecf20Sopenharmony_ci#define SMBBA3 0xD0 498c2ecf20Sopenharmony_ci 508c2ecf20Sopenharmony_ci/* SMBus address offsets */ 518c2ecf20Sopenharmony_cistatic unsigned short vt596_smba; 528c2ecf20Sopenharmony_ci#define SMBHSTSTS (vt596_smba + 0) 538c2ecf20Sopenharmony_ci#define SMBHSTCNT (vt596_smba + 2) 548c2ecf20Sopenharmony_ci#define SMBHSTCMD (vt596_smba + 3) 558c2ecf20Sopenharmony_ci#define SMBHSTADD (vt596_smba + 4) 568c2ecf20Sopenharmony_ci#define SMBHSTDAT0 (vt596_smba + 5) 578c2ecf20Sopenharmony_ci#define SMBHSTDAT1 (vt596_smba + 6) 588c2ecf20Sopenharmony_ci#define SMBBLKDAT (vt596_smba + 7) 598c2ecf20Sopenharmony_ci 608c2ecf20Sopenharmony_ci/* PCI Address Constants */ 618c2ecf20Sopenharmony_ci 628c2ecf20Sopenharmony_ci/* SMBus data in configuration space can be found in two places, 638c2ecf20Sopenharmony_ci We try to select the better one */ 648c2ecf20Sopenharmony_ci 658c2ecf20Sopenharmony_cistatic unsigned short SMBHSTCFG = 0xD2; 668c2ecf20Sopenharmony_ci 678c2ecf20Sopenharmony_ci/* Other settings */ 688c2ecf20Sopenharmony_ci#define MAX_TIMEOUT 500 698c2ecf20Sopenharmony_ci 708c2ecf20Sopenharmony_ci/* VT82C596 constants */ 718c2ecf20Sopenharmony_ci#define VT596_QUICK 0x00 728c2ecf20Sopenharmony_ci#define VT596_BYTE 0x04 738c2ecf20Sopenharmony_ci#define VT596_BYTE_DATA 0x08 748c2ecf20Sopenharmony_ci#define VT596_WORD_DATA 0x0C 758c2ecf20Sopenharmony_ci#define VT596_PROC_CALL 0x10 768c2ecf20Sopenharmony_ci#define VT596_BLOCK_DATA 0x14 778c2ecf20Sopenharmony_ci#define VT596_I2C_BLOCK_DATA 0x34 788c2ecf20Sopenharmony_ci 798c2ecf20Sopenharmony_ci 808c2ecf20Sopenharmony_ci/* If force is set to anything different from 0, we forcibly enable the 818c2ecf20Sopenharmony_ci VT596. DANGEROUS! */ 828c2ecf20Sopenharmony_cistatic bool force; 838c2ecf20Sopenharmony_cimodule_param(force, bool, 0); 848c2ecf20Sopenharmony_ciMODULE_PARM_DESC(force, "Forcibly enable the SMBus. DANGEROUS!"); 858c2ecf20Sopenharmony_ci 868c2ecf20Sopenharmony_ci/* If force_addr is set to anything different from 0, we forcibly enable 878c2ecf20Sopenharmony_ci the VT596 at the given address. VERY DANGEROUS! */ 888c2ecf20Sopenharmony_cistatic u16 force_addr; 898c2ecf20Sopenharmony_cimodule_param_hw(force_addr, ushort, ioport, 0); 908c2ecf20Sopenharmony_ciMODULE_PARM_DESC(force_addr, 918c2ecf20Sopenharmony_ci "Forcibly enable the SMBus at the given address. " 928c2ecf20Sopenharmony_ci "EXTREMELY DANGEROUS!"); 938c2ecf20Sopenharmony_ci 948c2ecf20Sopenharmony_ci 958c2ecf20Sopenharmony_cistatic struct pci_driver vt596_driver; 968c2ecf20Sopenharmony_cistatic struct i2c_adapter vt596_adapter; 978c2ecf20Sopenharmony_ci 988c2ecf20Sopenharmony_ci#define FEATURE_I2CBLOCK (1<<0) 998c2ecf20Sopenharmony_cistatic unsigned int vt596_features; 1008c2ecf20Sopenharmony_ci 1018c2ecf20Sopenharmony_ci#ifdef DEBUG 1028c2ecf20Sopenharmony_cistatic void vt596_dump_regs(const char *msg, u8 size) 1038c2ecf20Sopenharmony_ci{ 1048c2ecf20Sopenharmony_ci dev_dbg(&vt596_adapter.dev, "%s: STS=%02x CNT=%02x CMD=%02x ADD=%02x " 1058c2ecf20Sopenharmony_ci "DAT=%02x,%02x\n", msg, inb_p(SMBHSTSTS), inb_p(SMBHSTCNT), 1068c2ecf20Sopenharmony_ci inb_p(SMBHSTCMD), inb_p(SMBHSTADD), inb_p(SMBHSTDAT0), 1078c2ecf20Sopenharmony_ci inb_p(SMBHSTDAT1)); 1088c2ecf20Sopenharmony_ci 1098c2ecf20Sopenharmony_ci if (size == VT596_BLOCK_DATA 1108c2ecf20Sopenharmony_ci || size == VT596_I2C_BLOCK_DATA) { 1118c2ecf20Sopenharmony_ci int i; 1128c2ecf20Sopenharmony_ci 1138c2ecf20Sopenharmony_ci dev_dbg(&vt596_adapter.dev, "BLK="); 1148c2ecf20Sopenharmony_ci for (i = 0; i < I2C_SMBUS_BLOCK_MAX / 2; i++) 1158c2ecf20Sopenharmony_ci printk("%02x,", inb_p(SMBBLKDAT)); 1168c2ecf20Sopenharmony_ci printk("\n"); 1178c2ecf20Sopenharmony_ci dev_dbg(&vt596_adapter.dev, " "); 1188c2ecf20Sopenharmony_ci for (; i < I2C_SMBUS_BLOCK_MAX - 1; i++) 1198c2ecf20Sopenharmony_ci printk("%02x,", inb_p(SMBBLKDAT)); 1208c2ecf20Sopenharmony_ci printk("%02x\n", inb_p(SMBBLKDAT)); 1218c2ecf20Sopenharmony_ci } 1228c2ecf20Sopenharmony_ci} 1238c2ecf20Sopenharmony_ci#else 1248c2ecf20Sopenharmony_cistatic inline void vt596_dump_regs(const char *msg, u8 size) { } 1258c2ecf20Sopenharmony_ci#endif 1268c2ecf20Sopenharmony_ci 1278c2ecf20Sopenharmony_ci/* Return -1 on error, 0 on success */ 1288c2ecf20Sopenharmony_cistatic int vt596_transaction(u8 size) 1298c2ecf20Sopenharmony_ci{ 1308c2ecf20Sopenharmony_ci int temp; 1318c2ecf20Sopenharmony_ci int result = 0; 1328c2ecf20Sopenharmony_ci int timeout = 0; 1338c2ecf20Sopenharmony_ci 1348c2ecf20Sopenharmony_ci vt596_dump_regs("Transaction (pre)", size); 1358c2ecf20Sopenharmony_ci 1368c2ecf20Sopenharmony_ci /* Make sure the SMBus host is ready to start transmitting */ 1378c2ecf20Sopenharmony_ci if ((temp = inb_p(SMBHSTSTS)) & 0x1F) { 1388c2ecf20Sopenharmony_ci dev_dbg(&vt596_adapter.dev, "SMBus busy (0x%02x). " 1398c2ecf20Sopenharmony_ci "Resetting...\n", temp); 1408c2ecf20Sopenharmony_ci 1418c2ecf20Sopenharmony_ci outb_p(temp, SMBHSTSTS); 1428c2ecf20Sopenharmony_ci if ((temp = inb_p(SMBHSTSTS)) & 0x1F) { 1438c2ecf20Sopenharmony_ci dev_err(&vt596_adapter.dev, "SMBus reset failed! " 1448c2ecf20Sopenharmony_ci "(0x%02x)\n", temp); 1458c2ecf20Sopenharmony_ci return -EBUSY; 1468c2ecf20Sopenharmony_ci } 1478c2ecf20Sopenharmony_ci } 1488c2ecf20Sopenharmony_ci 1498c2ecf20Sopenharmony_ci /* Start the transaction by setting bit 6 */ 1508c2ecf20Sopenharmony_ci outb_p(0x40 | size, SMBHSTCNT); 1518c2ecf20Sopenharmony_ci 1528c2ecf20Sopenharmony_ci /* We will always wait for a fraction of a second */ 1538c2ecf20Sopenharmony_ci do { 1548c2ecf20Sopenharmony_ci msleep(1); 1558c2ecf20Sopenharmony_ci temp = inb_p(SMBHSTSTS); 1568c2ecf20Sopenharmony_ci } while ((temp & 0x01) && (++timeout < MAX_TIMEOUT)); 1578c2ecf20Sopenharmony_ci 1588c2ecf20Sopenharmony_ci /* If the SMBus is still busy, we give up */ 1598c2ecf20Sopenharmony_ci if (timeout == MAX_TIMEOUT) { 1608c2ecf20Sopenharmony_ci result = -ETIMEDOUT; 1618c2ecf20Sopenharmony_ci dev_err(&vt596_adapter.dev, "SMBus timeout!\n"); 1628c2ecf20Sopenharmony_ci } 1638c2ecf20Sopenharmony_ci 1648c2ecf20Sopenharmony_ci if (temp & 0x10) { 1658c2ecf20Sopenharmony_ci result = -EIO; 1668c2ecf20Sopenharmony_ci dev_err(&vt596_adapter.dev, "Transaction failed (0x%02x)\n", 1678c2ecf20Sopenharmony_ci size); 1688c2ecf20Sopenharmony_ci } 1698c2ecf20Sopenharmony_ci 1708c2ecf20Sopenharmony_ci if (temp & 0x08) { 1718c2ecf20Sopenharmony_ci result = -EIO; 1728c2ecf20Sopenharmony_ci dev_err(&vt596_adapter.dev, "SMBus collision!\n"); 1738c2ecf20Sopenharmony_ci } 1748c2ecf20Sopenharmony_ci 1758c2ecf20Sopenharmony_ci if (temp & 0x04) { 1768c2ecf20Sopenharmony_ci result = -ENXIO; 1778c2ecf20Sopenharmony_ci dev_dbg(&vt596_adapter.dev, "No response\n"); 1788c2ecf20Sopenharmony_ci } 1798c2ecf20Sopenharmony_ci 1808c2ecf20Sopenharmony_ci /* Resetting status register */ 1818c2ecf20Sopenharmony_ci if (temp & 0x1F) 1828c2ecf20Sopenharmony_ci outb_p(temp, SMBHSTSTS); 1838c2ecf20Sopenharmony_ci 1848c2ecf20Sopenharmony_ci vt596_dump_regs("Transaction (post)", size); 1858c2ecf20Sopenharmony_ci 1868c2ecf20Sopenharmony_ci return result; 1878c2ecf20Sopenharmony_ci} 1888c2ecf20Sopenharmony_ci 1898c2ecf20Sopenharmony_ci/* Return negative errno on error, 0 on success */ 1908c2ecf20Sopenharmony_cistatic s32 vt596_access(struct i2c_adapter *adap, u16 addr, 1918c2ecf20Sopenharmony_ci unsigned short flags, char read_write, u8 command, 1928c2ecf20Sopenharmony_ci int size, union i2c_smbus_data *data) 1938c2ecf20Sopenharmony_ci{ 1948c2ecf20Sopenharmony_ci int i; 1958c2ecf20Sopenharmony_ci int status; 1968c2ecf20Sopenharmony_ci 1978c2ecf20Sopenharmony_ci switch (size) { 1988c2ecf20Sopenharmony_ci case I2C_SMBUS_QUICK: 1998c2ecf20Sopenharmony_ci size = VT596_QUICK; 2008c2ecf20Sopenharmony_ci break; 2018c2ecf20Sopenharmony_ci case I2C_SMBUS_BYTE: 2028c2ecf20Sopenharmony_ci if (read_write == I2C_SMBUS_WRITE) 2038c2ecf20Sopenharmony_ci outb_p(command, SMBHSTCMD); 2048c2ecf20Sopenharmony_ci size = VT596_BYTE; 2058c2ecf20Sopenharmony_ci break; 2068c2ecf20Sopenharmony_ci case I2C_SMBUS_BYTE_DATA: 2078c2ecf20Sopenharmony_ci outb_p(command, SMBHSTCMD); 2088c2ecf20Sopenharmony_ci if (read_write == I2C_SMBUS_WRITE) 2098c2ecf20Sopenharmony_ci outb_p(data->byte, SMBHSTDAT0); 2108c2ecf20Sopenharmony_ci size = VT596_BYTE_DATA; 2118c2ecf20Sopenharmony_ci break; 2128c2ecf20Sopenharmony_ci case I2C_SMBUS_WORD_DATA: 2138c2ecf20Sopenharmony_ci outb_p(command, SMBHSTCMD); 2148c2ecf20Sopenharmony_ci if (read_write == I2C_SMBUS_WRITE) { 2158c2ecf20Sopenharmony_ci outb_p(data->word & 0xff, SMBHSTDAT0); 2168c2ecf20Sopenharmony_ci outb_p((data->word & 0xff00) >> 8, SMBHSTDAT1); 2178c2ecf20Sopenharmony_ci } 2188c2ecf20Sopenharmony_ci size = VT596_WORD_DATA; 2198c2ecf20Sopenharmony_ci break; 2208c2ecf20Sopenharmony_ci case I2C_SMBUS_PROC_CALL: 2218c2ecf20Sopenharmony_ci outb_p(command, SMBHSTCMD); 2228c2ecf20Sopenharmony_ci outb_p(data->word & 0xff, SMBHSTDAT0); 2238c2ecf20Sopenharmony_ci outb_p((data->word & 0xff00) >> 8, SMBHSTDAT1); 2248c2ecf20Sopenharmony_ci size = VT596_PROC_CALL; 2258c2ecf20Sopenharmony_ci break; 2268c2ecf20Sopenharmony_ci case I2C_SMBUS_I2C_BLOCK_DATA: 2278c2ecf20Sopenharmony_ci if (!(vt596_features & FEATURE_I2CBLOCK)) 2288c2ecf20Sopenharmony_ci goto exit_unsupported; 2298c2ecf20Sopenharmony_ci if (read_write == I2C_SMBUS_READ) 2308c2ecf20Sopenharmony_ci outb_p(data->block[0], SMBHSTDAT0); 2318c2ecf20Sopenharmony_ci fallthrough; 2328c2ecf20Sopenharmony_ci case I2C_SMBUS_BLOCK_DATA: 2338c2ecf20Sopenharmony_ci outb_p(command, SMBHSTCMD); 2348c2ecf20Sopenharmony_ci if (read_write == I2C_SMBUS_WRITE) { 2358c2ecf20Sopenharmony_ci u8 len = data->block[0]; 2368c2ecf20Sopenharmony_ci if (len > I2C_SMBUS_BLOCK_MAX) 2378c2ecf20Sopenharmony_ci len = I2C_SMBUS_BLOCK_MAX; 2388c2ecf20Sopenharmony_ci outb_p(len, SMBHSTDAT0); 2398c2ecf20Sopenharmony_ci inb_p(SMBHSTCNT); /* Reset SMBBLKDAT */ 2408c2ecf20Sopenharmony_ci for (i = 1; i <= len; i++) 2418c2ecf20Sopenharmony_ci outb_p(data->block[i], SMBBLKDAT); 2428c2ecf20Sopenharmony_ci } 2438c2ecf20Sopenharmony_ci size = (size == I2C_SMBUS_I2C_BLOCK_DATA) ? 2448c2ecf20Sopenharmony_ci VT596_I2C_BLOCK_DATA : VT596_BLOCK_DATA; 2458c2ecf20Sopenharmony_ci break; 2468c2ecf20Sopenharmony_ci default: 2478c2ecf20Sopenharmony_ci goto exit_unsupported; 2488c2ecf20Sopenharmony_ci } 2498c2ecf20Sopenharmony_ci 2508c2ecf20Sopenharmony_ci outb_p(((addr & 0x7f) << 1) | read_write, SMBHSTADD); 2518c2ecf20Sopenharmony_ci 2528c2ecf20Sopenharmony_ci status = vt596_transaction(size); 2538c2ecf20Sopenharmony_ci if (status) 2548c2ecf20Sopenharmony_ci return status; 2558c2ecf20Sopenharmony_ci 2568c2ecf20Sopenharmony_ci if (size == VT596_PROC_CALL) 2578c2ecf20Sopenharmony_ci read_write = I2C_SMBUS_READ; 2588c2ecf20Sopenharmony_ci 2598c2ecf20Sopenharmony_ci if ((read_write == I2C_SMBUS_WRITE) || (size == VT596_QUICK)) 2608c2ecf20Sopenharmony_ci return 0; 2618c2ecf20Sopenharmony_ci 2628c2ecf20Sopenharmony_ci switch (size) { 2638c2ecf20Sopenharmony_ci case VT596_BYTE: 2648c2ecf20Sopenharmony_ci case VT596_BYTE_DATA: 2658c2ecf20Sopenharmony_ci data->byte = inb_p(SMBHSTDAT0); 2668c2ecf20Sopenharmony_ci break; 2678c2ecf20Sopenharmony_ci case VT596_WORD_DATA: 2688c2ecf20Sopenharmony_ci case VT596_PROC_CALL: 2698c2ecf20Sopenharmony_ci data->word = inb_p(SMBHSTDAT0) + (inb_p(SMBHSTDAT1) << 8); 2708c2ecf20Sopenharmony_ci break; 2718c2ecf20Sopenharmony_ci case VT596_I2C_BLOCK_DATA: 2728c2ecf20Sopenharmony_ci case VT596_BLOCK_DATA: 2738c2ecf20Sopenharmony_ci data->block[0] = inb_p(SMBHSTDAT0); 2748c2ecf20Sopenharmony_ci if (data->block[0] > I2C_SMBUS_BLOCK_MAX) 2758c2ecf20Sopenharmony_ci data->block[0] = I2C_SMBUS_BLOCK_MAX; 2768c2ecf20Sopenharmony_ci inb_p(SMBHSTCNT); /* Reset SMBBLKDAT */ 2778c2ecf20Sopenharmony_ci for (i = 1; i <= data->block[0]; i++) 2788c2ecf20Sopenharmony_ci data->block[i] = inb_p(SMBBLKDAT); 2798c2ecf20Sopenharmony_ci break; 2808c2ecf20Sopenharmony_ci } 2818c2ecf20Sopenharmony_ci return 0; 2828c2ecf20Sopenharmony_ci 2838c2ecf20Sopenharmony_ciexit_unsupported: 2848c2ecf20Sopenharmony_ci dev_warn(&vt596_adapter.dev, "Unsupported transaction %d\n", 2858c2ecf20Sopenharmony_ci size); 2868c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 2878c2ecf20Sopenharmony_ci} 2888c2ecf20Sopenharmony_ci 2898c2ecf20Sopenharmony_cistatic u32 vt596_func(struct i2c_adapter *adapter) 2908c2ecf20Sopenharmony_ci{ 2918c2ecf20Sopenharmony_ci u32 func = I2C_FUNC_SMBUS_QUICK | I2C_FUNC_SMBUS_BYTE | 2928c2ecf20Sopenharmony_ci I2C_FUNC_SMBUS_BYTE_DATA | I2C_FUNC_SMBUS_WORD_DATA | 2938c2ecf20Sopenharmony_ci I2C_SMBUS_PROC_CALL | I2C_FUNC_SMBUS_BLOCK_DATA; 2948c2ecf20Sopenharmony_ci 2958c2ecf20Sopenharmony_ci if (vt596_features & FEATURE_I2CBLOCK) 2968c2ecf20Sopenharmony_ci func |= I2C_FUNC_SMBUS_I2C_BLOCK; 2978c2ecf20Sopenharmony_ci return func; 2988c2ecf20Sopenharmony_ci} 2998c2ecf20Sopenharmony_ci 3008c2ecf20Sopenharmony_cistatic const struct i2c_algorithm smbus_algorithm = { 3018c2ecf20Sopenharmony_ci .smbus_xfer = vt596_access, 3028c2ecf20Sopenharmony_ci .functionality = vt596_func, 3038c2ecf20Sopenharmony_ci}; 3048c2ecf20Sopenharmony_ci 3058c2ecf20Sopenharmony_cistatic struct i2c_adapter vt596_adapter = { 3068c2ecf20Sopenharmony_ci .owner = THIS_MODULE, 3078c2ecf20Sopenharmony_ci .class = I2C_CLASS_HWMON | I2C_CLASS_SPD, 3088c2ecf20Sopenharmony_ci .algo = &smbus_algorithm, 3098c2ecf20Sopenharmony_ci}; 3108c2ecf20Sopenharmony_ci 3118c2ecf20Sopenharmony_cistatic int vt596_probe(struct pci_dev *pdev, 3128c2ecf20Sopenharmony_ci const struct pci_device_id *id) 3138c2ecf20Sopenharmony_ci{ 3148c2ecf20Sopenharmony_ci unsigned char temp; 3158c2ecf20Sopenharmony_ci int error; 3168c2ecf20Sopenharmony_ci 3178c2ecf20Sopenharmony_ci /* Determine the address of the SMBus areas */ 3188c2ecf20Sopenharmony_ci if (force_addr) { 3198c2ecf20Sopenharmony_ci vt596_smba = force_addr & 0xfff0; 3208c2ecf20Sopenharmony_ci force = 0; 3218c2ecf20Sopenharmony_ci goto found; 3228c2ecf20Sopenharmony_ci } 3238c2ecf20Sopenharmony_ci 3248c2ecf20Sopenharmony_ci if ((pci_read_config_word(pdev, id->driver_data, &vt596_smba)) || 3258c2ecf20Sopenharmony_ci !(vt596_smba & 0x0001)) { 3268c2ecf20Sopenharmony_ci /* try 2nd address and config reg. for 596 */ 3278c2ecf20Sopenharmony_ci if (id->device == PCI_DEVICE_ID_VIA_82C596_3 && 3288c2ecf20Sopenharmony_ci !pci_read_config_word(pdev, SMBBA2, &vt596_smba) && 3298c2ecf20Sopenharmony_ci (vt596_smba & 0x0001)) { 3308c2ecf20Sopenharmony_ci SMBHSTCFG = 0x84; 3318c2ecf20Sopenharmony_ci } else { 3328c2ecf20Sopenharmony_ci /* no matches at all */ 3338c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "Cannot configure " 3348c2ecf20Sopenharmony_ci "SMBus I/O Base address\n"); 3358c2ecf20Sopenharmony_ci return -ENODEV; 3368c2ecf20Sopenharmony_ci } 3378c2ecf20Sopenharmony_ci } 3388c2ecf20Sopenharmony_ci 3398c2ecf20Sopenharmony_ci vt596_smba &= 0xfff0; 3408c2ecf20Sopenharmony_ci if (vt596_smba == 0) { 3418c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "SMBus base address " 3428c2ecf20Sopenharmony_ci "uninitialized - upgrade BIOS or use " 3438c2ecf20Sopenharmony_ci "force_addr=0xaddr\n"); 3448c2ecf20Sopenharmony_ci return -ENODEV; 3458c2ecf20Sopenharmony_ci } 3468c2ecf20Sopenharmony_ci 3478c2ecf20Sopenharmony_cifound: 3488c2ecf20Sopenharmony_ci error = acpi_check_region(vt596_smba, 8, vt596_driver.name); 3498c2ecf20Sopenharmony_ci if (error) 3508c2ecf20Sopenharmony_ci return -ENODEV; 3518c2ecf20Sopenharmony_ci 3528c2ecf20Sopenharmony_ci if (!request_region(vt596_smba, 8, vt596_driver.name)) { 3538c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "SMBus region 0x%x already in use!\n", 3548c2ecf20Sopenharmony_ci vt596_smba); 3558c2ecf20Sopenharmony_ci return -ENODEV; 3568c2ecf20Sopenharmony_ci } 3578c2ecf20Sopenharmony_ci 3588c2ecf20Sopenharmony_ci pci_read_config_byte(pdev, SMBHSTCFG, &temp); 3598c2ecf20Sopenharmony_ci /* If force_addr is set, we program the new address here. Just to make 3608c2ecf20Sopenharmony_ci sure, we disable the VT596 first. */ 3618c2ecf20Sopenharmony_ci if (force_addr) { 3628c2ecf20Sopenharmony_ci pci_write_config_byte(pdev, SMBHSTCFG, temp & 0xfe); 3638c2ecf20Sopenharmony_ci pci_write_config_word(pdev, id->driver_data, vt596_smba); 3648c2ecf20Sopenharmony_ci pci_write_config_byte(pdev, SMBHSTCFG, temp | 0x01); 3658c2ecf20Sopenharmony_ci dev_warn(&pdev->dev, "WARNING: SMBus interface set to new " 3668c2ecf20Sopenharmony_ci "address 0x%04x!\n", vt596_smba); 3678c2ecf20Sopenharmony_ci } else if (!(temp & 0x01)) { 3688c2ecf20Sopenharmony_ci if (force) { 3698c2ecf20Sopenharmony_ci /* NOTE: This assumes I/O space and other allocations 3708c2ecf20Sopenharmony_ci * WERE done by the Bios! Don't complain if your 3718c2ecf20Sopenharmony_ci * hardware does weird things after enabling this. 3728c2ecf20Sopenharmony_ci * :') Check for Bios updates before resorting to 3738c2ecf20Sopenharmony_ci * this. 3748c2ecf20Sopenharmony_ci */ 3758c2ecf20Sopenharmony_ci pci_write_config_byte(pdev, SMBHSTCFG, temp | 0x01); 3768c2ecf20Sopenharmony_ci dev_info(&pdev->dev, "Enabling SMBus device\n"); 3778c2ecf20Sopenharmony_ci } else { 3788c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "SMBUS: Error: Host SMBus " 3798c2ecf20Sopenharmony_ci "controller not enabled! - upgrade BIOS or " 3808c2ecf20Sopenharmony_ci "use force=1\n"); 3818c2ecf20Sopenharmony_ci error = -ENODEV; 3828c2ecf20Sopenharmony_ci goto release_region; 3838c2ecf20Sopenharmony_ci } 3848c2ecf20Sopenharmony_ci } 3858c2ecf20Sopenharmony_ci 3868c2ecf20Sopenharmony_ci dev_dbg(&pdev->dev, "VT596_smba = 0x%X\n", vt596_smba); 3878c2ecf20Sopenharmony_ci 3888c2ecf20Sopenharmony_ci switch (pdev->device) { 3898c2ecf20Sopenharmony_ci case PCI_DEVICE_ID_VIA_CX700: 3908c2ecf20Sopenharmony_ci case PCI_DEVICE_ID_VIA_VX800: 3918c2ecf20Sopenharmony_ci case PCI_DEVICE_ID_VIA_VX855: 3928c2ecf20Sopenharmony_ci case PCI_DEVICE_ID_VIA_VX900: 3938c2ecf20Sopenharmony_ci case PCI_DEVICE_ID_VIA_8251: 3948c2ecf20Sopenharmony_ci case PCI_DEVICE_ID_VIA_8237: 3958c2ecf20Sopenharmony_ci case PCI_DEVICE_ID_VIA_8237A: 3968c2ecf20Sopenharmony_ci case PCI_DEVICE_ID_VIA_8237S: 3978c2ecf20Sopenharmony_ci case PCI_DEVICE_ID_VIA_8235: 3988c2ecf20Sopenharmony_ci case PCI_DEVICE_ID_VIA_8233A: 3998c2ecf20Sopenharmony_ci case PCI_DEVICE_ID_VIA_8233_0: 4008c2ecf20Sopenharmony_ci vt596_features |= FEATURE_I2CBLOCK; 4018c2ecf20Sopenharmony_ci break; 4028c2ecf20Sopenharmony_ci case PCI_DEVICE_ID_VIA_82C686_4: 4038c2ecf20Sopenharmony_ci /* The VT82C686B (rev 0x40) does support I2C block 4048c2ecf20Sopenharmony_ci transactions, but the VT82C686A (rev 0x30) doesn't */ 4058c2ecf20Sopenharmony_ci if (pdev->revision >= 0x40) 4068c2ecf20Sopenharmony_ci vt596_features |= FEATURE_I2CBLOCK; 4078c2ecf20Sopenharmony_ci break; 4088c2ecf20Sopenharmony_ci } 4098c2ecf20Sopenharmony_ci 4108c2ecf20Sopenharmony_ci vt596_adapter.dev.parent = &pdev->dev; 4118c2ecf20Sopenharmony_ci snprintf(vt596_adapter.name, sizeof(vt596_adapter.name), 4128c2ecf20Sopenharmony_ci "SMBus Via Pro adapter at %04x", vt596_smba); 4138c2ecf20Sopenharmony_ci 4148c2ecf20Sopenharmony_ci vt596_pdev = pci_dev_get(pdev); 4158c2ecf20Sopenharmony_ci error = i2c_add_adapter(&vt596_adapter); 4168c2ecf20Sopenharmony_ci if (error) { 4178c2ecf20Sopenharmony_ci pci_dev_put(vt596_pdev); 4188c2ecf20Sopenharmony_ci vt596_pdev = NULL; 4198c2ecf20Sopenharmony_ci goto release_region; 4208c2ecf20Sopenharmony_ci } 4218c2ecf20Sopenharmony_ci 4228c2ecf20Sopenharmony_ci /* Always return failure here. This is to allow other drivers to bind 4238c2ecf20Sopenharmony_ci * to this pci device. We don't really want to have control over the 4248c2ecf20Sopenharmony_ci * pci device, we only wanted to read as few register values from it. 4258c2ecf20Sopenharmony_ci */ 4268c2ecf20Sopenharmony_ci return -ENODEV; 4278c2ecf20Sopenharmony_ci 4288c2ecf20Sopenharmony_cirelease_region: 4298c2ecf20Sopenharmony_ci release_region(vt596_smba, 8); 4308c2ecf20Sopenharmony_ci return error; 4318c2ecf20Sopenharmony_ci} 4328c2ecf20Sopenharmony_ci 4338c2ecf20Sopenharmony_cistatic const struct pci_device_id vt596_ids[] = { 4348c2ecf20Sopenharmony_ci { PCI_DEVICE(PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_82C596_3), 4358c2ecf20Sopenharmony_ci .driver_data = SMBBA1 }, 4368c2ecf20Sopenharmony_ci { PCI_DEVICE(PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_82C596B_3), 4378c2ecf20Sopenharmony_ci .driver_data = SMBBA1 }, 4388c2ecf20Sopenharmony_ci { PCI_DEVICE(PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_82C686_4), 4398c2ecf20Sopenharmony_ci .driver_data = SMBBA1 }, 4408c2ecf20Sopenharmony_ci { PCI_DEVICE(PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_8233_0), 4418c2ecf20Sopenharmony_ci .driver_data = SMBBA3 }, 4428c2ecf20Sopenharmony_ci { PCI_DEVICE(PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_8233A), 4438c2ecf20Sopenharmony_ci .driver_data = SMBBA3 }, 4448c2ecf20Sopenharmony_ci { PCI_DEVICE(PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_8235), 4458c2ecf20Sopenharmony_ci .driver_data = SMBBA3 }, 4468c2ecf20Sopenharmony_ci { PCI_DEVICE(PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_8237), 4478c2ecf20Sopenharmony_ci .driver_data = SMBBA3 }, 4488c2ecf20Sopenharmony_ci { PCI_DEVICE(PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_8237A), 4498c2ecf20Sopenharmony_ci .driver_data = SMBBA3 }, 4508c2ecf20Sopenharmony_ci { PCI_DEVICE(PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_8237S), 4518c2ecf20Sopenharmony_ci .driver_data = SMBBA3 }, 4528c2ecf20Sopenharmony_ci { PCI_DEVICE(PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_8231_4), 4538c2ecf20Sopenharmony_ci .driver_data = SMBBA1 }, 4548c2ecf20Sopenharmony_ci { PCI_DEVICE(PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_8251), 4558c2ecf20Sopenharmony_ci .driver_data = SMBBA3 }, 4568c2ecf20Sopenharmony_ci { PCI_DEVICE(PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_CX700), 4578c2ecf20Sopenharmony_ci .driver_data = SMBBA3 }, 4588c2ecf20Sopenharmony_ci { PCI_DEVICE(PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_VX800), 4598c2ecf20Sopenharmony_ci .driver_data = SMBBA3 }, 4608c2ecf20Sopenharmony_ci { PCI_DEVICE(PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_VX855), 4618c2ecf20Sopenharmony_ci .driver_data = SMBBA3 }, 4628c2ecf20Sopenharmony_ci { PCI_DEVICE(PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_VX900), 4638c2ecf20Sopenharmony_ci .driver_data = SMBBA3 }, 4648c2ecf20Sopenharmony_ci { 0, } 4658c2ecf20Sopenharmony_ci}; 4668c2ecf20Sopenharmony_ci 4678c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(pci, vt596_ids); 4688c2ecf20Sopenharmony_ci 4698c2ecf20Sopenharmony_cistatic struct pci_driver vt596_driver = { 4708c2ecf20Sopenharmony_ci .name = "vt596_smbus", 4718c2ecf20Sopenharmony_ci .id_table = vt596_ids, 4728c2ecf20Sopenharmony_ci .probe = vt596_probe, 4738c2ecf20Sopenharmony_ci}; 4748c2ecf20Sopenharmony_ci 4758c2ecf20Sopenharmony_cistatic int __init i2c_vt596_init(void) 4768c2ecf20Sopenharmony_ci{ 4778c2ecf20Sopenharmony_ci return pci_register_driver(&vt596_driver); 4788c2ecf20Sopenharmony_ci} 4798c2ecf20Sopenharmony_ci 4808c2ecf20Sopenharmony_ci 4818c2ecf20Sopenharmony_cistatic void __exit i2c_vt596_exit(void) 4828c2ecf20Sopenharmony_ci{ 4838c2ecf20Sopenharmony_ci pci_unregister_driver(&vt596_driver); 4848c2ecf20Sopenharmony_ci if (vt596_pdev != NULL) { 4858c2ecf20Sopenharmony_ci i2c_del_adapter(&vt596_adapter); 4868c2ecf20Sopenharmony_ci release_region(vt596_smba, 8); 4878c2ecf20Sopenharmony_ci pci_dev_put(vt596_pdev); 4888c2ecf20Sopenharmony_ci vt596_pdev = NULL; 4898c2ecf20Sopenharmony_ci } 4908c2ecf20Sopenharmony_ci} 4918c2ecf20Sopenharmony_ci 4928c2ecf20Sopenharmony_ciMODULE_AUTHOR("Kyosti Malkki <kmalkki@cc.hut.fi>"); 4938c2ecf20Sopenharmony_ciMODULE_AUTHOR("Mark D. Studebaker <mdsxyz123@yahoo.com>"); 4948c2ecf20Sopenharmony_ciMODULE_AUTHOR("Jean Delvare <jdelvare@suse.de>"); 4958c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("vt82c596 SMBus driver"); 4968c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 4978c2ecf20Sopenharmony_ci 4988c2ecf20Sopenharmony_cimodule_init(i2c_vt596_init); 4998c2ecf20Sopenharmony_cimodule_exit(i2c_vt596_exit); 500