diff options
Diffstat (limited to 'drivers/i2c/busses/i2c-parport.c')
-rw-r--r-- | drivers/i2c/busses/i2c-parport.c | 267 |
1 files changed, 267 insertions, 0 deletions
diff --git a/drivers/i2c/busses/i2c-parport.c b/drivers/i2c/busses/i2c-parport.c new file mode 100644 index 000000000000..e9560bab51c4 --- /dev/null +++ b/drivers/i2c/busses/i2c-parport.c | |||
@@ -0,0 +1,267 @@ | |||
1 | /* ------------------------------------------------------------------------ * | ||
2 | * i2c-parport.c I2C bus over parallel port * | ||
3 | * ------------------------------------------------------------------------ * | ||
4 | Copyright (C) 2003-2004 Jean Delvare <khali@linux-fr.org> | ||
5 | |||
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> | ||
10 | Kyösti Mälkki <kmalkki@cc.hut.fi> | ||
11 | |||
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 | |||
27 | #include <linux/config.h> | ||
28 | #include <linux/kernel.h> | ||
29 | #include <linux/module.h> | ||
30 | #include <linux/init.h> | ||
31 | #include <linux/parport.h> | ||
32 | #include <linux/i2c.h> | ||
33 | #include <linux/i2c-algo-bit.h> | ||
34 | #include "i2c-parport.h" | ||
35 | |||
36 | /* ----- Device list ------------------------------------------------------ */ | ||
37 | |||
38 | struct i2c_par { | ||
39 | struct pardevice *pdev; | ||
40 | struct i2c_adapter adapter; | ||
41 | struct i2c_algo_bit_data algo_data; | ||
42 | struct i2c_par *next; | ||
43 | }; | ||
44 | |||
45 | static struct i2c_par *adapter_list; | ||
46 | |||
47 | /* ----- Low-level parallel port access ----------------------------------- */ | ||
48 | |||
49 | static void port_write_data(struct parport *p, unsigned char d) | ||
50 | { | ||
51 | parport_write_data(p, d); | ||
52 | } | ||
53 | |||
54 | static void port_write_control(struct parport *p, unsigned char d) | ||
55 | { | ||
56 | parport_write_control(p, d); | ||
57 | } | ||
58 | |||
59 | static unsigned char port_read_data(struct parport *p) | ||
60 | { | ||
61 | return parport_read_data(p); | ||
62 | } | ||
63 | |||
64 | static unsigned char port_read_status(struct parport *p) | ||
65 | { | ||
66 | return parport_read_status(p); | ||
67 | } | ||
68 | |||
69 | static unsigned char port_read_control(struct parport *p) | ||
70 | { | ||
71 | return parport_read_control(p); | ||
72 | } | ||
73 | |||
74 | static void (*port_write[])(struct parport *, unsigned char) = { | ||
75 | port_write_data, | ||
76 | NULL, | ||
77 | port_write_control, | ||
78 | }; | ||
79 | |||
80 | static unsigned char (*port_read[])(struct parport *) = { | ||
81 | port_read_data, | ||
82 | port_read_status, | ||
83 | port_read_control, | ||
84 | }; | ||
85 | |||
86 | /* ----- Unified line operation functions --------------------------------- */ | ||
87 | |||
88 | static inline void line_set(struct parport *data, int state, | ||
89 | const struct lineop *op) | ||
90 | { | ||
91 | u8 oldval = port_read[op->port](data); | ||
92 | |||
93 | /* Touch only the bit(s) needed */ | ||
94 | if ((op->inverted && !state) || (!op->inverted && state)) | ||
95 | port_write[op->port](data, oldval | op->val); | ||
96 | else | ||
97 | port_write[op->port](data, oldval & ~op->val); | ||
98 | } | ||
99 | |||
100 | static inline int line_get(struct parport *data, | ||
101 | const struct lineop *op) | ||
102 | { | ||
103 | u8 oldval = port_read[op->port](data); | ||
104 | |||
105 | return ((op->inverted && (oldval & op->val) != op->val) | ||
106 | || (!op->inverted && (oldval & op->val) == op->val)); | ||
107 | } | ||
108 | |||
109 | /* ----- I2C algorithm call-back functions and structures ----------------- */ | ||
110 | |||
111 | static void parport_setscl(void *data, int state) | ||
112 | { | ||
113 | line_set((struct parport *) data, state, &adapter_parm[type].setscl); | ||
114 | } | ||
115 | |||
116 | static void parport_setsda(void *data, int state) | ||
117 | { | ||
118 | line_set((struct parport *) data, state, &adapter_parm[type].setsda); | ||
119 | } | ||
120 | |||
121 | static int parport_getscl(void *data) | ||
122 | { | ||
123 | return line_get((struct parport *) data, &adapter_parm[type].getscl); | ||
124 | } | ||
125 | |||
126 | static int parport_getsda(void *data) | ||
127 | { | ||
128 | return line_get((struct parport *) data, &adapter_parm[type].getsda); | ||
129 | } | ||
130 | |||
131 | /* Encapsulate the functions above in the correct structure. | ||
132 | Note that this is only a template, from which the real structures are | ||
133 | copied. The attaching code will set getscl to NULL for adapters that | ||
134 | cannot read SCL back, and will also make the the data field point to | ||
135 | the parallel port structure. */ | ||
136 | static struct i2c_algo_bit_data parport_algo_data = { | ||
137 | .setsda = parport_setsda, | ||
138 | .setscl = parport_setscl, | ||
139 | .getsda = parport_getsda, | ||
140 | .getscl = parport_getscl, | ||
141 | .udelay = 60, | ||
142 | .mdelay = 60, | ||
143 | .timeout = HZ, | ||
144 | }; | ||
145 | |||
146 | /* ----- I2c and parallel port call-back functions and structures --------- */ | ||
147 | |||
148 | static struct i2c_adapter parport_adapter = { | ||
149 | .owner = THIS_MODULE, | ||
150 | .class = I2C_CLASS_HWMON, | ||
151 | .id = I2C_HW_B_LP, | ||
152 | .name = "Parallel port adapter", | ||
153 | }; | ||
154 | |||
155 | static void i2c_parport_attach (struct parport *port) | ||
156 | { | ||
157 | struct i2c_par *adapter; | ||
158 | |||
159 | adapter = kmalloc(sizeof(struct i2c_par), GFP_KERNEL); | ||
160 | if (adapter == NULL) { | ||
161 | printk(KERN_ERR "i2c-parport: Failed to kmalloc\n"); | ||
162 | return; | ||
163 | } | ||
164 | memset(adapter, 0x00, sizeof(struct i2c_par)); | ||
165 | |||
166 | pr_debug("i2c-parport: attaching to %s\n", port->name); | ||
167 | adapter->pdev = parport_register_device(port, "i2c-parport", | ||
168 | NULL, NULL, NULL, PARPORT_FLAG_EXCL, NULL); | ||
169 | if (!adapter->pdev) { | ||
170 | printk(KERN_ERR "i2c-parport: Unable to register with parport\n"); | ||
171 | goto ERROR0; | ||
172 | } | ||
173 | |||
174 | /* Fill the rest of the structure */ | ||
175 | adapter->adapter = parport_adapter; | ||
176 | adapter->algo_data = parport_algo_data; | ||
177 | if (!adapter_parm[type].getscl.val) | ||
178 | adapter->algo_data.getscl = NULL; | ||
179 | adapter->algo_data.data = port; | ||
180 | adapter->adapter.algo_data = &adapter->algo_data; | ||
181 | |||
182 | if (parport_claim_or_block(adapter->pdev) < 0) { | ||
183 | printk(KERN_ERR "i2c-parport: Could not claim parallel port\n"); | ||
184 | goto ERROR1; | ||
185 | } | ||
186 | |||
187 | /* Reset hardware to a sane state (SCL and SDA high) */ | ||
188 | parport_setsda(port, 1); | ||
189 | parport_setscl(port, 1); | ||
190 | /* Other init if needed (power on...) */ | ||
191 | if (adapter_parm[type].init.val) | ||
192 | line_set(port, 1, &adapter_parm[type].init); | ||
193 | |||
194 | parport_release(adapter->pdev); | ||
195 | |||
196 | if (i2c_bit_add_bus(&adapter->adapter) < 0) { | ||
197 | printk(KERN_ERR "i2c-parport: Unable to register with I2C\n"); | ||
198 | goto ERROR1; | ||
199 | } | ||
200 | |||
201 | /* Add the new adapter to the list */ | ||
202 | adapter->next = adapter_list; | ||
203 | adapter_list = adapter; | ||
204 | return; | ||
205 | |||
206 | ERROR1: | ||
207 | parport_unregister_device(adapter->pdev); | ||
208 | ERROR0: | ||
209 | kfree(adapter); | ||
210 | } | ||
211 | |||
212 | static void i2c_parport_detach (struct parport *port) | ||
213 | { | ||
214 | struct i2c_par *adapter, *prev; | ||
215 | |||
216 | /* Walk the list */ | ||
217 | for (prev = NULL, adapter = adapter_list; adapter; | ||
218 | prev = adapter, adapter = adapter->next) { | ||
219 | if (adapter->pdev->port == port) { | ||
220 | /* Un-init if needed (power off...) */ | ||
221 | if (adapter_parm[type].init.val) | ||
222 | line_set(port, 0, &adapter_parm[type].init); | ||
223 | |||
224 | i2c_bit_del_bus(&adapter->adapter); | ||
225 | parport_unregister_device(adapter->pdev); | ||
226 | if (prev) | ||
227 | prev->next = adapter->next; | ||
228 | else | ||
229 | adapter_list = adapter->next; | ||
230 | kfree(adapter); | ||
231 | return; | ||
232 | } | ||
233 | } | ||
234 | } | ||
235 | |||
236 | static struct parport_driver i2c_driver = { | ||
237 | .name = "i2c-parport", | ||
238 | .attach = i2c_parport_attach, | ||
239 | .detach = i2c_parport_detach, | ||
240 | }; | ||
241 | |||
242 | /* ----- Module loading, unloading and information ------------------------ */ | ||
243 | |||
244 | static int __init i2c_parport_init(void) | ||
245 | { | ||
246 | int type_count; | ||
247 | |||
248 | type_count = sizeof(adapter_parm)/sizeof(struct adapter_parm); | ||
249 | if (type < 0 || type >= type_count) { | ||
250 | printk(KERN_WARNING "i2c-parport: invalid type (%d)\n", type); | ||
251 | type = 0; | ||
252 | } | ||
253 | |||
254 | return parport_register_driver(&i2c_driver); | ||
255 | } | ||
256 | |||
257 | static void __exit i2c_parport_exit(void) | ||
258 | { | ||
259 | parport_unregister_driver(&i2c_driver); | ||
260 | } | ||
261 | |||
262 | MODULE_AUTHOR("Jean Delvare <khali@linux-fr.org>"); | ||
263 | MODULE_DESCRIPTION("I2C bus over parallel port"); | ||
264 | MODULE_LICENSE("GPL"); | ||
265 | |||
266 | module_init(i2c_parport_init); | ||
267 | module_exit(i2c_parport_exit); | ||