File Index
Files and directories
Sign in
FILE

via_i2c.c

File preview with syntax highlighting.

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