[glsdk/meta-ti-glsdk.git] / recipes-bsp / linux / linux-omap / base / 0018-MFD-add-twl4030-madc-driver.patch
1 From 562dc52ebe3df1e5d23416e78306db7c568dc427 Mon Sep 17 00:00:00 2001
2 From: Steve Sakoman <steve@sakoman.com>
3 Date: Thu, 17 Dec 2009 14:19:34 -0800
4 Subject: [PATCH 18/28] MFD: add twl4030 madc driver
6 ---
7 drivers/mfd/Kconfig | 21 ++
8 drivers/mfd/Makefile | 1 +
9 drivers/mfd/twl4030-madc.c | 537 ++++++++++++++++++++++++++++++++++++++
10 include/linux/i2c/twl4030-madc.h | 130 +++++++++
11 4 files changed, 689 insertions(+), 0 deletions(-)
12 create mode 100644 drivers/mfd/twl4030-madc.c
13 create mode 100644 include/linux/i2c/twl4030-madc.h
15 diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig
16 index 3a1493b..26ca160 100644
17 --- a/drivers/mfd/Kconfig
18 +++ b/drivers/mfd/Kconfig
19 @@ -186,6 +186,27 @@ config TWL4030_CODEC
20 select MFD_CORE
21 default n
23 +config TWL4030_MADC
24 + tristate "TWL4030 MADC Driver"
25 + depends on TWL4030_CORE
26 + help
27 + The TWL4030 Monitoring ADC driver enables the host
28 + processor to monitor analog signals using analog-to-digital
29 + conversions on the input source. TWL4030 MADC provides the
30 + following features:
31 + - Single 10-bit ADC with successive approximation register (SAR) conversion;
32 + - Analog multiplexer for 16 inputs;
33 + - Seven (of the 16) inputs are freely available;
34 + - Battery voltage monitoring;
35 + - Concurrent conversion request management;
36 + - Interrupt signal to Primary Interrupt Handler;
37 + - Averaging feature;
38 + - Selective enable/disable of the averaging feature.
39 +
40 + Say 'y' here to statically link this module into the kernel or 'm'
41 + to build it as a dinamically loadable module. The module will be
42 + called twl4030-madc.ko
43 +
44 config TWL6030_PWM
45 tristate "TWL6030 PWM (Pulse Width Modulator) Support"
46 depends on TWL4030_CORE
47 diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile
48 index f54b365..8c4ccb2 100644
49 --- a/drivers/mfd/Makefile
50 +++ b/drivers/mfd/Makefile
51 @@ -39,6 +39,7 @@ obj-$(CONFIG_MENELAUS) += menelaus.o
52 obj-$(CONFIG_TWL4030_CORE) += twl-core.o twl4030-irq.o twl6030-irq.o
53 obj-$(CONFIG_TWL4030_POWER) += twl4030-power.o
54 obj-$(CONFIG_TWL4030_CODEC) += twl4030-codec.o
55 +obj-$(CONFIG_TWL4030_MADC) += twl4030-madc.o
56 obj-$(CONFIG_TWL6030_PWM) += twl6030-pwm.o
58 obj-$(CONFIG_MFD_MC13XXX) += mc13xxx-core.o
59 diff --git a/drivers/mfd/twl4030-madc.c b/drivers/mfd/twl4030-madc.c
60 new file mode 100644
61 index 0000000..4adf880
62 --- /dev/null
63 +++ b/drivers/mfd/twl4030-madc.c
64 @@ -0,0 +1,537 @@
65 +/*
66 + * TWL4030 MADC module driver
67 + *
68 + * Copyright (C) 2008 Nokia Corporation
69 + * Mikko Ylinen <mikko.k.ylinen@nokia.com>
70 + *
71 + * This program is free software; you can redistribute it and/or
72 + * modify it under the terms of the GNU General Public License
73 + * version 2 as published by the Free Software Foundation.
74 + *
75 + * This program is distributed in the hope that it will be useful, but
76 + * WITHOUT ANY WARRANTY; without even the implied warranty of
77 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
78 + * General Public License for more details.
79 + *
80 + * You should have received a copy of the GNU General Public License
81 + * along with this program; if not, write to the Free Software
82 + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
83 + * 02110-1301 USA
84 + *
85 + */
86 +
87 +#include <linux/delay.h>
88 +#include <linux/fs.h>
89 +#include <linux/init.h>
90 +#include <linux/interrupt.h>
91 +#include <linux/kernel.h>
92 +#include <linux/miscdevice.h>
93 +#include <linux/module.h>
94 +#include <linux/platform_device.h>
95 +#include <linux/slab.h>
96 +#include <linux/types.h>
97 +#include <linux/i2c/twl.h>
98 +#include <linux/i2c/twl4030-madc.h>
99 +
100 +#include <asm/uaccess.h>
101 +
102 +#define TWL4030_MADC_PFX "twl4030-madc: "
103 +
104 +struct twl4030_madc_data {
105 + struct device *dev;
106 + struct mutex lock;
107 + struct work_struct ws;
108 + struct twl4030_madc_request requests[TWL4030_MADC_NUM_METHODS];
109 + int imr;
110 + int isr;
111 +};
112 +
113 +static struct twl4030_madc_data *the_madc;
114 +
115 +static
116 +const struct twl4030_madc_conversion_method twl4030_conversion_methods[] = {
117 + [TWL4030_MADC_RT] = {
118 + .sel = TWL4030_MADC_RTSELECT_LSB,
119 + .avg = TWL4030_MADC_RTAVERAGE_LSB,
120 + .rbase = TWL4030_MADC_RTCH0_LSB,
121 + },
122 + [TWL4030_MADC_SW1] = {
123 + .sel = TWL4030_MADC_SW1SELECT_LSB,
124 + .avg = TWL4030_MADC_SW1AVERAGE_LSB,
125 + .rbase = TWL4030_MADC_GPCH0_LSB,
126 + .ctrl = TWL4030_MADC_CTRL_SW1,
127 + },
128 + [TWL4030_MADC_SW2] = {
129 + .sel = TWL4030_MADC_SW2SELECT_LSB,
130 + .avg = TWL4030_MADC_SW2AVERAGE_LSB,
131 + .rbase = TWL4030_MADC_GPCH0_LSB,
132 + .ctrl = TWL4030_MADC_CTRL_SW2,
133 + },
134 +};
135 +
136 +static int twl4030_madc_read(struct twl4030_madc_data *madc, u8 reg)
137 +{
138 + int ret;
139 + u8 val;
140 +
141 + ret = twl_i2c_read_u8(TWL4030_MODULE_MADC, &val, reg);
142 + if (ret) {
143 + dev_dbg(madc->dev, "unable to read register 0x%X\n", reg);
144 + return ret;
145 + }
146 +
147 + return val;
148 +}
149 +
150 +static void twl4030_madc_write(struct twl4030_madc_data *madc, u8 reg, u8 val)
151 +{
152 + int ret;
153 +
154 + ret = twl_i2c_write_u8(TWL4030_MODULE_MADC, val, reg);
155 + if (ret)
156 + dev_err(madc->dev, "unable to write register 0x%X\n", reg);
157 +}
158 +
159 +static int twl4030_madc_channel_raw_read(struct twl4030_madc_data *madc, u8 reg)
160 +{
161 + u8 msb, lsb;
162 +
163 + /* For each ADC channel, we have MSB and LSB register pair. MSB address
164 + * is always LSB address+1. reg parameter is the addr of LSB register */
165 + msb = twl4030_madc_read(madc, reg + 1);
166 + lsb = twl4030_madc_read(madc, reg);
167 +
168 + return (int)(((msb << 8) | lsb) >> 6);
169 +}
170 +
171 +static int twl4030_madc_read_channels(struct twl4030_madc_data *madc,
172 + u8 reg_base, u16 channels, int *buf)
173 +{
174 + int count = 0;
175 + u8 reg, i;
176 +
177 + if (unlikely(!buf))
178 + return 0;
179 +
180 + for (i = 0; i < TWL4030_MADC_MAX_CHANNELS; i++) {
181 + if (channels & (1<<i)) {
182 + reg = reg_base + 2*i;
183 + buf[i] = twl4030_madc_channel_raw_read(madc, reg);
184 + count++;
185 + }
186 + }
187 + return count;
188 +}
189 +
190 +static void twl4030_madc_enable_irq(struct twl4030_madc_data *madc, int id)
191 +{
192 + u8 val;
193 +
194 + val = twl4030_madc_read(madc, madc->imr);
195 + val &= ~(1 << id);
196 + twl4030_madc_write(madc, madc->imr, val);
197 +}
198 +
199 +static void twl4030_madc_disable_irq(struct twl4030_madc_data *madc, int id)
200 +{
201 + u8 val;
202 +
203 + val = twl4030_madc_read(madc, madc->imr);
204 + val |= (1 << id);
205 + twl4030_madc_write(madc, madc->imr, val);
206 +}
207 +
208 +static irqreturn_t twl4030_madc_irq_handler(int irq, void *_madc)
209 +{
210 + struct twl4030_madc_data *madc = _madc;
211 + u8 isr_val, imr_val;
212 + int i;
213 +
214 +#ifdef CONFIG_LOCKDEP
215 + /* WORKAROUND for lockdep forcing IRQF_DISABLED on us, which
216 + * we don't want and can't tolerate. Although it might be
217 + * friendlier not to borrow this thread context...
218 + */
219 + local_irq_enable();
220 +#endif
221 +
222 + /* Use COR to ack interrupts since we have no shared IRQs in ISRx */
223 + isr_val = twl4030_madc_read(madc, madc->isr);
224 + imr_val = twl4030_madc_read(madc, madc->imr);
225 +
226 + isr_val &= ~imr_val;
227 +
228 + for (i = 0; i < TWL4030_MADC_NUM_METHODS; i++) {
229 +
230 + if (!(isr_val & (1<<i)))
231 + continue;
232 +
233 + twl4030_madc_disable_irq(madc, i);
234 + madc->requests[i].result_pending = 1;
235 + }
236 +
237 + schedule_work(&madc->ws);
238 +
239 + return IRQ_HANDLED;
240 +}
241 +
242 +static void twl4030_madc_work(struct work_struct *ws)
243 +{
244 + const struct twl4030_madc_conversion_method *method;
245 + struct twl4030_madc_data *madc;
246 + struct twl4030_madc_request *r;
247 + int len, i;
248 +
249 + madc = container_of(ws, struct twl4030_madc_data, ws);
250 + mutex_lock(&madc->lock);
251 +
252 + for (i = 0; i < TWL4030_MADC_NUM_METHODS; i++) {
253 +
254 + r = &madc->requests[i];
255 +
256 + /* No pending results for this method, move to next one */
257 + if (!r->result_pending)
258 + continue;
259 +
260 + method = &twl4030_conversion_methods[r->method];
261 +
262 + /* Read results */
263 + len = twl4030_madc_read_channels(madc, method->rbase,
264 + r->channels, r->rbuf);
265 +
266 + /* Return results to caller */
267 + if (r->func_cb != NULL) {
268 + r->func_cb(len, r->channels, r->rbuf);
269 + r->func_cb = NULL;
270 + }
271 +
272 + /* Free request */
273 + r->result_pending = 0;
274 + r->active = 0;
275 + }
276 +
277 + mutex_unlock(&madc->lock);
278 +}
279 +
280 +static int twl4030_madc_set_irq(struct twl4030_madc_data *madc,
281 + struct twl4030_madc_request *req)
282 +{
283 + struct twl4030_madc_request *p;
284 +
285 + p = &madc->requests[req->method];
286 +
287 + memcpy(p, req, sizeof *req);
288 +
289 + twl4030_madc_enable_irq(madc, req->method);
290 +
291 + return 0;
292 +}
293 +
294 +static inline void twl4030_madc_start_conversion(struct twl4030_madc_data *madc,
295 + int conv_method)
296 +{
297 + const struct twl4030_madc_conversion_method *method;
298 +
299 + method = &twl4030_conversion_methods[conv_method];
300 +
301 + switch (conv_method) {
302 + case TWL4030_MADC_SW1:
303 + case TWL4030_MADC_SW2:
304 + twl4030_madc_write(madc, method->ctrl, TWL4030_MADC_SW_START);
305 + break;
306 + case TWL4030_MADC_RT:
307 + default:
308 + break;
309 + }
310 +}
311 +
312 +static int twl4030_madc_wait_conversion_ready(
313 + struct twl4030_madc_data *madc,
314 + unsigned int timeout_ms, u8 status_reg)
315 +{
316 + unsigned long timeout;
317 +
318 + timeout = jiffies + msecs_to_jiffies(timeout_ms);
319 + do {
320 + u8 reg;
321 +
322 + reg = twl4030_madc_read(madc, status_reg);
323 + if (!(reg & TWL4030_MADC_BUSY) && (reg & TWL4030_MADC_EOC_SW))
324 + return 0;
325 + } while (!time_after(jiffies, timeout));
326 +
327 + return -EAGAIN;
328 +}
329 +
330 +int twl4030_madc_conversion(struct twl4030_madc_request *req)
331 +{
332 + const struct twl4030_madc_conversion_method *method;
333 + u8 ch_msb, ch_lsb;
334 + int ret;
335 +
336 + if (unlikely(!req))
337 + return -EINVAL;
338 +
339 + mutex_lock(&the_madc->lock);
340 +
341 + /* Do we have a conversion request ongoing */
342 + if (the_madc->requests[req->method].active) {
343 + ret = -EBUSY;
344 + goto out;
345 + }
346 +
347 + ch_msb = (req->channels >> 8) & 0xff;
348 + ch_lsb = req->channels & 0xff;
349 +
350 + method = &twl4030_conversion_methods[req->method];
351 +
352 + /* Select channels to be converted */
353 + twl4030_madc_write(the_madc, method->sel + 1, ch_msb);
354 + twl4030_madc_write(the_madc, method->sel, ch_lsb);
355 +
356 + /* Select averaging for all channels if do_avg is set */
357 + if (req->do_avg) {
358 + twl4030_madc_write(the_madc, method->avg + 1, ch_msb);
359 + twl4030_madc_write(the_madc, method->avg, ch_lsb);
360 + }
361 +
362 + if ((req->type == TWL4030_MADC_IRQ_ONESHOT) && (req->func_cb != NULL)) {
363 + twl4030_madc_set_irq(the_madc, req);
364 + twl4030_madc_start_conversion(the_madc, req->method);
365 + the_madc->requests[req->method].active = 1;
366 + ret = 0;
367 + goto out;
368 + }
369 +
370 + /* With RT method we should not be here anymore */
371 + if (req->method == TWL4030_MADC_RT) {
372 + ret = -EINVAL;
373 + goto out;
374 + }
375 +
376 + twl4030_madc_start_conversion(the_madc, req->method);
377 + the_madc->requests[req->method].active = 1;
378 +
379 + /* Wait until conversion is ready (ctrl register returns EOC) */
380 + ret = twl4030_madc_wait_conversion_ready(the_madc, 5, method->ctrl);
381 + if (ret) {
382 + dev_dbg(the_madc->dev, "conversion timeout!\n");
383 + the_madc->requests[req->method].active = 0;
384 + goto out;
385 + }
386 +
387 + ret = twl4030_madc_read_channels(the_madc, method->rbase, req->channels,
388 + req->rbuf);
389 +
390 + the_madc->requests[req->method].active = 0;
391 +
392 +out:
393 + mutex_unlock(&the_madc->lock);
394 +
395 + return ret;
396 +}
397 +EXPORT_SYMBOL(twl4030_madc_conversion);
398 +
399 +static int twl4030_madc_set_current_generator(struct twl4030_madc_data *madc,
400 + int chan, int on)
401 +{
402 + int ret;
403 + u8 regval;
404 +
405 + /* Current generator is only available for ADCIN0 and ADCIN1. NB:
406 + * ADCIN1 current generator only works when AC or VBUS is present */
407 + if (chan > 1)
408 + return EINVAL;
409 +
410 + ret = twl_i2c_read_u8(TWL4030_MODULE_MAIN_CHARGE,
411 + ®val, TWL4030_BCI_BCICTL1);
412 + if (on)
413 + regval |= (chan) ? TWL4030_BCI_ITHEN : TWL4030_BCI_TYPEN;
414 + else
415 + regval &= (chan) ? ~TWL4030_BCI_ITHEN : ~TWL4030_BCI_TYPEN;
416 + ret = twl_i2c_write_u8(TWL4030_MODULE_MAIN_CHARGE,
417 + regval, TWL4030_BCI_BCICTL1);
418 +
419 + return ret;
420 +}
421 +
422 +static int twl4030_madc_set_power(struct twl4030_madc_data *madc, int on)
423 +{
424 + u8 regval;
425 +
426 + regval = twl4030_madc_read(madc, TWL4030_MADC_CTRL1);
427 + if (on)
428 + regval |= TWL4030_MADC_MADCON;
429 + else
430 + regval &= ~TWL4030_MADC_MADCON;
431 + twl4030_madc_write(madc, TWL4030_MADC_CTRL1, regval);
432 +
433 + return 0;
434 +}
435 +
436 +static long twl4030_madc_ioctl(struct file *filp, unsigned int cmd,
437 + unsigned long arg)
438 +{
439 + struct twl4030_madc_user_parms par;
440 + int val, ret;
441 +
442 + ret = copy_from_user(&par, (void __user *) arg, sizeof(par));
443 + if (ret) {
444 + dev_dbg(the_madc->dev, "copy_from_user: %d\n", ret);
445 + return -EACCES;
446 + }
447 +
448 + switch (cmd) {
449 + case TWL4030_MADC_IOCX_ADC_RAW_READ: {
450 + struct twl4030_madc_request req;
451 + if (par.channel >= TWL4030_MADC_MAX_CHANNELS)
452 + return -EINVAL;
453 +
454 + req.channels = (1 << par.channel);
455 + req.do_avg = par.average;
456 + req.method = TWL4030_MADC_SW1;
457 + req.func_cb = NULL;
458 +
459 + val = twl4030_madc_conversion(&req);
460 + if (val <= 0) {
461 + par.status = -1;
462 + } else {
463 + par.status = 0;
464 + par.result = (u16)req.rbuf[par.channel];
465 + }
466 + break;
467 + }
468 + default:
469 + return -EINVAL;
470 + }
471 +
472 + ret = copy_to_user((void __user *) arg, &par, sizeof(par));
473 + if (ret) {
474 + dev_dbg(the_madc->dev, "copy_to_user: %d\n", ret);
475 + return -EACCES;
476 + }
477 +
478 + return 0;
479 +}
480 +
481 +static struct file_operations twl4030_madc_fileops = {
482 + .owner = THIS_MODULE,
483 + .unlocked_ioctl = twl4030_madc_ioctl
484 +};
485 +
486 +static struct miscdevice twl4030_madc_device = {
487 + .minor = MISC_DYNAMIC_MINOR,
488 + .name = "twl4030-madc",
489 + .fops = &twl4030_madc_fileops
490 +};
491 +
492 +static int __init twl4030_madc_probe(struct platform_device *pdev)
493 +{
494 + struct twl4030_madc_data *madc;
495 + struct twl4030_madc_platform_data *pdata = pdev->dev.platform_data;
496 + int ret;
497 + u8 regval;
498 +
499 + madc = kzalloc(sizeof *madc, GFP_KERNEL);
500 + if (!madc)
501 + return -ENOMEM;
502 +
503 + if (!pdata) {
504 + dev_dbg(&pdev->dev, "platform_data not available\n");
505 + ret = -EINVAL;
506 + goto err_pdata;
507 + }
508 +
509 + madc->imr = (pdata->irq_line == 1) ? TWL4030_MADC_IMR1 : TWL4030_MADC_IMR2;
510 + madc->isr = (pdata->irq_line == 1) ? TWL4030_MADC_ISR1 : TWL4030_MADC_ISR2;
511 +
512 + ret = misc_register(&twl4030_madc_device);
513 + if (ret) {
514 + dev_dbg(&pdev->dev, "could not register misc_device\n");
515 + goto err_misc;
516 + }
517 + twl4030_madc_set_power(madc, 1);
518 + twl4030_madc_set_current_generator(madc, 0, 1);
519 +
520 + /* Enable ADCIN3 through 6 */
521 + ret = twl_i2c_read_u8(TWL4030_MODULE_USB,
522 + ®val, TWL4030_USB_CARKIT_ANA_CTRL);
523 +
524 + regval |= TWL4030_USB_SEL_MADC_MCPC;
525 +
526 + ret = twl_i2c_write_u8(TWL4030_MODULE_USB,
527 + regval, TWL4030_USB_CARKIT_ANA_CTRL);
528 +
529 +
530 + ret = twl_i2c_read_u8(TWL4030_MODULE_MAIN_CHARGE,
531 + ®val, TWL4030_BCI_BCICTL1);
532 +
533 + regval |= TWL4030_BCI_MESBAT;
534 +
535 + ret = twl_i2c_write_u8(TWL4030_MODULE_MAIN_CHARGE,
536 + regval, TWL4030_BCI_BCICTL1);
537 +
538 + ret = request_irq(platform_get_irq(pdev, 0), twl4030_madc_irq_handler,
539 + 0, "twl4030_madc", madc);
540 + if (ret) {
541 + dev_dbg(&pdev->dev, "could not request irq\n");
542 + goto err_irq;
543 + }
544 +
545 + platform_set_drvdata(pdev, madc);
546 + mutex_init(&madc->lock);
547 + INIT_WORK(&madc->ws, twl4030_madc_work);
548 +
549 + the_madc = madc;
550 +
551 + return 0;
552 +
553 +err_irq:
554 + misc_deregister(&twl4030_madc_device);
555 +
556 +err_misc:
557 +err_pdata:
558 + kfree(madc);
559 +
560 + return ret;
561 +}
562 +
563 +static int __exit twl4030_madc_remove(struct platform_device *pdev)
564 +{
565 + struct twl4030_madc_data *madc = platform_get_drvdata(pdev);
566 +
567 + twl4030_madc_set_power(madc, 0);
568 + twl4030_madc_set_current_generator(madc, 0, 0);
569 + free_irq(platform_get_irq(pdev, 0), madc);
570 + cancel_work_sync(&madc->ws);
571 + misc_deregister(&twl4030_madc_device);
572 +
573 + return 0;
574 +}
575 +
576 +static struct platform_driver twl4030_madc_driver = {
577 + .probe = twl4030_madc_probe,
578 + .remove = __exit_p(twl4030_madc_remove),
579 + .driver = {
580 + .name = "twl4030_madc",
581 + .owner = THIS_MODULE,
582 + },
583 +};
584 +
585 +static int __init twl4030_madc_init(void)
586 +{
587 + return platform_driver_register(&twl4030_madc_driver);
588 +}
589 +module_init(twl4030_madc_init);
590 +
591 +static void __exit twl4030_madc_exit(void)
592 +{
593 + platform_driver_unregister(&twl4030_madc_driver);
594 +}
595 +module_exit(twl4030_madc_exit);
596 +
597 +MODULE_ALIAS("platform:twl4030-madc");
598 +MODULE_AUTHOR("Nokia Corporation");
599 +MODULE_DESCRIPTION("twl4030 ADC driver");
600 +MODULE_LICENSE("GPL");
601 +
602 diff --git a/include/linux/i2c/twl4030-madc.h b/include/linux/i2c/twl4030-madc.h
603 new file mode 100644
604 index 0000000..341a665
605 --- /dev/null
606 +++ b/include/linux/i2c/twl4030-madc.h
607 @@ -0,0 +1,130 @@
608 +/*
609 + * include/linux/i2c/twl4030-madc.h
610 + *
611 + * TWL4030 MADC module driver header
612 + *
613 + * Copyright (C) 2008 Nokia Corporation
614 + * Mikko Ylinen <mikko.k.ylinen@nokia.com>
615 + *
616 + * This program is free software; you can redistribute it and/or
617 + * modify it under the terms of the GNU General Public License
618 + * version 2 as published by the Free Software Foundation.
619 + *
620 + * This program is distributed in the hope that it will be useful, but
621 + * WITHOUT ANY WARRANTY; without even the implied warranty of
622 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
623 + * General Public License for more details.
624 + *
625 + * You should have received a copy of the GNU General Public License
626 + * along with this program; if not, write to the Free Software
627 + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
628 + * 02110-1301 USA
629 + *
630 + */
631 +
632 +#ifndef _TWL4030_MADC_H
633 +#define _TWL4030_MADC_H
634 +
635 +struct twl4030_madc_conversion_method {
636 + u8 sel;
637 + u8 avg;
638 + u8 rbase;
639 + u8 ctrl;
640 +};
641 +
642 +#define TWL4030_MADC_MAX_CHANNELS 16
643 +
644 +struct twl4030_madc_request {
645 + u16 channels;
646 + u16 do_avg;
647 + u16 method;
648 + u16 type;
649 + int active;
650 + int result_pending;
651 + int rbuf[TWL4030_MADC_MAX_CHANNELS];
652 + void (*func_cb)(int len, int channels, int *buf);
653 +};
654 +
655 +enum conversion_methods {
656 + TWL4030_MADC_RT,
657 + TWL4030_MADC_SW1,
658 + TWL4030_MADC_SW2,
659 + TWL4030_MADC_NUM_METHODS
660 +};
661 +
662 +enum sample_type {
663 + TWL4030_MADC_WAIT,
664 + TWL4030_MADC_IRQ_ONESHOT,
665 + TWL4030_MADC_IRQ_REARM
666 +};
667 +
668 +#define TWL4030_MADC_CTRL1 0x00
669 +#define TWL4030_MADC_CTRL2 0x01
670 +
671 +#define TWL4030_MADC_RTSELECT_LSB 0x02
672 +#define TWL4030_MADC_SW1SELECT_LSB 0x06
673 +#define TWL4030_MADC_SW2SELECT_LSB 0x0A
674 +
675 +#define TWL4030_MADC_RTAVERAGE_LSB 0x04
676 +#define TWL4030_MADC_SW1AVERAGE_LSB 0x08
677 +#define TWL4030_MADC_SW2AVERAGE_LSB 0x0C
678 +
679 +#define TWL4030_MADC_CTRL_SW1 0x12
680 +#define TWL4030_MADC_CTRL_SW2 0x13
681 +
682 +#define TWL4030_MADC_RTCH0_LSB 0x17
683 +#define TWL4030_MADC_GPCH0_LSB 0x37
684 +
685 +#define TWL4030_MADC_MADCON (1<<0) /* MADC power on */
686 +#define TWL4030_MADC_BUSY (1<<0) /* MADC busy */
687 +#define TWL4030_MADC_EOC_SW (1<<1) /* MADC conversion completion */
688 +#define TWL4030_MADC_SW_START (1<<5) /* MADC SWx start conversion */
689 +
690 +#define TWL4030_MADC_ADCIN0 (1<<0)
691 +#define TWL4030_MADC_ADCIN1 (1<<1)
692 +#define TWL4030_MADC_ADCIN2 (1<<2)
693 +#define TWL4030_MADC_ADCIN3 (1<<3)
694 +#define TWL4030_MADC_ADCIN4 (1<<4)
695 +#define TWL4030_MADC_ADCIN5 (1<<5)
696 +#define TWL4030_MADC_ADCIN6 (1<<6)
697 +#define TWL4030_MADC_ADCIN7 (1<<7)
698 +#define TWL4030_MADC_ADCIN8 (1<<8)
699 +#define TWL4030_MADC_ADCIN9 (1<<9)
700 +#define TWL4030_MADC_ADCIN10 (1<<10)
701 +#define TWL4030_MADC_ADCIN11 (1<<11)
702 +#define TWL4030_MADC_ADCIN12 (1<<12)
703 +#define TWL4030_MADC_ADCIN13 (1<<13)
704 +#define TWL4030_MADC_ADCIN14 (1<<14)
705 +#define TWL4030_MADC_ADCIN15 (1<<15)
706 +
707 +/* Fixed channels */
708 +#define TWL4030_MADC_BTEMP TWL4030_MADC_ADCIN1
709 +#define TWL4030_MADC_VBUS TWL4030_MADC_ADCIN8
710 +#define TWL4030_MADC_VBKB TWL4030_MADC_ADCIN9
711 +#define TWL4030_MADC_ICHG TWL4030_MADC_ADCIN10
712 +#define TWL4030_MADC_VCHG TWL4030_MADC_ADCIN11
713 +#define TWL4030_MADC_VBAT TWL4030_MADC_ADCIN12
714 +
715 +/* BCI related - XXX To be moved elsewhere */
716 +#define TWL4030_BCI_BCICTL1 0x23
717 +#define TWL4030_BCI_MESBAT (1<<1)
718 +#define TWL4030_BCI_TYPEN (1<<4)
719 +#define TWL4030_BCI_ITHEN (1<<3)
720 +
721 +/* USB related - XXX To be moved elsewhere */
722 +#define TWL4030_USB_CARKIT_ANA_CTRL 0xBB
723 +#define TWL4030_USB_SEL_MADC_MCPC (1<<3)
724 +
725 +#define TWL4030_MADC_IOC_MAGIC '`'
726 +#define TWL4030_MADC_IOCX_ADC_RAW_READ _IO(TWL4030_MADC_IOC_MAGIC, 0)
727 +
728 +struct twl4030_madc_user_parms {
729 + int channel;
730 + int average;
731 + int status;
732 + u16 result;
733 +};
734 +
735 +int twl4030_madc_conversion(struct twl4030_madc_request *conv);
736 +
737 +#endif
738 --
739 1.6.6.1