Details | Last modification | View Log | RSS feed
Rev | Author | Line No. | Line |
---|---|---|---|
420 | giacomo | 1 | /* ------------------------------------------------------------------------- */ |
2 | /* i2c-iop3xx.c i2c driver algorithms for Intel XScale IOP3xx */ |
||
3 | /* ------------------------------------------------------------------------- */ |
||
4 | /* Copyright (C) 2003 Peter Milne, D-TACQ Solutions Ltd |
||
5 | * <Peter dot Milne at D hyphen TACQ dot com> |
||
6 | |||
7 | This program is free software; you can redistribute it and/or modify |
||
8 | it under the terms of the GNU General Public License as published by |
||
9 | the Free Software Foundation, version 2. |
||
10 | |||
11 | |||
12 | This program is distributed in the hope that it will be useful, |
||
13 | but WITHOUT ANY WARRANTY; without even the implied warranty of |
||
14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||
15 | GNU General Public License for more details. |
||
16 | |||
17 | You should have received a copy of the GNU General Public License |
||
18 | along with this program; if not, write to the Free Software |
||
19 | Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ |
||
20 | /* ------------------------------------------------------------------------- */ |
||
21 | /* |
||
22 | With acknowledgements to i2c-algo-ibm_ocp.c by |
||
23 | Ian DaSilva, MontaVista Software, Inc. idasilva@mvista.com |
||
24 | |||
25 | And i2c-algo-pcf.c, which was created by Simon G. Vogl and Hans Berglund: |
||
26 | |||
27 | Copyright (C) 1995-1997 Simon G. Vogl, 1998-2000 Hans Berglund |
||
28 | |||
29 | And which acknowledged Kyösti Mälkki <kmalkki@cc.hut.fi>, |
||
30 | Frodo Looijaard <frodol@dds.nl>, Martin Bailey<mbailey@littlefeet-inc.com> |
||
31 | |||
32 | ---------------------------------------------------------------------------*/ |
||
33 | |||
34 | |||
35 | #include <linux/interrupt.h> |
||
36 | #include <linux/kernel.h> |
||
37 | #include <linux/module.h> |
||
38 | #include <linux/delay.h> |
||
39 | #include <linux/slab.h> |
||
40 | #include <linux/init.h> |
||
41 | #include <linux/errno.h> |
||
42 | #include <linux/sched.h> |
||
43 | #include <linux/i2c.h> |
||
44 | |||
45 | |||
46 | #include <asm/arch-iop3xx/iop321.h> |
||
47 | #include <asm/arch-iop3xx/iop321-irqs.h> |
||
48 | #include "i2c-iop3xx.h" |
||
49 | |||
50 | |||
51 | /* ----- global defines ----------------------------------------------- */ |
||
52 | #define PASSERT(x) do { if (!(x) ) \ |
||
53 | printk(KERN_CRIT "PASSERT %s in %s:%d\n", #x, __FILE__, __LINE__ );\ |
||
54 | } while (0) |
||
55 | |||
56 | |||
57 | /* ----- global variables --------------------------------------------- */ |
||
58 | |||
59 | |||
60 | static inline unsigned char iic_cook_addr(struct i2c_msg *msg) |
||
61 | { |
||
62 | unsigned char addr; |
||
63 | |||
64 | addr = (msg->addr << 1); |
||
65 | |||
66 | if (msg->flags & I2C_M_RD) |
||
67 | addr |= 1; |
||
68 | |||
69 | /* PGM: what is M_REV_DIR_ADDR - do we need it ?? */ |
||
70 | if (msg->flags & I2C_M_REV_DIR_ADDR) |
||
71 | addr ^= 1; |
||
72 | |||
73 | return addr; |
||
74 | } |
||
75 | |||
76 | |||
77 | static inline void iop3xx_adap_reset(struct i2c_algo_iop3xx_data *iop3xx_adap) |
||
78 | { |
||
79 | /* Follows devman 9.3 */ |
||
80 | *iop3xx_adap->biu->CR = IOP321_ICR_UNIT_RESET; |
||
81 | *iop3xx_adap->biu->SR = IOP321_ISR_CLEARBITS; |
||
82 | *iop3xx_adap->biu->CR = 0; |
||
83 | } |
||
84 | |||
85 | static inline void iop3xx_adap_set_slave_addr(struct i2c_algo_iop3xx_data *iop3xx_adap) |
||
86 | { |
||
87 | *iop3xx_adap->biu->SAR = MYSAR; |
||
88 | } |
||
89 | |||
90 | static inline void iop3xx_adap_enable(struct i2c_algo_iop3xx_data *iop3xx_adap) |
||
91 | { |
||
92 | u32 cr = IOP321_ICR_GCD|IOP321_ICR_SCLEN|IOP321_ICR_UE; |
||
93 | |||
94 | /* NB SR bits not same position as CR IE bits :-( */ |
||
95 | iop3xx_adap->biu->SR_enabled = |
||
96 | IOP321_ISR_ALD | IOP321_ISR_BERRD | |
||
97 | IOP321_ISR_RXFULL | IOP321_ISR_TXEMPTY; |
||
98 | |||
99 | cr |= IOP321_ICR_ALDIE | IOP321_ICR_BERRIE | |
||
100 | IOP321_ICR_RXFULLIE | IOP321_ICR_TXEMPTYIE; |
||
101 | |||
102 | *iop3xx_adap->biu->CR = cr; |
||
103 | } |
||
104 | |||
105 | static void iop3xx_adap_transaction_cleanup(struct i2c_algo_iop3xx_data *iop3xx_adap) |
||
106 | { |
||
107 | unsigned cr = *iop3xx_adap->biu->CR; |
||
108 | |||
109 | cr &= ~(IOP321_ICR_MSTART | IOP321_ICR_TBYTE | |
||
110 | IOP321_ICR_MSTOP | IOP321_ICR_SCLEN); |
||
111 | *iop3xx_adap->biu->CR = cr; |
||
112 | } |
||
113 | |||
114 | static void iop3xx_adap_final_cleanup(struct i2c_algo_iop3xx_data *iop3xx_adap) |
||
115 | { |
||
116 | unsigned cr = *iop3xx_adap->biu->CR; |
||
117 | |||
118 | cr &= ~(IOP321_ICR_ALDIE | IOP321_ICR_BERRIE | |
||
119 | IOP321_ICR_RXFULLIE | IOP321_ICR_TXEMPTYIE); |
||
120 | iop3xx_adap->biu->SR_enabled = 0; |
||
121 | *iop3xx_adap->biu->CR = cr; |
||
122 | } |
||
123 | |||
124 | /* |
||
125 | * NB: the handler has to clear the source of the interrupt! |
||
126 | * Then it passes the SR flags of interest to BH via adap data |
||
127 | */ |
||
128 | static void iop3xx_i2c_handler(int this_irq, |
||
129 | void *dev_id, |
||
130 | struct pt_regs *regs) |
||
131 | { |
||
132 | struct i2c_algo_iop3xx_data *iop3xx_adap = dev_id; |
||
133 | |||
134 | u32 sr = *iop3xx_adap->biu->SR; |
||
135 | |||
136 | if ((sr &= iop3xx_adap->biu->SR_enabled)) { |
||
137 | *iop3xx_adap->biu->SR = sr; |
||
138 | iop3xx_adap->biu->SR_received |= sr; |
||
139 | wake_up_interruptible(&iop3xx_adap->waitq); |
||
140 | } |
||
141 | } |
||
142 | |||
143 | /* check all error conditions, clear them , report most important */ |
||
144 | static int iop3xx_adap_error(u32 sr) |
||
145 | { |
||
146 | int rc = 0; |
||
147 | |||
148 | if ((sr&IOP321_ISR_BERRD)) { |
||
149 | if ( !rc ) rc = -I2C_ERR_BERR; |
||
150 | } |
||
151 | if ((sr&IOP321_ISR_ALD)) { |
||
152 | if ( !rc ) rc = -I2C_ERR_ALD; |
||
153 | } |
||
154 | return rc; |
||
155 | } |
||
156 | |||
157 | static inline u32 get_srstat(struct i2c_algo_iop3xx_data *iop3xx_adap) |
||
158 | { |
||
159 | unsigned long flags; |
||
160 | u32 sr; |
||
161 | |||
162 | spin_lock_irqsave(&iop3xx_adap->lock, flags); |
||
163 | sr = iop3xx_adap->biu->SR_received; |
||
164 | iop3xx_adap->biu->SR_received = 0; |
||
165 | spin_unlock_irqrestore(&iop3xx_adap->lock, flags); |
||
166 | |||
167 | return sr; |
||
168 | } |
||
169 | |||
170 | /* |
||
171 | * sleep until interrupted, then recover and analyse the SR |
||
172 | * saved by handler |
||
173 | */ |
||
174 | typedef int (* compare_func)(unsigned test, unsigned mask); |
||
175 | /* returns 1 on correct comparison */ |
||
176 | |||
177 | static int iop3xx_adap_wait_event(struct i2c_algo_iop3xx_data *iop3xx_adap, |
||
178 | unsigned flags, unsigned* status, |
||
179 | compare_func compare) |
||
180 | { |
||
181 | unsigned sr = 0; |
||
182 | int interrupted; |
||
183 | int done; |
||
184 | int rc; |
||
185 | |||
186 | do { |
||
187 | interrupted = wait_event_interruptible_timeout ( |
||
188 | iop3xx_adap->waitq, |
||
189 | (done = compare( sr = get_srstat(iop3xx_adap),flags )), |
||
190 | iop3xx_adap->timeout |
||
191 | ); |
||
192 | if ((rc = iop3xx_adap_error(sr)) < 0) { |
||
193 | *status = sr; |
||
194 | return rc; |
||
195 | }else if (!interrupted) { |
||
196 | *status = sr; |
||
197 | return rc = -ETIMEDOUT; |
||
198 | } |
||
199 | } while(!done); |
||
200 | |||
201 | *status = sr; |
||
202 | |||
203 | return rc = 0; |
||
204 | } |
||
205 | |||
206 | /* |
||
207 | * Concrete compare_funcs |
||
208 | */ |
||
209 | static int all_bits_clear(unsigned test, unsigned mask) |
||
210 | { |
||
211 | return (test & mask) == 0; |
||
212 | } |
||
213 | static int any_bits_set(unsigned test, unsigned mask) |
||
214 | { |
||
215 | return (test & mask) != 0; |
||
216 | } |
||
217 | |||
218 | static int iop3xx_adap_wait_tx_done(struct i2c_algo_iop3xx_data *iop3xx_adap, int *status) |
||
219 | { |
||
220 | return iop3xx_adap_wait_event( |
||
221 | iop3xx_adap, |
||
222 | IOP321_ISR_TXEMPTY|IOP321_ISR_ALD|IOP321_ISR_BERRD, |
||
223 | status, any_bits_set); |
||
224 | } |
||
225 | |||
226 | static int iop3xx_adap_wait_rx_done(struct i2c_algo_iop3xx_data *iop3xx_adap, int *status) |
||
227 | { |
||
228 | return iop3xx_adap_wait_event( |
||
229 | iop3xx_adap, |
||
230 | IOP321_ISR_RXFULL|IOP321_ISR_ALD|IOP321_ISR_BERRD, |
||
231 | status, any_bits_set); |
||
232 | } |
||
233 | |||
234 | static int iop3xx_adap_wait_idle(struct i2c_algo_iop3xx_data *iop3xx_adap, int *status) |
||
235 | { |
||
236 | return iop3xx_adap_wait_event( |
||
237 | iop3xx_adap, IOP321_ISR_UNITBUSY, status, all_bits_clear); |
||
238 | } |
||
239 | |||
240 | /* |
||
241 | * Description: This performs the IOP3xx initialization sequence |
||
242 | * Valid for IOP321. Maybe valid for IOP310?. |
||
243 | */ |
||
244 | static int iop3xx_adap_init (struct i2c_algo_iop3xx_data *iop3xx_adap) |
||
245 | { |
||
246 | *IOP321_GPOD &= ~(iop3xx_adap->channel==0 ? |
||
247 | IOP321_GPOD_I2C0: |
||
248 | IOP321_GPOD_I2C1); |
||
249 | |||
250 | iop3xx_adap_reset(iop3xx_adap); |
||
251 | iop3xx_adap_set_slave_addr(iop3xx_adap); |
||
252 | iop3xx_adap_enable(iop3xx_adap); |
||
253 | |||
254 | return 0; |
||
255 | } |
||
256 | |||
257 | static int iop3xx_adap_send_target_slave_addr(struct i2c_algo_iop3xx_data *iop3xx_adap, |
||
258 | struct i2c_msg* msg) |
||
259 | { |
||
260 | unsigned cr = *iop3xx_adap->biu->CR; |
||
261 | int status; |
||
262 | int rc; |
||
263 | |||
264 | *iop3xx_adap->biu->DBR = iic_cook_addr(msg); |
||
265 | |||
266 | cr &= ~(IOP321_ICR_MSTOP | IOP321_ICR_NACK); |
||
267 | cr |= IOP321_ICR_MSTART | IOP321_ICR_TBYTE; |
||
268 | |||
269 | *iop3xx_adap->biu->CR = cr; |
||
270 | rc = iop3xx_adap_wait_tx_done(iop3xx_adap, &status); |
||
271 | /* this assert fires every time, contrary to IOP manual |
||
272 | PASSERT((status&IOP321_ISR_UNITBUSY)!=0); |
||
273 | */ |
||
274 | PASSERT((status&IOP321_ISR_RXREAD)==0); |
||
275 | |||
276 | return rc; |
||
277 | } |
||
278 | |||
279 | static int iop3xx_adap_write_byte(struct i2c_algo_iop3xx_data *iop3xx_adap, char byte, int stop) |
||
280 | { |
||
281 | unsigned cr = *iop3xx_adap->biu->CR; |
||
282 | int status; |
||
283 | int rc; |
||
284 | |||
285 | *iop3xx_adap->biu->DBR = byte; |
||
286 | cr &= ~IOP321_ICR_MSTART; |
||
287 | if (stop) { |
||
288 | cr |= IOP321_ICR_MSTOP; |
||
289 | } else { |
||
290 | cr &= ~IOP321_ICR_MSTOP; |
||
291 | } |
||
292 | *iop3xx_adap->biu->CR = cr |= IOP321_ICR_TBYTE; |
||
293 | rc = iop3xx_adap_wait_tx_done(iop3xx_adap, &status); |
||
294 | |||
295 | return rc; |
||
296 | } |
||
297 | |||
298 | static int iop3xx_adap_read_byte(struct i2c_algo_iop3xx_data *iop3xx_adap, |
||
299 | char* byte, int stop) |
||
300 | { |
||
301 | unsigned cr = *iop3xx_adap->biu->CR; |
||
302 | int status; |
||
303 | int rc; |
||
304 | |||
305 | cr &= ~IOP321_ICR_MSTART; |
||
306 | |||
307 | if (stop) { |
||
308 | cr |= IOP321_ICR_MSTOP|IOP321_ICR_NACK; |
||
309 | } else { |
||
310 | cr &= ~(IOP321_ICR_MSTOP|IOP321_ICR_NACK); |
||
311 | } |
||
312 | *iop3xx_adap->biu->CR = cr |= IOP321_ICR_TBYTE; |
||
313 | |||
314 | rc = iop3xx_adap_wait_rx_done(iop3xx_adap, &status); |
||
315 | |||
316 | *byte = *iop3xx_adap->biu->DBR; |
||
317 | |||
318 | return rc; |
||
319 | } |
||
320 | |||
321 | static int iop3xx_i2c_writebytes(struct i2c_adapter *i2c_adap, |
||
322 | const char *buf, int count) |
||
323 | { |
||
324 | struct i2c_algo_iop3xx_data *iop3xx_adap = i2c_adap->algo_data; |
||
325 | int ii; |
||
326 | int rc = 0; |
||
327 | |||
328 | for (ii = 0; rc == 0 && ii != count; ++ii) { |
||
329 | rc = iop3xx_adap_write_byte(iop3xx_adap, buf[ii], ii==count-1); |
||
330 | } |
||
331 | return rc; |
||
332 | } |
||
333 | |||
334 | static int iop3xx_i2c_readbytes(struct i2c_adapter *i2c_adap, |
||
335 | char *buf, int count) |
||
336 | { |
||
337 | struct i2c_algo_iop3xx_data *iop3xx_adap = i2c_adap->algo_data; |
||
338 | int ii; |
||
339 | int rc = 0; |
||
340 | |||
341 | for (ii = 0; rc == 0 && ii != count; ++ii) { |
||
342 | rc = iop3xx_adap_read_byte(iop3xx_adap, &buf[ii], ii==count-1); |
||
343 | } |
||
344 | return rc; |
||
345 | } |
||
346 | |||
347 | /* |
||
348 | * Description: This function implements combined transactions. Combined |
||
349 | * transactions consist of combinations of reading and writing blocks of data. |
||
350 | * FROM THE SAME ADDRESS |
||
351 | * Each transfer (i.e. a read or a write) is separated by a repeated start |
||
352 | * condition. |
||
353 | */ |
||
354 | static int iop3xx_handle_msg(struct i2c_adapter *i2c_adap, struct i2c_msg* pmsg) |
||
355 | { |
||
356 | struct i2c_algo_iop3xx_data *iop3xx_adap = i2c_adap->algo_data; |
||
357 | int rc; |
||
358 | |||
359 | rc = iop3xx_adap_send_target_slave_addr(iop3xx_adap, pmsg); |
||
360 | if (rc < 0) { |
||
361 | return rc; |
||
362 | } |
||
363 | |||
364 | if ((pmsg->flags&I2C_M_RD)) { |
||
365 | return iop3xx_i2c_readbytes(i2c_adap, pmsg->buf, pmsg->len); |
||
366 | } else { |
||
367 | return iop3xx_i2c_writebytes(i2c_adap, pmsg->buf, pmsg->len); |
||
368 | } |
||
369 | } |
||
370 | |||
371 | /* |
||
372 | * master_xfer() - main read/write entry |
||
373 | */ |
||
374 | static int iop3xx_master_xfer(struct i2c_adapter *i2c_adap, struct i2c_msg msgs[], int num) |
||
375 | { |
||
376 | struct i2c_algo_iop3xx_data *iop3xx_adap = i2c_adap->algo_data; |
||
377 | int im = 0; |
||
378 | int ret = 0; |
||
379 | int status; |
||
380 | |||
381 | iop3xx_adap_wait_idle(iop3xx_adap, &status); |
||
382 | iop3xx_adap_reset(iop3xx_adap); |
||
383 | iop3xx_adap_enable(iop3xx_adap); |
||
384 | |||
385 | for (im = 0; ret == 0 && im != num; ++im) { |
||
386 | ret = iop3xx_handle_msg(i2c_adap, &msgs[im]); |
||
387 | } |
||
388 | |||
389 | iop3xx_adap_transaction_cleanup(iop3xx_adap); |
||
390 | |||
391 | return ret; |
||
392 | } |
||
393 | |||
394 | static int algo_control(struct i2c_adapter *adapter, unsigned int cmd, |
||
395 | unsigned long arg) |
||
396 | { |
||
397 | return 0; |
||
398 | } |
||
399 | |||
400 | static u32 iic_func(struct i2c_adapter *adap) |
||
401 | { |
||
402 | return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL; |
||
403 | } |
||
404 | |||
405 | |||
406 | /* -----exported algorithm data: ------------------------------------- */ |
||
407 | |||
408 | static struct i2c_algorithm iic_algo = { |
||
409 | .name = "IOP3xx I2C algorithm", |
||
410 | .id = I2C_ALGO_OCP_IOP3XX, |
||
411 | .master_xfer = iop3xx_master_xfer, |
||
412 | .algo_control = algo_control, |
||
413 | .functionality = iic_func, |
||
414 | }; |
||
415 | |||
416 | /* |
||
417 | * registering functions to load algorithms at runtime |
||
418 | */ |
||
419 | static int i2c_iop3xx_add_bus(struct i2c_adapter *iic_adap) |
||
420 | { |
||
421 | struct i2c_algo_iop3xx_data *iop3xx_adap = iic_adap->algo_data; |
||
422 | |||
423 | if (!request_region( REGION_START(iop3xx_adap), |
||
424 | REGION_LENGTH(iop3xx_adap), |
||
425 | iic_adap->name)) { |
||
426 | return -ENODEV; |
||
427 | } |
||
428 | |||
429 | init_waitqueue_head(&iop3xx_adap->waitq); |
||
430 | spin_lock_init(&iop3xx_adap->lock); |
||
431 | |||
432 | if (request_irq( |
||
433 | iop3xx_adap->biu->irq, |
||
434 | iop3xx_i2c_handler, |
||
435 | /* SA_SAMPLE_RANDOM */ 0, |
||
436 | iic_adap->name, |
||
437 | iop3xx_adap)) { |
||
438 | return -ENODEV; |
||
439 | } |
||
440 | |||
441 | /* register new iic_adapter to i2c module... */ |
||
442 | iic_adap->id |= iic_algo.id; |
||
443 | iic_adap->algo = &iic_algo; |
||
444 | |||
445 | iic_adap->timeout = 100; /* default values, should */ |
||
446 | iic_adap->retries = 3; /* be replaced by defines */ |
||
447 | |||
448 | iop3xx_adap_init(iic_adap->algo_data); |
||
449 | i2c_add_adapter(iic_adap); |
||
450 | return 0; |
||
451 | } |
||
452 | |||
453 | static int i2c_iop3xx_del_bus(struct i2c_adapter *iic_adap) |
||
454 | { |
||
455 | struct i2c_algo_iop3xx_data *iop3xx_adap = iic_adap->algo_data; |
||
456 | |||
457 | iop3xx_adap_final_cleanup(iop3xx_adap); |
||
458 | free_irq(iop3xx_adap->biu->irq, iop3xx_adap); |
||
459 | |||
460 | release_region(REGION_START(iop3xx_adap), REGION_LENGTH(iop3xx_adap)); |
||
461 | |||
462 | return i2c_del_adapter(iic_adap); |
||
463 | } |
||
464 | |||
465 | #ifdef CONFIG_ARCH_IOP321 |
||
466 | |||
467 | static struct iop3xx_biu biu0 = { |
||
468 | .CR = IOP321_ICR0, |
||
469 | .SR = IOP321_ISR0, |
||
470 | .SAR = IOP321_ISAR0, |
||
471 | .DBR = IOP321_IDBR0, |
||
472 | .BMR = IOP321_IBMR0, |
||
473 | .irq = IRQ_IOP321_I2C_0, |
||
474 | }; |
||
475 | |||
476 | static struct iop3xx_biu biu1 = { |
||
477 | .CR = IOP321_ICR1, |
||
478 | .SR = IOP321_ISR1, |
||
479 | .SAR = IOP321_ISAR1, |
||
480 | .DBR = IOP321_IDBR1, |
||
481 | .BMR = IOP321_IBMR1, |
||
482 | .irq = IRQ_IOP321_I2C_1, |
||
483 | }; |
||
484 | |||
485 | #define ADAPTER_NAME_ROOT "IOP321 i2c biu adapter " |
||
486 | #else |
||
487 | #error Please define the BIU struct iop3xx_biu for your processor arch |
||
488 | #endif |
||
489 | |||
490 | static struct i2c_algo_iop3xx_data algo_iop3xx_data0 = { |
||
491 | .channel = 0, |
||
492 | .biu = &biu0, |
||
493 | .timeout = 1*HZ, |
||
494 | }; |
||
495 | static struct i2c_algo_iop3xx_data algo_iop3xx_data1 = { |
||
496 | .channel = 1, |
||
497 | .biu = &biu1, |
||
498 | .timeout = 1*HZ, |
||
499 | }; |
||
500 | |||
501 | static struct i2c_adapter iop3xx_ops0 = { |
||
502 | .owner = THIS_MODULE, |
||
503 | .name = ADAPTER_NAME_ROOT "0", |
||
504 | .id = I2C_HW_IOP321, |
||
505 | .algo_data = &algo_iop3xx_data0, |
||
506 | }; |
||
507 | static struct i2c_adapter iop3xx_ops1 = { |
||
508 | .owner = THIS_MODULE, |
||
509 | .name = ADAPTER_NAME_ROOT "1", |
||
510 | .id = I2C_HW_IOP321, |
||
511 | .algo_data = &algo_iop3xx_data1, |
||
512 | }; |
||
513 | |||
514 | static int __init i2c_iop3xx_init (void) |
||
515 | { |
||
516 | return i2c_iop3xx_add_bus(&iop3xx_ops0) || |
||
517 | i2c_iop3xx_add_bus(&iop3xx_ops1); |
||
518 | } |
||
519 | |||
520 | static void __exit i2c_iop3xx_exit (void) |
||
521 | { |
||
522 | i2c_iop3xx_del_bus(&iop3xx_ops0); |
||
523 | i2c_iop3xx_del_bus(&iop3xx_ops1); |
||
524 | } |
||
525 | |||
526 | module_init (i2c_iop3xx_init); |
||
527 | module_exit (i2c_iop3xx_exit); |
||
528 | |||
529 | MODULE_AUTHOR("D-TACQ Solutions Ltd <www.d-tacq.com>"); |
||
530 | MODULE_DESCRIPTION("IOP3xx iic algorithm and driver"); |
||
531 | MODULE_LICENSE("GPL"); |
||
532 | |||
533 | MODULE_PARM(i2c_debug,"i"); |
||
534 | |||
535 | MODULE_PARM_DESC(i2c_debug, "debug level - 0 off; 1 normal; 2,3 more verbose; 9 iic-protocol"); |
||
536 |