diff options
Diffstat (limited to 'drivers/i2c/busses/i2c-prosavage.c')
-rw-r--r-- | drivers/i2c/busses/i2c-prosavage.c | 334 |
1 files changed, 334 insertions, 0 deletions
diff --git a/drivers/i2c/busses/i2c-prosavage.c b/drivers/i2c/busses/i2c-prosavage.c new file mode 100644 index 000000000000..13d66289933b --- /dev/null +++ b/drivers/i2c/busses/i2c-prosavage.c @@ -0,0 +1,334 @@ +/* + * kernel/busses/i2c-prosavage.c + * + * i2c bus driver for S3/VIA 8365/8375 graphics processor. + * Copyright (c) 2003 Henk Vergonet <henk@god.dyndns.org> + * Based on code written by: + * Frodo Looijaard <frodol@dds.nl>, + * Philip Edelbrock <phil@netroedge.com>, + * Ralph Metzler <rjkm@thp.uni-koeln.de>, and + * Mark D. Studebaker <mdsxyz123@yahoo.com> + * Simon Vogl + * and others + * + * Please read the lm_sensors documentation for details on use. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ +/* 18-05-2003 HVE - created + * 14-06-2003 HVE - adapted for lm_sensors2 + * 17-06-2003 HVE - linux 2.5.xx compatible + * 18-06-2003 HVE - codingstyle + * 21-06-2003 HVE - compatibility lm_sensors2 and linux 2.5.xx + * codingstyle, mmio enabled + * + * This driver interfaces to the I2C bus of the VIA north bridge embedded + * ProSavage4/8 devices. Usefull for gaining access to the TV Encoder chips. + * + * Graphics cores: + * S3/VIA KM266/VT8375 aka ProSavage8 + * S3/VIA KM133/VT8365 aka Savage4 + * + * Two serial busses are implemented: + * SERIAL1 - I2C serial communications interface + * SERIAL2 - DDC2 monitor communications interface + * + * Tested on a FX41 mainboard, see http://www.shuttle.com + * + * + * TODO: + * - integration with prosavage framebuffer device + * (Additional documentation needed :( + */ + +#include <linux/config.h> +#include <linux/module.h> +#include <linux/init.h> +#include <linux/pci.h> +#include <linux/i2c.h> +#include <linux/i2c-algo-bit.h> +#include <asm/io.h> + +/* + * driver configuration + */ +#define MAX_BUSSES 2 + +struct s_i2c_bus { + void __iomem *mmvga; + int i2c_reg; + int adap_ok; + struct i2c_adapter adap; + struct i2c_algo_bit_data algo; +}; + +struct s_i2c_chip { + void __iomem *mmio; + struct s_i2c_bus i2c_bus[MAX_BUSSES]; +}; + + +/* + * i2c configuration + */ +#ifndef I2C_HW_B_S3VIA +#define I2C_HW_B_S3VIA 0x18 /* S3VIA ProSavage adapter */ +#endif + +/* delays */ +#define CYCLE_DELAY 10 +#define TIMEOUT (HZ / 2) + + +/* + * S3/VIA 8365/8375 registers + */ +#define VGA_CR_IX 0x3d4 +#define VGA_CR_DATA 0x3d5 + +#define CR_SERIAL1 0xa0 /* I2C serial communications interface */ +#define MM_SERIAL1 0xff20 +#define CR_SERIAL2 0xb1 /* DDC2 monitor communications interface */ + +/* based on vt8365 documentation */ +#define I2C_ENAB 0x10 +#define I2C_SCL_OUT 0x01 +#define I2C_SDA_OUT 0x02 +#define I2C_SCL_IN 0x04 +#define I2C_SDA_IN 0x08 + +#define SET_CR_IX(p, val) writeb((val), (p)->mmvga + VGA_CR_IX) +#define SET_CR_DATA(p, val) writeb((val), (p)->mmvga + VGA_CR_DATA) +#define GET_CR_DATA(p) readb((p)->mmvga + VGA_CR_DATA) + + +/* + * Serial bus line handling + * + * serial communications register as parameter in private data + * + * TODO: locks with other code sections accessing video registers? + */ +static void bit_s3via_setscl(void *bus, int val) +{ + struct s_i2c_bus *p = (struct s_i2c_bus *)bus; + unsigned int r; + + SET_CR_IX(p, p->i2c_reg); + r = GET_CR_DATA(p); + r |= I2C_ENAB; + if (val) { + r |= I2C_SCL_OUT; + } else { + r &= ~I2C_SCL_OUT; + } + SET_CR_DATA(p, r); +} + +static void bit_s3via_setsda(void *bus, int val) +{ + struct s_i2c_bus *p = (struct s_i2c_bus *)bus; + unsigned int r; + + SET_CR_IX(p, p->i2c_reg); + r = GET_CR_DATA(p); + r |= I2C_ENAB; + if (val) { + r |= I2C_SDA_OUT; + } else { + r &= ~I2C_SDA_OUT; + } + SET_CR_DATA(p, r); +} + +static int bit_s3via_getscl(void *bus) +{ + struct s_i2c_bus *p = (struct s_i2c_bus *)bus; + + SET_CR_IX(p, p->i2c_reg); + return (0 != (GET_CR_DATA(p) & I2C_SCL_IN)); +} + +static int bit_s3via_getsda(void *bus) +{ + struct s_i2c_bus *p = (struct s_i2c_bus *)bus; + + SET_CR_IX(p, p->i2c_reg); + return (0 != (GET_CR_DATA(p) & I2C_SDA_IN)); +} + + +/* + * adapter initialisation + */ +static int i2c_register_bus(struct pci_dev *dev, struct s_i2c_bus *p, void __iomem *mmvga, u32 i2c_reg) +{ + int ret; + p->adap.owner = THIS_MODULE; + p->adap.id = I2C_HW_B_S3VIA; + p->adap.algo_data = &p->algo; + p->adap.dev.parent = &dev->dev; + p->algo.setsda = bit_s3via_setsda; + p->algo.setscl = bit_s3via_setscl; + p->algo.getsda = bit_s3via_getsda; + p->algo.getscl = bit_s3via_getscl; + p->algo.udelay = CYCLE_DELAY; + p->algo.mdelay = CYCLE_DELAY; + p->algo.timeout = TIMEOUT; + p->algo.data = p; + p->mmvga = mmvga; + p->i2c_reg = i2c_reg; + + ret = i2c_bit_add_bus(&p->adap); + if (ret) { + return ret; + } + + p->adap_ok = 1; + return 0; +} + + +/* + * Cleanup stuff + */ +static void prosavage_remove(struct pci_dev *dev) +{ + struct s_i2c_chip *chip; + int i, ret; + + chip = (struct s_i2c_chip *)pci_get_drvdata(dev); + + if (!chip) { + return; + } + for (i = MAX_BUSSES - 1; i >= 0; i--) { + if (chip->i2c_bus[i].adap_ok == 0) + continue; + + ret = i2c_bit_del_bus(&chip->i2c_bus[i].adap); + if (ret) { + dev_err(&dev->dev, "%s not removed\n", + chip->i2c_bus[i].adap.name); + } + } + if (chip->mmio) { + iounmap(chip->mmio); + } + kfree(chip); +} + + +/* + * Detect chip and initialize it + */ +static int __devinit prosavage_probe(struct pci_dev *dev, const struct pci_device_id *id) +{ + int ret; + unsigned long base, len; + struct s_i2c_chip *chip; + struct s_i2c_bus *bus; + + pci_set_drvdata(dev, kmalloc(sizeof(struct s_i2c_chip), GFP_KERNEL)); + chip = (struct s_i2c_chip *)pci_get_drvdata(dev); + if (chip == NULL) { + return -ENOMEM; + } + + memset(chip, 0, sizeof(struct s_i2c_chip)); + + base = dev->resource[0].start & PCI_BASE_ADDRESS_MEM_MASK; + len = dev->resource[0].end - base + 1; + chip->mmio = ioremap_nocache(base, len); + + if (chip->mmio == NULL) { + dev_err(&dev->dev, "ioremap failed\n"); + prosavage_remove(dev); + return -ENODEV; + } + + + /* + * Chip initialisation + */ + /* Unlock Extended IO Space ??? */ + + + /* + * i2c bus registration + */ + bus = &chip->i2c_bus[0]; + snprintf(bus->adap.name, sizeof(bus->adap.name), + "ProSavage I2C bus at %02x:%02x.%x", + dev->bus->number, PCI_SLOT(dev->devfn), PCI_FUNC(dev->devfn)); + ret = i2c_register_bus(dev, bus, chip->mmio + 0x8000, CR_SERIAL1); + if (ret) { + goto err_adap; + } + /* + * ddc bus registration + */ + bus = &chip->i2c_bus[1]; + snprintf(bus->adap.name, sizeof(bus->adap.name), + "ProSavage DDC bus at %02x:%02x.%x", + dev->bus->number, PCI_SLOT(dev->devfn), PCI_FUNC(dev->devfn)); + ret = i2c_register_bus(dev, bus, chip->mmio + 0x8000, CR_SERIAL2); + if (ret) { + goto err_adap; + } + return 0; +err_adap: + dev_err(&dev->dev, "%s failed\n", bus->adap.name); + prosavage_remove(dev); + return ret; +} + + +/* + * Data for PCI driver interface + */ +static struct pci_device_id prosavage_pci_tbl[] = { + { PCI_DEVICE(PCI_VENDOR_ID_S3, PCI_DEVICE_ID_S3_SAVAGE4) }, + { PCI_DEVICE(PCI_VENDOR_ID_S3, PCI_DEVICE_ID_S3_PROSAVAGE8) }, + { 0, }, +}; + +MODULE_DEVICE_TABLE (pci, prosavage_pci_tbl); + +static struct pci_driver prosavage_driver = { + .name = "prosavage_smbus", + .id_table = prosavage_pci_tbl, + .probe = prosavage_probe, + .remove = prosavage_remove, +}; + +static int __init i2c_prosavage_init(void) +{ + return pci_register_driver(&prosavage_driver); +} + +static void __exit i2c_prosavage_exit(void) +{ + pci_unregister_driver(&prosavage_driver); +} + +MODULE_DEVICE_TABLE(pci, prosavage_pci_tbl); +MODULE_AUTHOR("Henk Vergonet"); +MODULE_DESCRIPTION("ProSavage VIA 8365/8375 smbus driver"); +MODULE_LICENSE("GPL"); + +module_init (i2c_prosavage_init); +module_exit (i2c_prosavage_exit); |