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