9025f29e2707403e5986ff2ee29a0f51f57eb811
[linux-2.6.git] / drivers / mfd / wm8350-irq.c
1 /*
2  * wm8350-irq.c  --  IRQ support for Wolfson WM8350
3  *
4  * Copyright 2007, 2008, 2009 Wolfson Microelectronics PLC.
5  *
6  * Author: Liam Girdwood, Mark Brown
7  *
8  *  This program is free software; you can redistribute  it and/or modify it
9  *  under  the terms of  the GNU General  Public License as published by the
10  *  Free Software Foundation;  either version 2 of the  License, or (at your
11  *  option) any later version.
12  *
13  */
14
15 #include <linux/kernel.h>
16 #include <linux/module.h>
17 #include <linux/init.h>
18 #include <linux/bug.h>
19 #include <linux/device.h>
20 #include <linux/interrupt.h>
21 #include <linux/workqueue.h>
22
23 #include <linux/mfd/wm8350/core.h>
24 #include <linux/mfd/wm8350/audio.h>
25 #include <linux/mfd/wm8350/comparator.h>
26 #include <linux/mfd/wm8350/gpio.h>
27 #include <linux/mfd/wm8350/pmic.h>
28 #include <linux/mfd/wm8350/rtc.h>
29 #include <linux/mfd/wm8350/supply.h>
30 #include <linux/mfd/wm8350/wdt.h>
31
32 #define WM8350_NUM_IRQ_REGS 7
33
34 #define WM8350_INT_OFFSET_1                     0
35 #define WM8350_INT_OFFSET_2                     1
36 #define WM8350_POWER_UP_INT_OFFSET              2
37 #define WM8350_UNDER_VOLTAGE_INT_OFFSET         3
38 #define WM8350_OVER_CURRENT_INT_OFFSET          4
39 #define WM8350_GPIO_INT_OFFSET                  5
40 #define WM8350_COMPARATOR_INT_OFFSET            6
41
42 struct wm8350_irq_data {
43         int primary;
44         int reg;
45         int mask;
46         int primary_only;
47 };
48
49 static struct wm8350_irq_data wm8350_irqs[] = {
50         [WM8350_IRQ_OC_LS] = {
51                 .primary = WM8350_OC_INT,
52                 .reg = WM8350_OVER_CURRENT_INT_OFFSET,
53                 .mask = WM8350_OC_LS_EINT,
54                 .primary_only = 1,
55         },
56         [WM8350_IRQ_UV_DC1] = {
57                 .primary = WM8350_UV_INT,
58                 .reg = WM8350_UNDER_VOLTAGE_INT_OFFSET,
59                 .mask = WM8350_UV_DC1_EINT,
60         },
61         [WM8350_IRQ_UV_DC2] = {
62                 .primary = WM8350_UV_INT,
63                 .reg = WM8350_UNDER_VOLTAGE_INT_OFFSET,
64                 .mask = WM8350_UV_DC2_EINT,
65         },
66         [WM8350_IRQ_UV_DC3] = {
67                 .primary = WM8350_UV_INT,
68                 .reg = WM8350_UNDER_VOLTAGE_INT_OFFSET,
69                 .mask = WM8350_UV_DC3_EINT,
70         },
71         [WM8350_IRQ_UV_DC4] = {
72                 .primary = WM8350_UV_INT,
73                 .reg = WM8350_UNDER_VOLTAGE_INT_OFFSET,
74                 .mask = WM8350_UV_DC4_EINT,
75         },
76         [WM8350_IRQ_UV_DC5] = {
77                 .primary = WM8350_UV_INT,
78                 .reg = WM8350_UNDER_VOLTAGE_INT_OFFSET,
79                 .mask = WM8350_UV_DC5_EINT,
80         },
81         [WM8350_IRQ_UV_DC6] = {
82                 .primary = WM8350_UV_INT,
83                 .reg = WM8350_UNDER_VOLTAGE_INT_OFFSET,
84                 .mask = WM8350_UV_DC6_EINT,
85         },
86         [WM8350_IRQ_UV_LDO1] = {
87                 .primary = WM8350_UV_INT,
88                 .reg = WM8350_UNDER_VOLTAGE_INT_OFFSET,
89                 .mask = WM8350_UV_LDO1_EINT,
90         },
91         [WM8350_IRQ_UV_LDO2] = {
92                 .primary = WM8350_UV_INT,
93                 .reg = WM8350_UNDER_VOLTAGE_INT_OFFSET,
94                 .mask = WM8350_UV_LDO2_EINT,
95         },
96         [WM8350_IRQ_UV_LDO3] = {
97                 .primary = WM8350_UV_INT,
98                 .reg = WM8350_UNDER_VOLTAGE_INT_OFFSET,
99                 .mask = WM8350_UV_LDO3_EINT,
100         },
101         [WM8350_IRQ_UV_LDO4] = {
102                 .primary = WM8350_UV_INT,
103                 .reg = WM8350_UNDER_VOLTAGE_INT_OFFSET,
104                 .mask = WM8350_UV_LDO4_EINT,
105         },
106         [WM8350_IRQ_CHG_BAT_HOT] = {
107                 .primary = WM8350_CHG_INT,
108                 .reg = WM8350_INT_OFFSET_1,
109                 .mask = WM8350_CHG_BAT_HOT_EINT,
110         },
111         [WM8350_IRQ_CHG_BAT_COLD] = {
112                 .primary = WM8350_CHG_INT,
113                 .reg = WM8350_INT_OFFSET_1,
114                 .mask = WM8350_CHG_BAT_COLD_EINT,
115         },
116         [WM8350_IRQ_CHG_BAT_FAIL] = {
117                 .primary = WM8350_CHG_INT,
118                 .reg = WM8350_INT_OFFSET_1,
119                 .mask = WM8350_CHG_BAT_FAIL_EINT,
120         },
121         [WM8350_IRQ_CHG_TO] = {
122                 .primary = WM8350_CHG_INT,
123                 .reg = WM8350_INT_OFFSET_1,
124                 .mask = WM8350_CHG_TO_EINT,
125         },
126         [WM8350_IRQ_CHG_END] = {
127                 .primary = WM8350_CHG_INT,
128                 .reg = WM8350_INT_OFFSET_1,
129                 .mask = WM8350_CHG_END_EINT,
130         },
131         [WM8350_IRQ_CHG_START] = {
132                 .primary = WM8350_CHG_INT,
133                 .reg = WM8350_INT_OFFSET_1,
134                 .mask = WM8350_CHG_START_EINT,
135         },
136         [WM8350_IRQ_CHG_FAST_RDY] = {
137                 .primary = WM8350_CHG_INT,
138                 .reg = WM8350_INT_OFFSET_1,
139                 .mask = WM8350_CHG_FAST_RDY_EINT,
140         },
141         [WM8350_IRQ_CHG_VBATT_LT_3P9] = {
142                 .primary = WM8350_CHG_INT,
143                 .reg = WM8350_INT_OFFSET_1,
144                 .mask = WM8350_CHG_VBATT_LT_3P9_EINT,
145         },
146         [WM8350_IRQ_CHG_VBATT_LT_3P1] = {
147                 .primary = WM8350_CHG_INT,
148                 .reg = WM8350_INT_OFFSET_1,
149                 .mask = WM8350_CHG_VBATT_LT_3P1_EINT,
150         },
151         [WM8350_IRQ_CHG_VBATT_LT_2P85] = {
152                 .primary = WM8350_CHG_INT,
153                 .reg = WM8350_INT_OFFSET_1,
154                 .mask = WM8350_CHG_VBATT_LT_2P85_EINT,
155         },
156         [WM8350_IRQ_RTC_ALM] = {
157                 .primary = WM8350_RTC_INT,
158                 .reg = WM8350_INT_OFFSET_1,
159                 .mask = WM8350_RTC_ALM_EINT,
160         },
161         [WM8350_IRQ_RTC_SEC] = {
162                 .primary = WM8350_RTC_INT,
163                 .reg = WM8350_INT_OFFSET_1,
164                 .mask = WM8350_RTC_SEC_EINT,
165         },
166         [WM8350_IRQ_RTC_PER] = {
167                 .primary = WM8350_RTC_INT,
168                 .reg = WM8350_INT_OFFSET_1,
169                 .mask = WM8350_RTC_PER_EINT,
170         },
171         [WM8350_IRQ_CS1] = {
172                 .primary = WM8350_CS_INT,
173                 .reg = WM8350_INT_OFFSET_2,
174                 .mask = WM8350_CS1_EINT,
175         },
176         [WM8350_IRQ_CS2] = {
177                 .primary = WM8350_CS_INT,
178                 .reg = WM8350_INT_OFFSET_2,
179                 .mask = WM8350_CS2_EINT,
180         },
181         [WM8350_IRQ_SYS_HYST_COMP_FAIL] = {
182                 .primary = WM8350_SYS_INT,
183                 .reg = WM8350_INT_OFFSET_2,
184                 .mask = WM8350_SYS_HYST_COMP_FAIL_EINT,
185         },
186         [WM8350_IRQ_SYS_CHIP_GT115] = {
187                 .primary = WM8350_SYS_INT,
188                 .reg = WM8350_INT_OFFSET_2,
189                 .mask = WM8350_SYS_CHIP_GT115_EINT,
190         },
191         [WM8350_IRQ_SYS_CHIP_GT140] = {
192                 .primary = WM8350_SYS_INT,
193                 .reg = WM8350_INT_OFFSET_2,
194                 .mask = WM8350_SYS_CHIP_GT140_EINT,
195         },
196         [WM8350_IRQ_SYS_WDOG_TO] = {
197                 .primary = WM8350_SYS_INT,
198                 .reg = WM8350_INT_OFFSET_2,
199                 .mask = WM8350_SYS_WDOG_TO_EINT,
200         },
201         [WM8350_IRQ_AUXADC_DATARDY] = {
202                 .primary = WM8350_AUXADC_INT,
203                 .reg = WM8350_INT_OFFSET_2,
204                 .mask = WM8350_AUXADC_DATARDY_EINT,
205         },
206         [WM8350_IRQ_AUXADC_DCOMP4] = {
207                 .primary = WM8350_AUXADC_INT,
208                 .reg = WM8350_INT_OFFSET_2,
209                 .mask = WM8350_AUXADC_DCOMP4_EINT,
210         },
211         [WM8350_IRQ_AUXADC_DCOMP3] = {
212                 .primary = WM8350_AUXADC_INT,
213                 .reg = WM8350_INT_OFFSET_2,
214                 .mask = WM8350_AUXADC_DCOMP3_EINT,
215         },
216         [WM8350_IRQ_AUXADC_DCOMP2] = {
217                 .primary = WM8350_AUXADC_INT,
218                 .reg = WM8350_INT_OFFSET_2,
219                 .mask = WM8350_AUXADC_DCOMP2_EINT,
220         },
221         [WM8350_IRQ_AUXADC_DCOMP1] = {
222                 .primary = WM8350_AUXADC_INT,
223                 .reg = WM8350_INT_OFFSET_2,
224                 .mask = WM8350_AUXADC_DCOMP1_EINT,
225         },
226         [WM8350_IRQ_USB_LIMIT] = {
227                 .primary = WM8350_USB_INT,
228                 .reg = WM8350_INT_OFFSET_2,
229                 .mask = WM8350_USB_LIMIT_EINT,
230                 .primary_only = 1,
231         },
232         [WM8350_IRQ_WKUP_OFF_STATE] = {
233                 .primary = WM8350_WKUP_INT,
234                 .reg = WM8350_COMPARATOR_INT_OFFSET,
235                 .mask = WM8350_WKUP_OFF_STATE_EINT,
236         },
237         [WM8350_IRQ_WKUP_HIB_STATE] = {
238                 .primary = WM8350_WKUP_INT,
239                 .reg = WM8350_COMPARATOR_INT_OFFSET,
240                 .mask = WM8350_WKUP_HIB_STATE_EINT,
241         },
242         [WM8350_IRQ_WKUP_CONV_FAULT] = {
243                 .primary = WM8350_WKUP_INT,
244                 .reg = WM8350_COMPARATOR_INT_OFFSET,
245                 .mask = WM8350_WKUP_CONV_FAULT_EINT,
246         },
247         [WM8350_IRQ_WKUP_WDOG_RST] = {
248                 .primary = WM8350_WKUP_INT,
249                 .reg = WM8350_COMPARATOR_INT_OFFSET,
250                 .mask = WM8350_WKUP_WDOG_RST_EINT,
251         },
252         [WM8350_IRQ_WKUP_GP_PWR_ON] = {
253                 .primary = WM8350_WKUP_INT,
254                 .reg = WM8350_COMPARATOR_INT_OFFSET,
255                 .mask = WM8350_WKUP_GP_PWR_ON_EINT,
256         },
257         [WM8350_IRQ_WKUP_ONKEY] = {
258                 .primary = WM8350_WKUP_INT,
259                 .reg = WM8350_COMPARATOR_INT_OFFSET,
260                 .mask = WM8350_WKUP_ONKEY_EINT,
261         },
262         [WM8350_IRQ_WKUP_GP_WAKEUP] = {
263                 .primary = WM8350_WKUP_INT,
264                 .reg = WM8350_COMPARATOR_INT_OFFSET,
265                 .mask = WM8350_WKUP_GP_WAKEUP_EINT,
266         },
267         [WM8350_IRQ_CODEC_JCK_DET_L] = {
268                 .primary = WM8350_CODEC_INT,
269                 .reg = WM8350_COMPARATOR_INT_OFFSET,
270                 .mask = WM8350_CODEC_JCK_DET_L_EINT,
271         },
272         [WM8350_IRQ_CODEC_JCK_DET_R] = {
273                 .primary = WM8350_CODEC_INT,
274                 .reg = WM8350_COMPARATOR_INT_OFFSET,
275                 .mask = WM8350_CODEC_JCK_DET_R_EINT,
276         },
277         [WM8350_IRQ_CODEC_MICSCD] = {
278                 .primary = WM8350_CODEC_INT,
279                 .reg = WM8350_COMPARATOR_INT_OFFSET,
280                 .mask = WM8350_CODEC_MICSCD_EINT,
281         },
282         [WM8350_IRQ_CODEC_MICD] = {
283                 .primary = WM8350_CODEC_INT,
284                 .reg = WM8350_COMPARATOR_INT_OFFSET,
285                 .mask = WM8350_CODEC_MICD_EINT,
286         },
287         [WM8350_IRQ_EXT_USB_FB] = {
288                 .primary = WM8350_EXT_INT,
289                 .reg = WM8350_COMPARATOR_INT_OFFSET,
290                 .mask = WM8350_EXT_USB_FB_EINT,
291         },
292         [WM8350_IRQ_EXT_WALL_FB] = {
293                 .primary = WM8350_EXT_INT,
294                 .reg = WM8350_COMPARATOR_INT_OFFSET,
295                 .mask = WM8350_EXT_WALL_FB_EINT,
296         },
297         [WM8350_IRQ_EXT_BAT_FB] = {
298                 .primary = WM8350_EXT_INT,
299                 .reg = WM8350_COMPARATOR_INT_OFFSET,
300                 .mask = WM8350_EXT_BAT_FB_EINT,
301         },
302         [WM8350_IRQ_GPIO(0)] = {
303                 .primary = WM8350_GP_INT,
304                 .reg = WM8350_GPIO_INT_OFFSET,
305                 .mask = WM8350_GP0_EINT,
306         },
307         [WM8350_IRQ_GPIO(1)] = {
308                 .primary = WM8350_GP_INT,
309                 .reg = WM8350_GPIO_INT_OFFSET,
310                 .mask = WM8350_GP1_EINT,
311         },
312         [WM8350_IRQ_GPIO(2)] = {
313                 .primary = WM8350_GP_INT,
314                 .reg = WM8350_GPIO_INT_OFFSET,
315                 .mask = WM8350_GP2_EINT,
316         },
317         [WM8350_IRQ_GPIO(3)] = {
318                 .primary = WM8350_GP_INT,
319                 .reg = WM8350_GPIO_INT_OFFSET,
320                 .mask = WM8350_GP3_EINT,
321         },
322         [WM8350_IRQ_GPIO(4)] = {
323                 .primary = WM8350_GP_INT,
324                 .reg = WM8350_GPIO_INT_OFFSET,
325                 .mask = WM8350_GP4_EINT,
326         },
327         [WM8350_IRQ_GPIO(5)] = {
328                 .primary = WM8350_GP_INT,
329                 .reg = WM8350_GPIO_INT_OFFSET,
330                 .mask = WM8350_GP5_EINT,
331         },
332         [WM8350_IRQ_GPIO(6)] = {
333                 .primary = WM8350_GP_INT,
334                 .reg = WM8350_GPIO_INT_OFFSET,
335                 .mask = WM8350_GP6_EINT,
336         },
337         [WM8350_IRQ_GPIO(7)] = {
338                 .primary = WM8350_GP_INT,
339                 .reg = WM8350_GPIO_INT_OFFSET,
340                 .mask = WM8350_GP7_EINT,
341         },
342         [WM8350_IRQ_GPIO(8)] = {
343                 .primary = WM8350_GP_INT,
344                 .reg = WM8350_GPIO_INT_OFFSET,
345                 .mask = WM8350_GP8_EINT,
346         },
347         [WM8350_IRQ_GPIO(9)] = {
348                 .primary = WM8350_GP_INT,
349                 .reg = WM8350_GPIO_INT_OFFSET,
350                 .mask = WM8350_GP9_EINT,
351         },
352         [WM8350_IRQ_GPIO(10)] = {
353                 .primary = WM8350_GP_INT,
354                 .reg = WM8350_GPIO_INT_OFFSET,
355                 .mask = WM8350_GP10_EINT,
356         },
357         [WM8350_IRQ_GPIO(11)] = {
358                 .primary = WM8350_GP_INT,
359                 .reg = WM8350_GPIO_INT_OFFSET,
360                 .mask = WM8350_GP11_EINT,
361         },
362         [WM8350_IRQ_GPIO(12)] = {
363                 .primary = WM8350_GP_INT,
364                 .reg = WM8350_GPIO_INT_OFFSET,
365                 .mask = WM8350_GP12_EINT,
366         },
367 };
368
369 static void wm8350_irq_call_handler(struct wm8350 *wm8350, int irq)
370 {
371         mutex_lock(&wm8350->irq_mutex);
372
373         if (wm8350->irq[irq].handler)
374                 wm8350->irq[irq].handler(irq, wm8350->irq[irq].data);
375         else {
376                 dev_err(wm8350->dev, "irq %d nobody cared. now masked.\n",
377                         irq);
378                 wm8350_mask_irq(wm8350, irq);
379         }
380
381         mutex_unlock(&wm8350->irq_mutex);
382 }
383
384 /*
385  * This is a threaded IRQ handler so can access I2C/SPI.  Since all
386  * interrupts are clear on read the IRQ line will be reasserted and
387  * the physical IRQ will be handled again if another interrupt is
388  * asserted while we run - in the normal course of events this is a
389  * rare occurrence so we save I2C/SPI reads.
390  */
391 static irqreturn_t wm8350_irq(int irq, void *irq_data)
392 {
393         struct wm8350 *wm8350 = irq_data;
394         u16 level_one;
395         u16 sub_reg[WM8350_NUM_IRQ_REGS];
396         int read_done[WM8350_NUM_IRQ_REGS];
397         struct wm8350_irq_data *data;
398         int i;
399
400         /* TODO: Use block reads to improve performance? */
401         level_one = wm8350_reg_read(wm8350, WM8350_SYSTEM_INTERRUPTS)
402                 & ~wm8350_reg_read(wm8350, WM8350_SYSTEM_INTERRUPTS_MASK);
403
404         if (!level_one)
405                 return IRQ_NONE;
406
407         memset(&read_done, 0, sizeof(read_done));
408
409         for (i = 0; i < ARRAY_SIZE(wm8350_irqs); i++) {
410                 data = &wm8350_irqs[i];
411
412                 if (!(level_one & data->primary))
413                         continue;
414
415                 if (!read_done[data->reg]) {
416                         sub_reg[data->reg] =
417                                 wm8350_reg_read(wm8350, WM8350_INT_STATUS_1 +
418                                                 data->reg);
419                         sub_reg[data->reg] &=
420                                 ~wm8350_reg_read(wm8350,
421                                                  WM8350_INT_STATUS_1_MASK +
422                                                  data->reg);
423                         read_done[data->reg] = 1;
424                 }
425
426                 if (sub_reg[data->reg] & data->mask)
427                         wm8350_irq_call_handler(wm8350, i);
428         }
429
430         return IRQ_HANDLED;
431 }
432
433 int wm8350_register_irq(struct wm8350 *wm8350, int irq,
434                         irq_handler_t handler, unsigned long flags,
435                         const char *name, void *data)
436 {
437         if (irq < 0 || irq >= WM8350_NUM_IRQ || !handler)
438                 return -EINVAL;
439
440         if (wm8350->irq[irq].handler)
441                 return -EBUSY;
442
443         mutex_lock(&wm8350->irq_mutex);
444         wm8350->irq[irq].handler = handler;
445         wm8350->irq[irq].data = data;
446         mutex_unlock(&wm8350->irq_mutex);
447
448         wm8350_unmask_irq(wm8350, irq);
449
450         return 0;
451 }
452 EXPORT_SYMBOL_GPL(wm8350_register_irq);
453
454 int wm8350_free_irq(struct wm8350 *wm8350, int irq)
455 {
456         if (irq < 0 || irq >= WM8350_NUM_IRQ)
457                 return -EINVAL;
458
459         wm8350_mask_irq(wm8350, irq);
460
461         mutex_lock(&wm8350->irq_mutex);
462         wm8350->irq[irq].handler = NULL;
463         mutex_unlock(&wm8350->irq_mutex);
464         return 0;
465 }
466 EXPORT_SYMBOL_GPL(wm8350_free_irq);
467
468 int wm8350_mask_irq(struct wm8350 *wm8350, int irq)
469 {
470         return wm8350_set_bits(wm8350, WM8350_INT_STATUS_1_MASK +
471                                wm8350_irqs[irq].reg,
472                                wm8350_irqs[irq].mask);
473 }
474 EXPORT_SYMBOL_GPL(wm8350_mask_irq);
475
476 int wm8350_unmask_irq(struct wm8350 *wm8350, int irq)
477 {
478         return wm8350_clear_bits(wm8350, WM8350_INT_STATUS_1_MASK +
479                                  wm8350_irqs[irq].reg,
480                                  wm8350_irqs[irq].mask);
481 }
482 EXPORT_SYMBOL_GPL(wm8350_unmask_irq);
483
484 int wm8350_irq_init(struct wm8350 *wm8350, int irq,
485                     struct wm8350_platform_data *pdata)
486 {
487         int ret;
488         int flags = IRQF_ONESHOT;
489
490         if (!irq) {
491                 dev_err(wm8350->dev, "No IRQ configured\n");
492                 return -EINVAL;
493         }
494
495         wm8350_reg_write(wm8350, WM8350_SYSTEM_INTERRUPTS_MASK, 0xFFFF);
496         wm8350_reg_write(wm8350, WM8350_INT_STATUS_1_MASK, 0xFFFF);
497         wm8350_reg_write(wm8350, WM8350_INT_STATUS_2_MASK, 0xFFFF);
498         wm8350_reg_write(wm8350, WM8350_UNDER_VOLTAGE_INT_STATUS_MASK, 0xFFFF);
499         wm8350_reg_write(wm8350, WM8350_GPIO_INT_STATUS_MASK, 0xFFFF);
500         wm8350_reg_write(wm8350, WM8350_COMPARATOR_INT_STATUS_MASK, 0xFFFF);
501
502         mutex_init(&wm8350->irq_mutex);
503         wm8350->chip_irq = irq;
504
505         if (pdata && pdata->irq_high) {
506                 flags |= IRQF_TRIGGER_HIGH;
507
508                 wm8350_set_bits(wm8350, WM8350_SYSTEM_CONTROL_1,
509                                 WM8350_IRQ_POL);
510         } else {
511                 flags |= IRQF_TRIGGER_LOW;
512
513                 wm8350_clear_bits(wm8350, WM8350_SYSTEM_CONTROL_1,
514                                   WM8350_IRQ_POL);
515         }
516
517         ret = request_threaded_irq(irq, NULL, wm8350_irq, flags,
518                                    "wm8350", wm8350);
519         if (ret != 0)
520                 dev_err(wm8350->dev, "Failed to request IRQ: %d\n", ret);
521
522         return ret;
523 }
524
525 int wm8350_irq_exit(struct wm8350 *wm8350)
526 {
527         free_irq(wm8350->chip_irq, wm8350);
528         return 0;
529 }