1/* 2 voodoo3.c - Part of lm_sensors, Linux kernel modules for hardware 3 monitoring 4 Copyright (c) 1998, 1999 Frodo Looijaard <frodol@dds.nl>, 5 Philip Edelbrock <phil@netroedge.com>, 6 Ralph Metzler <rjkm@thp.uni-koeln.de>, and 7 Mark D. Studebaker <mdsxyz123@yahoo.com> 8 9 Based on code written by Ralph Metzler <rjkm@thp.uni-koeln.de> and 10 Simon Vogl 11 12 This program is free software; you can redistribute it and/or modify 13 it under the terms of the GNU General Public License as published by 14 the Free Software Foundation; either version 2 of the License, or 15 (at your option) any later version. 16 17 This program is distributed in the hope that it will be useful, 18 but WITHOUT ANY WARRANTY; without even the implied warranty of 19 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 20 GNU General Public License for more details. 21 22 You should have received a copy of the GNU General Public License 23 along with this program; if not, write to the Free Software 24 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 25*/ 26 27/* This interfaces to the I2C bus of the Voodoo3 to gain access to 28 the BT869 and possibly other I2C devices. */ 29 30#include <linux/kernel.h> 31#include <linux/module.h> 32#include <linux/init.h> 33#include <linux/pci.h> 34#include <linux/i2c.h> 35#include <linux/i2c-algo-bit.h> 36#include <asm/io.h> 37 38/* the only registers we use */ 39#define REG 0x78 40#define REG2 0x70 41 42/* bit locations in the register */ 43#define DDC_ENAB 0x00040000 44#define DDC_SCL_OUT 0x00080000 45#define DDC_SDA_OUT 0x00100000 46#define DDC_SCL_IN 0x00200000 47#define DDC_SDA_IN 0x00400000 48#define I2C_ENAB 0x00800000 49#define I2C_SCL_OUT 0x01000000 50#define I2C_SDA_OUT 0x02000000 51#define I2C_SCL_IN 0x04000000 52#define I2C_SDA_IN 0x08000000 53 54/* initialization states */ 55#define INIT2 0x2 56#define INIT3 0x4 57 58/* delays */ 59#define CYCLE_DELAY 10 60#define TIMEOUT (HZ / 2) 61 62 63static void __iomem *ioaddr; 64 65/* The voo GPIO registers don't have individual masks for each bit 66 so we always have to read before writing. */ 67 68static void bit_vooi2c_setscl(void *data, int val) 69{ 70 unsigned int r; 71 r = readl(ioaddr + REG); 72 if (val) 73 r |= I2C_SCL_OUT; 74 else 75 r &= ~I2C_SCL_OUT; 76 writel(r, ioaddr + REG); 77 readl(ioaddr + REG); /* flush posted write */ 78} 79 80static void bit_vooi2c_setsda(void *data, int val) 81{ 82 unsigned int r; 83 r = readl(ioaddr + REG); 84 if (val) 85 r |= I2C_SDA_OUT; 86 else 87 r &= ~I2C_SDA_OUT; 88 writel(r, ioaddr + REG); 89 readl(ioaddr + REG); /* flush posted write */ 90} 91 92/* The GPIO pins are open drain, so the pins always remain outputs. 93 We rely on the i2c-algo-bit routines to set the pins high before 94 reading the input from other chips. */ 95 96static int bit_vooi2c_getscl(void *data) 97{ 98 return (0 != (readl(ioaddr + REG) & I2C_SCL_IN)); 99} 100 101static int bit_vooi2c_getsda(void *data) 102{ 103 return (0 != (readl(ioaddr + REG) & I2C_SDA_IN)); 104} 105 106static void bit_vooddc_setscl(void *data, int val) 107{ 108 unsigned int r; 109 r = readl(ioaddr + REG); 110 if (val) 111 r |= DDC_SCL_OUT; 112 else 113 r &= ~DDC_SCL_OUT; 114 writel(r, ioaddr + REG); 115 readl(ioaddr + REG); /* flush posted write */ 116} 117 118static void bit_vooddc_setsda(void *data, int val) 119{ 120 unsigned int r; 121 r = readl(ioaddr + REG); 122 if (val) 123 r |= DDC_SDA_OUT; 124 else 125 r &= ~DDC_SDA_OUT; 126 writel(r, ioaddr + REG); 127 readl(ioaddr + REG); /* flush posted write */ 128} 129 130static int bit_vooddc_getscl(void *data) 131{ 132 return (0 != (readl(ioaddr + REG) & DDC_SCL_IN)); 133} 134 135static int bit_vooddc_getsda(void *data) 136{ 137 return (0 != (readl(ioaddr + REG) & DDC_SDA_IN)); 138} 139 140static int config_v3(struct pci_dev *dev) 141{ 142 unsigned long cadr; 143 144 /* map Voodoo3 memory */ 145 cadr = dev->resource[0].start; 146 cadr &= PCI_BASE_ADDRESS_MEM_MASK; 147 ioaddr = ioremap_nocache(cadr, 0x1000); 148 if (ioaddr) { 149 writel(0x8160, ioaddr + REG2); 150 writel(0xcffc0020, ioaddr + REG); 151 dev_info(&dev->dev, "Using Banshee/Voodoo3 I2C device at %p\n", ioaddr); 152 return 0; 153 } 154 return -ENODEV; 155} 156 157static struct i2c_algo_bit_data voo_i2c_bit_data = { 158 .setsda = bit_vooi2c_setsda, 159 .setscl = bit_vooi2c_setscl, 160 .getsda = bit_vooi2c_getsda, 161 .getscl = bit_vooi2c_getscl, 162 .udelay = CYCLE_DELAY, 163 .timeout = TIMEOUT 164}; 165 166static struct i2c_adapter voodoo3_i2c_adapter = { 167 .owner = THIS_MODULE, 168 .id = I2C_HW_B_VOO, 169 .class = I2C_CLASS_TV_ANALOG, 170 .name = "I2C Voodoo3/Banshee adapter", 171 .algo_data = &voo_i2c_bit_data, 172}; 173 174static struct i2c_algo_bit_data voo_ddc_bit_data = { 175 .setsda = bit_vooddc_setsda, 176 .setscl = bit_vooddc_setscl, 177 .getsda = bit_vooddc_getsda, 178 .getscl = bit_vooddc_getscl, 179 .udelay = CYCLE_DELAY, 180 .timeout = TIMEOUT 181}; 182 183static struct i2c_adapter voodoo3_ddc_adapter = { 184 .owner = THIS_MODULE, 185 .id = I2C_HW_B_VOO, 186 .class = I2C_CLASS_DDC, 187 .name = "DDC Voodoo3/Banshee adapter", 188 .algo_data = &voo_ddc_bit_data, 189}; 190 191static struct pci_device_id voodoo3_ids[] __devinitdata = { 192 { PCI_DEVICE(PCI_VENDOR_ID_3DFX, PCI_DEVICE_ID_3DFX_VOODOO3) }, 193 { PCI_DEVICE(PCI_VENDOR_ID_3DFX, PCI_DEVICE_ID_3DFX_BANSHEE) }, 194 { 0, } 195}; 196 197MODULE_DEVICE_TABLE (pci, voodoo3_ids); 198 199static int __devinit voodoo3_probe(struct pci_dev *dev, const struct pci_device_id *id) 200{ 201 int retval; 202 203 retval = config_v3(dev); 204 if (retval) 205 return retval; 206 207 /* set up the sysfs linkage to our parent device */ 208 voodoo3_i2c_adapter.dev.parent = &dev->dev; 209 voodoo3_ddc_adapter.dev.parent = &dev->dev; 210 211 retval = i2c_bit_add_bus(&voodoo3_i2c_adapter); 212 if (retval) 213 return retval; 214 retval = i2c_bit_add_bus(&voodoo3_ddc_adapter); 215 if (retval) 216 i2c_del_adapter(&voodoo3_i2c_adapter); 217 return retval; 218} 219 220static void __devexit voodoo3_remove(struct pci_dev *dev) 221{ 222 i2c_del_adapter(&voodoo3_i2c_adapter); 223 i2c_del_adapter(&voodoo3_ddc_adapter); 224 iounmap(ioaddr); 225} 226 227static struct pci_driver voodoo3_driver = { 228 .name = "voodoo3_smbus", 229 .id_table = voodoo3_ids, 230 .probe = voodoo3_probe, 231 .remove = __devexit_p(voodoo3_remove), 232}; 233 234static int __init i2c_voodoo3_init(void) 235{ 236 return pci_register_driver(&voodoo3_driver); 237} 238 239static void __exit i2c_voodoo3_exit(void) 240{ 241 pci_unregister_driver(&voodoo3_driver); 242} 243 244 245MODULE_AUTHOR("Frodo Looijaard <frodol@dds.nl>, " 246 "Philip Edelbrock <phil@netroedge.com>, " 247 "Ralph Metzler <rjkm@thp.uni-koeln.de>, " 248 "and Mark D. Studebaker <mdsxyz123@yahoo.com>"); 249MODULE_DESCRIPTION("Voodoo3 I2C/SMBus driver"); 250MODULE_LICENSE("GPL"); 251 252module_init(i2c_voodoo3_init); 253module_exit(i2c_voodoo3_exit); 254