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