]> Gitweb @ Texas Instruments - Open Source Git Repositories - git.TI.com/gitweb - glsdk/meta-ti-glsdk.git/blob - recipes-bsp/linux/linux-omap-psp-2.6.32/0019-drivers-mfd-add-twl4030-madc-driver.patch
netbase: automatically bring up usb0 on BeagleBoard xM
[glsdk/meta-ti-glsdk.git] / recipes-bsp / linux / linux-omap-psp-2.6.32 / 0019-drivers-mfd-add-twl4030-madc-driver.patch
1 From b9270dc07b1b66cce33580bca6276c20f1bf68d2 Mon Sep 17 00:00:00 2001
2 From: Steve Sakoman <steve@sakoman.com>
3 Date: Wed, 19 Jan 2011 16:06:42 +0100
4 Subject: [PATCH 19/45] drivers: mfd: add twl4030 madc driver
6 ---
7  drivers/mfd/Kconfig        |   21 ++
8  drivers/mfd/Makefile       |    1 +
9  drivers/mfd/twl4030-madc.c |  536 ++++++++++++++++++++++++++++++++++++++++++++
10  3 files changed, 558 insertions(+), 0 deletions(-)
11  create mode 100644 drivers/mfd/twl4030-madc.c
13 diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig
14 index 306b346..6221146 100644
15 --- a/drivers/mfd/Kconfig
16 +++ b/drivers/mfd/Kconfig
17 @@ -148,6 +148,27 @@ config TWL4030_CODEC
18         default n
19  
20  
21 +config TWL4030_MADC
22 +       tristate "TWL4030 MADC Driver"
23 +       depends on TWL4030_CORE
24 +       help
25 +         The TWL4030 Monitoring ADC driver enables the host
26 +         processor to monitor analog signals using analog-to-digital
27 +         conversions on the input source. TWL4030 MADC provides the
28 +         following features:
29 +          - Single 10-bit ADC with successive approximation register (SAR) conversion;
30 +          - Analog multiplexer for 16 inputs;
31 +          - Seven (of the 16) inputs are freely available;
32 +          - Battery voltage monitoring;
33 +          - Concurrent conversion request management;
34 +          - Interrupt signal to Primary Interrupt Handler;
35 +          - Averaging feature;
36 +          - Selective enable/disable of the averaging feature.
37 +
38 +         Say 'y' here to statically link this module into the kernel or 'm'
39 +         to build it as a dinamically loadable module. The module will be
40 +         called twl4030-madc.ko
41 +
42  config MFD_TMIO
43         bool
44         default n
45 diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile
46 index 85dc3a7..44350e7 100644
47 --- a/drivers/mfd/Makefile
48 +++ b/drivers/mfd/Makefile
49 @@ -29,6 +29,7 @@ obj-$(CONFIG_MENELAUS)                += menelaus.o
50  obj-$(CONFIG_TWL4030_CORE)     += twl-core.o twl4030-irq.o twl6030-irq.o
51  obj-$(CONFIG_TWL4030_POWER)    += twl4030-power.o
52  obj-$(CONFIG_TWL4030_CODEC)    += twl4030-codec.o
53 +obj-$(CONFIG_TWL4030_MADC)     += twl4030-madc.o
54  
55  obj-$(CONFIG_TPS65910_CORE)     += tps65910-core.o
56  
57 diff --git a/drivers/mfd/twl4030-madc.c b/drivers/mfd/twl4030-madc.c
58 new file mode 100644
59 index 0000000..7d83ab8
60 --- /dev/null
61 +++ b/drivers/mfd/twl4030-madc.c
62 @@ -0,0 +1,536 @@
63 +/*
64 + * TWL4030 MADC module driver
65 + *
66 + * Copyright (C) 2008 Nokia Corporation
67 + * Mikko Ylinen <mikko.k.ylinen@nokia.com>
68 + *
69 + * This program is free software; you can redistribute it and/or
70 + * modify it under the terms of the GNU General Public License
71 + * version 2 as published by the Free Software Foundation.
72 + *
73 + * This program is distributed in the hope that it will be useful, but
74 + * WITHOUT ANY WARRANTY; without even the implied warranty of
75 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
76 + * General Public License for more details.
77 + *
78 + * You should have received a copy of the GNU General Public License
79 + * along with this program; if not, write to the Free Software
80 + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
81 + * 02110-1301 USA
82 + *
83 + */
84 +
85 +#include <linux/init.h>
86 +#include <linux/interrupt.h>
87 +#include <linux/kernel.h>
88 +#include <linux/types.h>
89 +#include <linux/module.h>
90 +#include <linux/delay.h>
91 +#include <linux/fs.h>
92 +#include <linux/platform_device.h>
93 +#include <linux/miscdevice.h>
94 +#include <linux/i2c/twl4030.h>
95 +#include <linux/i2c/twl4030-madc.h>
96 +
97 +#include <asm/uaccess.h>
98 +
99 +#define TWL4030_MADC_PFX       "twl4030-madc: "
101 +struct twl4030_madc_data {
102 +       struct device           *dev;
103 +       struct mutex            lock;
104 +       struct work_struct      ws;
105 +       struct twl4030_madc_request     requests[TWL4030_MADC_NUM_METHODS];
106 +       int imr;
107 +       int isr;
108 +};
110 +static struct twl4030_madc_data *the_madc;
112 +static
113 +const struct twl4030_madc_conversion_method twl4030_conversion_methods[] = {
114 +       [TWL4030_MADC_RT] = {
115 +               .sel    = TWL4030_MADC_RTSELECT_LSB,
116 +               .avg    = TWL4030_MADC_RTAVERAGE_LSB,
117 +               .rbase  = TWL4030_MADC_RTCH0_LSB,
118 +       },
119 +       [TWL4030_MADC_SW1] = {
120 +               .sel    = TWL4030_MADC_SW1SELECT_LSB,
121 +               .avg    = TWL4030_MADC_SW1AVERAGE_LSB,
122 +               .rbase  = TWL4030_MADC_GPCH0_LSB,
123 +               .ctrl   = TWL4030_MADC_CTRL_SW1,
124 +       },
125 +       [TWL4030_MADC_SW2] = {
126 +               .sel    = TWL4030_MADC_SW2SELECT_LSB,
127 +               .avg    = TWL4030_MADC_SW2AVERAGE_LSB,
128 +               .rbase  = TWL4030_MADC_GPCH0_LSB,
129 +               .ctrl   = TWL4030_MADC_CTRL_SW2,
130 +       },
131 +};
133 +static int twl4030_madc_read(struct twl4030_madc_data *madc, u8 reg)
134 +{
135 +       int ret;
136 +       u8 val;
138 +       ret = twl4030_i2c_read_u8(TWL4030_MODULE_MADC, &val, reg);
139 +       if (ret) {
140 +               dev_dbg(madc->dev, "unable to read register 0x%X\n", reg);
141 +               return ret;
142 +       }
144 +       return val;
145 +}
147 +static void twl4030_madc_write(struct twl4030_madc_data *madc, u8 reg, u8 val)
148 +{
149 +       int ret;
151 +       ret = twl4030_i2c_write_u8(TWL4030_MODULE_MADC, val, reg);
152 +       if (ret)
153 +               dev_err(madc->dev, "unable to write register 0x%X\n", reg);
154 +}
156 +static int twl4030_madc_channel_raw_read(struct twl4030_madc_data *madc, u8 reg)
157 +{
158 +       u8 msb, lsb;
160 +       /* For each ADC channel, we have MSB and LSB register pair. MSB address
161 +        * is always LSB address+1. reg parameter is the addr of LSB register */
162 +       msb = twl4030_madc_read(madc, reg + 1);
163 +       lsb = twl4030_madc_read(madc, reg);
165 +       return (int)(((msb << 8) | lsb) >> 6);
166 +}
168 +static int twl4030_madc_read_channels(struct twl4030_madc_data *madc,
169 +               u8 reg_base, u16 channels, int *buf)
170 +{
171 +       int count = 0;
172 +       u8 reg, i;
174 +       if (unlikely(!buf))
175 +               return 0;
177 +       for (i = 0; i < TWL4030_MADC_MAX_CHANNELS; i++) {
178 +               if (channels & (1<<i)) {
179 +                       reg = reg_base + 2*i;
180 +                       buf[i] = twl4030_madc_channel_raw_read(madc, reg);
181 +                       count++;
182 +               }
183 +       }
184 +       return count;
185 +}
187 +static void twl4030_madc_enable_irq(struct twl4030_madc_data *madc, int id)
188 +{
189 +       u8 val;
191 +       val = twl4030_madc_read(madc, madc->imr);
192 +       val &= ~(1 << id);
193 +       twl4030_madc_write(madc, madc->imr, val);
194 +}
196 +static void twl4030_madc_disable_irq(struct twl4030_madc_data *madc, int id)
197 +{
198 +       u8 val;
200 +       val = twl4030_madc_read(madc, madc->imr);
201 +       val |= (1 << id);
202 +       twl4030_madc_write(madc, madc->imr, val);
203 +}
205 +static irqreturn_t twl4030_madc_irq_handler(int irq, void *_madc)
206 +{
207 +       struct twl4030_madc_data *madc = _madc;
208 +       u8 isr_val, imr_val;
209 +       int i;
211 +#ifdef CONFIG_LOCKDEP
212 +       /* WORKAROUND for lockdep forcing IRQF_DISABLED on us, which
213 +        * we don't want and can't tolerate.  Although it might be
214 +        * friendlier not to borrow this thread context...
215 +        */
216 +       local_irq_enable();
217 +#endif
219 +       /* Use COR to ack interrupts since we have no shared IRQs in ISRx */
220 +       isr_val = twl4030_madc_read(madc, madc->isr);
221 +       imr_val = twl4030_madc_read(madc, madc->imr);
223 +       isr_val &= ~imr_val;
225 +       for (i = 0; i < TWL4030_MADC_NUM_METHODS; i++) {
227 +               if (!(isr_val & (1<<i)))
228 +                       continue;
230 +               twl4030_madc_disable_irq(madc, i);
231 +               madc->requests[i].result_pending = 1;
232 +       }
234 +       schedule_work(&madc->ws);
236 +       return IRQ_HANDLED;
237 +}
239 +static void twl4030_madc_work(struct work_struct *ws)
240 +{
241 +       const struct twl4030_madc_conversion_method *method;
242 +       struct twl4030_madc_data *madc;
243 +       struct twl4030_madc_request *r;
244 +       int len, i;
246 +       madc = container_of(ws, struct twl4030_madc_data, ws);
247 +       mutex_lock(&madc->lock);
249 +       for (i = 0; i < TWL4030_MADC_NUM_METHODS; i++) {
251 +               r = &madc->requests[i];
253 +               /* No pending results for this method, move to next one */
254 +               if (!r->result_pending)
255 +                       continue;
257 +               method = &twl4030_conversion_methods[r->method];
259 +               /* Read results */
260 +               len = twl4030_madc_read_channels(madc, method->rbase,
261 +                                                r->channels, r->rbuf);
263 +               /* Return results to caller */
264 +               if (r->func_cb != NULL) {
265 +                       r->func_cb(len, r->channels, r->rbuf);
266 +                       r->func_cb = NULL;
267 +               }
269 +               /* Free request */
270 +               r->result_pending = 0;
271 +               r->active         = 0;
272 +       }
274 +       mutex_unlock(&madc->lock);
275 +}
277 +static int twl4030_madc_set_irq(struct twl4030_madc_data *madc,
278 +               struct twl4030_madc_request *req)
279 +{
280 +       struct twl4030_madc_request *p;
282 +       p = &madc->requests[req->method];
284 +       memcpy(p, req, sizeof *req);
286 +       twl4030_madc_enable_irq(madc, req->method);
288 +       return 0;
289 +}
291 +static inline void twl4030_madc_start_conversion(struct twl4030_madc_data *madc,
292 +               int conv_method)
293 +{
294 +       const struct twl4030_madc_conversion_method *method;
296 +       method = &twl4030_conversion_methods[conv_method];
298 +       switch (conv_method) {
299 +       case TWL4030_MADC_SW1:
300 +       case TWL4030_MADC_SW2:
301 +               twl4030_madc_write(madc, method->ctrl, TWL4030_MADC_SW_START);
302 +               break;
303 +       case TWL4030_MADC_RT:
304 +       default:
305 +               break;
306 +       }
307 +}
309 +static int twl4030_madc_wait_conversion_ready(
310 +               struct twl4030_madc_data *madc,
311 +               unsigned int timeout_ms, u8 status_reg)
312 +{
313 +       unsigned long timeout;
315 +       timeout = jiffies + msecs_to_jiffies(timeout_ms);
316 +       do {
317 +               u8 reg;
319 +               reg = twl4030_madc_read(madc, status_reg);
320 +               if (!(reg & TWL4030_MADC_BUSY) && (reg & TWL4030_MADC_EOC_SW))
321 +                       return 0;
322 +       } while (!time_after(jiffies, timeout));
324 +       return -EAGAIN;
325 +}
327 +int twl4030_madc_conversion(struct twl4030_madc_request *req)
328 +{
329 +       const struct twl4030_madc_conversion_method *method;
330 +       u8 ch_msb, ch_lsb;
331 +       int ret;
333 +       if (unlikely(!req))
334 +               return -EINVAL;
336 +       mutex_lock(&the_madc->lock);
338 +       /* Do we have a conversion request ongoing */
339 +       if (the_madc->requests[req->method].active) {
340 +               ret = -EBUSY;
341 +               goto out;
342 +       }
344 +       ch_msb = (req->channels >> 8) & 0xff;
345 +       ch_lsb = req->channels & 0xff;
347 +       method = &twl4030_conversion_methods[req->method];
349 +       /* Select channels to be converted */
350 +       twl4030_madc_write(the_madc, method->sel + 1, ch_msb);
351 +       twl4030_madc_write(the_madc, method->sel, ch_lsb);
353 +       /* Select averaging for all channels if do_avg is set */
354 +       if (req->do_avg) {
355 +               twl4030_madc_write(the_madc, method->avg + 1, ch_msb);
356 +               twl4030_madc_write(the_madc, method->avg, ch_lsb);
357 +       }
359 +       if ((req->type == TWL4030_MADC_IRQ_ONESHOT) && (req->func_cb != NULL)) {
360 +               twl4030_madc_set_irq(the_madc, req);
361 +               twl4030_madc_start_conversion(the_madc, req->method);
362 +               the_madc->requests[req->method].active = 1;
363 +               ret = 0;
364 +               goto out;
365 +       }
367 +       /* With RT method we should not be here anymore */
368 +       if (req->method == TWL4030_MADC_RT) {
369 +               ret = -EINVAL;
370 +               goto out;
371 +       }
373 +       twl4030_madc_start_conversion(the_madc, req->method);
374 +       the_madc->requests[req->method].active = 1;
376 +       /* Wait until conversion is ready (ctrl register returns EOC) */
377 +       ret = twl4030_madc_wait_conversion_ready(the_madc, 5, method->ctrl);
378 +       if (ret) {
379 +               dev_dbg(the_madc->dev, "conversion timeout!\n");
380 +               the_madc->requests[req->method].active = 0;
381 +               goto out;
382 +       }
384 +       ret = twl4030_madc_read_channels(the_madc, method->rbase, req->channels,
385 +                                        req->rbuf);
387 +       the_madc->requests[req->method].active = 0;
389 +out:
390 +       mutex_unlock(&the_madc->lock);
392 +       return ret;
393 +}
394 +EXPORT_SYMBOL(twl4030_madc_conversion);
396 +static int twl4030_madc_set_current_generator(struct twl4030_madc_data *madc,
397 +               int chan, int on)
398 +{
399 +       int ret;
400 +       u8 regval;
402 +       /* Current generator is only available for ADCIN0 and ADCIN1. NB:
403 +        * ADCIN1 current generator only works when AC or VBUS is present */
404 +       if (chan > 1)
405 +               return EINVAL;
407 +       ret = twl4030_i2c_read_u8(TWL4030_MODULE_MAIN_CHARGE,
408 +                                 &regval, TWL4030_BCI_BCICTL1);
409 +       if (on)
410 +               regval |= (chan) ? TWL4030_BCI_ITHEN : TWL4030_BCI_TYPEN;
411 +       else
412 +               regval &= (chan) ? ~TWL4030_BCI_ITHEN : ~TWL4030_BCI_TYPEN;
413 +       ret = twl4030_i2c_write_u8(TWL4030_MODULE_MAIN_CHARGE,
414 +                                  regval, TWL4030_BCI_BCICTL1);
416 +       return ret;
417 +}
419 +static int twl4030_madc_set_power(struct twl4030_madc_data *madc, int on)
420 +{
421 +       u8 regval;
423 +       regval = twl4030_madc_read(madc, TWL4030_MADC_CTRL1);
424 +       if (on)
425 +               regval |= TWL4030_MADC_MADCON;
426 +       else
427 +               regval &= ~TWL4030_MADC_MADCON;
428 +       twl4030_madc_write(madc, TWL4030_MADC_CTRL1, regval);
430 +       return 0;
431 +}
433 +static long twl4030_madc_ioctl(struct file *filp, unsigned int cmd,
434 +                              unsigned long arg)
435 +{
436 +       struct twl4030_madc_user_parms par;
437 +       int val, ret;
439 +       ret = copy_from_user(&par, (void __user *) arg, sizeof(par));
440 +       if (ret) {
441 +               dev_dbg(the_madc->dev, "copy_from_user: %d\n", ret);
442 +               return -EACCES;
443 +       }
445 +       switch (cmd) {
446 +       case TWL4030_MADC_IOCX_ADC_RAW_READ: {
447 +               struct twl4030_madc_request req;
448 +               if (par.channel >= TWL4030_MADC_MAX_CHANNELS)
449 +                       return -EINVAL;
451 +               req.channels = (1 << par.channel);
452 +               req.do_avg      = par.average;
453 +               req.method      = TWL4030_MADC_SW1;
454 +               req.func_cb     = NULL;
456 +               val = twl4030_madc_conversion(&req);
457 +               if (val <= 0) {
458 +                       par.status = -1;
459 +               } else {
460 +                       par.status = 0;
461 +                       par.result = (u16)req.rbuf[par.channel];
462 +               }
463 +               break;
464 +                                            }
465 +       default:
466 +               return -EINVAL;
467 +       }
469 +       ret = copy_to_user((void __user *) arg, &par, sizeof(par));
470 +       if (ret) {
471 +               dev_dbg(the_madc->dev, "copy_to_user: %d\n", ret);
472 +               return -EACCES;
473 +       }
475 +       return 0;
476 +}
478 +static struct file_operations twl4030_madc_fileops = {
479 +       .owner = THIS_MODULE,
480 +       .unlocked_ioctl = twl4030_madc_ioctl
481 +};
483 +static struct miscdevice twl4030_madc_device = {
484 +       .minor = MISC_DYNAMIC_MINOR,
485 +       .name = "twl4030-madc",
486 +       .fops = &twl4030_madc_fileops
487 +};
489 +static int __init twl4030_madc_probe(struct platform_device *pdev)
490 +{
491 +       struct twl4030_madc_data *madc;
492 +       struct twl4030_madc_platform_data *pdata = pdev->dev.platform_data;
493 +       int ret;
494 +       u8 regval;
496 +       madc = kzalloc(sizeof *madc, GFP_KERNEL);
497 +       if (!madc)
498 +               return -ENOMEM;
500 +       if (!pdata) {
501 +               dev_dbg(&pdev->dev, "platform_data not available\n");
502 +               ret = -EINVAL;
503 +               goto err_pdata;
504 +       }
506 +       madc->imr = (pdata->irq_line == 1) ? TWL4030_MADC_IMR1 : TWL4030_MADC_IMR2;
507 +       madc->isr = (pdata->irq_line == 1) ? TWL4030_MADC_ISR1 : TWL4030_MADC_ISR2;
509 +       ret = misc_register(&twl4030_madc_device);
510 +       if (ret) {
511 +               dev_dbg(&pdev->dev, "could not register misc_device\n");
512 +               goto err_misc;
513 +       }
514 +       twl4030_madc_set_power(madc, 1);
515 +       twl4030_madc_set_current_generator(madc, 0, 1);
517 +       /* Enable ADCIN3 through 6 */
518 +       ret = twl4030_i2c_read_u8(TWL4030_MODULE_USB,
519 +                                 &regval, TWL4030_USB_CARKIT_ANA_CTRL);
521 +       regval |= TWL4030_USB_SEL_MADC_MCPC;
523 +       ret = twl4030_i2c_write_u8(TWL4030_MODULE_USB,
524 +                               regval, TWL4030_USB_CARKIT_ANA_CTRL);
527 +       ret = twl4030_i2c_read_u8(TWL4030_MODULE_MAIN_CHARGE,
528 +                                 &regval, TWL4030_BCI_BCICTL1);
530 +       regval |= TWL4030_BCI_MESBAT;
532 +       ret = twl4030_i2c_write_u8(TWL4030_MODULE_MAIN_CHARGE,
533 +                                  regval, TWL4030_BCI_BCICTL1);
535 +       ret = request_irq(platform_get_irq(pdev, 0), twl4030_madc_irq_handler,
536 +                         0, "twl4030_madc", madc);
537 +       if (ret) {
538 +               dev_dbg(&pdev->dev, "could not request irq\n");
539 +               goto err_irq;
540 +       }
542 +       platform_set_drvdata(pdev, madc);
543 +       mutex_init(&madc->lock);
544 +       INIT_WORK(&madc->ws, twl4030_madc_work);
546 +       the_madc = madc;
548 +       return 0;
550 +err_irq:
551 +       misc_deregister(&twl4030_madc_device);
553 +err_misc:
554 +err_pdata:
555 +       kfree(madc);
557 +       return ret;
558 +}
560 +static int __exit twl4030_madc_remove(struct platform_device *pdev)
561 +{
562 +       struct twl4030_madc_data *madc = platform_get_drvdata(pdev);
564 +       twl4030_madc_set_power(madc, 0);
565 +       twl4030_madc_set_current_generator(madc, 0, 0);
566 +       free_irq(platform_get_irq(pdev, 0), madc);
567 +       cancel_work_sync(&madc->ws);
568 +       misc_deregister(&twl4030_madc_device);
570 +       return 0;
571 +}
573 +static struct platform_driver twl4030_madc_driver = {
574 +       .probe          = twl4030_madc_probe,
575 +       .remove         = __exit_p(twl4030_madc_remove),
576 +       .driver         = {
577 +               .name   = "twl4030_madc",
578 +               .owner  = THIS_MODULE,
579 +       },
580 +};
582 +static int __init twl4030_madc_init(void)
583 +{
584 +       return platform_driver_register(&twl4030_madc_driver);
585 +}
586 +module_init(twl4030_madc_init);
588 +static void __exit twl4030_madc_exit(void)
589 +{
590 +       platform_driver_unregister(&twl4030_madc_driver);
591 +}
592 +module_exit(twl4030_madc_exit);
594 +MODULE_ALIAS("platform:twl4030-madc");
595 +MODULE_AUTHOR("Nokia Corporation");
596 +MODULE_DESCRIPTION("twl4030 ADC driver");
597 +MODULE_LICENSE("GPL");
599 -- 
600 1.6.6.1