1/*
2 *  Custom GPIO-based I2C driver
3 *
4 *  Copyright (C) 2007-2008 Gabor Juhos <juhosg@openwrt.org>
5 *
6 *  This program is free software; you can redistribute it and/or modify
7 *  it under the terms of the GNU General Public License version 2 as
8 *  published by the Free Software Foundation.
9 *
10 * ---------------------------------------------------------------------------
11 *
12 *  The behaviour of this driver can be altered by setting some parameters
13 *  from the insmod command line.
14 *
15 *  The following parameters are adjustable:
16 *
17 *	bus0	These four arguments can be arrays of
18 *	bus1	1-8 unsigned integers as follows:
19 *	bus2
20 *	bus3	<id>,<sda>,<scl>,<udelay>,<timeout>,<sda_od>,<scl_od>,<scl_oo>
21 *
22 *  where:
23 *
24 *  <id>	ID to used as device_id for the corresponding bus (required)
25 *  <sda>	GPIO pin ID to used for SDA (required)
26 *  <scl>	GPIO pin ID to used for SCL (required)
27 *  <udelay>	signal toggle delay.
28 *  <timeout>	clock stretching timeout.
29 *  <sda_od>	SDA is configured as open drain.
30 *  <scl_od>	SCL is configured as open drain.
31 *  <scl_oo>	SCL output drivers cannot be turned off.
32 *
33 *  See include/i2c-gpio.h for more information about the parameters.
34 *
35 *  If this driver is built into the kernel, you can use the following kernel
36 *  command line parameters, with the same values as the corresponding module
37 *  parameters listed above:
38 *
39 *	i2c-gpio-custom.bus0
40 *	i2c-gpio-custom.bus1
41 *	i2c-gpio-custom.bus2
42 *	i2c-gpio-custom.bus3
43 */
44
45#include <linux/kernel.h>
46#include <linux/module.h>
47#include <linux/init.h>
48#include <linux/platform_device.h>
49
50#include <linux/i2c-gpio.h>
51
52#define DRV_NAME	"i2c-gpio-custom"
53#define DRV_DESC	"Custom GPIO-based I2C driver"
54#define DRV_VERSION	"0.1.1"
55
56#define PFX		DRV_NAME ": "
57
58#define BUS_PARAM_ID		0
59#define BUS_PARAM_SDA		1
60#define BUS_PARAM_SCL		2
61#define BUS_PARAM_UDELAY	3
62#define BUS_PARAM_TIMEOUT	4
63#define BUS_PARAM_SDA_OD	5
64#define BUS_PARAM_SCL_OD	6
65#define BUS_PARAM_SCL_OO	7
66
67#define BUS_PARAM_REQUIRED	3
68#define BUS_PARAM_COUNT		8
69#define BUS_COUNT_MAX		4
70
71static unsigned int bus0[BUS_PARAM_COUNT] __initdata;
72static unsigned int bus1[BUS_PARAM_COUNT] __initdata;
73static unsigned int bus2[BUS_PARAM_COUNT] __initdata;
74static unsigned int bus3[BUS_PARAM_COUNT] __initdata;
75
76static unsigned int bus_nump[BUS_COUNT_MAX] __initdata;
77
78#define BUS_PARM_DESC \
79	" config -> id,sda,scl[,udelay,timeout,sda_od,scl_od,scl_oo]"
80
81module_param_array(bus0, uint, &bus_nump[0], 0);
82MODULE_PARM_DESC(bus0, "bus0" BUS_PARM_DESC);
83module_param_array(bus1, uint, &bus_nump[1], 0);
84MODULE_PARM_DESC(bus1, "bus1" BUS_PARM_DESC);
85module_param_array(bus2, uint, &bus_nump[2], 0);
86MODULE_PARM_DESC(bus2, "bus2" BUS_PARM_DESC);
87module_param_array(bus3, uint, &bus_nump[3], 0);
88MODULE_PARM_DESC(bus3, "bus3" BUS_PARM_DESC);
89
90static struct platform_device *devices[BUS_COUNT_MAX];
91static unsigned int nr_devices;
92
93static void i2c_gpio_custom_cleanup(void)
94{
95	int i;
96
97	for (i = 0; i < nr_devices; i++)
98		if (devices[i])
99			platform_device_put(devices[i]);
100}
101
102static int __init i2c_gpio_custom_add_one(unsigned int id, unsigned int *params)
103{
104	struct platform_device *pdev;
105	struct i2c_gpio_platform_data pdata;
106	int err;
107
108	if (!bus_nump[id])
109		return 0;
110
111	if (bus_nump[id] < BUS_PARAM_REQUIRED) {
112		printk(KERN_ERR PFX "not enough parameters for bus%d\n", id);
113		err = -EINVAL;
114		goto err;
115	}
116
117	pdev = platform_device_alloc("i2c-gpio", params[BUS_PARAM_ID]);
118	if (!pdev) {
119		err = -ENOMEM;
120		goto err;
121	}
122
123	pdata.sda_pin = params[BUS_PARAM_SDA];
124	pdata.scl_pin = params[BUS_PARAM_SCL];
125	pdata.udelay = params[BUS_PARAM_UDELAY];
126	pdata.timeout = params[BUS_PARAM_TIMEOUT];
127	pdata.sda_is_open_drain = params[BUS_PARAM_SDA_OD] != 0;
128	pdata.scl_is_open_drain = params[BUS_PARAM_SCL_OD] != 0;
129	pdata.scl_is_output_only = params[BUS_PARAM_SCL_OO] != 0;
130
131	err = platform_device_add_data(pdev, &pdata, sizeof(pdata));
132	if (err)
133		goto err_put;
134
135	err = platform_device_add(pdev);
136	if (err)
137		goto err_put;
138
139	devices[nr_devices++] = pdev;
140	return 0;
141
142err_put:
143	platform_device_put(pdev);
144err:
145	return err;
146}
147
148static int __init i2c_gpio_custom_probe(void)
149{
150	int err;
151
152	printk(KERN_INFO DRV_DESC " version " DRV_VERSION "\n");
153
154	err = i2c_gpio_custom_add_one(0, bus0);
155	if (err)
156		goto err;
157
158	err = i2c_gpio_custom_add_one(1, bus1);
159	if (err)
160		goto err;
161
162	err = i2c_gpio_custom_add_one(2, bus2);
163	if (err)
164		goto err;
165
166	err = i2c_gpio_custom_add_one(3, bus3);
167	if (err)
168		goto err;
169
170	if (!nr_devices) {
171		printk(KERN_ERR PFX "no bus parameter(s) specified\n");
172		err = -ENODEV;
173		goto err;
174	}
175
176	return 0;
177
178err:
179	i2c_gpio_custom_cleanup();
180	return err;
181}
182
183#ifdef MODULE
184static int __init i2c_gpio_custom_init(void)
185{
186	return i2c_gpio_custom_probe();
187}
188module_init(i2c_gpio_custom_init);
189
190static void __exit i2c_gpio_custom_exit(void)
191{
192	i2c_gpio_custom_cleanup();
193}
194module_exit(i2c_gpio_custom_exit);
195#else
196subsys_initcall(i2c_gpio_custom_probe);
197#endif /* MODULE*/
198
199MODULE_LICENSE("GPL v2");
200MODULE_AUTHOR("Gabor Juhos <juhosg@openwrt.org >");
201MODULE_DESCRIPTION(DRV_DESC);
202MODULE_VERSION(DRV_VERSION);
203