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/bug.h>
18 #include <linux/device.h>
19 #include <linux/interrupt.h>
20 #include <linux/irq.h>
22 #include <linux/mfd/wm8350/core.h>
23 #include <linux/mfd/wm8350/audio.h>
24 #include <linux/mfd/wm8350/comparator.h>
25 #include <linux/mfd/wm8350/gpio.h>
26 #include <linux/mfd/wm8350/pmic.h>
27 #include <linux/mfd/wm8350/rtc.h>
28 #include <linux/mfd/wm8350/supply.h>
29 #include <linux/mfd/wm8350/wdt.h>
31 #define WM8350_INT_OFFSET_1 0
32 #define WM8350_INT_OFFSET_2 1
33 #define WM8350_POWER_UP_INT_OFFSET 2
34 #define WM8350_UNDER_VOLTAGE_INT_OFFSET 3
35 #define WM8350_OVER_CURRENT_INT_OFFSET 4
36 #define WM8350_GPIO_INT_OFFSET 5
37 #define WM8350_COMPARATOR_INT_OFFSET 6
39 struct wm8350_irq_data
{
46 static struct wm8350_irq_data wm8350_irqs
[] = {
47 [WM8350_IRQ_OC_LS
] = {
48 .primary
= WM8350_OC_INT
,
49 .reg
= WM8350_OVER_CURRENT_INT_OFFSET
,
50 .mask
= WM8350_OC_LS_EINT
,
53 [WM8350_IRQ_UV_DC1
] = {
54 .primary
= WM8350_UV_INT
,
55 .reg
= WM8350_UNDER_VOLTAGE_INT_OFFSET
,
56 .mask
= WM8350_UV_DC1_EINT
,
58 [WM8350_IRQ_UV_DC2
] = {
59 .primary
= WM8350_UV_INT
,
60 .reg
= WM8350_UNDER_VOLTAGE_INT_OFFSET
,
61 .mask
= WM8350_UV_DC2_EINT
,
63 [WM8350_IRQ_UV_DC3
] = {
64 .primary
= WM8350_UV_INT
,
65 .reg
= WM8350_UNDER_VOLTAGE_INT_OFFSET
,
66 .mask
= WM8350_UV_DC3_EINT
,
68 [WM8350_IRQ_UV_DC4
] = {
69 .primary
= WM8350_UV_INT
,
70 .reg
= WM8350_UNDER_VOLTAGE_INT_OFFSET
,
71 .mask
= WM8350_UV_DC4_EINT
,
73 [WM8350_IRQ_UV_DC5
] = {
74 .primary
= WM8350_UV_INT
,
75 .reg
= WM8350_UNDER_VOLTAGE_INT_OFFSET
,
76 .mask
= WM8350_UV_DC5_EINT
,
78 [WM8350_IRQ_UV_DC6
] = {
79 .primary
= WM8350_UV_INT
,
80 .reg
= WM8350_UNDER_VOLTAGE_INT_OFFSET
,
81 .mask
= WM8350_UV_DC6_EINT
,
83 [WM8350_IRQ_UV_LDO1
] = {
84 .primary
= WM8350_UV_INT
,
85 .reg
= WM8350_UNDER_VOLTAGE_INT_OFFSET
,
86 .mask
= WM8350_UV_LDO1_EINT
,
88 [WM8350_IRQ_UV_LDO2
] = {
89 .primary
= WM8350_UV_INT
,
90 .reg
= WM8350_UNDER_VOLTAGE_INT_OFFSET
,
91 .mask
= WM8350_UV_LDO2_EINT
,
93 [WM8350_IRQ_UV_LDO3
] = {
94 .primary
= WM8350_UV_INT
,
95 .reg
= WM8350_UNDER_VOLTAGE_INT_OFFSET
,
96 .mask
= WM8350_UV_LDO3_EINT
,
98 [WM8350_IRQ_UV_LDO4
] = {
99 .primary
= WM8350_UV_INT
,
100 .reg
= WM8350_UNDER_VOLTAGE_INT_OFFSET
,
101 .mask
= WM8350_UV_LDO4_EINT
,
103 [WM8350_IRQ_CHG_BAT_HOT
] = {
104 .primary
= WM8350_CHG_INT
,
105 .reg
= WM8350_INT_OFFSET_1
,
106 .mask
= WM8350_CHG_BAT_HOT_EINT
,
108 [WM8350_IRQ_CHG_BAT_COLD
] = {
109 .primary
= WM8350_CHG_INT
,
110 .reg
= WM8350_INT_OFFSET_1
,
111 .mask
= WM8350_CHG_BAT_COLD_EINT
,
113 [WM8350_IRQ_CHG_BAT_FAIL
] = {
114 .primary
= WM8350_CHG_INT
,
115 .reg
= WM8350_INT_OFFSET_1
,
116 .mask
= WM8350_CHG_BAT_FAIL_EINT
,
118 [WM8350_IRQ_CHG_TO
] = {
119 .primary
= WM8350_CHG_INT
,
120 .reg
= WM8350_INT_OFFSET_1
,
121 .mask
= WM8350_CHG_TO_EINT
,
123 [WM8350_IRQ_CHG_END
] = {
124 .primary
= WM8350_CHG_INT
,
125 .reg
= WM8350_INT_OFFSET_1
,
126 .mask
= WM8350_CHG_END_EINT
,
128 [WM8350_IRQ_CHG_START
] = {
129 .primary
= WM8350_CHG_INT
,
130 .reg
= WM8350_INT_OFFSET_1
,
131 .mask
= WM8350_CHG_START_EINT
,
133 [WM8350_IRQ_CHG_FAST_RDY
] = {
134 .primary
= WM8350_CHG_INT
,
135 .reg
= WM8350_INT_OFFSET_1
,
136 .mask
= WM8350_CHG_FAST_RDY_EINT
,
138 [WM8350_IRQ_CHG_VBATT_LT_3P9
] = {
139 .primary
= WM8350_CHG_INT
,
140 .reg
= WM8350_INT_OFFSET_1
,
141 .mask
= WM8350_CHG_VBATT_LT_3P9_EINT
,
143 [WM8350_IRQ_CHG_VBATT_LT_3P1
] = {
144 .primary
= WM8350_CHG_INT
,
145 .reg
= WM8350_INT_OFFSET_1
,
146 .mask
= WM8350_CHG_VBATT_LT_3P1_EINT
,
148 [WM8350_IRQ_CHG_VBATT_LT_2P85
] = {
149 .primary
= WM8350_CHG_INT
,
150 .reg
= WM8350_INT_OFFSET_1
,
151 .mask
= WM8350_CHG_VBATT_LT_2P85_EINT
,
153 [WM8350_IRQ_RTC_ALM
] = {
154 .primary
= WM8350_RTC_INT
,
155 .reg
= WM8350_INT_OFFSET_1
,
156 .mask
= WM8350_RTC_ALM_EINT
,
158 [WM8350_IRQ_RTC_SEC
] = {
159 .primary
= WM8350_RTC_INT
,
160 .reg
= WM8350_INT_OFFSET_1
,
161 .mask
= WM8350_RTC_SEC_EINT
,
163 [WM8350_IRQ_RTC_PER
] = {
164 .primary
= WM8350_RTC_INT
,
165 .reg
= WM8350_INT_OFFSET_1
,
166 .mask
= WM8350_RTC_PER_EINT
,
169 .primary
= WM8350_CS_INT
,
170 .reg
= WM8350_INT_OFFSET_2
,
171 .mask
= WM8350_CS1_EINT
,
174 .primary
= WM8350_CS_INT
,
175 .reg
= WM8350_INT_OFFSET_2
,
176 .mask
= WM8350_CS2_EINT
,
178 [WM8350_IRQ_SYS_HYST_COMP_FAIL
] = {
179 .primary
= WM8350_SYS_INT
,
180 .reg
= WM8350_INT_OFFSET_2
,
181 .mask
= WM8350_SYS_HYST_COMP_FAIL_EINT
,
183 [WM8350_IRQ_SYS_CHIP_GT115
] = {
184 .primary
= WM8350_SYS_INT
,
185 .reg
= WM8350_INT_OFFSET_2
,
186 .mask
= WM8350_SYS_CHIP_GT115_EINT
,
188 [WM8350_IRQ_SYS_CHIP_GT140
] = {
189 .primary
= WM8350_SYS_INT
,
190 .reg
= WM8350_INT_OFFSET_2
,
191 .mask
= WM8350_SYS_CHIP_GT140_EINT
,
193 [WM8350_IRQ_SYS_WDOG_TO
] = {
194 .primary
= WM8350_SYS_INT
,
195 .reg
= WM8350_INT_OFFSET_2
,
196 .mask
= WM8350_SYS_WDOG_TO_EINT
,
198 [WM8350_IRQ_AUXADC_DATARDY
] = {
199 .primary
= WM8350_AUXADC_INT
,
200 .reg
= WM8350_INT_OFFSET_2
,
201 .mask
= WM8350_AUXADC_DATARDY_EINT
,
203 [WM8350_IRQ_AUXADC_DCOMP4
] = {
204 .primary
= WM8350_AUXADC_INT
,
205 .reg
= WM8350_INT_OFFSET_2
,
206 .mask
= WM8350_AUXADC_DCOMP4_EINT
,
208 [WM8350_IRQ_AUXADC_DCOMP3
] = {
209 .primary
= WM8350_AUXADC_INT
,
210 .reg
= WM8350_INT_OFFSET_2
,
211 .mask
= WM8350_AUXADC_DCOMP3_EINT
,
213 [WM8350_IRQ_AUXADC_DCOMP2
] = {
214 .primary
= WM8350_AUXADC_INT
,
215 .reg
= WM8350_INT_OFFSET_2
,
216 .mask
= WM8350_AUXADC_DCOMP2_EINT
,
218 [WM8350_IRQ_AUXADC_DCOMP1
] = {
219 .primary
= WM8350_AUXADC_INT
,
220 .reg
= WM8350_INT_OFFSET_2
,
221 .mask
= WM8350_AUXADC_DCOMP1_EINT
,
223 [WM8350_IRQ_USB_LIMIT
] = {
224 .primary
= WM8350_USB_INT
,
225 .reg
= WM8350_INT_OFFSET_2
,
226 .mask
= WM8350_USB_LIMIT_EINT
,
229 [WM8350_IRQ_WKUP_OFF_STATE
] = {
230 .primary
= WM8350_WKUP_INT
,
231 .reg
= WM8350_COMPARATOR_INT_OFFSET
,
232 .mask
= WM8350_WKUP_OFF_STATE_EINT
,
234 [WM8350_IRQ_WKUP_HIB_STATE
] = {
235 .primary
= WM8350_WKUP_INT
,
236 .reg
= WM8350_COMPARATOR_INT_OFFSET
,
237 .mask
= WM8350_WKUP_HIB_STATE_EINT
,
239 [WM8350_IRQ_WKUP_CONV_FAULT
] = {
240 .primary
= WM8350_WKUP_INT
,
241 .reg
= WM8350_COMPARATOR_INT_OFFSET
,
242 .mask
= WM8350_WKUP_CONV_FAULT_EINT
,
244 [WM8350_IRQ_WKUP_WDOG_RST
] = {
245 .primary
= WM8350_WKUP_INT
,
246 .reg
= WM8350_COMPARATOR_INT_OFFSET
,
247 .mask
= WM8350_WKUP_WDOG_RST_EINT
,
249 [WM8350_IRQ_WKUP_GP_PWR_ON
] = {
250 .primary
= WM8350_WKUP_INT
,
251 .reg
= WM8350_COMPARATOR_INT_OFFSET
,
252 .mask
= WM8350_WKUP_GP_PWR_ON_EINT
,
254 [WM8350_IRQ_WKUP_ONKEY
] = {
255 .primary
= WM8350_WKUP_INT
,
256 .reg
= WM8350_COMPARATOR_INT_OFFSET
,
257 .mask
= WM8350_WKUP_ONKEY_EINT
,
259 [WM8350_IRQ_WKUP_GP_WAKEUP
] = {
260 .primary
= WM8350_WKUP_INT
,
261 .reg
= WM8350_COMPARATOR_INT_OFFSET
,
262 .mask
= WM8350_WKUP_GP_WAKEUP_EINT
,
264 [WM8350_IRQ_CODEC_JCK_DET_L
] = {
265 .primary
= WM8350_CODEC_INT
,
266 .reg
= WM8350_COMPARATOR_INT_OFFSET
,
267 .mask
= WM8350_CODEC_JCK_DET_L_EINT
,
269 [WM8350_IRQ_CODEC_JCK_DET_R
] = {
270 .primary
= WM8350_CODEC_INT
,
271 .reg
= WM8350_COMPARATOR_INT_OFFSET
,
272 .mask
= WM8350_CODEC_JCK_DET_R_EINT
,
274 [WM8350_IRQ_CODEC_MICSCD
] = {
275 .primary
= WM8350_CODEC_INT
,
276 .reg
= WM8350_COMPARATOR_INT_OFFSET
,
277 .mask
= WM8350_CODEC_MICSCD_EINT
,
279 [WM8350_IRQ_CODEC_MICD
] = {
280 .primary
= WM8350_CODEC_INT
,
281 .reg
= WM8350_COMPARATOR_INT_OFFSET
,
282 .mask
= WM8350_CODEC_MICD_EINT
,
284 [WM8350_IRQ_EXT_USB_FB
] = {
285 .primary
= WM8350_EXT_INT
,
286 .reg
= WM8350_COMPARATOR_INT_OFFSET
,
287 .mask
= WM8350_EXT_USB_FB_EINT
,
289 [WM8350_IRQ_EXT_WALL_FB
] = {
290 .primary
= WM8350_EXT_INT
,
291 .reg
= WM8350_COMPARATOR_INT_OFFSET
,
292 .mask
= WM8350_EXT_WALL_FB_EINT
,
294 [WM8350_IRQ_EXT_BAT_FB
] = {
295 .primary
= WM8350_EXT_INT
,
296 .reg
= WM8350_COMPARATOR_INT_OFFSET
,
297 .mask
= WM8350_EXT_BAT_FB_EINT
,
299 [WM8350_IRQ_GPIO(0)] = {
300 .primary
= WM8350_GP_INT
,
301 .reg
= WM8350_GPIO_INT_OFFSET
,
302 .mask
= WM8350_GP0_EINT
,
304 [WM8350_IRQ_GPIO(1)] = {
305 .primary
= WM8350_GP_INT
,
306 .reg
= WM8350_GPIO_INT_OFFSET
,
307 .mask
= WM8350_GP1_EINT
,
309 [WM8350_IRQ_GPIO(2)] = {
310 .primary
= WM8350_GP_INT
,
311 .reg
= WM8350_GPIO_INT_OFFSET
,
312 .mask
= WM8350_GP2_EINT
,
314 [WM8350_IRQ_GPIO(3)] = {
315 .primary
= WM8350_GP_INT
,
316 .reg
= WM8350_GPIO_INT_OFFSET
,
317 .mask
= WM8350_GP3_EINT
,
319 [WM8350_IRQ_GPIO(4)] = {
320 .primary
= WM8350_GP_INT
,
321 .reg
= WM8350_GPIO_INT_OFFSET
,
322 .mask
= WM8350_GP4_EINT
,
324 [WM8350_IRQ_GPIO(5)] = {
325 .primary
= WM8350_GP_INT
,
326 .reg
= WM8350_GPIO_INT_OFFSET
,
327 .mask
= WM8350_GP5_EINT
,
329 [WM8350_IRQ_GPIO(6)] = {
330 .primary
= WM8350_GP_INT
,
331 .reg
= WM8350_GPIO_INT_OFFSET
,
332 .mask
= WM8350_GP6_EINT
,
334 [WM8350_IRQ_GPIO(7)] = {
335 .primary
= WM8350_GP_INT
,
336 .reg
= WM8350_GPIO_INT_OFFSET
,
337 .mask
= WM8350_GP7_EINT
,
339 [WM8350_IRQ_GPIO(8)] = {
340 .primary
= WM8350_GP_INT
,
341 .reg
= WM8350_GPIO_INT_OFFSET
,
342 .mask
= WM8350_GP8_EINT
,
344 [WM8350_IRQ_GPIO(9)] = {
345 .primary
= WM8350_GP_INT
,
346 .reg
= WM8350_GPIO_INT_OFFSET
,
347 .mask
= WM8350_GP9_EINT
,
349 [WM8350_IRQ_GPIO(10)] = {
350 .primary
= WM8350_GP_INT
,
351 .reg
= WM8350_GPIO_INT_OFFSET
,
352 .mask
= WM8350_GP10_EINT
,
354 [WM8350_IRQ_GPIO(11)] = {
355 .primary
= WM8350_GP_INT
,
356 .reg
= WM8350_GPIO_INT_OFFSET
,
357 .mask
= WM8350_GP11_EINT
,
359 [WM8350_IRQ_GPIO(12)] = {
360 .primary
= WM8350_GP_INT
,
361 .reg
= WM8350_GPIO_INT_OFFSET
,
362 .mask
= WM8350_GP12_EINT
,
366 static inline struct wm8350_irq_data
*irq_to_wm8350_irq(struct wm8350
*wm8350
,
369 return &wm8350_irqs
[irq
- wm8350
->irq_base
];
373 * This is a threaded IRQ handler so can access I2C/SPI. Since all
374 * interrupts are clear on read the IRQ line will be reasserted and
375 * the physical IRQ will be handled again if another interrupt is
376 * asserted while we run - in the normal course of events this is a
377 * rare occurrence so we save I2C/SPI reads. We're also assuming that
378 * it's rare to get lots of interrupts firing simultaneously so try to
381 static irqreturn_t
wm8350_irq(int irq
, void *irq_data
)
383 struct wm8350
*wm8350
= irq_data
;
385 u16 sub_reg
[WM8350_NUM_IRQ_REGS
];
386 int read_done
[WM8350_NUM_IRQ_REGS
];
387 struct wm8350_irq_data
*data
;
390 level_one
= wm8350_reg_read(wm8350
, WM8350_SYSTEM_INTERRUPTS
)
391 & ~wm8350_reg_read(wm8350
, WM8350_SYSTEM_INTERRUPTS_MASK
);
396 memset(&read_done
, 0, sizeof(read_done
));
398 for (i
= 0; i
< ARRAY_SIZE(wm8350_irqs
); i
++) {
399 data
= &wm8350_irqs
[i
];
401 if (!(level_one
& data
->primary
))
404 if (!read_done
[data
->reg
]) {
406 wm8350_reg_read(wm8350
, WM8350_INT_STATUS_1
+
408 sub_reg
[data
->reg
] &= ~wm8350
->irq_masks
[data
->reg
];
409 read_done
[data
->reg
] = 1;
412 if (sub_reg
[data
->reg
] & data
->mask
)
413 handle_nested_irq(wm8350
->irq_base
+ i
);
419 static void wm8350_irq_lock(struct irq_data
*data
)
421 struct wm8350
*wm8350
= irq_data_get_irq_chip_data(data
);
423 mutex_lock(&wm8350
->irq_lock
);
426 static void wm8350_irq_sync_unlock(struct irq_data
*data
)
428 struct wm8350
*wm8350
= irq_data_get_irq_chip_data(data
);
431 for (i
= 0; i
< ARRAY_SIZE(wm8350
->irq_masks
); i
++) {
432 /* If there's been a change in the mask write it back
433 * to the hardware. */
434 WARN_ON(regmap_update_bits(wm8350
->regmap
,
435 WM8350_INT_STATUS_1_MASK
+ i
,
436 0xffff, wm8350
->irq_masks
[i
]));
439 mutex_unlock(&wm8350
->irq_lock
);
442 static void wm8350_irq_enable(struct irq_data
*data
)
444 struct wm8350
*wm8350
= irq_data_get_irq_chip_data(data
);
445 struct wm8350_irq_data
*irq_data
= irq_to_wm8350_irq(wm8350
,
448 wm8350
->irq_masks
[irq_data
->reg
] &= ~irq_data
->mask
;
451 static void wm8350_irq_disable(struct irq_data
*data
)
453 struct wm8350
*wm8350
= irq_data_get_irq_chip_data(data
);
454 struct wm8350_irq_data
*irq_data
= irq_to_wm8350_irq(wm8350
,
457 wm8350
->irq_masks
[irq_data
->reg
] |= irq_data
->mask
;
460 static struct irq_chip wm8350_irq_chip
= {
462 .irq_bus_lock
= wm8350_irq_lock
,
463 .irq_bus_sync_unlock
= wm8350_irq_sync_unlock
,
464 .irq_disable
= wm8350_irq_disable
,
465 .irq_enable
= wm8350_irq_enable
,
468 int wm8350_irq_init(struct wm8350
*wm8350
, int irq
,
469 struct wm8350_platform_data
*pdata
)
472 int flags
= IRQF_ONESHOT
;
476 dev_warn(wm8350
->dev
, "No interrupt support, no core IRQ\n");
480 /* Mask top level interrupts */
481 wm8350_reg_write(wm8350
, WM8350_SYSTEM_INTERRUPTS_MASK
, 0xFFFF);
483 /* Mask all individual interrupts by default and cache the
484 * masks. We read the masks back since there are unwritable
485 * bits in the mask registers. */
486 for (i
= 0; i
< ARRAY_SIZE(wm8350
->irq_masks
); i
++) {
487 wm8350_reg_write(wm8350
, WM8350_INT_STATUS_1_MASK
+ i
,
489 wm8350
->irq_masks
[i
] =
490 wm8350_reg_read(wm8350
,
491 WM8350_INT_STATUS_1_MASK
+ i
);
494 mutex_init(&wm8350
->irq_lock
);
495 wm8350
->chip_irq
= irq
;
497 if (pdata
&& pdata
->irq_base
> 0)
498 irq_base
= pdata
->irq_base
;
501 irq_alloc_descs(irq_base
, 0, ARRAY_SIZE(wm8350_irqs
), 0);
502 if (wm8350
->irq_base
< 0) {
503 dev_warn(wm8350
->dev
, "Allocating irqs failed with %d\n",
508 if (pdata
&& pdata
->irq_high
) {
509 flags
|= IRQF_TRIGGER_HIGH
;
511 wm8350_set_bits(wm8350
, WM8350_SYSTEM_CONTROL_1
,
514 flags
|= IRQF_TRIGGER_LOW
;
516 wm8350_clear_bits(wm8350
, WM8350_SYSTEM_CONTROL_1
,
520 /* Register with genirq */
521 for (cur_irq
= wm8350
->irq_base
;
522 cur_irq
< ARRAY_SIZE(wm8350_irqs
) + wm8350
->irq_base
;
524 irq_set_chip_data(cur_irq
, wm8350
);
525 irq_set_chip_and_handler(cur_irq
, &wm8350_irq_chip
,
527 irq_set_nested_thread(cur_irq
, 1);
529 irq_clear_status_flags(cur_irq
, IRQ_NOREQUEST
| IRQ_NOPROBE
);
532 ret
= request_threaded_irq(irq
, NULL
, wm8350_irq
, flags
,
535 dev_err(wm8350
->dev
, "Failed to request IRQ: %d\n", ret
);
537 /* Allow interrupts to fire */
538 wm8350_reg_write(wm8350
, WM8350_SYSTEM_INTERRUPTS_MASK
, 0);
543 int wm8350_irq_exit(struct wm8350
*wm8350
)
545 free_irq(wm8350
->chip_irq
, wm8350
);