Logo Search packages:      
Sourcecode: linux version File versions  Download package

i2c-prosavage.c

/*
 *    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.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_del_adapter(&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);

Generated by  Doxygen 1.6.0   Back to index