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