18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci i2c Support for Via Technologies 82C586B South Bridge 48c2ecf20Sopenharmony_ci 58c2ecf20Sopenharmony_ci Copyright (c) 1998, 1999 Kyösti Mälkki <kmalkki@cc.hut.fi> 68c2ecf20Sopenharmony_ci 78c2ecf20Sopenharmony_ci*/ 88c2ecf20Sopenharmony_ci 98c2ecf20Sopenharmony_ci#include <linux/kernel.h> 108c2ecf20Sopenharmony_ci#include <linux/module.h> 118c2ecf20Sopenharmony_ci#include <linux/pci.h> 128c2ecf20Sopenharmony_ci#include <linux/ioport.h> 138c2ecf20Sopenharmony_ci#include <linux/i2c.h> 148c2ecf20Sopenharmony_ci#include <linux/i2c-algo-bit.h> 158c2ecf20Sopenharmony_ci#include <linux/io.h> 168c2ecf20Sopenharmony_ci 178c2ecf20Sopenharmony_ci/* Power management registers */ 188c2ecf20Sopenharmony_ci#define PM_CFG_REVID 0x08 /* silicon revision code */ 198c2ecf20Sopenharmony_ci#define PM_CFG_IOBASE0 0x20 208c2ecf20Sopenharmony_ci#define PM_CFG_IOBASE1 0x48 218c2ecf20Sopenharmony_ci 228c2ecf20Sopenharmony_ci#define I2C_DIR (pm_io_base+0x40) 238c2ecf20Sopenharmony_ci#define I2C_OUT (pm_io_base+0x42) 248c2ecf20Sopenharmony_ci#define I2C_IN (pm_io_base+0x44) 258c2ecf20Sopenharmony_ci#define I2C_SCL 0x02 /* clock bit in DIR/OUT/IN register */ 268c2ecf20Sopenharmony_ci#define I2C_SDA 0x04 278c2ecf20Sopenharmony_ci 288c2ecf20Sopenharmony_ci/* io-region reservation */ 298c2ecf20Sopenharmony_ci#define IOSPACE 0x06 308c2ecf20Sopenharmony_ci 318c2ecf20Sopenharmony_cistatic struct pci_driver vt586b_driver; 328c2ecf20Sopenharmony_cistatic u16 pm_io_base; 338c2ecf20Sopenharmony_ci 348c2ecf20Sopenharmony_ci/* 358c2ecf20Sopenharmony_ci It does not appear from the datasheet that the GPIO pins are 368c2ecf20Sopenharmony_ci open drain. So a we set a low value by setting the direction to 378c2ecf20Sopenharmony_ci output and a high value by setting the direction to input and 388c2ecf20Sopenharmony_ci relying on the required I2C pullup. The data value is initialized 398c2ecf20Sopenharmony_ci to 0 in via_init() and never changed. 408c2ecf20Sopenharmony_ci*/ 418c2ecf20Sopenharmony_cistatic void bit_via_setscl(void *data, int state) 428c2ecf20Sopenharmony_ci{ 438c2ecf20Sopenharmony_ci outb(state ? inb(I2C_DIR) & ~I2C_SCL : inb(I2C_DIR) | I2C_SCL, I2C_DIR); 448c2ecf20Sopenharmony_ci} 458c2ecf20Sopenharmony_ci 468c2ecf20Sopenharmony_cistatic void bit_via_setsda(void *data, int state) 478c2ecf20Sopenharmony_ci{ 488c2ecf20Sopenharmony_ci outb(state ? inb(I2C_DIR) & ~I2C_SDA : inb(I2C_DIR) | I2C_SDA, I2C_DIR); 498c2ecf20Sopenharmony_ci} 508c2ecf20Sopenharmony_ci 518c2ecf20Sopenharmony_cistatic int bit_via_getscl(void *data) 528c2ecf20Sopenharmony_ci{ 538c2ecf20Sopenharmony_ci return (0 != (inb(I2C_IN) & I2C_SCL)); 548c2ecf20Sopenharmony_ci} 558c2ecf20Sopenharmony_ci 568c2ecf20Sopenharmony_cistatic int bit_via_getsda(void *data) 578c2ecf20Sopenharmony_ci{ 588c2ecf20Sopenharmony_ci return (0 != (inb(I2C_IN) & I2C_SDA)); 598c2ecf20Sopenharmony_ci} 608c2ecf20Sopenharmony_ci 618c2ecf20Sopenharmony_ci 628c2ecf20Sopenharmony_cistatic struct i2c_algo_bit_data bit_data = { 638c2ecf20Sopenharmony_ci .setsda = bit_via_setsda, 648c2ecf20Sopenharmony_ci .setscl = bit_via_setscl, 658c2ecf20Sopenharmony_ci .getsda = bit_via_getsda, 668c2ecf20Sopenharmony_ci .getscl = bit_via_getscl, 678c2ecf20Sopenharmony_ci .udelay = 5, 688c2ecf20Sopenharmony_ci .timeout = HZ 698c2ecf20Sopenharmony_ci}; 708c2ecf20Sopenharmony_ci 718c2ecf20Sopenharmony_cistatic struct i2c_adapter vt586b_adapter = { 728c2ecf20Sopenharmony_ci .owner = THIS_MODULE, 738c2ecf20Sopenharmony_ci .class = I2C_CLASS_HWMON | I2C_CLASS_SPD, 748c2ecf20Sopenharmony_ci .name = "VIA i2c", 758c2ecf20Sopenharmony_ci .algo_data = &bit_data, 768c2ecf20Sopenharmony_ci}; 778c2ecf20Sopenharmony_ci 788c2ecf20Sopenharmony_ci 798c2ecf20Sopenharmony_cistatic const struct pci_device_id vt586b_ids[] = { 808c2ecf20Sopenharmony_ci { PCI_DEVICE(PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_82C586_3) }, 818c2ecf20Sopenharmony_ci { 0, } 828c2ecf20Sopenharmony_ci}; 838c2ecf20Sopenharmony_ci 848c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE (pci, vt586b_ids); 858c2ecf20Sopenharmony_ci 868c2ecf20Sopenharmony_cistatic int vt586b_probe(struct pci_dev *dev, const struct pci_device_id *id) 878c2ecf20Sopenharmony_ci{ 888c2ecf20Sopenharmony_ci u16 base; 898c2ecf20Sopenharmony_ci u8 rev; 908c2ecf20Sopenharmony_ci int res; 918c2ecf20Sopenharmony_ci 928c2ecf20Sopenharmony_ci if (pm_io_base) { 938c2ecf20Sopenharmony_ci dev_err(&dev->dev, "i2c-via: Will only support one host\n"); 948c2ecf20Sopenharmony_ci return -ENODEV; 958c2ecf20Sopenharmony_ci } 968c2ecf20Sopenharmony_ci 978c2ecf20Sopenharmony_ci pci_read_config_byte(dev, PM_CFG_REVID, &rev); 988c2ecf20Sopenharmony_ci 998c2ecf20Sopenharmony_ci switch (rev) { 1008c2ecf20Sopenharmony_ci case 0x00: 1018c2ecf20Sopenharmony_ci base = PM_CFG_IOBASE0; 1028c2ecf20Sopenharmony_ci break; 1038c2ecf20Sopenharmony_ci case 0x01: 1048c2ecf20Sopenharmony_ci case 0x10: 1058c2ecf20Sopenharmony_ci base = PM_CFG_IOBASE1; 1068c2ecf20Sopenharmony_ci break; 1078c2ecf20Sopenharmony_ci 1088c2ecf20Sopenharmony_ci default: 1098c2ecf20Sopenharmony_ci base = PM_CFG_IOBASE1; 1108c2ecf20Sopenharmony_ci /* later revision */ 1118c2ecf20Sopenharmony_ci } 1128c2ecf20Sopenharmony_ci 1138c2ecf20Sopenharmony_ci pci_read_config_word(dev, base, &pm_io_base); 1148c2ecf20Sopenharmony_ci pm_io_base &= (0xff << 8); 1158c2ecf20Sopenharmony_ci 1168c2ecf20Sopenharmony_ci if (!request_region(I2C_DIR, IOSPACE, vt586b_driver.name)) { 1178c2ecf20Sopenharmony_ci dev_err(&dev->dev, "IO 0x%x-0x%x already in use\n", I2C_DIR, I2C_DIR + IOSPACE); 1188c2ecf20Sopenharmony_ci return -ENODEV; 1198c2ecf20Sopenharmony_ci } 1208c2ecf20Sopenharmony_ci 1218c2ecf20Sopenharmony_ci outb(inb(I2C_DIR) & ~(I2C_SDA | I2C_SCL), I2C_DIR); 1228c2ecf20Sopenharmony_ci outb(inb(I2C_OUT) & ~(I2C_SDA | I2C_SCL), I2C_OUT); 1238c2ecf20Sopenharmony_ci 1248c2ecf20Sopenharmony_ci /* set up the sysfs linkage to our parent device */ 1258c2ecf20Sopenharmony_ci vt586b_adapter.dev.parent = &dev->dev; 1268c2ecf20Sopenharmony_ci 1278c2ecf20Sopenharmony_ci res = i2c_bit_add_bus(&vt586b_adapter); 1288c2ecf20Sopenharmony_ci if ( res < 0 ) { 1298c2ecf20Sopenharmony_ci release_region(I2C_DIR, IOSPACE); 1308c2ecf20Sopenharmony_ci pm_io_base = 0; 1318c2ecf20Sopenharmony_ci return res; 1328c2ecf20Sopenharmony_ci } 1338c2ecf20Sopenharmony_ci return 0; 1348c2ecf20Sopenharmony_ci} 1358c2ecf20Sopenharmony_ci 1368c2ecf20Sopenharmony_cistatic void vt586b_remove(struct pci_dev *dev) 1378c2ecf20Sopenharmony_ci{ 1388c2ecf20Sopenharmony_ci i2c_del_adapter(&vt586b_adapter); 1398c2ecf20Sopenharmony_ci release_region(I2C_DIR, IOSPACE); 1408c2ecf20Sopenharmony_ci pm_io_base = 0; 1418c2ecf20Sopenharmony_ci} 1428c2ecf20Sopenharmony_ci 1438c2ecf20Sopenharmony_ci 1448c2ecf20Sopenharmony_cistatic struct pci_driver vt586b_driver = { 1458c2ecf20Sopenharmony_ci .name = "vt586b_smbus", 1468c2ecf20Sopenharmony_ci .id_table = vt586b_ids, 1478c2ecf20Sopenharmony_ci .probe = vt586b_probe, 1488c2ecf20Sopenharmony_ci .remove = vt586b_remove, 1498c2ecf20Sopenharmony_ci}; 1508c2ecf20Sopenharmony_ci 1518c2ecf20Sopenharmony_cimodule_pci_driver(vt586b_driver); 1528c2ecf20Sopenharmony_ci 1538c2ecf20Sopenharmony_ciMODULE_AUTHOR("Kyösti Mälkki <kmalkki@cc.hut.fi>"); 1548c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("i2c for Via vt82c586b southbridge"); 1558c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 156