linux/drivers/video/fbdev/via/via_i2c.c
<<
>>
Prefs
   1/*
   2 * Copyright 1998-2009 VIA Technologies, Inc. All Rights Reserved.
   3 * Copyright 2001-2008 S3 Graphics, Inc. All Rights Reserved.
   4
   5 * This program is free software; you can redistribute it and/or
   6 * modify it under the terms of the GNU General Public
   7 * License as published by the Free Software Foundation;
   8 * either version 2, or (at your option) any later version.
   9
  10 * This program is distributed in the hope that it will be useful,
  11 * but WITHOUT ANY WARRANTIES OR REPRESENTATIONS; without even
  12 * the implied warranty of MERCHANTABILITY or FITNESS FOR
  13 * A PARTICULAR PURPOSE.See the GNU General Public License
  14 * for more details.
  15
  16 * You should have received a copy of the GNU General Public License
  17 * along with this program; if not, write to the Free Software
  18 * Foundation, Inc.,
  19 * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
  20 */
  21
  22#include <linux/platform_device.h>
  23#include <linux/delay.h>
  24#include <linux/spinlock.h>
  25#include <linux/module.h>
  26#include <linux/via-core.h>
  27#include <linux/via_i2c.h>
  28
  29/*
  30 * There can only be one set of these, so there's no point in having
  31 * them be dynamically allocated...
  32 */
  33#define VIAFB_NUM_I2C           5
  34static struct via_i2c_stuff via_i2c_par[VIAFB_NUM_I2C];
  35static struct viafb_dev *i2c_vdev;  /* Passed in from core */
  36
  37static void via_i2c_setscl(void *data, int state)
  38{
  39        u8 val;
  40        struct via_port_cfg *adap_data = data;
  41        unsigned long flags;
  42
  43        spin_lock_irqsave(&i2c_vdev->reg_lock, flags);
  44        val = via_read_reg(adap_data->io_port, adap_data->ioport_index) & 0xF0;
  45        if (state)
  46                val |= 0x20;
  47        else
  48                val &= ~0x20;
  49        switch (adap_data->type) {
  50        case VIA_PORT_I2C:
  51                val |= 0x01;
  52                break;
  53        case VIA_PORT_GPIO:
  54                val |= 0x82;
  55                break;
  56        default:
  57                printk(KERN_ERR "viafb_i2c: specify wrong i2c type.\n");
  58        }
  59        via_write_reg(adap_data->io_port, adap_data->ioport_index, val);
  60        spin_unlock_irqrestore(&i2c_vdev->reg_lock, flags);
  61}
  62
  63static int via_i2c_getscl(void *data)
  64{
  65        struct via_port_cfg *adap_data = data;
  66        unsigned long flags;
  67        int ret = 0;
  68
  69        spin_lock_irqsave(&i2c_vdev->reg_lock, flags);
  70        if (adap_data->type == VIA_PORT_GPIO)
  71                via_write_reg_mask(adap_data->io_port, adap_data->ioport_index,
  72                        0, 0x80);
  73        if (via_read_reg(adap_data->io_port, adap_data->ioport_index) & 0x08)
  74                ret = 1;
  75        spin_unlock_irqrestore(&i2c_vdev->reg_lock, flags);
  76        return ret;
  77}
  78
  79static int via_i2c_getsda(void *data)
  80{
  81        struct via_port_cfg *adap_data = data;
  82        unsigned long flags;
  83        int ret = 0;
  84
  85        spin_lock_irqsave(&i2c_vdev->reg_lock, flags);
  86        if (adap_data->type == VIA_PORT_GPIO)
  87                via_write_reg_mask(adap_data->io_port, adap_data->ioport_index,
  88                        0, 0x40);
  89        if (via_read_reg(adap_data->io_port, adap_data->ioport_index) & 0x04)
  90                ret = 1;
  91        spin_unlock_irqrestore(&i2c_vdev->reg_lock, flags);
  92        return ret;
  93}
  94
  95static void via_i2c_setsda(void *data, int state)
  96{
  97        u8 val;
  98        struct via_port_cfg *adap_data = data;
  99        unsigned long flags;
 100
 101        spin_lock_irqsave(&i2c_vdev->reg_lock, flags);
 102        val = via_read_reg(adap_data->io_port, adap_data->ioport_index) & 0xF0;
 103        if (state)
 104                val |= 0x10;
 105        else
 106                val &= ~0x10;
 107        switch (adap_data->type) {
 108        case VIA_PORT_I2C:
 109                val |= 0x01;
 110                break;
 111        case VIA_PORT_GPIO:
 112                val |= 0x42;
 113                break;
 114        default:
 115                printk(KERN_ERR "viafb_i2c: specify wrong i2c type.\n");
 116        }
 117        via_write_reg(adap_data->io_port, adap_data->ioport_index, val);
 118        spin_unlock_irqrestore(&i2c_vdev->reg_lock, flags);
 119}
 120
 121int viafb_i2c_readbyte(u8 adap, u8 slave_addr, u8 index, u8 *pdata)
 122{
 123        int ret;
 124        u8 mm1[] = {0x00};
 125        struct i2c_msg msgs[2];
 126
 127        if (!via_i2c_par[adap].is_active)
 128                return -ENODEV;
 129        *pdata = 0;
 130        msgs[0].flags = 0;
 131        msgs[1].flags = I2C_M_RD;
 132        msgs[0].addr = msgs[1].addr = slave_addr / 2;
 133        mm1[0] = index;
 134        msgs[0].len = 1; msgs[1].len = 1;
 135        msgs[0].buf = mm1; msgs[1].buf = pdata;
 136        ret = i2c_transfer(&via_i2c_par[adap].adapter, msgs, 2);
 137        if (ret == 2)
 138                ret = 0;
 139        else if (ret >= 0)
 140                ret = -EIO;
 141
 142        return ret;
 143}
 144
 145int viafb_i2c_writebyte(u8 adap, u8 slave_addr, u8 index, u8 data)
 146{
 147        int ret;
 148        u8 msg[2] = { index, data };
 149        struct i2c_msg msgs;
 150
 151        if (!via_i2c_par[adap].is_active)
 152                return -ENODEV;
 153        msgs.flags = 0;
 154        msgs.addr = slave_addr / 2;
 155        msgs.len = 2;
 156        msgs.buf = msg;
 157        ret = i2c_transfer(&via_i2c_par[adap].adapter, &msgs, 1);
 158        if (ret == 1)
 159                ret = 0;
 160        else if (ret >= 0)
 161                ret = -EIO;
 162
 163        return ret;
 164}
 165
 166int viafb_i2c_readbytes(u8 adap, u8 slave_addr, u8 index, u8 *buff, int buff_len)
 167{
 168        int ret;
 169        u8 mm1[] = {0x00};
 170        struct i2c_msg msgs[2];
 171
 172        if (!via_i2c_par[adap].is_active)
 173                return -ENODEV;
 174        msgs[0].flags = 0;
 175        msgs[1].flags = I2C_M_RD;
 176        msgs[0].addr = msgs[1].addr = slave_addr / 2;
 177        mm1[0] = index;
 178        msgs[0].len = 1; msgs[1].len = buff_len;
 179        msgs[0].buf = mm1; msgs[1].buf = buff;
 180        ret = i2c_transfer(&via_i2c_par[adap].adapter, msgs, 2);
 181        if (ret == 2)
 182                ret = 0;
 183        else if (ret >= 0)
 184                ret = -EIO;
 185
 186        return ret;
 187}
 188
 189/*
 190 * Allow other viafb subdevices to look up a specific adapter
 191 * by port name.
 192 */
 193struct i2c_adapter *viafb_find_i2c_adapter(enum viafb_i2c_adap which)
 194{
 195        struct via_i2c_stuff *stuff = &via_i2c_par[which];
 196
 197        return &stuff->adapter;
 198}
 199EXPORT_SYMBOL_GPL(viafb_find_i2c_adapter);
 200
 201
 202static int create_i2c_bus(struct i2c_adapter *adapter,
 203                          struct i2c_algo_bit_data *algo,
 204                          struct via_port_cfg *adap_cfg,
 205                          struct pci_dev *pdev)
 206{
 207        algo->setsda = via_i2c_setsda;
 208        algo->setscl = via_i2c_setscl;
 209        algo->getsda = via_i2c_getsda;
 210        algo->getscl = via_i2c_getscl;
 211        algo->udelay = 10;
 212        algo->timeout = 2;
 213        algo->data = adap_cfg;
 214
 215        sprintf(adapter->name, "viafb i2c io_port idx 0x%02x",
 216                adap_cfg->ioport_index);
 217        adapter->owner = THIS_MODULE;
 218        adapter->class = I2C_CLASS_DDC;
 219        adapter->algo_data = algo;
 220        if (pdev)
 221                adapter->dev.parent = &pdev->dev;
 222        else
 223                adapter->dev.parent = NULL;
 224        /* i2c_set_adapdata(adapter, adap_cfg); */
 225
 226        /* Raise SCL and SDA */
 227        via_i2c_setsda(adap_cfg, 1);
 228        via_i2c_setscl(adap_cfg, 1);
 229        udelay(20);
 230
 231        return i2c_bit_add_bus(adapter);
 232}
 233
 234static int viafb_i2c_probe(struct platform_device *platdev)
 235{
 236        int i, ret;
 237        struct via_port_cfg *configs;
 238
 239        i2c_vdev = platdev->dev.platform_data;
 240        configs = i2c_vdev->port_cfg;
 241
 242        for (i = 0; i < VIAFB_NUM_PORTS; i++) {
 243                struct via_port_cfg *adap_cfg = configs++;
 244                struct via_i2c_stuff *i2c_stuff = &via_i2c_par[i];
 245
 246                i2c_stuff->is_active = 0;
 247                if (adap_cfg->type == 0 || adap_cfg->mode != VIA_MODE_I2C)
 248                        continue;
 249                ret = create_i2c_bus(&i2c_stuff->adapter,
 250                                     &i2c_stuff->algo, adap_cfg,
 251                                NULL); /* FIXME: PCIDEV */
 252                if (ret < 0) {
 253                        printk(KERN_ERR "viafb: cannot create i2c bus %u:%d\n",
 254                                i, ret);
 255                        continue;  /* Still try to make the rest */
 256                }
 257                i2c_stuff->is_active = 1;
 258        }
 259
 260        return 0;
 261}
 262
 263static int viafb_i2c_remove(struct platform_device *platdev)
 264{
 265        int i;
 266
 267        for (i = 0; i < VIAFB_NUM_PORTS; i++) {
 268                struct via_i2c_stuff *i2c_stuff = &via_i2c_par[i];
 269                /*
 270                 * Only remove those entries in the array that we've
 271                 * actually used (and thus initialized algo_data)
 272                 */
 273                if (i2c_stuff->is_active)
 274                        i2c_del_adapter(&i2c_stuff->adapter);
 275        }
 276        return 0;
 277}
 278
 279static struct platform_driver via_i2c_driver = {
 280        .driver = {
 281                .name = "viafb-i2c",
 282        },
 283        .probe = viafb_i2c_probe,
 284        .remove = viafb_i2c_remove,
 285};
 286
 287int viafb_i2c_init(void)
 288{
 289        return platform_driver_register(&via_i2c_driver);
 290}
 291
 292void viafb_i2c_exit(void)
 293{
 294        platform_driver_unregister(&via_i2c_driver);
 295}
 296