123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326 |
- /*
- * 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/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
- */
- #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, kzalloc(sizeof(struct s_i2c_chip), GFP_KERNEL));
- chip = (struct s_i2c_chip *)pci_get_drvdata(dev);
- if (chip == NULL) {
- return -ENOMEM;
- }
- 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);
|