root/drivers/video/fbdev/via/via_i2c.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. via_i2c_setscl
  2. via_i2c_getscl
  3. via_i2c_getsda
  4. via_i2c_setsda
  5. viafb_i2c_readbyte
  6. viafb_i2c_writebyte
  7. viafb_i2c_readbytes
  8. viafb_find_i2c_adapter
  9. create_i2c_bus
  10. viafb_i2c_probe
  11. viafb_i2c_remove
  12. viafb_i2c_init
  13. viafb_i2c_exit

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

/* [<][>][^][v][top][bottom][index][help] */