xref: /openbmc/linux/drivers/video/fbdev/via/via_i2c.c (revision 1ac731c529cd4d6adbce134754b51ff7d822b145)
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