Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

at 77b2555b52a894a2e39a42e43d993df875c46a6a 333 lines 7.8 kB view raw
1/* 2 * kernel/busses/i2c-prosavage.c 3 * 4 * i2c bus driver for S3/VIA 8365/8375 graphics processor. 5 * Copyright (c) 2003 Henk Vergonet <henk@god.dyndns.org> 6 * Based on code written by: 7 * Frodo Looijaard <frodol@dds.nl>, 8 * Philip Edelbrock <phil@netroedge.com>, 9 * Ralph Metzler <rjkm@thp.uni-koeln.de>, and 10 * Mark D. Studebaker <mdsxyz123@yahoo.com> 11 * Simon Vogl 12 * and others 13 * 14 * Please read the lm_sensors documentation for details on use. 15 * 16 * This program is free software; you can redistribute it and/or modify 17 * it under the terms of the GNU General Public License as published by 18 * the Free Software Foundation; either version 2 of the License, or 19 * (at your option) any later version. 20 * 21 * This program is distributed in the hope that it will be useful, 22 * but WITHOUT ANY WARRANTY; without even the implied warranty of 23 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 24 * GNU General Public License for more details. 25 * 26 * You should have received a copy of the GNU General Public License 27 * along with this program; if not, write to the Free Software 28 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 29 * 30 */ 31/* 18-05-2003 HVE - created 32 * 14-06-2003 HVE - adapted for lm_sensors2 33 * 17-06-2003 HVE - linux 2.5.xx compatible 34 * 18-06-2003 HVE - codingstyle 35 * 21-06-2003 HVE - compatibility lm_sensors2 and linux 2.5.xx 36 * codingstyle, mmio enabled 37 * 38 * This driver interfaces to the I2C bus of the VIA north bridge embedded 39 * ProSavage4/8 devices. Usefull for gaining access to the TV Encoder chips. 40 * 41 * Graphics cores: 42 * S3/VIA KM266/VT8375 aka ProSavage8 43 * S3/VIA KM133/VT8365 aka Savage4 44 * 45 * Two serial busses are implemented: 46 * SERIAL1 - I2C serial communications interface 47 * SERIAL2 - DDC2 monitor communications interface 48 * 49 * Tested on a FX41 mainboard, see http://www.shuttle.com 50 * 51 * 52 * TODO: 53 * - integration with prosavage framebuffer device 54 * (Additional documentation needed :( 55 */ 56 57#include <linux/module.h> 58#include <linux/init.h> 59#include <linux/pci.h> 60#include <linux/i2c.h> 61#include <linux/i2c-algo-bit.h> 62#include <asm/io.h> 63 64/* 65 * driver configuration 66 */ 67#define MAX_BUSSES 2 68 69struct s_i2c_bus { 70 void __iomem *mmvga; 71 int i2c_reg; 72 int adap_ok; 73 struct i2c_adapter adap; 74 struct i2c_algo_bit_data algo; 75}; 76 77struct s_i2c_chip { 78 void __iomem *mmio; 79 struct s_i2c_bus i2c_bus[MAX_BUSSES]; 80}; 81 82 83/* 84 * i2c configuration 85 */ 86#ifndef I2C_HW_B_S3VIA 87#define I2C_HW_B_S3VIA 0x18 /* S3VIA ProSavage adapter */ 88#endif 89 90/* delays */ 91#define CYCLE_DELAY 10 92#define TIMEOUT (HZ / 2) 93 94 95/* 96 * S3/VIA 8365/8375 registers 97 */ 98#define VGA_CR_IX 0x3d4 99#define VGA_CR_DATA 0x3d5 100 101#define CR_SERIAL1 0xa0 /* I2C serial communications interface */ 102#define MM_SERIAL1 0xff20 103#define CR_SERIAL2 0xb1 /* DDC2 monitor communications interface */ 104 105/* based on vt8365 documentation */ 106#define I2C_ENAB 0x10 107#define I2C_SCL_OUT 0x01 108#define I2C_SDA_OUT 0x02 109#define I2C_SCL_IN 0x04 110#define I2C_SDA_IN 0x08 111 112#define SET_CR_IX(p, val) writeb((val), (p)->mmvga + VGA_CR_IX) 113#define SET_CR_DATA(p, val) writeb((val), (p)->mmvga + VGA_CR_DATA) 114#define GET_CR_DATA(p) readb((p)->mmvga + VGA_CR_DATA) 115 116 117/* 118 * Serial bus line handling 119 * 120 * serial communications register as parameter in private data 121 * 122 * TODO: locks with other code sections accessing video registers? 123 */ 124static void bit_s3via_setscl(void *bus, int val) 125{ 126 struct s_i2c_bus *p = (struct s_i2c_bus *)bus; 127 unsigned int r; 128 129 SET_CR_IX(p, p->i2c_reg); 130 r = GET_CR_DATA(p); 131 r |= I2C_ENAB; 132 if (val) { 133 r |= I2C_SCL_OUT; 134 } else { 135 r &= ~I2C_SCL_OUT; 136 } 137 SET_CR_DATA(p, r); 138} 139 140static void bit_s3via_setsda(void *bus, int val) 141{ 142 struct s_i2c_bus *p = (struct s_i2c_bus *)bus; 143 unsigned int r; 144 145 SET_CR_IX(p, p->i2c_reg); 146 r = GET_CR_DATA(p); 147 r |= I2C_ENAB; 148 if (val) { 149 r |= I2C_SDA_OUT; 150 } else { 151 r &= ~I2C_SDA_OUT; 152 } 153 SET_CR_DATA(p, r); 154} 155 156static int bit_s3via_getscl(void *bus) 157{ 158 struct s_i2c_bus *p = (struct s_i2c_bus *)bus; 159 160 SET_CR_IX(p, p->i2c_reg); 161 return (0 != (GET_CR_DATA(p) & I2C_SCL_IN)); 162} 163 164static int bit_s3via_getsda(void *bus) 165{ 166 struct s_i2c_bus *p = (struct s_i2c_bus *)bus; 167 168 SET_CR_IX(p, p->i2c_reg); 169 return (0 != (GET_CR_DATA(p) & I2C_SDA_IN)); 170} 171 172 173/* 174 * adapter initialisation 175 */ 176static int i2c_register_bus(struct pci_dev *dev, struct s_i2c_bus *p, void __iomem *mmvga, u32 i2c_reg) 177{ 178 int ret; 179 p->adap.owner = THIS_MODULE; 180 p->adap.id = I2C_HW_B_S3VIA; 181 p->adap.algo_data = &p->algo; 182 p->adap.dev.parent = &dev->dev; 183 p->algo.setsda = bit_s3via_setsda; 184 p->algo.setscl = bit_s3via_setscl; 185 p->algo.getsda = bit_s3via_getsda; 186 p->algo.getscl = bit_s3via_getscl; 187 p->algo.udelay = CYCLE_DELAY; 188 p->algo.mdelay = CYCLE_DELAY; 189 p->algo.timeout = TIMEOUT; 190 p->algo.data = p; 191 p->mmvga = mmvga; 192 p->i2c_reg = i2c_reg; 193 194 ret = i2c_bit_add_bus(&p->adap); 195 if (ret) { 196 return ret; 197 } 198 199 p->adap_ok = 1; 200 return 0; 201} 202 203 204/* 205 * Cleanup stuff 206 */ 207static void prosavage_remove(struct pci_dev *dev) 208{ 209 struct s_i2c_chip *chip; 210 int i, ret; 211 212 chip = (struct s_i2c_chip *)pci_get_drvdata(dev); 213 214 if (!chip) { 215 return; 216 } 217 for (i = MAX_BUSSES - 1; i >= 0; i--) { 218 if (chip->i2c_bus[i].adap_ok == 0) 219 continue; 220 221 ret = i2c_bit_del_bus(&chip->i2c_bus[i].adap); 222 if (ret) { 223 dev_err(&dev->dev, "%s not removed\n", 224 chip->i2c_bus[i].adap.name); 225 } 226 } 227 if (chip->mmio) { 228 iounmap(chip->mmio); 229 } 230 kfree(chip); 231} 232 233 234/* 235 * Detect chip and initialize it 236 */ 237static int __devinit prosavage_probe(struct pci_dev *dev, const struct pci_device_id *id) 238{ 239 int ret; 240 unsigned long base, len; 241 struct s_i2c_chip *chip; 242 struct s_i2c_bus *bus; 243 244 pci_set_drvdata(dev, kmalloc(sizeof(struct s_i2c_chip), GFP_KERNEL)); 245 chip = (struct s_i2c_chip *)pci_get_drvdata(dev); 246 if (chip == NULL) { 247 return -ENOMEM; 248 } 249 250 memset(chip, 0, sizeof(struct s_i2c_chip)); 251 252 base = dev->resource[0].start & PCI_BASE_ADDRESS_MEM_MASK; 253 len = dev->resource[0].end - base + 1; 254 chip->mmio = ioremap_nocache(base, len); 255 256 if (chip->mmio == NULL) { 257 dev_err(&dev->dev, "ioremap failed\n"); 258 prosavage_remove(dev); 259 return -ENODEV; 260 } 261 262 263 /* 264 * Chip initialisation 265 */ 266 /* Unlock Extended IO Space ??? */ 267 268 269 /* 270 * i2c bus registration 271 */ 272 bus = &chip->i2c_bus[0]; 273 snprintf(bus->adap.name, sizeof(bus->adap.name), 274 "ProSavage I2C bus at %02x:%02x.%x", 275 dev->bus->number, PCI_SLOT(dev->devfn), PCI_FUNC(dev->devfn)); 276 ret = i2c_register_bus(dev, bus, chip->mmio + 0x8000, CR_SERIAL1); 277 if (ret) { 278 goto err_adap; 279 } 280 /* 281 * ddc bus registration 282 */ 283 bus = &chip->i2c_bus[1]; 284 snprintf(bus->adap.name, sizeof(bus->adap.name), 285 "ProSavage DDC bus at %02x:%02x.%x", 286 dev->bus->number, PCI_SLOT(dev->devfn), PCI_FUNC(dev->devfn)); 287 ret = i2c_register_bus(dev, bus, chip->mmio + 0x8000, CR_SERIAL2); 288 if (ret) { 289 goto err_adap; 290 } 291 return 0; 292err_adap: 293 dev_err(&dev->dev, "%s failed\n", bus->adap.name); 294 prosavage_remove(dev); 295 return ret; 296} 297 298 299/* 300 * Data for PCI driver interface 301 */ 302static struct pci_device_id prosavage_pci_tbl[] = { 303 { PCI_DEVICE(PCI_VENDOR_ID_S3, PCI_DEVICE_ID_S3_SAVAGE4) }, 304 { PCI_DEVICE(PCI_VENDOR_ID_S3, PCI_DEVICE_ID_S3_PROSAVAGE8) }, 305 { 0, }, 306}; 307 308MODULE_DEVICE_TABLE (pci, prosavage_pci_tbl); 309 310static struct pci_driver prosavage_driver = { 311 .name = "prosavage_smbus", 312 .id_table = prosavage_pci_tbl, 313 .probe = prosavage_probe, 314 .remove = prosavage_remove, 315}; 316 317static int __init i2c_prosavage_init(void) 318{ 319 return pci_register_driver(&prosavage_driver); 320} 321 322static void __exit i2c_prosavage_exit(void) 323{ 324 pci_unregister_driver(&prosavage_driver); 325} 326 327MODULE_DEVICE_TABLE(pci, prosavage_pci_tbl); 328MODULE_AUTHOR("Henk Vergonet"); 329MODULE_DESCRIPTION("ProSavage VIA 8365/8375 smbus driver"); 330MODULE_LICENSE("GPL"); 331 332module_init (i2c_prosavage_init); 333module_exit (i2c_prosavage_exit);