blob: 846583ed476352168bd4bde779adb224cad08470 [file] [log] [blame]
Linus Torvalds1da177e2005-04-16 15:20:36 -07001/* ------------------------------------------------------------------------ *
2 * i2c-parport.c I2C bus over parallel port *
3 * ------------------------------------------------------------------------ *
Jean Delvare35859252010-03-02 12:23:44 +01004 Copyright (C) 2003-2010 Jean Delvare <khali@linux-fr.org>
Linus Torvalds1da177e2005-04-16 15:20:36 -07005
6 Based on older i2c-philips-par.c driver
7 Copyright (C) 1995-2000 Simon G. Vogl
8 With some changes from:
9 Frodo Looijaard <frodol@dds.nl>
Jan Engelhardt96de0e22007-10-19 23:21:04 +020010 Kyösti Mälkki <kmalkki@cc.hut.fi>
Linus Torvalds1da177e2005-04-16 15:20:36 -070011
12 This program is free software; you can redistribute it and/or modify
13 it under the terms of the GNU General Public License as published by
14 the Free Software Foundation; either version 2 of the License, or
15 (at your option) any later version.
16
17 This program is distributed in the hope that it will be useful,
18 but WITHOUT ANY WARRANTY; without even the implied warranty of
19 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 GNU General Public License for more details.
21
22 You should have received a copy of the GNU General Public License
23 along with this program; if not, write to the Free Software
24 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
25 * ------------------------------------------------------------------------ */
26
Linus Torvalds1da177e2005-04-16 15:20:36 -070027#include <linux/kernel.h>
28#include <linux/module.h>
29#include <linux/init.h>
Jean Delvare6d376fc2010-03-02 12:23:41 +010030#include <linux/delay.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070031#include <linux/parport.h>
32#include <linux/i2c.h>
33#include <linux/i2c-algo-bit.h>
Jean Delvare35859252010-03-02 12:23:44 +010034#include <linux/i2c-smbus.h>
Tejun Heo5a0e3ad2010-03-24 17:04:11 +090035#include <linux/slab.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070036#include "i2c-parport.h"
37
38/* ----- Device list ------------------------------------------------------ */
39
40struct i2c_par {
41 struct pardevice *pdev;
42 struct i2c_adapter adapter;
43 struct i2c_algo_bit_data algo_data;
Jean Delvare35859252010-03-02 12:23:44 +010044 struct i2c_smbus_alert_setup alert_data;
45 struct i2c_client *ara;
Linus Torvalds1da177e2005-04-16 15:20:36 -070046 struct i2c_par *next;
47};
48
49static struct i2c_par *adapter_list;
50
51/* ----- Low-level parallel port access ----------------------------------- */
52
53static void port_write_data(struct parport *p, unsigned char d)
54{
55 parport_write_data(p, d);
56}
57
58static void port_write_control(struct parport *p, unsigned char d)
59{
60 parport_write_control(p, d);
61}
62
63static unsigned char port_read_data(struct parport *p)
64{
65 return parport_read_data(p);
66}
67
68static unsigned char port_read_status(struct parport *p)
69{
70 return parport_read_status(p);
71}
72
73static unsigned char port_read_control(struct parport *p)
74{
75 return parport_read_control(p);
76}
77
78static void (*port_write[])(struct parport *, unsigned char) = {
79 port_write_data,
80 NULL,
81 port_write_control,
82};
83
84static unsigned char (*port_read[])(struct parport *) = {
85 port_read_data,
86 port_read_status,
87 port_read_control,
88};
89
90/* ----- Unified line operation functions --------------------------------- */
91
92static inline void line_set(struct parport *data, int state,
93 const struct lineop *op)
94{
95 u8 oldval = port_read[op->port](data);
96
97 /* Touch only the bit(s) needed */
98 if ((op->inverted && !state) || (!op->inverted && state))
99 port_write[op->port](data, oldval | op->val);
100 else
101 port_write[op->port](data, oldval & ~op->val);
102}
103
104static inline int line_get(struct parport *data,
105 const struct lineop *op)
106{
107 u8 oldval = port_read[op->port](data);
108
109 return ((op->inverted && (oldval & op->val) != op->val)
110 || (!op->inverted && (oldval & op->val) == op->val));
111}
112
113/* ----- I2C algorithm call-back functions and structures ----------------- */
114
115static void parport_setscl(void *data, int state)
116{
117 line_set((struct parport *) data, state, &adapter_parm[type].setscl);
118}
119
120static void parport_setsda(void *data, int state)
121{
122 line_set((struct parport *) data, state, &adapter_parm[type].setsda);
123}
124
125static int parport_getscl(void *data)
126{
127 return line_get((struct parport *) data, &adapter_parm[type].getscl);
128}
129
130static int parport_getsda(void *data)
131{
132 return line_get((struct parport *) data, &adapter_parm[type].getsda);
133}
134
135/* Encapsulate the functions above in the correct structure.
136 Note that this is only a template, from which the real structures are
137 copied. The attaching code will set getscl to NULL for adapters that
Tobias Klauser614e24b2005-05-19 21:39:06 +0200138 cannot read SCL back, and will also make the data field point to
Linus Torvalds1da177e2005-04-16 15:20:36 -0700139 the parallel port structure. */
140static struct i2c_algo_bit_data parport_algo_data = {
141 .setsda = parport_setsda,
142 .setscl = parport_setscl,
143 .getsda = parport_getsda,
144 .getscl = parport_getscl,
Jean Delvare3af07bd2007-05-01 23:26:30 +0200145 .udelay = 10, /* ~50 kbps */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700146 .timeout = HZ,
147};
148
149/* ----- I2c and parallel port call-back functions and structures --------- */
150
Jean Delvare35859252010-03-02 12:23:44 +0100151void i2c_parport_irq(void *data)
152{
153 struct i2c_par *adapter = data;
154 struct i2c_client *ara = adapter->ara;
155
156 if (ara) {
157 dev_dbg(&ara->dev, "SMBus alert received\n");
158 i2c_handle_smbus_alert(ara);
159 } else
160 dev_dbg(&adapter->adapter.dev,
161 "SMBus alert received but no ARA client!\n");
162}
163
Linus Torvalds1da177e2005-04-16 15:20:36 -0700164static void i2c_parport_attach (struct parport *port)
165{
166 struct i2c_par *adapter;
167
Deepak Saxena5263ebb2005-10-17 23:09:43 +0200168 adapter = kzalloc(sizeof(struct i2c_par), GFP_KERNEL);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700169 if (adapter == NULL) {
Deepak Saxena5263ebb2005-10-17 23:09:43 +0200170 printk(KERN_ERR "i2c-parport: Failed to kzalloc\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700171 return;
172 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700173
174 pr_debug("i2c-parport: attaching to %s\n", port->name);
Jean Delvare35859252010-03-02 12:23:44 +0100175 parport_disable_irq(port);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700176 adapter->pdev = parport_register_device(port, "i2c-parport",
Jean Delvare35859252010-03-02 12:23:44 +0100177 NULL, NULL, i2c_parport_irq, PARPORT_FLAG_EXCL, adapter);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700178 if (!adapter->pdev) {
179 printk(KERN_ERR "i2c-parport: Unable to register with parport\n");
180 goto ERROR0;
181 }
182
183 /* Fill the rest of the structure */
Jean Delvarecacf2262007-05-01 23:26:29 +0200184 adapter->adapter.owner = THIS_MODULE;
185 adapter->adapter.class = I2C_CLASS_HWMON;
Jean Delvarecacf2262007-05-01 23:26:29 +0200186 strlcpy(adapter->adapter.name, "Parallel port adapter",
187 sizeof(adapter->adapter.name));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700188 adapter->algo_data = parport_algo_data;
Jean Delvare3af07bd2007-05-01 23:26:30 +0200189 /* Slow down if we can't sense SCL */
190 if (!adapter_parm[type].getscl.val) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700191 adapter->algo_data.getscl = NULL;
Jean Delvare3af07bd2007-05-01 23:26:30 +0200192 adapter->algo_data.udelay = 50; /* ~10 kbps */
193 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700194 adapter->algo_data.data = port;
195 adapter->adapter.algo_data = &adapter->algo_data;
David Brownellda675292007-05-08 00:27:42 -0700196 adapter->adapter.dev.parent = port->physport->dev;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700197
198 if (parport_claim_or_block(adapter->pdev) < 0) {
199 printk(KERN_ERR "i2c-parport: Could not claim parallel port\n");
200 goto ERROR1;
201 }
202
203 /* Reset hardware to a sane state (SCL and SDA high) */
204 parport_setsda(port, 1);
205 parport_setscl(port, 1);
206 /* Other init if needed (power on...) */
Jean Delvare6d376fc2010-03-02 12:23:41 +0100207 if (adapter_parm[type].init.val) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700208 line_set(port, 1, &adapter_parm[type].init);
Jean Delvare6d376fc2010-03-02 12:23:41 +0100209 /* Give powered devices some time to settle */
210 msleep(100);
211 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700212
Linus Torvalds1da177e2005-04-16 15:20:36 -0700213 if (i2c_bit_add_bus(&adapter->adapter) < 0) {
214 printk(KERN_ERR "i2c-parport: Unable to register with I2C\n");
215 goto ERROR1;
216 }
217
Jean Delvare35859252010-03-02 12:23:44 +0100218 /* Setup SMBus alert if supported */
219 if (adapter_parm[type].smbus_alert) {
220 adapter->alert_data.alert_edge_triggered = 1;
221 adapter->ara = i2c_setup_smbus_alert(&adapter->adapter,
222 &adapter->alert_data);
223 if (adapter->ara)
224 parport_enable_irq(port);
225 else
226 printk(KERN_WARNING "i2c-parport: Failed to register "
227 "ARA client\n");
228 }
229
Linus Torvalds1da177e2005-04-16 15:20:36 -0700230 /* Add the new adapter to the list */
231 adapter->next = adapter_list;
232 adapter_list = adapter;
233 return;
234
235ERROR1:
Jean Delvare7b964f7332008-11-28 15:24:39 +0100236 parport_release(adapter->pdev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700237 parport_unregister_device(adapter->pdev);
238ERROR0:
239 kfree(adapter);
240}
241
242static void i2c_parport_detach (struct parport *port)
243{
244 struct i2c_par *adapter, *prev;
245
246 /* Walk the list */
247 for (prev = NULL, adapter = adapter_list; adapter;
248 prev = adapter, adapter = adapter->next) {
249 if (adapter->pdev->port == port) {
Jean Delvare35859252010-03-02 12:23:44 +0100250 if (adapter->ara) {
251 parport_disable_irq(port);
252 i2c_unregister_device(adapter->ara);
253 }
Jean Delvare3af07bd2007-05-01 23:26:30 +0200254 i2c_del_adapter(&adapter->adapter);
255
Linus Torvalds1da177e2005-04-16 15:20:36 -0700256 /* Un-init if needed (power off...) */
257 if (adapter_parm[type].init.val)
258 line_set(port, 0, &adapter_parm[type].init);
259
Jean Delvare7b964f7332008-11-28 15:24:39 +0100260 parport_release(adapter->pdev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700261 parport_unregister_device(adapter->pdev);
262 if (prev)
263 prev->next = adapter->next;
264 else
265 adapter_list = adapter->next;
266 kfree(adapter);
267 return;
268 }
269 }
270}
271
Jean Delvare6c129be2005-10-08 00:17:35 +0200272static struct parport_driver i2c_parport_driver = {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700273 .name = "i2c-parport",
274 .attach = i2c_parport_attach,
275 .detach = i2c_parport_detach,
276};
277
278/* ----- Module loading, unloading and information ------------------------ */
279
280static int __init i2c_parport_init(void)
281{
Mark M. Hoffmane97b81d2006-03-23 16:50:25 +0100282 if (type < 0) {
283 printk(KERN_WARNING "i2c-parport: adapter type unspecified\n");
284 return -ENODEV;
285 }
286
287 if (type >= ARRAY_SIZE(adapter_parm)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700288 printk(KERN_WARNING "i2c-parport: invalid type (%d)\n", type);
Mark M. Hoffmane97b81d2006-03-23 16:50:25 +0100289 return -ENODEV;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700290 }
Tobias Klauser7e3d7db2006-01-09 23:19:51 +0100291
Jean Delvare6c129be2005-10-08 00:17:35 +0200292 return parport_register_driver(&i2c_parport_driver);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700293}
294
295static void __exit i2c_parport_exit(void)
296{
Jean Delvare6c129be2005-10-08 00:17:35 +0200297 parport_unregister_driver(&i2c_parport_driver);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700298}
299
300MODULE_AUTHOR("Jean Delvare <khali@linux-fr.org>");
301MODULE_DESCRIPTION("I2C bus over parallel port");
302MODULE_LICENSE("GPL");
303
304module_init(i2c_parport_init);
305module_exit(i2c_parport_exit);