diff options
Diffstat (limited to 'drivers/i2c/busses/i2c-iop3xx.c')
-rw-r--r-- | drivers/i2c/busses/i2c-iop3xx.c | 554 |
1 files changed, 554 insertions, 0 deletions
diff --git a/drivers/i2c/busses/i2c-iop3xx.c b/drivers/i2c/busses/i2c-iop3xx.c new file mode 100644 index 000000000000..c961ba4cfb32 --- /dev/null +++ b/drivers/i2c/busses/i2c-iop3xx.c | |||
@@ -0,0 +1,554 @@ | |||
1 | /* ------------------------------------------------------------------------- */ | ||
2 | /* i2c-iop3xx.c i2c driver algorithms for Intel XScale IOP3xx & IXP46x */ | ||
3 | /* ------------------------------------------------------------------------- */ | ||
4 | /* Copyright (C) 2003 Peter Milne, D-TACQ Solutions Ltd | ||
5 | * <Peter dot Milne at D hyphen TACQ dot com> | ||
6 | * | ||
7 | * With acknowledgements to i2c-algo-ibm_ocp.c by | ||
8 | * Ian DaSilva, MontaVista Software, Inc. idasilva@mvista.com | ||
9 | * | ||
10 | * And i2c-algo-pcf.c, which was created by Simon G. Vogl and Hans Berglund: | ||
11 | * | ||
12 | * Copyright (C) 1995-1997 Simon G. Vogl, 1998-2000 Hans Berglund | ||
13 | * | ||
14 | * And which acknowledged Kyösti Mälkki <kmalkki@cc.hut.fi>, | ||
15 | * Frodo Looijaard <frodol@dds.nl>, Martin Bailey<mbailey@littlefeet-inc.com> | ||
16 | * | ||
17 | * Major cleanup by Deepak Saxena <dsaxena@plexity.net>, 01/2005: | ||
18 | * | ||
19 | * - Use driver model to pass per-chip info instead of hardcoding and #ifdefs | ||
20 | * - Use ioremap/__raw_readl/__raw_writel instead of direct dereference | ||
21 | * - Make it work with IXP46x chips | ||
22 | * - Cleanup function names, coding style, etc | ||
23 | * | ||
24 | * This program is free software; you can redistribute it and/or modify | ||
25 | * it under the terms of the GNU General Public License as published by | ||
26 | * the Free Software Foundation, version 2. | ||
27 | */ | ||
28 | |||
29 | #include <linux/config.h> | ||
30 | #include <linux/interrupt.h> | ||
31 | #include <linux/kernel.h> | ||
32 | #include <linux/module.h> | ||
33 | #include <linux/delay.h> | ||
34 | #include <linux/slab.h> | ||
35 | #include <linux/init.h> | ||
36 | #include <linux/errno.h> | ||
37 | #include <linux/sched.h> | ||
38 | #include <linux/device.h> | ||
39 | #include <linux/i2c.h> | ||
40 | |||
41 | #include <asm/io.h> | ||
42 | |||
43 | #include "i2c-iop3xx.h" | ||
44 | |||
45 | /* global unit counter */ | ||
46 | static int i2c_id = 0; | ||
47 | |||
48 | static inline unsigned char | ||
49 | iic_cook_addr(struct i2c_msg *msg) | ||
50 | { | ||
51 | unsigned char addr; | ||
52 | |||
53 | addr = (msg->addr << 1); | ||
54 | |||
55 | if (msg->flags & I2C_M_RD) | ||
56 | addr |= 1; | ||
57 | |||
58 | /* | ||
59 | * Read or Write? | ||
60 | */ | ||
61 | if (msg->flags & I2C_M_REV_DIR_ADDR) | ||
62 | addr ^= 1; | ||
63 | |||
64 | return addr; | ||
65 | } | ||
66 | |||
67 | static void | ||
68 | iop3xx_i2c_reset(struct i2c_algo_iop3xx_data *iop3xx_adap) | ||
69 | { | ||
70 | /* Follows devman 9.3 */ | ||
71 | __raw_writel(IOP3XX_ICR_UNIT_RESET, iop3xx_adap->ioaddr + CR_OFFSET); | ||
72 | __raw_writel(IOP3XX_ISR_CLEARBITS, iop3xx_adap->ioaddr + SR_OFFSET); | ||
73 | __raw_writel(0, iop3xx_adap->ioaddr + CR_OFFSET); | ||
74 | } | ||
75 | |||
76 | static void | ||
77 | iop3xx_i2c_set_slave_addr(struct i2c_algo_iop3xx_data *iop3xx_adap) | ||
78 | { | ||
79 | __raw_writel(MYSAR, iop3xx_adap->ioaddr + SAR_OFFSET); | ||
80 | } | ||
81 | |||
82 | static void | ||
83 | iop3xx_i2c_enable(struct i2c_algo_iop3xx_data *iop3xx_adap) | ||
84 | { | ||
85 | u32 cr = IOP3XX_ICR_GCD | IOP3XX_ICR_SCLEN | IOP3XX_ICR_UE; | ||
86 | |||
87 | /* | ||
88 | * Everytime unit enable is asserted, GPOD needs to be cleared | ||
89 | * on IOP321 to avoid data corruption on the bus. | ||
90 | */ | ||
91 | #ifdef CONFIG_ARCH_IOP321 | ||
92 | #define IOP321_GPOD_I2C0 0x00c0 /* clear these bits to enable ch0 */ | ||
93 | #define IOP321_GPOD_I2C1 0x0030 /* clear these bits to enable ch1 */ | ||
94 | |||
95 | *IOP321_GPOD &= (iop3xx_adap->id == 0) ? ~IOP321_GPOD_I2C0 : | ||
96 | ~IOP321_GPOD_I2C1; | ||
97 | #endif | ||
98 | /* NB SR bits not same position as CR IE bits :-( */ | ||
99 | iop3xx_adap->SR_enabled = | ||
100 | IOP3XX_ISR_ALD | IOP3XX_ISR_BERRD | | ||
101 | IOP3XX_ISR_RXFULL | IOP3XX_ISR_TXEMPTY; | ||
102 | |||
103 | cr |= IOP3XX_ICR_ALD_IE | IOP3XX_ICR_BERR_IE | | ||
104 | IOP3XX_ICR_RXFULL_IE | IOP3XX_ICR_TXEMPTY_IE; | ||
105 | |||
106 | __raw_writel(cr, iop3xx_adap->ioaddr + CR_OFFSET); | ||
107 | } | ||
108 | |||
109 | static void | ||
110 | iop3xx_i2c_transaction_cleanup(struct i2c_algo_iop3xx_data *iop3xx_adap) | ||
111 | { | ||
112 | unsigned long cr = __raw_readl(iop3xx_adap->ioaddr + CR_OFFSET); | ||
113 | |||
114 | cr &= ~(IOP3XX_ICR_MSTART | IOP3XX_ICR_TBYTE | | ||
115 | IOP3XX_ICR_MSTOP | IOP3XX_ICR_SCLEN); | ||
116 | |||
117 | __raw_writel(cr, iop3xx_adap->ioaddr + CR_OFFSET); | ||
118 | } | ||
119 | |||
120 | /* | ||
121 | * NB: the handler has to clear the source of the interrupt! | ||
122 | * Then it passes the SR flags of interest to BH via adap data | ||
123 | */ | ||
124 | static irqreturn_t | ||
125 | iop3xx_i2c_irq_handler(int this_irq, void *dev_id, struct pt_regs *regs) | ||
126 | { | ||
127 | struct i2c_algo_iop3xx_data *iop3xx_adap = dev_id; | ||
128 | u32 sr = __raw_readl(iop3xx_adap->ioaddr + SR_OFFSET); | ||
129 | |||
130 | if ((sr &= iop3xx_adap->SR_enabled)) { | ||
131 | __raw_writel(sr, iop3xx_adap->ioaddr + SR_OFFSET); | ||
132 | iop3xx_adap->SR_received |= sr; | ||
133 | wake_up_interruptible(&iop3xx_adap->waitq); | ||
134 | } | ||
135 | return IRQ_HANDLED; | ||
136 | } | ||
137 | |||
138 | /* check all error conditions, clear them , report most important */ | ||
139 | static int | ||
140 | iop3xx_i2c_error(u32 sr) | ||
141 | { | ||
142 | int rc = 0; | ||
143 | |||
144 | if ((sr & IOP3XX_ISR_BERRD)) { | ||
145 | if ( !rc ) rc = -I2C_ERR_BERR; | ||
146 | } | ||
147 | if ((sr & IOP3XX_ISR_ALD)) { | ||
148 | if ( !rc ) rc = -I2C_ERR_ALD; | ||
149 | } | ||
150 | return rc; | ||
151 | } | ||
152 | |||
153 | static inline u32 | ||
154 | iop3xx_i2c_get_srstat(struct i2c_algo_iop3xx_data *iop3xx_adap) | ||
155 | { | ||
156 | unsigned long flags; | ||
157 | u32 sr; | ||
158 | |||
159 | spin_lock_irqsave(&iop3xx_adap->lock, flags); | ||
160 | sr = iop3xx_adap->SR_received; | ||
161 | iop3xx_adap->SR_received = 0; | ||
162 | spin_unlock_irqrestore(&iop3xx_adap->lock, flags); | ||
163 | |||
164 | return sr; | ||
165 | } | ||
166 | |||
167 | /* | ||
168 | * sleep until interrupted, then recover and analyse the SR | ||
169 | * saved by handler | ||
170 | */ | ||
171 | typedef int (* compare_func)(unsigned test, unsigned mask); | ||
172 | /* returns 1 on correct comparison */ | ||
173 | |||
174 | static int | ||
175 | iop3xx_i2c_wait_event(struct i2c_algo_iop3xx_data *iop3xx_adap, | ||
176 | unsigned flags, unsigned* status, | ||
177 | compare_func compare) | ||
178 | { | ||
179 | unsigned sr = 0; | ||
180 | int interrupted; | ||
181 | int done; | ||
182 | int rc = 0; | ||
183 | |||
184 | do { | ||
185 | interrupted = wait_event_interruptible_timeout ( | ||
186 | iop3xx_adap->waitq, | ||
187 | (done = compare( sr = iop3xx_i2c_get_srstat(iop3xx_adap) ,flags )), | ||
188 | 1 * HZ; | ||
189 | ); | ||
190 | if ((rc = iop3xx_i2c_error(sr)) < 0) { | ||
191 | *status = sr; | ||
192 | return rc; | ||
193 | } else if (!interrupted) { | ||
194 | *status = sr; | ||
195 | return -ETIMEDOUT; | ||
196 | } | ||
197 | } while(!done); | ||
198 | |||
199 | *status = sr; | ||
200 | |||
201 | return 0; | ||
202 | } | ||
203 | |||
204 | /* | ||
205 | * Concrete compare_funcs | ||
206 | */ | ||
207 | static int | ||
208 | all_bits_clear(unsigned test, unsigned mask) | ||
209 | { | ||
210 | return (test & mask) == 0; | ||
211 | } | ||
212 | |||
213 | static int | ||
214 | any_bits_set(unsigned test, unsigned mask) | ||
215 | { | ||
216 | return (test & mask) != 0; | ||
217 | } | ||
218 | |||
219 | static int | ||
220 | iop3xx_i2c_wait_tx_done(struct i2c_algo_iop3xx_data *iop3xx_adap, int *status) | ||
221 | { | ||
222 | return iop3xx_i2c_wait_event( | ||
223 | iop3xx_adap, | ||
224 | IOP3XX_ISR_TXEMPTY | IOP3XX_ISR_ALD | IOP3XX_ISR_BERRD, | ||
225 | status, any_bits_set); | ||
226 | } | ||
227 | |||
228 | static int | ||
229 | iop3xx_i2c_wait_rx_done(struct i2c_algo_iop3xx_data *iop3xx_adap, int *status) | ||
230 | { | ||
231 | return iop3xx_i2c_wait_event( | ||
232 | iop3xx_adap, | ||
233 | IOP3XX_ISR_RXFULL | IOP3XX_ISR_ALD | IOP3XX_ISR_BERRD, | ||
234 | status, any_bits_set); | ||
235 | } | ||
236 | |||
237 | static int | ||
238 | iop3xx_i2c_wait_idle(struct i2c_algo_iop3xx_data *iop3xx_adap, int *status) | ||
239 | { | ||
240 | return iop3xx_i2c_wait_event( | ||
241 | iop3xx_adap, IOP3XX_ISR_UNITBUSY, status, all_bits_clear); | ||
242 | } | ||
243 | |||
244 | static int | ||
245 | iop3xx_i2c_send_target_addr(struct i2c_algo_iop3xx_data *iop3xx_adap, | ||
246 | struct i2c_msg* msg) | ||
247 | { | ||
248 | unsigned long cr = __raw_readl(iop3xx_adap->ioaddr + CR_OFFSET); | ||
249 | int status; | ||
250 | int rc; | ||
251 | |||
252 | __raw_writel(iic_cook_addr(msg), iop3xx_adap->ioaddr + DBR_OFFSET); | ||
253 | |||
254 | cr &= ~(IOP3XX_ICR_MSTOP | IOP3XX_ICR_NACK); | ||
255 | cr |= IOP3XX_ICR_MSTART | IOP3XX_ICR_TBYTE; | ||
256 | |||
257 | __raw_writel(cr, iop3xx_adap->ioaddr + CR_OFFSET); | ||
258 | rc = iop3xx_i2c_wait_tx_done(iop3xx_adap, &status); | ||
259 | |||
260 | return rc; | ||
261 | } | ||
262 | |||
263 | static int | ||
264 | iop3xx_i2c_write_byte(struct i2c_algo_iop3xx_data *iop3xx_adap, char byte, | ||
265 | int stop) | ||
266 | { | ||
267 | unsigned long cr = __raw_readl(iop3xx_adap->ioaddr + CR_OFFSET); | ||
268 | int status; | ||
269 | int rc = 0; | ||
270 | |||
271 | __raw_writel(byte, iop3xx_adap->ioaddr + DBR_OFFSET); | ||
272 | cr &= ~IOP3XX_ICR_MSTART; | ||
273 | if (stop) { | ||
274 | cr |= IOP3XX_ICR_MSTOP; | ||
275 | } else { | ||
276 | cr &= ~IOP3XX_ICR_MSTOP; | ||
277 | } | ||
278 | cr |= IOP3XX_ICR_TBYTE; | ||
279 | __raw_writel(cr, iop3xx_adap->ioaddr + CR_OFFSET); | ||
280 | rc = iop3xx_i2c_wait_tx_done(iop3xx_adap, &status); | ||
281 | |||
282 | return rc; | ||
283 | } | ||
284 | |||
285 | static int | ||
286 | iop3xx_i2c_read_byte(struct i2c_algo_iop3xx_data *iop3xx_adap, char* byte, | ||
287 | int stop) | ||
288 | { | ||
289 | unsigned long cr = __raw_readl(iop3xx_adap->ioaddr + CR_OFFSET); | ||
290 | int status; | ||
291 | int rc = 0; | ||
292 | |||
293 | cr &= ~IOP3XX_ICR_MSTART; | ||
294 | |||
295 | if (stop) { | ||
296 | cr |= IOP3XX_ICR_MSTOP | IOP3XX_ICR_NACK; | ||
297 | } else { | ||
298 | cr &= ~(IOP3XX_ICR_MSTOP | IOP3XX_ICR_NACK); | ||
299 | } | ||
300 | cr |= IOP3XX_ICR_TBYTE; | ||
301 | __raw_writel(cr, iop3xx_adap->ioaddr + CR_OFFSET); | ||
302 | |||
303 | rc = iop3xx_i2c_wait_rx_done(iop3xx_adap, &status); | ||
304 | |||
305 | *byte = __raw_readl(iop3xx_adap->ioaddr + DBR_OFFSET); | ||
306 | |||
307 | return rc; | ||
308 | } | ||
309 | |||
310 | static int | ||
311 | iop3xx_i2c_writebytes(struct i2c_adapter *i2c_adap, const char *buf, int count) | ||
312 | { | ||
313 | struct i2c_algo_iop3xx_data *iop3xx_adap = i2c_adap->algo_data; | ||
314 | int ii; | ||
315 | int rc = 0; | ||
316 | |||
317 | for (ii = 0; rc == 0 && ii != count; ++ii) | ||
318 | rc = iop3xx_i2c_write_byte(iop3xx_adap, buf[ii], ii==count-1); | ||
319 | return rc; | ||
320 | } | ||
321 | |||
322 | static int | ||
323 | iop3xx_i2c_readbytes(struct i2c_adapter *i2c_adap, char *buf, int count) | ||
324 | { | ||
325 | struct i2c_algo_iop3xx_data *iop3xx_adap = i2c_adap->algo_data; | ||
326 | int ii; | ||
327 | int rc = 0; | ||
328 | |||
329 | for (ii = 0; rc == 0 && ii != count; ++ii) | ||
330 | rc = iop3xx_i2c_read_byte(iop3xx_adap, &buf[ii], ii==count-1); | ||
331 | |||
332 | return rc; | ||
333 | } | ||
334 | |||
335 | /* | ||
336 | * Description: This function implements combined transactions. Combined | ||
337 | * transactions consist of combinations of reading and writing blocks of data. | ||
338 | * FROM THE SAME ADDRESS | ||
339 | * Each transfer (i.e. a read or a write) is separated by a repeated start | ||
340 | * condition. | ||
341 | */ | ||
342 | static int | ||
343 | iop3xx_i2c_handle_msg(struct i2c_adapter *i2c_adap, struct i2c_msg* pmsg) | ||
344 | { | ||
345 | struct i2c_algo_iop3xx_data *iop3xx_adap = i2c_adap->algo_data; | ||
346 | int rc; | ||
347 | |||
348 | rc = iop3xx_i2c_send_target_addr(iop3xx_adap, pmsg); | ||
349 | if (rc < 0) { | ||
350 | return rc; | ||
351 | } | ||
352 | |||
353 | if ((pmsg->flags&I2C_M_RD)) { | ||
354 | return iop3xx_i2c_readbytes(i2c_adap, pmsg->buf, pmsg->len); | ||
355 | } else { | ||
356 | return iop3xx_i2c_writebytes(i2c_adap, pmsg->buf, pmsg->len); | ||
357 | } | ||
358 | } | ||
359 | |||
360 | /* | ||
361 | * master_xfer() - main read/write entry | ||
362 | */ | ||
363 | static int | ||
364 | iop3xx_i2c_master_xfer(struct i2c_adapter *i2c_adap, struct i2c_msg *msgs, | ||
365 | int num) | ||
366 | { | ||
367 | struct i2c_algo_iop3xx_data *iop3xx_adap = i2c_adap->algo_data; | ||
368 | int im = 0; | ||
369 | int ret = 0; | ||
370 | int status; | ||
371 | |||
372 | iop3xx_i2c_wait_idle(iop3xx_adap, &status); | ||
373 | iop3xx_i2c_reset(iop3xx_adap); | ||
374 | iop3xx_i2c_enable(iop3xx_adap); | ||
375 | |||
376 | for (im = 0; ret == 0 && im != num; im++) { | ||
377 | ret = iop3xx_i2c_handle_msg(i2c_adap, &msgs[im]); | ||
378 | } | ||
379 | |||
380 | iop3xx_i2c_transaction_cleanup(iop3xx_adap); | ||
381 | |||
382 | if(ret) | ||
383 | return ret; | ||
384 | |||
385 | return im; | ||
386 | } | ||
387 | |||
388 | static int | ||
389 | iop3xx_i2c_algo_control(struct i2c_adapter *adapter, unsigned int cmd, | ||
390 | unsigned long arg) | ||
391 | { | ||
392 | return 0; | ||
393 | } | ||
394 | |||
395 | static u32 | ||
396 | iop3xx_i2c_func(struct i2c_adapter *adap) | ||
397 | { | ||
398 | return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL; | ||
399 | } | ||
400 | |||
401 | static struct i2c_algorithm iop3xx_i2c_algo = { | ||
402 | .name = "IOP3xx I2C algorithm", | ||
403 | .id = I2C_ALGO_IOP3XX, | ||
404 | .master_xfer = iop3xx_i2c_master_xfer, | ||
405 | .algo_control = iop3xx_i2c_algo_control, | ||
406 | .functionality = iop3xx_i2c_func, | ||
407 | }; | ||
408 | |||
409 | static int | ||
410 | iop3xx_i2c_remove(struct device *device) | ||
411 | { | ||
412 | struct platform_device *pdev = to_platform_device(device); | ||
413 | struct i2c_adapter *padapter = dev_get_drvdata(&pdev->dev); | ||
414 | struct i2c_algo_iop3xx_data *adapter_data = | ||
415 | (struct i2c_algo_iop3xx_data *)padapter->algo_data; | ||
416 | struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0); | ||
417 | unsigned long cr = __raw_readl(adapter_data->ioaddr + CR_OFFSET); | ||
418 | |||
419 | /* | ||
420 | * Disable the actual HW unit | ||
421 | */ | ||
422 | cr &= ~(IOP3XX_ICR_ALD_IE | IOP3XX_ICR_BERR_IE | | ||
423 | IOP3XX_ICR_RXFULL_IE | IOP3XX_ICR_TXEMPTY_IE); | ||
424 | __raw_writel(cr, adapter_data->ioaddr + CR_OFFSET); | ||
425 | |||
426 | iounmap((void __iomem*)adapter_data->ioaddr); | ||
427 | release_mem_region(res->start, IOP3XX_I2C_IO_SIZE); | ||
428 | kfree(adapter_data); | ||
429 | kfree(padapter); | ||
430 | |||
431 | dev_set_drvdata(&pdev->dev, NULL); | ||
432 | |||
433 | return 0; | ||
434 | } | ||
435 | |||
436 | static int | ||
437 | iop3xx_i2c_probe(struct device *dev) | ||
438 | { | ||
439 | struct platform_device *pdev = to_platform_device(dev); | ||
440 | struct resource *res; | ||
441 | int ret; | ||
442 | struct i2c_adapter *new_adapter; | ||
443 | struct i2c_algo_iop3xx_data *adapter_data; | ||
444 | |||
445 | new_adapter = kmalloc(sizeof(struct i2c_adapter), GFP_KERNEL); | ||
446 | if (!new_adapter) { | ||
447 | ret = -ENOMEM; | ||
448 | goto out; | ||
449 | } | ||
450 | memset((void*)new_adapter, 0, sizeof(*new_adapter)); | ||
451 | |||
452 | adapter_data = kmalloc(sizeof(struct i2c_algo_iop3xx_data), GFP_KERNEL); | ||
453 | if (!adapter_data) { | ||
454 | ret = -ENOMEM; | ||
455 | goto free_adapter; | ||
456 | } | ||
457 | memset((void*)adapter_data, 0, sizeof(*adapter_data)); | ||
458 | |||
459 | res = platform_get_resource(pdev, IORESOURCE_MEM, 0); | ||
460 | if (!res) { | ||
461 | ret = -ENODEV; | ||
462 | goto free_both; | ||
463 | } | ||
464 | |||
465 | if (!request_mem_region(res->start, IOP3XX_I2C_IO_SIZE, pdev->name)) { | ||
466 | ret = -EBUSY; | ||
467 | goto free_both; | ||
468 | } | ||
469 | |||
470 | /* set the adapter enumeration # */ | ||
471 | adapter_data->id = i2c_id++; | ||
472 | |||
473 | adapter_data->ioaddr = (u32)ioremap(res->start, IOP3XX_I2C_IO_SIZE); | ||
474 | if (!adapter_data->ioaddr) { | ||
475 | ret = -ENOMEM; | ||
476 | goto release_region; | ||
477 | } | ||
478 | |||
479 | res = request_irq(platform_get_irq(pdev, 0), iop3xx_i2c_irq_handler, 0, | ||
480 | pdev->name, adapter_data); | ||
481 | if (res) { | ||
482 | ret = -EIO; | ||
483 | goto unmap; | ||
484 | } | ||
485 | |||
486 | memcpy(new_adapter->name, pdev->name, strlen(pdev->name)); | ||
487 | new_adapter->id = I2C_HW_IOP3XX; | ||
488 | new_adapter->owner = THIS_MODULE; | ||
489 | new_adapter->dev.parent = &pdev->dev; | ||
490 | |||
491 | /* | ||
492 | * Default values...should these come in from board code? | ||
493 | */ | ||
494 | new_adapter->timeout = 100; | ||
495 | new_adapter->retries = 3; | ||
496 | new_adapter->algo = &iop3xx_i2c_algo; | ||
497 | |||
498 | init_waitqueue_head(&adapter_data->waitq); | ||
499 | spin_lock_init(&adapter_data->lock); | ||
500 | |||
501 | iop3xx_i2c_reset(adapter_data); | ||
502 | iop3xx_i2c_set_slave_addr(adapter_data); | ||
503 | iop3xx_i2c_enable(adapter_data); | ||
504 | |||
505 | dev_set_drvdata(&pdev->dev, new_adapter); | ||
506 | new_adapter->algo_data = adapter_data; | ||
507 | |||
508 | i2c_add_adapter(new_adapter); | ||
509 | |||
510 | return 0; | ||
511 | |||
512 | unmap: | ||
513 | iounmap((void __iomem*)adapter_data->ioaddr); | ||
514 | |||
515 | release_region: | ||
516 | release_mem_region(res->start, IOP3XX_I2C_IO_SIZE); | ||
517 | |||
518 | free_both: | ||
519 | kfree(adapter_data); | ||
520 | |||
521 | free_adapter: | ||
522 | kfree(new_adapter); | ||
523 | |||
524 | out: | ||
525 | return ret; | ||
526 | } | ||
527 | |||
528 | |||
529 | static struct device_driver iop3xx_i2c_driver = { | ||
530 | .name = "IOP3xx-I2C", | ||
531 | .bus = &platform_bus_type, | ||
532 | .probe = iop3xx_i2c_probe, | ||
533 | .remove = iop3xx_i2c_remove | ||
534 | }; | ||
535 | |||
536 | static int __init | ||
537 | i2c_iop3xx_init (void) | ||
538 | { | ||
539 | return driver_register(&iop3xx_i2c_driver); | ||
540 | } | ||
541 | |||
542 | static void __exit | ||
543 | i2c_iop3xx_exit (void) | ||
544 | { | ||
545 | driver_unregister(&iop3xx_i2c_driver); | ||
546 | return; | ||
547 | } | ||
548 | |||
549 | module_init (i2c_iop3xx_init); | ||
550 | module_exit (i2c_iop3xx_exit); | ||
551 | |||
552 | MODULE_AUTHOR("D-TACQ Solutions Ltd <www.d-tacq.com>"); | ||
553 | MODULE_DESCRIPTION("IOP3xx iic algorithm and driver"); | ||
554 | MODULE_LICENSE("GPL"); | ||