Merge git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/driver-core-2.6
[linux-2.6/linux-2.6-openrd.git] / drivers / mfd / wm8350-irq.c
blob9025f29e2707403e5986ff2ee29a0f51f57eb811
1 /*
2 * wm8350-irq.c -- IRQ support for Wolfson WM8350
4 * Copyright 2007, 2008, 2009 Wolfson Microelectronics PLC.
6 * Author: Liam Girdwood, Mark Brown
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.
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>
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>
32 #define WM8350_NUM_IRQ_REGS 7
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
42 struct wm8350_irq_data {
43 int primary;
44 int reg;
45 int mask;
46 int primary_only;
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,
56 [WM8350_IRQ_UV_DC1] = {
57 .primary = WM8350_UV_INT,
58 .reg = WM8350_UNDER_VOLTAGE_INT_OFFSET,
59 .mask = WM8350_UV_DC1_EINT,
61 [WM8350_IRQ_UV_DC2] = {
62 .primary = WM8350_UV_INT,
63 .reg = WM8350_UNDER_VOLTAGE_INT_OFFSET,
64 .mask = WM8350_UV_DC2_EINT,
66 [WM8350_IRQ_UV_DC3] = {
67 .primary = WM8350_UV_INT,
68 .reg = WM8350_UNDER_VOLTAGE_INT_OFFSET,
69 .mask = WM8350_UV_DC3_EINT,
71 [WM8350_IRQ_UV_DC4] = {
72 .primary = WM8350_UV_INT,
73 .reg = WM8350_UNDER_VOLTAGE_INT_OFFSET,
74 .mask = WM8350_UV_DC4_EINT,
76 [WM8350_IRQ_UV_DC5] = {
77 .primary = WM8350_UV_INT,
78 .reg = WM8350_UNDER_VOLTAGE_INT_OFFSET,
79 .mask = WM8350_UV_DC5_EINT,
81 [WM8350_IRQ_UV_DC6] = {
82 .primary = WM8350_UV_INT,
83 .reg = WM8350_UNDER_VOLTAGE_INT_OFFSET,
84 .mask = WM8350_UV_DC6_EINT,
86 [WM8350_IRQ_UV_LDO1] = {
87 .primary = WM8350_UV_INT,
88 .reg = WM8350_UNDER_VOLTAGE_INT_OFFSET,
89 .mask = WM8350_UV_LDO1_EINT,
91 [WM8350_IRQ_UV_LDO2] = {
92 .primary = WM8350_UV_INT,
93 .reg = WM8350_UNDER_VOLTAGE_INT_OFFSET,
94 .mask = WM8350_UV_LDO2_EINT,
96 [WM8350_IRQ_UV_LDO3] = {
97 .primary = WM8350_UV_INT,
98 .reg = WM8350_UNDER_VOLTAGE_INT_OFFSET,
99 .mask = WM8350_UV_LDO3_EINT,
101 [WM8350_IRQ_UV_LDO4] = {
102 .primary = WM8350_UV_INT,
103 .reg = WM8350_UNDER_VOLTAGE_INT_OFFSET,
104 .mask = WM8350_UV_LDO4_EINT,
106 [WM8350_IRQ_CHG_BAT_HOT] = {
107 .primary = WM8350_CHG_INT,
108 .reg = WM8350_INT_OFFSET_1,
109 .mask = WM8350_CHG_BAT_HOT_EINT,
111 [WM8350_IRQ_CHG_BAT_COLD] = {
112 .primary = WM8350_CHG_INT,
113 .reg = WM8350_INT_OFFSET_1,
114 .mask = WM8350_CHG_BAT_COLD_EINT,
116 [WM8350_IRQ_CHG_BAT_FAIL] = {
117 .primary = WM8350_CHG_INT,
118 .reg = WM8350_INT_OFFSET_1,
119 .mask = WM8350_CHG_BAT_FAIL_EINT,
121 [WM8350_IRQ_CHG_TO] = {
122 .primary = WM8350_CHG_INT,
123 .reg = WM8350_INT_OFFSET_1,
124 .mask = WM8350_CHG_TO_EINT,
126 [WM8350_IRQ_CHG_END] = {
127 .primary = WM8350_CHG_INT,
128 .reg = WM8350_INT_OFFSET_1,
129 .mask = WM8350_CHG_END_EINT,
131 [WM8350_IRQ_CHG_START] = {
132 .primary = WM8350_CHG_INT,
133 .reg = WM8350_INT_OFFSET_1,
134 .mask = WM8350_CHG_START_EINT,
136 [WM8350_IRQ_CHG_FAST_RDY] = {
137 .primary = WM8350_CHG_INT,
138 .reg = WM8350_INT_OFFSET_1,
139 .mask = WM8350_CHG_FAST_RDY_EINT,
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,
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,
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,
156 [WM8350_IRQ_RTC_ALM] = {
157 .primary = WM8350_RTC_INT,
158 .reg = WM8350_INT_OFFSET_1,
159 .mask = WM8350_RTC_ALM_EINT,
161 [WM8350_IRQ_RTC_SEC] = {
162 .primary = WM8350_RTC_INT,
163 .reg = WM8350_INT_OFFSET_1,
164 .mask = WM8350_RTC_SEC_EINT,
166 [WM8350_IRQ_RTC_PER] = {
167 .primary = WM8350_RTC_INT,
168 .reg = WM8350_INT_OFFSET_1,
169 .mask = WM8350_RTC_PER_EINT,
171 [WM8350_IRQ_CS1] = {
172 .primary = WM8350_CS_INT,
173 .reg = WM8350_INT_OFFSET_2,
174 .mask = WM8350_CS1_EINT,
176 [WM8350_IRQ_CS2] = {
177 .primary = WM8350_CS_INT,
178 .reg = WM8350_INT_OFFSET_2,
179 .mask = WM8350_CS2_EINT,
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,
186 [WM8350_IRQ_SYS_CHIP_GT115] = {
187 .primary = WM8350_SYS_INT,
188 .reg = WM8350_INT_OFFSET_2,
189 .mask = WM8350_SYS_CHIP_GT115_EINT,
191 [WM8350_IRQ_SYS_CHIP_GT140] = {
192 .primary = WM8350_SYS_INT,
193 .reg = WM8350_INT_OFFSET_2,
194 .mask = WM8350_SYS_CHIP_GT140_EINT,
196 [WM8350_IRQ_SYS_WDOG_TO] = {
197 .primary = WM8350_SYS_INT,
198 .reg = WM8350_INT_OFFSET_2,
199 .mask = WM8350_SYS_WDOG_TO_EINT,
201 [WM8350_IRQ_AUXADC_DATARDY] = {
202 .primary = WM8350_AUXADC_INT,
203 .reg = WM8350_INT_OFFSET_2,
204 .mask = WM8350_AUXADC_DATARDY_EINT,
206 [WM8350_IRQ_AUXADC_DCOMP4] = {
207 .primary = WM8350_AUXADC_INT,
208 .reg = WM8350_INT_OFFSET_2,
209 .mask = WM8350_AUXADC_DCOMP4_EINT,
211 [WM8350_IRQ_AUXADC_DCOMP3] = {
212 .primary = WM8350_AUXADC_INT,
213 .reg = WM8350_INT_OFFSET_2,
214 .mask = WM8350_AUXADC_DCOMP3_EINT,
216 [WM8350_IRQ_AUXADC_DCOMP2] = {
217 .primary = WM8350_AUXADC_INT,
218 .reg = WM8350_INT_OFFSET_2,
219 .mask = WM8350_AUXADC_DCOMP2_EINT,
221 [WM8350_IRQ_AUXADC_DCOMP1] = {
222 .primary = WM8350_AUXADC_INT,
223 .reg = WM8350_INT_OFFSET_2,
224 .mask = WM8350_AUXADC_DCOMP1_EINT,
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,
232 [WM8350_IRQ_WKUP_OFF_STATE] = {
233 .primary = WM8350_WKUP_INT,
234 .reg = WM8350_COMPARATOR_INT_OFFSET,
235 .mask = WM8350_WKUP_OFF_STATE_EINT,
237 [WM8350_IRQ_WKUP_HIB_STATE] = {
238 .primary = WM8350_WKUP_INT,
239 .reg = WM8350_COMPARATOR_INT_OFFSET,
240 .mask = WM8350_WKUP_HIB_STATE_EINT,
242 [WM8350_IRQ_WKUP_CONV_FAULT] = {
243 .primary = WM8350_WKUP_INT,
244 .reg = WM8350_COMPARATOR_INT_OFFSET,
245 .mask = WM8350_WKUP_CONV_FAULT_EINT,
247 [WM8350_IRQ_WKUP_WDOG_RST] = {
248 .primary = WM8350_WKUP_INT,
249 .reg = WM8350_COMPARATOR_INT_OFFSET,
250 .mask = WM8350_WKUP_WDOG_RST_EINT,
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,
257 [WM8350_IRQ_WKUP_ONKEY] = {
258 .primary = WM8350_WKUP_INT,
259 .reg = WM8350_COMPARATOR_INT_OFFSET,
260 .mask = WM8350_WKUP_ONKEY_EINT,
262 [WM8350_IRQ_WKUP_GP_WAKEUP] = {
263 .primary = WM8350_WKUP_INT,
264 .reg = WM8350_COMPARATOR_INT_OFFSET,
265 .mask = WM8350_WKUP_GP_WAKEUP_EINT,
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,
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,
277 [WM8350_IRQ_CODEC_MICSCD] = {
278 .primary = WM8350_CODEC_INT,
279 .reg = WM8350_COMPARATOR_INT_OFFSET,
280 .mask = WM8350_CODEC_MICSCD_EINT,
282 [WM8350_IRQ_CODEC_MICD] = {
283 .primary = WM8350_CODEC_INT,
284 .reg = WM8350_COMPARATOR_INT_OFFSET,
285 .mask = WM8350_CODEC_MICD_EINT,
287 [WM8350_IRQ_EXT_USB_FB] = {
288 .primary = WM8350_EXT_INT,
289 .reg = WM8350_COMPARATOR_INT_OFFSET,
290 .mask = WM8350_EXT_USB_FB_EINT,
292 [WM8350_IRQ_EXT_WALL_FB] = {
293 .primary = WM8350_EXT_INT,
294 .reg = WM8350_COMPARATOR_INT_OFFSET,
295 .mask = WM8350_EXT_WALL_FB_EINT,
297 [WM8350_IRQ_EXT_BAT_FB] = {
298 .primary = WM8350_EXT_INT,
299 .reg = WM8350_COMPARATOR_INT_OFFSET,
300 .mask = WM8350_EXT_BAT_FB_EINT,
302 [WM8350_IRQ_GPIO(0)] = {
303 .primary = WM8350_GP_INT,
304 .reg = WM8350_GPIO_INT_OFFSET,
305 .mask = WM8350_GP0_EINT,
307 [WM8350_IRQ_GPIO(1)] = {
308 .primary = WM8350_GP_INT,
309 .reg = WM8350_GPIO_INT_OFFSET,
310 .mask = WM8350_GP1_EINT,
312 [WM8350_IRQ_GPIO(2)] = {
313 .primary = WM8350_GP_INT,
314 .reg = WM8350_GPIO_INT_OFFSET,
315 .mask = WM8350_GP2_EINT,
317 [WM8350_IRQ_GPIO(3)] = {
318 .primary = WM8350_GP_INT,
319 .reg = WM8350_GPIO_INT_OFFSET,
320 .mask = WM8350_GP3_EINT,
322 [WM8350_IRQ_GPIO(4)] = {
323 .primary = WM8350_GP_INT,
324 .reg = WM8350_GPIO_INT_OFFSET,
325 .mask = WM8350_GP4_EINT,
327 [WM8350_IRQ_GPIO(5)] = {
328 .primary = WM8350_GP_INT,
329 .reg = WM8350_GPIO_INT_OFFSET,
330 .mask = WM8350_GP5_EINT,
332 [WM8350_IRQ_GPIO(6)] = {
333 .primary = WM8350_GP_INT,
334 .reg = WM8350_GPIO_INT_OFFSET,
335 .mask = WM8350_GP6_EINT,
337 [WM8350_IRQ_GPIO(7)] = {
338 .primary = WM8350_GP_INT,
339 .reg = WM8350_GPIO_INT_OFFSET,
340 .mask = WM8350_GP7_EINT,
342 [WM8350_IRQ_GPIO(8)] = {
343 .primary = WM8350_GP_INT,
344 .reg = WM8350_GPIO_INT_OFFSET,
345 .mask = WM8350_GP8_EINT,
347 [WM8350_IRQ_GPIO(9)] = {
348 .primary = WM8350_GP_INT,
349 .reg = WM8350_GPIO_INT_OFFSET,
350 .mask = WM8350_GP9_EINT,
352 [WM8350_IRQ_GPIO(10)] = {
353 .primary = WM8350_GP_INT,
354 .reg = WM8350_GPIO_INT_OFFSET,
355 .mask = WM8350_GP10_EINT,
357 [WM8350_IRQ_GPIO(11)] = {
358 .primary = WM8350_GP_INT,
359 .reg = WM8350_GPIO_INT_OFFSET,
360 .mask = WM8350_GP11_EINT,
362 [WM8350_IRQ_GPIO(12)] = {
363 .primary = WM8350_GP_INT,
364 .reg = WM8350_GPIO_INT_OFFSET,
365 .mask = WM8350_GP12_EINT,
369 static void wm8350_irq_call_handler(struct wm8350 *wm8350, int irq)
371 mutex_lock(&wm8350->irq_mutex);
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);
381 mutex_unlock(&wm8350->irq_mutex);
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.
391 static irqreturn_t wm8350_irq(int irq, void *irq_data)
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;
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);
404 if (!level_one)
405 return IRQ_NONE;
407 memset(&read_done, 0, sizeof(read_done));
409 for (i = 0; i < ARRAY_SIZE(wm8350_irqs); i++) {
410 data = &wm8350_irqs[i];
412 if (!(level_one & data->primary))
413 continue;
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;
426 if (sub_reg[data->reg] & data->mask)
427 wm8350_irq_call_handler(wm8350, i);
430 return IRQ_HANDLED;
433 int wm8350_register_irq(struct wm8350 *wm8350, int irq,
434 irq_handler_t handler, unsigned long flags,
435 const char *name, void *data)
437 if (irq < 0 || irq >= WM8350_NUM_IRQ || !handler)
438 return -EINVAL;
440 if (wm8350->irq[irq].handler)
441 return -EBUSY;
443 mutex_lock(&wm8350->irq_mutex);
444 wm8350->irq[irq].handler = handler;
445 wm8350->irq[irq].data = data;
446 mutex_unlock(&wm8350->irq_mutex);
448 wm8350_unmask_irq(wm8350, irq);
450 return 0;
452 EXPORT_SYMBOL_GPL(wm8350_register_irq);
454 int wm8350_free_irq(struct wm8350 *wm8350, int irq)
456 if (irq < 0 || irq >= WM8350_NUM_IRQ)
457 return -EINVAL;
459 wm8350_mask_irq(wm8350, irq);
461 mutex_lock(&wm8350->irq_mutex);
462 wm8350->irq[irq].handler = NULL;
463 mutex_unlock(&wm8350->irq_mutex);
464 return 0;
466 EXPORT_SYMBOL_GPL(wm8350_free_irq);
468 int wm8350_mask_irq(struct wm8350 *wm8350, int irq)
470 return wm8350_set_bits(wm8350, WM8350_INT_STATUS_1_MASK +
471 wm8350_irqs[irq].reg,
472 wm8350_irqs[irq].mask);
474 EXPORT_SYMBOL_GPL(wm8350_mask_irq);
476 int wm8350_unmask_irq(struct wm8350 *wm8350, int irq)
478 return wm8350_clear_bits(wm8350, WM8350_INT_STATUS_1_MASK +
479 wm8350_irqs[irq].reg,
480 wm8350_irqs[irq].mask);
482 EXPORT_SYMBOL_GPL(wm8350_unmask_irq);
484 int wm8350_irq_init(struct wm8350 *wm8350, int irq,
485 struct wm8350_platform_data *pdata)
487 int ret;
488 int flags = IRQF_ONESHOT;
490 if (!irq) {
491 dev_err(wm8350->dev, "No IRQ configured\n");
492 return -EINVAL;
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);
502 mutex_init(&wm8350->irq_mutex);
503 wm8350->chip_irq = irq;
505 if (pdata && pdata->irq_high) {
506 flags |= IRQF_TRIGGER_HIGH;
508 wm8350_set_bits(wm8350, WM8350_SYSTEM_CONTROL_1,
509 WM8350_IRQ_POL);
510 } else {
511 flags |= IRQF_TRIGGER_LOW;
513 wm8350_clear_bits(wm8350, WM8350_SYSTEM_CONTROL_1,
514 WM8350_IRQ_POL);
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);
522 return ret;
525 int wm8350_irq_exit(struct wm8350 *wm8350)
527 free_irq(wm8350->chip_irq, wm8350);
528 return 0;