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/irq.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_INT_OFFSET_1 0
33 #define WM8350_INT_OFFSET_2 1
34 #define WM8350_POWER_UP_INT_OFFSET 2
35 #define WM8350_UNDER_VOLTAGE_INT_OFFSET 3
36 #define WM8350_OVER_CURRENT_INT_OFFSET 4
37 #define WM8350_GPIO_INT_OFFSET 5
38 #define WM8350_COMPARATOR_INT_OFFSET 6
40 struct wm8350_irq_data
{
47 static struct wm8350_irq_data wm8350_irqs
[] = {
48 [WM8350_IRQ_OC_LS
] = {
49 .primary
= WM8350_OC_INT
,
50 .reg
= WM8350_OVER_CURRENT_INT_OFFSET
,
51 .mask
= WM8350_OC_LS_EINT
,
54 [WM8350_IRQ_UV_DC1
] = {
55 .primary
= WM8350_UV_INT
,
56 .reg
= WM8350_UNDER_VOLTAGE_INT_OFFSET
,
57 .mask
= WM8350_UV_DC1_EINT
,
59 [WM8350_IRQ_UV_DC2
] = {
60 .primary
= WM8350_UV_INT
,
61 .reg
= WM8350_UNDER_VOLTAGE_INT_OFFSET
,
62 .mask
= WM8350_UV_DC2_EINT
,
64 [WM8350_IRQ_UV_DC3
] = {
65 .primary
= WM8350_UV_INT
,
66 .reg
= WM8350_UNDER_VOLTAGE_INT_OFFSET
,
67 .mask
= WM8350_UV_DC3_EINT
,
69 [WM8350_IRQ_UV_DC4
] = {
70 .primary
= WM8350_UV_INT
,
71 .reg
= WM8350_UNDER_VOLTAGE_INT_OFFSET
,
72 .mask
= WM8350_UV_DC4_EINT
,
74 [WM8350_IRQ_UV_DC5
] = {
75 .primary
= WM8350_UV_INT
,
76 .reg
= WM8350_UNDER_VOLTAGE_INT_OFFSET
,
77 .mask
= WM8350_UV_DC5_EINT
,
79 [WM8350_IRQ_UV_DC6
] = {
80 .primary
= WM8350_UV_INT
,
81 .reg
= WM8350_UNDER_VOLTAGE_INT_OFFSET
,
82 .mask
= WM8350_UV_DC6_EINT
,
84 [WM8350_IRQ_UV_LDO1
] = {
85 .primary
= WM8350_UV_INT
,
86 .reg
= WM8350_UNDER_VOLTAGE_INT_OFFSET
,
87 .mask
= WM8350_UV_LDO1_EINT
,
89 [WM8350_IRQ_UV_LDO2
] = {
90 .primary
= WM8350_UV_INT
,
91 .reg
= WM8350_UNDER_VOLTAGE_INT_OFFSET
,
92 .mask
= WM8350_UV_LDO2_EINT
,
94 [WM8350_IRQ_UV_LDO3
] = {
95 .primary
= WM8350_UV_INT
,
96 .reg
= WM8350_UNDER_VOLTAGE_INT_OFFSET
,
97 .mask
= WM8350_UV_LDO3_EINT
,
99 [WM8350_IRQ_UV_LDO4
] = {
100 .primary
= WM8350_UV_INT
,
101 .reg
= WM8350_UNDER_VOLTAGE_INT_OFFSET
,
102 .mask
= WM8350_UV_LDO4_EINT
,
104 [WM8350_IRQ_CHG_BAT_HOT
] = {
105 .primary
= WM8350_CHG_INT
,
106 .reg
= WM8350_INT_OFFSET_1
,
107 .mask
= WM8350_CHG_BAT_HOT_EINT
,
109 [WM8350_IRQ_CHG_BAT_COLD
] = {
110 .primary
= WM8350_CHG_INT
,
111 .reg
= WM8350_INT_OFFSET_1
,
112 .mask
= WM8350_CHG_BAT_COLD_EINT
,
114 [WM8350_IRQ_CHG_BAT_FAIL
] = {
115 .primary
= WM8350_CHG_INT
,
116 .reg
= WM8350_INT_OFFSET_1
,
117 .mask
= WM8350_CHG_BAT_FAIL_EINT
,
119 [WM8350_IRQ_CHG_TO
] = {
120 .primary
= WM8350_CHG_INT
,
121 .reg
= WM8350_INT_OFFSET_1
,
122 .mask
= WM8350_CHG_TO_EINT
,
124 [WM8350_IRQ_CHG_END
] = {
125 .primary
= WM8350_CHG_INT
,
126 .reg
= WM8350_INT_OFFSET_1
,
127 .mask
= WM8350_CHG_END_EINT
,
129 [WM8350_IRQ_CHG_START
] = {
130 .primary
= WM8350_CHG_INT
,
131 .reg
= WM8350_INT_OFFSET_1
,
132 .mask
= WM8350_CHG_START_EINT
,
134 [WM8350_IRQ_CHG_FAST_RDY
] = {
135 .primary
= WM8350_CHG_INT
,
136 .reg
= WM8350_INT_OFFSET_1
,
137 .mask
= WM8350_CHG_FAST_RDY_EINT
,
139 [WM8350_IRQ_CHG_VBATT_LT_3P9
] = {
140 .primary
= WM8350_CHG_INT
,
141 .reg
= WM8350_INT_OFFSET_1
,
142 .mask
= WM8350_CHG_VBATT_LT_3P9_EINT
,
144 [WM8350_IRQ_CHG_VBATT_LT_3P1
] = {
145 .primary
= WM8350_CHG_INT
,
146 .reg
= WM8350_INT_OFFSET_1
,
147 .mask
= WM8350_CHG_VBATT_LT_3P1_EINT
,
149 [WM8350_IRQ_CHG_VBATT_LT_2P85
] = {
150 .primary
= WM8350_CHG_INT
,
151 .reg
= WM8350_INT_OFFSET_1
,
152 .mask
= WM8350_CHG_VBATT_LT_2P85_EINT
,
154 [WM8350_IRQ_RTC_ALM
] = {
155 .primary
= WM8350_RTC_INT
,
156 .reg
= WM8350_INT_OFFSET_1
,
157 .mask
= WM8350_RTC_ALM_EINT
,
159 [WM8350_IRQ_RTC_SEC
] = {
160 .primary
= WM8350_RTC_INT
,
161 .reg
= WM8350_INT_OFFSET_1
,
162 .mask
= WM8350_RTC_SEC_EINT
,
164 [WM8350_IRQ_RTC_PER
] = {
165 .primary
= WM8350_RTC_INT
,
166 .reg
= WM8350_INT_OFFSET_1
,
167 .mask
= WM8350_RTC_PER_EINT
,
170 .primary
= WM8350_CS_INT
,
171 .reg
= WM8350_INT_OFFSET_2
,
172 .mask
= WM8350_CS1_EINT
,
175 .primary
= WM8350_CS_INT
,
176 .reg
= WM8350_INT_OFFSET_2
,
177 .mask
= WM8350_CS2_EINT
,
179 [WM8350_IRQ_SYS_HYST_COMP_FAIL
] = {
180 .primary
= WM8350_SYS_INT
,
181 .reg
= WM8350_INT_OFFSET_2
,
182 .mask
= WM8350_SYS_HYST_COMP_FAIL_EINT
,
184 [WM8350_IRQ_SYS_CHIP_GT115
] = {
185 .primary
= WM8350_SYS_INT
,
186 .reg
= WM8350_INT_OFFSET_2
,
187 .mask
= WM8350_SYS_CHIP_GT115_EINT
,
189 [WM8350_IRQ_SYS_CHIP_GT140
] = {
190 .primary
= WM8350_SYS_INT
,
191 .reg
= WM8350_INT_OFFSET_2
,
192 .mask
= WM8350_SYS_CHIP_GT140_EINT
,
194 [WM8350_IRQ_SYS_WDOG_TO
] = {
195 .primary
= WM8350_SYS_INT
,
196 .reg
= WM8350_INT_OFFSET_2
,
197 .mask
= WM8350_SYS_WDOG_TO_EINT
,
199 [WM8350_IRQ_AUXADC_DATARDY
] = {
200 .primary
= WM8350_AUXADC_INT
,
201 .reg
= WM8350_INT_OFFSET_2
,
202 .mask
= WM8350_AUXADC_DATARDY_EINT
,
204 [WM8350_IRQ_AUXADC_DCOMP4
] = {
205 .primary
= WM8350_AUXADC_INT
,
206 .reg
= WM8350_INT_OFFSET_2
,
207 .mask
= WM8350_AUXADC_DCOMP4_EINT
,
209 [WM8350_IRQ_AUXADC_DCOMP3
] = {
210 .primary
= WM8350_AUXADC_INT
,
211 .reg
= WM8350_INT_OFFSET_2
,
212 .mask
= WM8350_AUXADC_DCOMP3_EINT
,
214 [WM8350_IRQ_AUXADC_DCOMP2
] = {
215 .primary
= WM8350_AUXADC_INT
,
216 .reg
= WM8350_INT_OFFSET_2
,
217 .mask
= WM8350_AUXADC_DCOMP2_EINT
,
219 [WM8350_IRQ_AUXADC_DCOMP1
] = {
220 .primary
= WM8350_AUXADC_INT
,
221 .reg
= WM8350_INT_OFFSET_2
,
222 .mask
= WM8350_AUXADC_DCOMP1_EINT
,
224 [WM8350_IRQ_USB_LIMIT
] = {
225 .primary
= WM8350_USB_INT
,
226 .reg
= WM8350_INT_OFFSET_2
,
227 .mask
= WM8350_USB_LIMIT_EINT
,
230 [WM8350_IRQ_WKUP_OFF_STATE
] = {
231 .primary
= WM8350_WKUP_INT
,
232 .reg
= WM8350_COMPARATOR_INT_OFFSET
,
233 .mask
= WM8350_WKUP_OFF_STATE_EINT
,
235 [WM8350_IRQ_WKUP_HIB_STATE
] = {
236 .primary
= WM8350_WKUP_INT
,
237 .reg
= WM8350_COMPARATOR_INT_OFFSET
,
238 .mask
= WM8350_WKUP_HIB_STATE_EINT
,
240 [WM8350_IRQ_WKUP_CONV_FAULT
] = {
241 .primary
= WM8350_WKUP_INT
,
242 .reg
= WM8350_COMPARATOR_INT_OFFSET
,
243 .mask
= WM8350_WKUP_CONV_FAULT_EINT
,
245 [WM8350_IRQ_WKUP_WDOG_RST
] = {
246 .primary
= WM8350_WKUP_INT
,
247 .reg
= WM8350_COMPARATOR_INT_OFFSET
,
248 .mask
= WM8350_WKUP_WDOG_RST_EINT
,
250 [WM8350_IRQ_WKUP_GP_PWR_ON
] = {
251 .primary
= WM8350_WKUP_INT
,
252 .reg
= WM8350_COMPARATOR_INT_OFFSET
,
253 .mask
= WM8350_WKUP_GP_PWR_ON_EINT
,
255 [WM8350_IRQ_WKUP_ONKEY
] = {
256 .primary
= WM8350_WKUP_INT
,
257 .reg
= WM8350_COMPARATOR_INT_OFFSET
,
258 .mask
= WM8350_WKUP_ONKEY_EINT
,
260 [WM8350_IRQ_WKUP_GP_WAKEUP
] = {
261 .primary
= WM8350_WKUP_INT
,
262 .reg
= WM8350_COMPARATOR_INT_OFFSET
,
263 .mask
= WM8350_WKUP_GP_WAKEUP_EINT
,
265 [WM8350_IRQ_CODEC_JCK_DET_L
] = {
266 .primary
= WM8350_CODEC_INT
,
267 .reg
= WM8350_COMPARATOR_INT_OFFSET
,
268 .mask
= WM8350_CODEC_JCK_DET_L_EINT
,
270 [WM8350_IRQ_CODEC_JCK_DET_R
] = {
271 .primary
= WM8350_CODEC_INT
,
272 .reg
= WM8350_COMPARATOR_INT_OFFSET
,
273 .mask
= WM8350_CODEC_JCK_DET_R_EINT
,
275 [WM8350_IRQ_CODEC_MICSCD
] = {
276 .primary
= WM8350_CODEC_INT
,
277 .reg
= WM8350_COMPARATOR_INT_OFFSET
,
278 .mask
= WM8350_CODEC_MICSCD_EINT
,
280 [WM8350_IRQ_CODEC_MICD
] = {
281 .primary
= WM8350_CODEC_INT
,
282 .reg
= WM8350_COMPARATOR_INT_OFFSET
,
283 .mask
= WM8350_CODEC_MICD_EINT
,
285 [WM8350_IRQ_EXT_USB_FB
] = {
286 .primary
= WM8350_EXT_INT
,
287 .reg
= WM8350_COMPARATOR_INT_OFFSET
,
288 .mask
= WM8350_EXT_USB_FB_EINT
,
290 [WM8350_IRQ_EXT_WALL_FB
] = {
291 .primary
= WM8350_EXT_INT
,
292 .reg
= WM8350_COMPARATOR_INT_OFFSET
,
293 .mask
= WM8350_EXT_WALL_FB_EINT
,
295 [WM8350_IRQ_EXT_BAT_FB
] = {
296 .primary
= WM8350_EXT_INT
,
297 .reg
= WM8350_COMPARATOR_INT_OFFSET
,
298 .mask
= WM8350_EXT_BAT_FB_EINT
,
300 [WM8350_IRQ_GPIO(0)] = {
301 .primary
= WM8350_GP_INT
,
302 .reg
= WM8350_GPIO_INT_OFFSET
,
303 .mask
= WM8350_GP0_EINT
,
305 [WM8350_IRQ_GPIO(1)] = {
306 .primary
= WM8350_GP_INT
,
307 .reg
= WM8350_GPIO_INT_OFFSET
,
308 .mask
= WM8350_GP1_EINT
,
310 [WM8350_IRQ_GPIO(2)] = {
311 .primary
= WM8350_GP_INT
,
312 .reg
= WM8350_GPIO_INT_OFFSET
,
313 .mask
= WM8350_GP2_EINT
,
315 [WM8350_IRQ_GPIO(3)] = {
316 .primary
= WM8350_GP_INT
,
317 .reg
= WM8350_GPIO_INT_OFFSET
,
318 .mask
= WM8350_GP3_EINT
,
320 [WM8350_IRQ_GPIO(4)] = {
321 .primary
= WM8350_GP_INT
,
322 .reg
= WM8350_GPIO_INT_OFFSET
,
323 .mask
= WM8350_GP4_EINT
,
325 [WM8350_IRQ_GPIO(5)] = {
326 .primary
= WM8350_GP_INT
,
327 .reg
= WM8350_GPIO_INT_OFFSET
,
328 .mask
= WM8350_GP5_EINT
,
330 [WM8350_IRQ_GPIO(6)] = {
331 .primary
= WM8350_GP_INT
,
332 .reg
= WM8350_GPIO_INT_OFFSET
,
333 .mask
= WM8350_GP6_EINT
,
335 [WM8350_IRQ_GPIO(7)] = {
336 .primary
= WM8350_GP_INT
,
337 .reg
= WM8350_GPIO_INT_OFFSET
,
338 .mask
= WM8350_GP7_EINT
,
340 [WM8350_IRQ_GPIO(8)] = {
341 .primary
= WM8350_GP_INT
,
342 .reg
= WM8350_GPIO_INT_OFFSET
,
343 .mask
= WM8350_GP8_EINT
,
345 [WM8350_IRQ_GPIO(9)] = {
346 .primary
= WM8350_GP_INT
,
347 .reg
= WM8350_GPIO_INT_OFFSET
,
348 .mask
= WM8350_GP9_EINT
,
350 [WM8350_IRQ_GPIO(10)] = {
351 .primary
= WM8350_GP_INT
,
352 .reg
= WM8350_GPIO_INT_OFFSET
,
353 .mask
= WM8350_GP10_EINT
,
355 [WM8350_IRQ_GPIO(11)] = {
356 .primary
= WM8350_GP_INT
,
357 .reg
= WM8350_GPIO_INT_OFFSET
,
358 .mask
= WM8350_GP11_EINT
,
360 [WM8350_IRQ_GPIO(12)] = {
361 .primary
= WM8350_GP_INT
,
362 .reg
= WM8350_GPIO_INT_OFFSET
,
363 .mask
= WM8350_GP12_EINT
,
367 static inline struct wm8350_irq_data
*irq_to_wm8350_irq(struct wm8350
*wm8350
,
370 return &wm8350_irqs
[irq
- wm8350
->irq_base
];
374 * This is a threaded IRQ handler so can access I2C/SPI. Since all
375 * interrupts are clear on read the IRQ line will be reasserted and
376 * the physical IRQ will be handled again if another interrupt is
377 * asserted while we run - in the normal course of events this is a
378 * rare occurrence so we save I2C/SPI reads. We're also assuming that
379 * it's rare to get lots of interrupts firing simultaneously so try to
382 static irqreturn_t
wm8350_irq(int irq
, void *irq_data
)
384 struct wm8350
*wm8350
= irq_data
;
386 u16 sub_reg
[WM8350_NUM_IRQ_REGS
];
387 int read_done
[WM8350_NUM_IRQ_REGS
];
388 struct wm8350_irq_data
*data
;
391 level_one
= wm8350_reg_read(wm8350
, WM8350_SYSTEM_INTERRUPTS
)
392 & ~wm8350_reg_read(wm8350
, WM8350_SYSTEM_INTERRUPTS_MASK
);
397 memset(&read_done
, 0, sizeof(read_done
));
399 for (i
= 0; i
< ARRAY_SIZE(wm8350_irqs
); i
++) {
400 data
= &wm8350_irqs
[i
];
402 if (!(level_one
& data
->primary
))
405 if (!read_done
[data
->reg
]) {
407 wm8350_reg_read(wm8350
, WM8350_INT_STATUS_1
+
409 sub_reg
[data
->reg
] &= ~wm8350
->irq_masks
[data
->reg
];
410 read_done
[data
->reg
] = 1;
413 if (sub_reg
[data
->reg
] & data
->mask
)
414 handle_nested_irq(wm8350
->irq_base
+ i
);
420 static void wm8350_irq_lock(unsigned int irq
)
422 struct wm8350
*wm8350
= get_irq_chip_data(irq
);
424 mutex_lock(&wm8350
->irq_lock
);
427 static void wm8350_irq_sync_unlock(unsigned int irq
)
429 struct wm8350
*wm8350
= get_irq_chip_data(irq
);
432 for (i
= 0; i
< ARRAY_SIZE(wm8350
->irq_masks
); i
++) {
433 /* If there's been a change in the mask write it back
434 * to the hardware. */
435 if (wm8350
->irq_masks
[i
] !=
436 wm8350
->reg_cache
[WM8350_INT_STATUS_1_MASK
+ i
])
437 WARN_ON(wm8350_reg_write(wm8350
,
438 WM8350_INT_STATUS_1_MASK
+ i
,
439 wm8350
->irq_masks
[i
]));
442 mutex_unlock(&wm8350
->irq_lock
);
445 static void wm8350_irq_enable(unsigned int irq
)
447 struct wm8350
*wm8350
= get_irq_chip_data(irq
);
448 struct wm8350_irq_data
*irq_data
= irq_to_wm8350_irq(wm8350
, irq
);
450 wm8350
->irq_masks
[irq_data
->reg
] &= ~irq_data
->mask
;
453 static void wm8350_irq_disable(unsigned int irq
)
455 struct wm8350
*wm8350
= get_irq_chip_data(irq
);
456 struct wm8350_irq_data
*irq_data
= irq_to_wm8350_irq(wm8350
, irq
);
458 wm8350
->irq_masks
[irq_data
->reg
] |= irq_data
->mask
;
461 static struct irq_chip wm8350_irq_chip
= {
463 .bus_lock
= wm8350_irq_lock
,
464 .bus_sync_unlock
= wm8350_irq_sync_unlock
,
465 .disable
= wm8350_irq_disable
,
466 .enable
= wm8350_irq_enable
,
469 int wm8350_irq_init(struct wm8350
*wm8350
, int irq
,
470 struct wm8350_platform_data
*pdata
)
473 int flags
= IRQF_ONESHOT
;
476 dev_warn(wm8350
->dev
, "No interrupt support, no core IRQ\n");
480 if (!pdata
|| !pdata
->irq_base
) {
481 dev_warn(wm8350
->dev
, "No interrupt support, no IRQ base\n");
485 /* Mask top level interrupts */
486 wm8350_reg_write(wm8350
, WM8350_SYSTEM_INTERRUPTS_MASK
, 0xFFFF);
488 /* Mask all individual interrupts by default and cache the
489 * masks. We read the masks back since there are unwritable
490 * bits in the mask registers. */
491 for (i
= 0; i
< ARRAY_SIZE(wm8350
->irq_masks
); i
++) {
492 wm8350_reg_write(wm8350
, WM8350_INT_STATUS_1_MASK
+ i
,
494 wm8350
->irq_masks
[i
] =
495 wm8350_reg_read(wm8350
,
496 WM8350_INT_STATUS_1_MASK
+ i
);
499 mutex_init(&wm8350
->irq_lock
);
500 wm8350
->chip_irq
= irq
;
501 wm8350
->irq_base
= pdata
->irq_base
;
503 if (pdata
->irq_high
) {
504 flags
|= IRQF_TRIGGER_HIGH
;
506 wm8350_set_bits(wm8350
, WM8350_SYSTEM_CONTROL_1
,
509 flags
|= IRQF_TRIGGER_LOW
;
511 wm8350_clear_bits(wm8350
, WM8350_SYSTEM_CONTROL_1
,
515 /* Register with genirq */
516 for (cur_irq
= wm8350
->irq_base
;
517 cur_irq
< ARRAY_SIZE(wm8350_irqs
) + wm8350
->irq_base
;
519 set_irq_chip_data(cur_irq
, wm8350
);
520 set_irq_chip_and_handler(cur_irq
, &wm8350_irq_chip
,
522 set_irq_nested_thread(cur_irq
, 1);
524 /* ARM needs us to explicitly flag the IRQ as valid
525 * and will set them noprobe when we do so. */
527 set_irq_flags(cur_irq
, IRQF_VALID
);
529 set_irq_noprobe(cur_irq
);
533 ret
= request_threaded_irq(irq
, NULL
, wm8350_irq
, flags
,
536 dev_err(wm8350
->dev
, "Failed to request IRQ: %d\n", ret
);
538 /* Allow interrupts to fire */
539 wm8350_reg_write(wm8350
, WM8350_SYSTEM_INTERRUPTS_MASK
, 0);
544 int wm8350_irq_exit(struct wm8350
*wm8350
)
546 free_irq(wm8350
->chip_irq
, wm8350
);