]> www.pilppa.org Git - linux-2.6-omap-h63xx.git/blob - drivers/i2c/chips/twl4030-madc.c
f53a3db7fa9e6219f20ea03b00c2583df5e2072c
[linux-2.6-omap-h63xx.git] / drivers / i2c / chips / twl4030-madc.c
1 /*
2  * drivers/i2c/chips/twl4030-madc.c
3  *
4  * TWL4030 MADC module driver
5  *
6  * Copyright (C) 2008 Nokia Corporation
7  * Mikko Ylinen <mikko.k.ylinen@nokia.com>
8  *
9  * This program is free software; you can redistribute it and/or
10  * modify it under the terms of the GNU General Public License
11  * version 2 as published by the Free Software Foundation.
12  *
13  * This program is distributed in the hope that it will be useful, but
14  * WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16  * General Public License for more details.
17  *
18  * You should have received a copy of the GNU General Public License
19  * along with this program; if not, write to the Free Software
20  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
21  * 02110-1301 USA
22  *
23  */
24
25 #include <linux/init.h>
26 #include <linux/interrupt.h>
27 #include <linux/kernel.h>
28 #include <linux/types.h>
29 #include <linux/module.h>
30 #include <linux/delay.h>
31 #include <linux/fs.h>
32 #include <linux/miscdevice.h>
33 #include <linux/i2c/twl4030.h>
34 #include <linux/i2c/twl4030-madc.h>
35
36 #include <asm/uaccess.h>
37
38 #define TWL4030_MADC_PFX        "twl4030-madc: "
39
40 static struct twl4030_madc_data {
41         struct mutex            lock;
42         struct work_struct      ws;
43         struct twl4030_madc_request     requests[TWL4030_MADC_NUM_METHODS];
44 } twl4030_madc;
45
46 static const char irq_pin = 1; /* XXX Read from platfrom data */
47
48 static
49 const struct twl4030_madc_conversion_method twl4030_conversion_methods[] = {
50         [TWL4030_MADC_RT] = {
51                 .sel    = TWL4030_MADC_RTSELECT_LSB,
52                 .avg    = TWL4030_MADC_RTAVERAGE_LSB,
53                 .rbase  = TWL4030_MADC_RTCH0_LSB,
54         },
55         [TWL4030_MADC_SW1] = {
56                 .sel    = TWL4030_MADC_SW1SELECT_LSB,
57                 .avg    = TWL4030_MADC_SW1AVERAGE_LSB,
58                 .rbase  = TWL4030_MADC_GPCH0_LSB,
59                 .ctrl   = TWL4030_MADC_CTRL_SW1,
60         },
61         [TWL4030_MADC_SW2] = {
62                 .sel    = TWL4030_MADC_SW2SELECT_LSB,
63                 .avg    = TWL4030_MADC_SW2AVERAGE_LSB,
64                 .rbase  = TWL4030_MADC_GPCH0_LSB,
65                 .ctrl   = TWL4030_MADC_CTRL_SW2,
66         },
67 };
68
69 static void twl4030_madc_read(u8 reg, u8 *val)
70 {
71         int ret = twl4030_i2c_read_u8(TWL4030_MODULE_MADC, val, reg);
72         if (ret)
73                 printk(KERN_ERR TWL4030_MADC_PFX
74                        "unable to read register 0x%X\n", reg);
75 }
76
77 static void twl4030_madc_write(u8 reg, u8 val)
78 {
79         int ret = twl4030_i2c_write_u8(TWL4030_MODULE_MADC, val, reg);
80         if (ret)
81                 printk(KERN_ERR TWL4030_MADC_PFX
82                        "unable to write register 0x%X\n", reg);
83 }
84
85 static int twl4030_madc_channel_raw_read(u8 reg)
86 {
87         u8 msb, lsb;
88
89         /* For each ADC channel, we have MSB and LSB register pair. MSB address
90          * is always LSB address+1. reg parameter is the addr of LSB register */
91         twl4030_madc_read(reg+1, &msb);
92         twl4030_madc_read(reg, &lsb);
93
94         return (int)(((msb << 8) | lsb) >> 6);
95 }
96
97 static int twl4030_madc_read_channels(u8 reg_base, u16 channels, int *buf)
98 {
99         int count = 0;
100         u8 reg, i;
101
102         if (unlikely(!buf))
103                 return 0;
104
105         for (i = 0; i < TWL4030_MADC_MAX_CHANNELS; i++) {
106                 if (channels & (1<<i)) {
107                         reg = reg_base + 2*i;
108                         buf[i] = twl4030_madc_channel_raw_read(reg);
109                         count++;
110                 }
111         }
112         return count;
113 }
114
115 static void twl4030_madc_enable_irq(int id)
116 {
117         u8 val;
118
119         static u8 imr = (irq_pin == 1) ? TWL4030_MADC_IMR1 : TWL4030_MADC_IMR2;
120
121         twl4030_madc_read(imr, &val);
122         val &= ~(1 << id);
123         twl4030_madc_write(imr, val);
124 }
125
126 static void twl4030_madc_disable_irq(int id)
127 {
128         u8 val;
129
130         static u8 imr = (irq_pin == 1) ? TWL4030_MADC_IMR1 : TWL4030_MADC_IMR2;
131
132         twl4030_madc_read(imr, &val);
133         val |= (1 << id);
134         twl4030_madc_write(imr, val);
135 }
136
137 static irqreturn_t twl4030_madc_irq_handler(int irq, void *madc_dev)
138 {
139         u8 isr_val, imr_val;
140         static u8 isr, imr;
141         int i;
142
143         imr = (irq_pin == 1) ? TWL4030_MADC_IMR1 : TWL4030_MADC_IMR2;
144         isr = (irq_pin == 1) ? TWL4030_MADC_ISR1 : TWL4030_MADC_ISR2;
145
146         /* Use COR to ack interrupts since we have no shared IRQs in ISRx */
147         twl4030_madc_read(isr, &isr_val);
148         twl4030_madc_read(imr, &imr_val);
149
150         isr_val &= ~imr_val;
151
152         for (i = 0; i < TWL4030_MADC_NUM_METHODS; i++) {
153
154                 if (!(isr_val & (1<<i)))
155                         continue;
156
157                 twl4030_madc_disable_irq(i);
158                 twl4030_madc.requests[i].result_pending = 1;
159         }
160
161         schedule_work(&twl4030_madc.ws);
162
163         return IRQ_HANDLED;
164 }
165
166 static void twl4030_madc_work(struct work_struct *ws)
167 {
168         const struct twl4030_madc_conversion_method *method;
169         struct twl4030_madc_request *r;
170         int len, i;
171
172         mutex_lock(&twl4030_madc.lock);
173
174         for (i = 0; i < TWL4030_MADC_NUM_METHODS; i++) {
175
176                 r = &twl4030_madc.requests[i];
177
178                 /* No pending results for this method, move to next one */
179                 if (!r->result_pending)
180                         continue;
181
182                 method = &twl4030_conversion_methods[r->method];
183
184                 /* Read results */
185                 len = twl4030_madc_read_channels(method->rbase,
186                                                  r->channels, r->rbuf);
187
188                 /* Return results to caller */
189                 if (r->func_cb != NULL) {
190                         r->func_cb(len, r->channels, r->rbuf);
191                         r->func_cb = NULL;
192                 }
193
194                 /* Free request */
195                 r->result_pending = 0;
196                 r->active         = 0;
197         }
198
199         mutex_unlock(&twl4030_madc.lock);
200 }
201
202 static int twl4030_madc_set_irq(struct twl4030_madc_request *req)
203 {
204         struct twl4030_madc_request *p;
205
206         p = &twl4030_madc.requests[req->method];
207
208         memcpy(p, req, sizeof *req);
209
210         twl4030_madc_enable_irq(req->method);
211
212         return 0;
213 }
214
215 static inline void twl4030_madc_start_conversion(int conv_method)
216 {
217         const struct twl4030_madc_conversion_method *method;
218
219         method = &twl4030_conversion_methods[conv_method];
220
221         switch (conv_method) {
222         case TWL4030_MADC_SW1:
223         case TWL4030_MADC_SW2:
224                 twl4030_madc_write(method->ctrl, TWL4030_MADC_SW_START);
225                 break;
226         case TWL4030_MADC_RT:
227         default:
228                 break;
229         }
230 }
231
232 static void twl4030_madc_wait_conversion_ready_ms(u8 *time, u8 status_reg)
233 {
234         u8 reg = 0;
235
236         do {
237                 msleep(1);
238                 (*time)--;
239                 twl4030_madc_read(status_reg, &reg);
240         } while (((reg & TWL4030_MADC_BUSY) && !(reg & TWL4030_MADC_EOC_SW)) &&
241                   (*time != 0));
242 }
243
244 int twl4030_madc_conversion(struct twl4030_madc_request *req)
245 {
246         const struct twl4030_madc_conversion_method *method;
247         u8 wait_time, ch_msb, ch_lsb;
248         int ret;
249
250         if (unlikely(!req))
251                 return -EINVAL;
252
253         /* Do we have a conversion request ongoing */
254         if (twl4030_madc.requests[req->method].active)
255                 return -EBUSY;
256
257         ch_msb = (req->channels >> 8) & 0xff;
258         ch_lsb = req->channels & 0xff;
259
260         method = &twl4030_conversion_methods[req->method];
261
262         mutex_lock(&twl4030_madc.lock);
263
264         /* Select channels to be converted */
265         twl4030_madc_write(method->sel + 1, ch_msb);
266         twl4030_madc_write(method->sel, ch_lsb);
267
268         /* Select averaging for all channels if do_avg is set */
269         if (req->do_avg) {
270                 twl4030_madc_write(method->avg + 1, ch_msb);
271                 twl4030_madc_write(method->avg, ch_lsb);
272         }
273
274         if ((req->type == TWL4030_MADC_IRQ_ONESHOT) && (req->func_cb != NULL)) {
275                 twl4030_madc_set_irq(req);
276                 twl4030_madc_start_conversion(req->method);
277                 twl4030_madc.requests[req->method].active = 1;
278                 ret = 0;
279                 goto out;
280         }
281
282         /* With RT method we should not be here anymore */
283         if (req->method == TWL4030_MADC_RT) {
284                 ret = -EINVAL;
285                 goto out;
286         }
287
288         twl4030_madc_start_conversion(req->method);
289         twl4030_madc.requests[req->method].active = 1;
290
291         /* Wait until conversion is ready (ctrl register returns EOC) */
292         wait_time = 50;
293         twl4030_madc_wait_conversion_ready_ms(&wait_time, method->ctrl);
294         if (wait_time == 0) {
295                 printk(KERN_ERR TWL4030_MADC_PFX "conversion timeout!\n");
296                 ret = -EAGAIN;
297                 goto out;
298         }
299
300         ret = twl4030_madc_read_channels(method->rbase, req->channels,
301                                          req->rbuf);
302
303         twl4030_madc.requests[req->method].active = 0;
304
305 out:
306         mutex_unlock(&twl4030_madc.lock);
307
308         return ret;
309 }
310
311 EXPORT_SYMBOL(twl4030_madc_conversion);
312
313 static int twl4030_madc_set_current_generator(int chan, int on)
314 {
315         int ret;
316         u8 regval;
317
318         /* Current generator is only available for ADCIN0 and ADCIN1. NB:
319          * ADCIN1 current generator only works when AC or VBUS is present */
320         if (chan > 1)
321                 return EINVAL;
322
323         ret = twl4030_i2c_read_u8(TWL4030_MODULE_MAIN_CHARGE,
324                                   &regval, TWL4030_BCI_BCICTL1);
325         if (on)
326                 regval |= (chan) ? TWL4030_BCI_ITHEN : TWL4030_BCI_TYPEN;
327         else
328                 regval &= (chan) ? ~TWL4030_BCI_ITHEN : ~TWL4030_BCI_TYPEN;
329         ret = twl4030_i2c_write_u8(TWL4030_MODULE_MAIN_CHARGE,
330                                    regval, TWL4030_BCI_BCICTL1);
331
332         return ret;
333 }
334
335 static int twl4030_madc_set_power(int on)
336 {
337         u8 regval;
338
339         twl4030_madc_read(TWL4030_MADC_CTRL1, &regval);
340         if (on)
341                 regval |= TWL4030_MADC_MADCON;
342         else
343                 regval &= ~TWL4030_MADC_MADCON;
344         twl4030_madc_write(TWL4030_MADC_CTRL1, regval);
345
346         return 0;
347 }
348
349 static int twl4030_madc_ioctl(struct inode *inode, struct file *filp,
350                               unsigned int cmd, unsigned long arg)
351 {
352         struct twl4030_madc_user_parms par;
353         int val, ret;
354
355         ret = copy_from_user(&par, (void __user *) arg, sizeof(par));
356         if (ret) {
357                 printk(KERN_ERR TWL4030_MADC_PFX "copy_from_user: %d\n", ret);
358                 return -EACCES;
359         }
360
361         switch (cmd) {
362         case TWL4030_MADC_IOCX_ADC_RAW_READ: {
363                 struct twl4030_madc_request req;
364                 if (par.channel >= TWL4030_MADC_MAX_CHANNELS)
365                         return -EINVAL;
366
367                 req.channels = (1<<par.channel);
368                 req.do_avg      = par.average;
369                 req.method      = TWL4030_MADC_SW1;
370
371                 val = twl4030_madc_conversion(&req);
372                 if (val <= 0) {
373                         par.status = -1;
374                 } else {
375                         par.status = 0;
376                         par.result = (u16)req.rbuf[par.channel];
377                 }
378                 break;
379                                              }
380         default:
381                 return -EINVAL;
382         }
383
384         ret = copy_to_user((void __user *) arg, &par, sizeof(par));
385         if (ret) {
386                 printk(KERN_ERR TWL4030_MADC_PFX "copy_to_user: %d\n", ret);
387                 return -EACCES;
388         }
389
390         return 0;
391 }
392
393 static struct file_operations twl4030_madc_fileops = {
394         .owner = THIS_MODULE,
395         .ioctl = twl4030_madc_ioctl
396 };
397
398 static struct miscdevice twl4030_madc_device = {
399         .minor = MISC_DYNAMIC_MINOR,
400         .name = "twl4030-adc",
401         .fops = &twl4030_madc_fileops
402 };
403
404 static int __init twl4030_madc_init(void)
405 {
406         int ret;
407         u8 regval;
408
409         ret = misc_register(&twl4030_madc_device);
410         if (ret == -1) {
411                 printk(KERN_ERR TWL4030_MADC_PFX "misc_register() failed!\n");
412                 return ret;
413         }
414         twl4030_madc_set_power(1);
415         twl4030_madc_set_current_generator(0, 1);
416
417         ret = twl4030_i2c_read_u8(TWL4030_MODULE_MAIN_CHARGE,
418                                   &regval, TWL4030_BCI_BCICTL1);
419
420         regval |= TWL4030_BCI_MESBAT;
421
422         ret = twl4030_i2c_write_u8(TWL4030_MODULE_MAIN_CHARGE,
423                                    regval, TWL4030_BCI_BCICTL1);
424
425         ret = request_irq(TWL4030_MODIRQ_MADC, twl4030_madc_irq_handler,
426                           IRQF_DISABLED, "twl4030_madc", &twl4030_madc);
427         if (ret)
428                 printk(KERN_ERR TWL4030_MADC_PFX "request_irq: %d\n", ret);
429
430         mutex_init(&twl4030_madc.lock);
431
432         INIT_WORK(&twl4030_madc.ws, twl4030_madc_work);
433
434         printk(KERN_INFO TWL4030_MADC_PFX "initialised\n");
435
436         return ret;
437 }
438
439 static void __exit twl4030_madc_exit(void)
440 {
441         twl4030_madc_set_power(0);
442         twl4030_madc_set_current_generator(0, 0);
443         free_irq(TWL4030_MODIRQ_MADC, &twl4030_madc);
444         cancel_work_sync(&twl4030_madc.ws);
445         misc_deregister(&twl4030_madc_device);
446 }
447
448 module_init(twl4030_madc_init);
449 module_exit(twl4030_madc_exit);
450
451 MODULE_AUTHOR("Nokia Corporation");
452 MODULE_DESCRIPTION("twl4030 ADC driver");
453 MODULE_LICENSE("GPL");