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
via_i2c_setscl(void * data,int state)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
via_i2c_getscl(void * data)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
via_i2c_getsda(void * data)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
via_i2c_setsda(void * data,int state)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
viafb_i2c_readbyte(u8 adap,u8 slave_addr,u8 index,u8 * pdata)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
viafb_i2c_writebyte(u8 adap,u8 slave_addr,u8 index,u8 data)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
viafb_i2c_readbytes(u8 adap,u8 slave_addr,u8 index,u8 * buff,int buff_len)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 */
viafb_find_i2c_adapter(enum viafb_i2c_adap which)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
create_i2c_bus(struct i2c_adapter * adapter,struct i2c_algo_bit_data * algo,struct via_port_cfg * adap_cfg,struct pci_dev * pdev)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
viafb_i2c_probe(struct platform_device * platdev)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
viafb_i2c_remove(struct platform_device * platdev)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
viafb_i2c_init(void)272f7018c21STomi Valkeinen int viafb_i2c_init(void)
273f7018c21STomi Valkeinen {
274f7018c21STomi Valkeinen return platform_driver_register(&via_i2c_driver);
275f7018c21STomi Valkeinen }
276f7018c21STomi Valkeinen
viafb_i2c_exit(void)277f7018c21STomi Valkeinen void viafb_i2c_exit(void)
278f7018c21STomi Valkeinen {
279f7018c21STomi Valkeinen platform_driver_unregister(&via_i2c_driver);
280f7018c21STomi Valkeinen }
281