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
{
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
,
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
,
172 .primary
= WM8350_CS_INT
,
173 .reg
= WM8350_INT_OFFSET_2
,
174 .mask
= WM8350_CS1_EINT
,
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
,
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
);
376 dev_err(wm8350
->dev
, "irq %d nobody cared. now masked.\n",
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
;
395 u16 sub_reg
[WM8350_NUM_IRQ_REGS
];
396 int read_done
[WM8350_NUM_IRQ_REGS
];
397 struct wm8350_irq_data
*data
;
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
);
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
))
415 if (!read_done
[data
->reg
]) {
417 wm8350_reg_read(wm8350
, WM8350_INT_STATUS_1
+
419 sub_reg
[data
->reg
] &=
420 ~wm8350_reg_read(wm8350
,
421 WM8350_INT_STATUS_1_MASK
+
423 read_done
[data
->reg
] = 1;
426 if (sub_reg
[data
->reg
] & data
->mask
)
427 wm8350_irq_call_handler(wm8350
, i
);
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
)
440 if (wm8350
->irq
[irq
].handler
)
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
);
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
)
459 wm8350_mask_irq(wm8350
, irq
);
461 mutex_lock(&wm8350
->irq_mutex
);
462 wm8350
->irq
[irq
].handler
= NULL
;
463 mutex_unlock(&wm8350
->irq_mutex
);
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
)
488 int flags
= IRQF_ONESHOT
;
491 dev_err(wm8350
->dev
, "No IRQ configured\n");
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
,
511 flags
|= IRQF_TRIGGER_LOW
;
513 wm8350_clear_bits(wm8350
, WM8350_SYSTEM_CONTROL_1
,
517 ret
= request_threaded_irq(irq
, NULL
, wm8350_irq
, flags
,
520 dev_err(wm8350
->dev
, "Failed to request IRQ: %d\n", ret
);
525 int wm8350_irq_exit(struct wm8350
*wm8350
)
527 free_irq(wm8350
->chip_irq
, wm8350
);