xref: /openbmc/linux/drivers/mfd/wm831x-irq.c (revision 180e4f5f)
17d4d0a3eSMark Brown /*
27d4d0a3eSMark Brown  * wm831x-irq.c  --  Interrupt controller support for Wolfson WM831x PMICs
37d4d0a3eSMark Brown  *
47d4d0a3eSMark Brown  * Copyright 2009 Wolfson Microelectronics PLC.
57d4d0a3eSMark Brown  *
67d4d0a3eSMark Brown  * Author: Mark Brown <broonie@opensource.wolfsonmicro.com>
77d4d0a3eSMark Brown  *
87d4d0a3eSMark Brown  *  This program is free software; you can redistribute  it and/or modify it
97d4d0a3eSMark Brown  *  under  the terms of  the GNU General  Public License as published by the
107d4d0a3eSMark Brown  *  Free Software Foundation;  either version 2 of the  License, or (at your
117d4d0a3eSMark Brown  *  option) any later version.
127d4d0a3eSMark Brown  *
137d4d0a3eSMark Brown  */
147d4d0a3eSMark Brown 
157d4d0a3eSMark Brown #include <linux/kernel.h>
167d4d0a3eSMark Brown #include <linux/module.h>
177d4d0a3eSMark Brown #include <linux/i2c.h>
185fb4d38bSMark Brown #include <linux/irq.h>
197d4d0a3eSMark Brown #include <linux/mfd/core.h>
207d4d0a3eSMark Brown #include <linux/interrupt.h>
217d4d0a3eSMark Brown 
227d4d0a3eSMark Brown #include <linux/mfd/wm831x/core.h>
237d4d0a3eSMark Brown #include <linux/mfd/wm831x/pdata.h>
24896060c7SMark Brown #include <linux/mfd/wm831x/gpio.h>
257d4d0a3eSMark Brown #include <linux/mfd/wm831x/irq.h>
267d4d0a3eSMark Brown 
277d4d0a3eSMark Brown #include <linux/delay.h>
287d4d0a3eSMark Brown 
297d4d0a3eSMark Brown /*
307d4d0a3eSMark Brown  * Since generic IRQs don't currently support interrupt controllers on
317d4d0a3eSMark Brown  * interrupt driven buses we don't use genirq but instead provide an
327d4d0a3eSMark Brown  * interface that looks very much like the standard ones.  This leads
337d4d0a3eSMark Brown  * to some bodges, including storing interrupt handler information in
347d4d0a3eSMark Brown  * the static irq_data table we use to look up the data for individual
357d4d0a3eSMark Brown  * interrupts, but hopefully won't last too long.
367d4d0a3eSMark Brown  */
377d4d0a3eSMark Brown 
387d4d0a3eSMark Brown struct wm831x_irq_data {
397d4d0a3eSMark Brown 	int primary;
407d4d0a3eSMark Brown 	int reg;
417d4d0a3eSMark Brown 	int mask;
427d4d0a3eSMark Brown };
437d4d0a3eSMark Brown 
447d4d0a3eSMark Brown static struct wm831x_irq_data wm831x_irqs[] = {
457d4d0a3eSMark Brown 	[WM831X_IRQ_TEMP_THW] = {
467d4d0a3eSMark Brown 		.primary = WM831X_TEMP_INT,
477d4d0a3eSMark Brown 		.reg = 1,
487d4d0a3eSMark Brown 		.mask = WM831X_TEMP_THW_EINT,
497d4d0a3eSMark Brown 	},
507d4d0a3eSMark Brown 	[WM831X_IRQ_GPIO_1] = {
517d4d0a3eSMark Brown 		.primary = WM831X_GP_INT,
527d4d0a3eSMark Brown 		.reg = 5,
537d4d0a3eSMark Brown 		.mask = WM831X_GP1_EINT,
547d4d0a3eSMark Brown 	},
557d4d0a3eSMark Brown 	[WM831X_IRQ_GPIO_2] = {
567d4d0a3eSMark Brown 		.primary = WM831X_GP_INT,
577d4d0a3eSMark Brown 		.reg = 5,
587d4d0a3eSMark Brown 		.mask = WM831X_GP2_EINT,
597d4d0a3eSMark Brown 	},
607d4d0a3eSMark Brown 	[WM831X_IRQ_GPIO_3] = {
617d4d0a3eSMark Brown 		.primary = WM831X_GP_INT,
627d4d0a3eSMark Brown 		.reg = 5,
637d4d0a3eSMark Brown 		.mask = WM831X_GP3_EINT,
647d4d0a3eSMark Brown 	},
657d4d0a3eSMark Brown 	[WM831X_IRQ_GPIO_4] = {
667d4d0a3eSMark Brown 		.primary = WM831X_GP_INT,
677d4d0a3eSMark Brown 		.reg = 5,
687d4d0a3eSMark Brown 		.mask = WM831X_GP4_EINT,
697d4d0a3eSMark Brown 	},
707d4d0a3eSMark Brown 	[WM831X_IRQ_GPIO_5] = {
717d4d0a3eSMark Brown 		.primary = WM831X_GP_INT,
727d4d0a3eSMark Brown 		.reg = 5,
737d4d0a3eSMark Brown 		.mask = WM831X_GP5_EINT,
747d4d0a3eSMark Brown 	},
757d4d0a3eSMark Brown 	[WM831X_IRQ_GPIO_6] = {
767d4d0a3eSMark Brown 		.primary = WM831X_GP_INT,
777d4d0a3eSMark Brown 		.reg = 5,
787d4d0a3eSMark Brown 		.mask = WM831X_GP6_EINT,
797d4d0a3eSMark Brown 	},
807d4d0a3eSMark Brown 	[WM831X_IRQ_GPIO_7] = {
817d4d0a3eSMark Brown 		.primary = WM831X_GP_INT,
827d4d0a3eSMark Brown 		.reg = 5,
837d4d0a3eSMark Brown 		.mask = WM831X_GP7_EINT,
847d4d0a3eSMark Brown 	},
857d4d0a3eSMark Brown 	[WM831X_IRQ_GPIO_8] = {
867d4d0a3eSMark Brown 		.primary = WM831X_GP_INT,
877d4d0a3eSMark Brown 		.reg = 5,
887d4d0a3eSMark Brown 		.mask = WM831X_GP8_EINT,
897d4d0a3eSMark Brown 	},
907d4d0a3eSMark Brown 	[WM831X_IRQ_GPIO_9] = {
917d4d0a3eSMark Brown 		.primary = WM831X_GP_INT,
927d4d0a3eSMark Brown 		.reg = 5,
937d4d0a3eSMark Brown 		.mask = WM831X_GP9_EINT,
947d4d0a3eSMark Brown 	},
957d4d0a3eSMark Brown 	[WM831X_IRQ_GPIO_10] = {
967d4d0a3eSMark Brown 		.primary = WM831X_GP_INT,
977d4d0a3eSMark Brown 		.reg = 5,
987d4d0a3eSMark Brown 		.mask = WM831X_GP10_EINT,
997d4d0a3eSMark Brown 	},
1007d4d0a3eSMark Brown 	[WM831X_IRQ_GPIO_11] = {
1017d4d0a3eSMark Brown 		.primary = WM831X_GP_INT,
1027d4d0a3eSMark Brown 		.reg = 5,
1037d4d0a3eSMark Brown 		.mask = WM831X_GP11_EINT,
1047d4d0a3eSMark Brown 	},
1057d4d0a3eSMark Brown 	[WM831X_IRQ_GPIO_12] = {
1067d4d0a3eSMark Brown 		.primary = WM831X_GP_INT,
1077d4d0a3eSMark Brown 		.reg = 5,
1087d4d0a3eSMark Brown 		.mask = WM831X_GP12_EINT,
1097d4d0a3eSMark Brown 	},
1107d4d0a3eSMark Brown 	[WM831X_IRQ_GPIO_13] = {
1117d4d0a3eSMark Brown 		.primary = WM831X_GP_INT,
1127d4d0a3eSMark Brown 		.reg = 5,
1137d4d0a3eSMark Brown 		.mask = WM831X_GP13_EINT,
1147d4d0a3eSMark Brown 	},
1157d4d0a3eSMark Brown 	[WM831X_IRQ_GPIO_14] = {
1167d4d0a3eSMark Brown 		.primary = WM831X_GP_INT,
1177d4d0a3eSMark Brown 		.reg = 5,
1187d4d0a3eSMark Brown 		.mask = WM831X_GP14_EINT,
1197d4d0a3eSMark Brown 	},
1207d4d0a3eSMark Brown 	[WM831X_IRQ_GPIO_15] = {
1217d4d0a3eSMark Brown 		.primary = WM831X_GP_INT,
1227d4d0a3eSMark Brown 		.reg = 5,
1237d4d0a3eSMark Brown 		.mask = WM831X_GP15_EINT,
1247d4d0a3eSMark Brown 	},
1257d4d0a3eSMark Brown 	[WM831X_IRQ_GPIO_16] = {
1267d4d0a3eSMark Brown 		.primary = WM831X_GP_INT,
1277d4d0a3eSMark Brown 		.reg = 5,
1287d4d0a3eSMark Brown 		.mask = WM831X_GP16_EINT,
1297d4d0a3eSMark Brown 	},
1307d4d0a3eSMark Brown 	[WM831X_IRQ_ON] = {
1317d4d0a3eSMark Brown 		.primary = WM831X_ON_PIN_INT,
1327d4d0a3eSMark Brown 		.reg = 1,
1337d4d0a3eSMark Brown 		.mask = WM831X_ON_PIN_EINT,
1347d4d0a3eSMark Brown 	},
1357d4d0a3eSMark Brown 	[WM831X_IRQ_PPM_SYSLO] = {
1367d4d0a3eSMark Brown 		.primary = WM831X_PPM_INT,
1377d4d0a3eSMark Brown 		.reg = 1,
1387d4d0a3eSMark Brown 		.mask = WM831X_PPM_SYSLO_EINT,
1397d4d0a3eSMark Brown 	},
1407d4d0a3eSMark Brown 	[WM831X_IRQ_PPM_PWR_SRC] = {
1417d4d0a3eSMark Brown 		.primary = WM831X_PPM_INT,
1427d4d0a3eSMark Brown 		.reg = 1,
1437d4d0a3eSMark Brown 		.mask = WM831X_PPM_PWR_SRC_EINT,
1447d4d0a3eSMark Brown 	},
1457d4d0a3eSMark Brown 	[WM831X_IRQ_PPM_USB_CURR] = {
1467d4d0a3eSMark Brown 		.primary = WM831X_PPM_INT,
1477d4d0a3eSMark Brown 		.reg = 1,
1487d4d0a3eSMark Brown 		.mask = WM831X_PPM_USB_CURR_EINT,
1497d4d0a3eSMark Brown 	},
1507d4d0a3eSMark Brown 	[WM831X_IRQ_WDOG_TO] = {
1517d4d0a3eSMark Brown 		.primary = WM831X_WDOG_INT,
1527d4d0a3eSMark Brown 		.reg = 1,
1537d4d0a3eSMark Brown 		.mask = WM831X_WDOG_TO_EINT,
1547d4d0a3eSMark Brown 	},
1557d4d0a3eSMark Brown 	[WM831X_IRQ_RTC_PER] = {
1567d4d0a3eSMark Brown 		.primary = WM831X_RTC_INT,
1577d4d0a3eSMark Brown 		.reg = 1,
1587d4d0a3eSMark Brown 		.mask = WM831X_RTC_PER_EINT,
1597d4d0a3eSMark Brown 	},
1607d4d0a3eSMark Brown 	[WM831X_IRQ_RTC_ALM] = {
1617d4d0a3eSMark Brown 		.primary = WM831X_RTC_INT,
1627d4d0a3eSMark Brown 		.reg = 1,
1637d4d0a3eSMark Brown 		.mask = WM831X_RTC_ALM_EINT,
1647d4d0a3eSMark Brown 	},
1657d4d0a3eSMark Brown 	[WM831X_IRQ_CHG_BATT_HOT] = {
1667d4d0a3eSMark Brown 		.primary = WM831X_CHG_INT,
1677d4d0a3eSMark Brown 		.reg = 2,
1687d4d0a3eSMark Brown 		.mask = WM831X_CHG_BATT_HOT_EINT,
1697d4d0a3eSMark Brown 	},
1707d4d0a3eSMark Brown 	[WM831X_IRQ_CHG_BATT_COLD] = {
1717d4d0a3eSMark Brown 		.primary = WM831X_CHG_INT,
1727d4d0a3eSMark Brown 		.reg = 2,
1737d4d0a3eSMark Brown 		.mask = WM831X_CHG_BATT_COLD_EINT,
1747d4d0a3eSMark Brown 	},
1757d4d0a3eSMark Brown 	[WM831X_IRQ_CHG_BATT_FAIL] = {
1767d4d0a3eSMark Brown 		.primary = WM831X_CHG_INT,
1777d4d0a3eSMark Brown 		.reg = 2,
1787d4d0a3eSMark Brown 		.mask = WM831X_CHG_BATT_FAIL_EINT,
1797d4d0a3eSMark Brown 	},
1807d4d0a3eSMark Brown 	[WM831X_IRQ_CHG_OV] = {
1817d4d0a3eSMark Brown 		.primary = WM831X_CHG_INT,
1827d4d0a3eSMark Brown 		.reg = 2,
1837d4d0a3eSMark Brown 		.mask = WM831X_CHG_OV_EINT,
1847d4d0a3eSMark Brown 	},
1857d4d0a3eSMark Brown 	[WM831X_IRQ_CHG_END] = {
1867d4d0a3eSMark Brown 		.primary = WM831X_CHG_INT,
1877d4d0a3eSMark Brown 		.reg = 2,
1887d4d0a3eSMark Brown 		.mask = WM831X_CHG_END_EINT,
1897d4d0a3eSMark Brown 	},
1907d4d0a3eSMark Brown 	[WM831X_IRQ_CHG_TO] = {
1917d4d0a3eSMark Brown 		.primary = WM831X_CHG_INT,
1927d4d0a3eSMark Brown 		.reg = 2,
1937d4d0a3eSMark Brown 		.mask = WM831X_CHG_TO_EINT,
1947d4d0a3eSMark Brown 	},
1957d4d0a3eSMark Brown 	[WM831X_IRQ_CHG_MODE] = {
1967d4d0a3eSMark Brown 		.primary = WM831X_CHG_INT,
1977d4d0a3eSMark Brown 		.reg = 2,
1987d4d0a3eSMark Brown 		.mask = WM831X_CHG_MODE_EINT,
1997d4d0a3eSMark Brown 	},
2007d4d0a3eSMark Brown 	[WM831X_IRQ_CHG_START] = {
2017d4d0a3eSMark Brown 		.primary = WM831X_CHG_INT,
2027d4d0a3eSMark Brown 		.reg = 2,
2037d4d0a3eSMark Brown 		.mask = WM831X_CHG_START_EINT,
2047d4d0a3eSMark Brown 	},
2057d4d0a3eSMark Brown 	[WM831X_IRQ_TCHDATA] = {
2067d4d0a3eSMark Brown 		.primary = WM831X_TCHDATA_INT,
2077d4d0a3eSMark Brown 		.reg = 1,
2087d4d0a3eSMark Brown 		.mask = WM831X_TCHDATA_EINT,
2097d4d0a3eSMark Brown 	},
2107d4d0a3eSMark Brown 	[WM831X_IRQ_TCHPD] = {
2117d4d0a3eSMark Brown 		.primary = WM831X_TCHPD_INT,
2127d4d0a3eSMark Brown 		.reg = 1,
2137d4d0a3eSMark Brown 		.mask = WM831X_TCHPD_EINT,
2147d4d0a3eSMark Brown 	},
2157d4d0a3eSMark Brown 	[WM831X_IRQ_AUXADC_DATA] = {
2167d4d0a3eSMark Brown 		.primary = WM831X_AUXADC_INT,
2177d4d0a3eSMark Brown 		.reg = 1,
2187d4d0a3eSMark Brown 		.mask = WM831X_AUXADC_DATA_EINT,
2197d4d0a3eSMark Brown 	},
2207d4d0a3eSMark Brown 	[WM831X_IRQ_AUXADC_DCOMP1] = {
2217d4d0a3eSMark Brown 		.primary = WM831X_AUXADC_INT,
2227d4d0a3eSMark Brown 		.reg = 1,
2237d4d0a3eSMark Brown 		.mask = WM831X_AUXADC_DCOMP1_EINT,
2247d4d0a3eSMark Brown 	},
2257d4d0a3eSMark Brown 	[WM831X_IRQ_AUXADC_DCOMP2] = {
2267d4d0a3eSMark Brown 		.primary = WM831X_AUXADC_INT,
2277d4d0a3eSMark Brown 		.reg = 1,
2287d4d0a3eSMark Brown 		.mask = WM831X_AUXADC_DCOMP2_EINT,
2297d4d0a3eSMark Brown 	},
2307d4d0a3eSMark Brown 	[WM831X_IRQ_AUXADC_DCOMP3] = {
2317d4d0a3eSMark Brown 		.primary = WM831X_AUXADC_INT,
2327d4d0a3eSMark Brown 		.reg = 1,
2337d4d0a3eSMark Brown 		.mask = WM831X_AUXADC_DCOMP3_EINT,
2347d4d0a3eSMark Brown 	},
2357d4d0a3eSMark Brown 	[WM831X_IRQ_AUXADC_DCOMP4] = {
2367d4d0a3eSMark Brown 		.primary = WM831X_AUXADC_INT,
2377d4d0a3eSMark Brown 		.reg = 1,
2387d4d0a3eSMark Brown 		.mask = WM831X_AUXADC_DCOMP4_EINT,
2397d4d0a3eSMark Brown 	},
2407d4d0a3eSMark Brown 	[WM831X_IRQ_CS1] = {
2417d4d0a3eSMark Brown 		.primary = WM831X_CS_INT,
2427d4d0a3eSMark Brown 		.reg = 2,
2437d4d0a3eSMark Brown 		.mask = WM831X_CS1_EINT,
2447d4d0a3eSMark Brown 	},
2457d4d0a3eSMark Brown 	[WM831X_IRQ_CS2] = {
2467d4d0a3eSMark Brown 		.primary = WM831X_CS_INT,
2477d4d0a3eSMark Brown 		.reg = 2,
2487d4d0a3eSMark Brown 		.mask = WM831X_CS2_EINT,
2497d4d0a3eSMark Brown 	},
2507d4d0a3eSMark Brown 	[WM831X_IRQ_HC_DC1] = {
2517d4d0a3eSMark Brown 		.primary = WM831X_HC_INT,
2527d4d0a3eSMark Brown 		.reg = 4,
2537d4d0a3eSMark Brown 		.mask = WM831X_HC_DC1_EINT,
2547d4d0a3eSMark Brown 	},
2557d4d0a3eSMark Brown 	[WM831X_IRQ_HC_DC2] = {
2567d4d0a3eSMark Brown 		.primary = WM831X_HC_INT,
2577d4d0a3eSMark Brown 		.reg = 4,
2587d4d0a3eSMark Brown 		.mask = WM831X_HC_DC2_EINT,
2597d4d0a3eSMark Brown 	},
2607d4d0a3eSMark Brown 	[WM831X_IRQ_UV_LDO1] = {
2617d4d0a3eSMark Brown 		.primary = WM831X_UV_INT,
2627d4d0a3eSMark Brown 		.reg = 3,
2637d4d0a3eSMark Brown 		.mask = WM831X_UV_LDO1_EINT,
2647d4d0a3eSMark Brown 	},
2657d4d0a3eSMark Brown 	[WM831X_IRQ_UV_LDO2] = {
2667d4d0a3eSMark Brown 		.primary = WM831X_UV_INT,
2677d4d0a3eSMark Brown 		.reg = 3,
2687d4d0a3eSMark Brown 		.mask = WM831X_UV_LDO2_EINT,
2697d4d0a3eSMark Brown 	},
2707d4d0a3eSMark Brown 	[WM831X_IRQ_UV_LDO3] = {
2717d4d0a3eSMark Brown 		.primary = WM831X_UV_INT,
2727d4d0a3eSMark Brown 		.reg = 3,
2737d4d0a3eSMark Brown 		.mask = WM831X_UV_LDO3_EINT,
2747d4d0a3eSMark Brown 	},
2757d4d0a3eSMark Brown 	[WM831X_IRQ_UV_LDO4] = {
2767d4d0a3eSMark Brown 		.primary = WM831X_UV_INT,
2777d4d0a3eSMark Brown 		.reg = 3,
2787d4d0a3eSMark Brown 		.mask = WM831X_UV_LDO4_EINT,
2797d4d0a3eSMark Brown 	},
2807d4d0a3eSMark Brown 	[WM831X_IRQ_UV_LDO5] = {
2817d4d0a3eSMark Brown 		.primary = WM831X_UV_INT,
2827d4d0a3eSMark Brown 		.reg = 3,
2837d4d0a3eSMark Brown 		.mask = WM831X_UV_LDO5_EINT,
2847d4d0a3eSMark Brown 	},
2857d4d0a3eSMark Brown 	[WM831X_IRQ_UV_LDO6] = {
2867d4d0a3eSMark Brown 		.primary = WM831X_UV_INT,
2877d4d0a3eSMark Brown 		.reg = 3,
2887d4d0a3eSMark Brown 		.mask = WM831X_UV_LDO6_EINT,
2897d4d0a3eSMark Brown 	},
2907d4d0a3eSMark Brown 	[WM831X_IRQ_UV_LDO7] = {
2917d4d0a3eSMark Brown 		.primary = WM831X_UV_INT,
2927d4d0a3eSMark Brown 		.reg = 3,
2937d4d0a3eSMark Brown 		.mask = WM831X_UV_LDO7_EINT,
2947d4d0a3eSMark Brown 	},
2957d4d0a3eSMark Brown 	[WM831X_IRQ_UV_LDO8] = {
2967d4d0a3eSMark Brown 		.primary = WM831X_UV_INT,
2977d4d0a3eSMark Brown 		.reg = 3,
2987d4d0a3eSMark Brown 		.mask = WM831X_UV_LDO8_EINT,
2997d4d0a3eSMark Brown 	},
3007d4d0a3eSMark Brown 	[WM831X_IRQ_UV_LDO9] = {
3017d4d0a3eSMark Brown 		.primary = WM831X_UV_INT,
3027d4d0a3eSMark Brown 		.reg = 3,
3037d4d0a3eSMark Brown 		.mask = WM831X_UV_LDO9_EINT,
3047d4d0a3eSMark Brown 	},
3057d4d0a3eSMark Brown 	[WM831X_IRQ_UV_LDO10] = {
3067d4d0a3eSMark Brown 		.primary = WM831X_UV_INT,
3077d4d0a3eSMark Brown 		.reg = 3,
3087d4d0a3eSMark Brown 		.mask = WM831X_UV_LDO10_EINT,
3097d4d0a3eSMark Brown 	},
3107d4d0a3eSMark Brown 	[WM831X_IRQ_UV_DC1] = {
3117d4d0a3eSMark Brown 		.primary = WM831X_UV_INT,
3127d4d0a3eSMark Brown 		.reg = 4,
3137d4d0a3eSMark Brown 		.mask = WM831X_UV_DC1_EINT,
3147d4d0a3eSMark Brown 	},
3157d4d0a3eSMark Brown 	[WM831X_IRQ_UV_DC2] = {
3167d4d0a3eSMark Brown 		.primary = WM831X_UV_INT,
3177d4d0a3eSMark Brown 		.reg = 4,
3187d4d0a3eSMark Brown 		.mask = WM831X_UV_DC2_EINT,
3197d4d0a3eSMark Brown 	},
3207d4d0a3eSMark Brown 	[WM831X_IRQ_UV_DC3] = {
3217d4d0a3eSMark Brown 		.primary = WM831X_UV_INT,
3227d4d0a3eSMark Brown 		.reg = 4,
3237d4d0a3eSMark Brown 		.mask = WM831X_UV_DC3_EINT,
3247d4d0a3eSMark Brown 	},
3257d4d0a3eSMark Brown 	[WM831X_IRQ_UV_DC4] = {
3267d4d0a3eSMark Brown 		.primary = WM831X_UV_INT,
3277d4d0a3eSMark Brown 		.reg = 4,
3287d4d0a3eSMark Brown 		.mask = WM831X_UV_DC4_EINT,
3297d4d0a3eSMark Brown 	},
3307d4d0a3eSMark Brown };
3317d4d0a3eSMark Brown 
3327d4d0a3eSMark Brown static inline int irq_data_to_status_reg(struct wm831x_irq_data *irq_data)
3337d4d0a3eSMark Brown {
3347d4d0a3eSMark Brown 	return WM831X_INTERRUPT_STATUS_1 - 1 + irq_data->reg;
3357d4d0a3eSMark Brown }
3367d4d0a3eSMark Brown 
3377d4d0a3eSMark Brown static inline int irq_data_to_mask_reg(struct wm831x_irq_data *irq_data)
3387d4d0a3eSMark Brown {
3397d4d0a3eSMark Brown 	return WM831X_INTERRUPT_STATUS_1_MASK - 1 + irq_data->reg;
3407d4d0a3eSMark Brown }
3417d4d0a3eSMark Brown 
3425fb4d38bSMark Brown static inline struct wm831x_irq_data *irq_to_wm831x_irq(struct wm831x *wm831x,
3435fb4d38bSMark Brown 							int irq)
3447d4d0a3eSMark Brown {
3455fb4d38bSMark Brown 	return &wm831x_irqs[irq - wm831x->irq_base];
3467d4d0a3eSMark Brown }
3477d4d0a3eSMark Brown 
348ba81cd39SMark Brown static void wm831x_irq_lock(struct irq_data *data)
3497d4d0a3eSMark Brown {
35025a947f8SMark Brown 	struct wm831x *wm831x = irq_data_get_irq_chip_data(data);
3517d4d0a3eSMark Brown 
3527d4d0a3eSMark Brown 	mutex_lock(&wm831x->irq_lock);
3537d4d0a3eSMark Brown }
3547d4d0a3eSMark Brown 
355ba81cd39SMark Brown static void wm831x_irq_sync_unlock(struct irq_data *data)
3567d4d0a3eSMark Brown {
35725a947f8SMark Brown 	struct wm831x *wm831x = irq_data_get_irq_chip_data(data);
3585fb4d38bSMark Brown 	int i;
3597d4d0a3eSMark Brown 
3605fb4d38bSMark Brown 	for (i = 0; i < ARRAY_SIZE(wm831x->irq_masks_cur); i++) {
3615fb4d38bSMark Brown 		/* If there's been a change in the mask write it back
3625fb4d38bSMark Brown 		 * to the hardware. */
3635fb4d38bSMark Brown 		if (wm831x->irq_masks_cur[i] != wm831x->irq_masks_cache[i]) {
3645fb4d38bSMark Brown 			wm831x->irq_masks_cache[i] = wm831x->irq_masks_cur[i];
3655fb4d38bSMark Brown 			wm831x_reg_write(wm831x,
3665fb4d38bSMark Brown 					 WM831X_INTERRUPT_STATUS_1_MASK + i,
3675fb4d38bSMark Brown 					 wm831x->irq_masks_cur[i]);
3685fb4d38bSMark Brown 		}
3695fb4d38bSMark Brown 	}
3707d4d0a3eSMark Brown 
3717d4d0a3eSMark Brown 	mutex_unlock(&wm831x->irq_lock);
3727d4d0a3eSMark Brown }
3737d4d0a3eSMark Brown 
374ba81cd39SMark Brown static void wm831x_irq_unmask(struct irq_data *data)
3757d4d0a3eSMark Brown {
37625a947f8SMark Brown 	struct wm831x *wm831x = irq_data_get_irq_chip_data(data);
377ba81cd39SMark Brown 	struct wm831x_irq_data *irq_data = irq_to_wm831x_irq(wm831x,
378ba81cd39SMark Brown 							     data->irq);
3797d4d0a3eSMark Brown 
3805fb4d38bSMark Brown 	wm831x->irq_masks_cur[irq_data->reg - 1] &= ~irq_data->mask;
3817d4d0a3eSMark Brown }
3827d4d0a3eSMark Brown 
383ba81cd39SMark Brown static void wm831x_irq_mask(struct irq_data *data)
3847d4d0a3eSMark Brown {
38525a947f8SMark Brown 	struct wm831x *wm831x = irq_data_get_irq_chip_data(data);
386ba81cd39SMark Brown 	struct wm831x_irq_data *irq_data = irq_to_wm831x_irq(wm831x,
387ba81cd39SMark Brown 							     data->irq);
3885fb4d38bSMark Brown 
3895fb4d38bSMark Brown 	wm831x->irq_masks_cur[irq_data->reg - 1] |= irq_data->mask;
3905fb4d38bSMark Brown }
3915fb4d38bSMark Brown 
392ba81cd39SMark Brown static int wm831x_irq_set_type(struct irq_data *data, unsigned int type)
393896060c7SMark Brown {
39425a947f8SMark Brown 	struct wm831x *wm831x = irq_data_get_irq_chip_data(data);
395ba81cd39SMark Brown 	int val, irq;
396896060c7SMark Brown 
397ba81cd39SMark Brown 	irq = data->irq - wm831x->irq_base;
398896060c7SMark Brown 
399c9d66d35SMark Brown 	if (irq < WM831X_IRQ_GPIO_1 || irq > WM831X_IRQ_GPIO_11) {
400c9d66d35SMark Brown 		/* Ignore internal-only IRQs */
401c9d66d35SMark Brown 		if (irq >= 0 && irq < WM831X_NUM_IRQS)
402c9d66d35SMark Brown 			return 0;
403c9d66d35SMark Brown 		else
404896060c7SMark Brown 			return -EINVAL;
405c9d66d35SMark Brown 	}
406896060c7SMark Brown 
407896060c7SMark Brown 	switch (type) {
408896060c7SMark Brown 	case IRQ_TYPE_EDGE_BOTH:
409896060c7SMark Brown 		val = WM831X_GPN_INT_MODE;
410896060c7SMark Brown 		break;
411896060c7SMark Brown 	case IRQ_TYPE_EDGE_RISING:
412896060c7SMark Brown 		val = WM831X_GPN_POL;
413896060c7SMark Brown 		break;
414896060c7SMark Brown 	case IRQ_TYPE_EDGE_FALLING:
415896060c7SMark Brown 		val = 0;
416896060c7SMark Brown 		break;
417896060c7SMark Brown 	default:
418896060c7SMark Brown 		return -EINVAL;
419896060c7SMark Brown 	}
420896060c7SMark Brown 
421896060c7SMark Brown 	return wm831x_set_bits(wm831x, WM831X_GPIO1_CONTROL + irq,
422896060c7SMark Brown 			       WM831X_GPN_INT_MODE | WM831X_GPN_POL, val);
423896060c7SMark Brown }
424896060c7SMark Brown 
4255fb4d38bSMark Brown static struct irq_chip wm831x_irq_chip = {
4265fb4d38bSMark Brown 	.name			= "wm831x",
427ba81cd39SMark Brown 	.irq_bus_lock		= wm831x_irq_lock,
428ba81cd39SMark Brown 	.irq_bus_sync_unlock	= wm831x_irq_sync_unlock,
429ba81cd39SMark Brown 	.irq_mask		= wm831x_irq_mask,
430ba81cd39SMark Brown 	.irq_unmask		= wm831x_irq_unmask,
431ba81cd39SMark Brown 	.irq_set_type		= wm831x_irq_set_type,
4325fb4d38bSMark Brown };
4335fb4d38bSMark Brown 
4345fb4d38bSMark Brown /* The processing of the primary interrupt occurs in a thread so that
4355fb4d38bSMark Brown  * we can interact with the device over I2C or SPI. */
4365fb4d38bSMark Brown static irqreturn_t wm831x_irq_thread(int irq, void *data)
4375fb4d38bSMark Brown {
4385fb4d38bSMark Brown 	struct wm831x *wm831x = data;
4397d4d0a3eSMark Brown 	unsigned int i;
4407d4d0a3eSMark Brown 	int primary;
4415fb4d38bSMark Brown 	int status_regs[WM831X_NUM_IRQ_REGS] = { 0 };
4425fb4d38bSMark Brown 	int read[WM831X_NUM_IRQ_REGS] = { 0 };
4437d4d0a3eSMark Brown 	int *status;
4447d4d0a3eSMark Brown 
4457d4d0a3eSMark Brown 	primary = wm831x_reg_read(wm831x, WM831X_SYSTEM_INTERRUPTS);
4467d4d0a3eSMark Brown 	if (primary < 0) {
4477d4d0a3eSMark Brown 		dev_err(wm831x->dev, "Failed to read system interrupt: %d\n",
4487d4d0a3eSMark Brown 			primary);
4497d4d0a3eSMark Brown 		goto out;
4507d4d0a3eSMark Brown 	}
4517d4d0a3eSMark Brown 
4527d4d0a3eSMark Brown 	for (i = 0; i < ARRAY_SIZE(wm831x_irqs); i++) {
4537d4d0a3eSMark Brown 		int offset = wm831x_irqs[i].reg - 1;
4547d4d0a3eSMark Brown 
4557d4d0a3eSMark Brown 		if (!(primary & wm831x_irqs[i].primary))
4567d4d0a3eSMark Brown 			continue;
4577d4d0a3eSMark Brown 
4587d4d0a3eSMark Brown 		status = &status_regs[offset];
4597d4d0a3eSMark Brown 
4607d4d0a3eSMark Brown 		/* Hopefully there should only be one register to read
4617d4d0a3eSMark Brown 		 * each time otherwise we ought to do a block read. */
4627d4d0a3eSMark Brown 		if (!read[offset]) {
4637d4d0a3eSMark Brown 			*status = wm831x_reg_read(wm831x,
4647d4d0a3eSMark Brown 				     irq_data_to_status_reg(&wm831x_irqs[i]));
4657d4d0a3eSMark Brown 			if (*status < 0) {
4667d4d0a3eSMark Brown 				dev_err(wm831x->dev,
4677d4d0a3eSMark Brown 					"Failed to read IRQ status: %d\n",
4687d4d0a3eSMark Brown 					*status);
4695fb4d38bSMark Brown 				goto out;
4707d4d0a3eSMark Brown 			}
4717d4d0a3eSMark Brown 
4727d4d0a3eSMark Brown 			read[offset] = 1;
4737d4d0a3eSMark Brown 		}
4747d4d0a3eSMark Brown 
4755fb4d38bSMark Brown 		/* Report it if it isn't masked, or forget the status. */
4765fb4d38bSMark Brown 		if ((*status & ~wm831x->irq_masks_cur[offset])
4775fb4d38bSMark Brown 		    & wm831x_irqs[i].mask)
4785fb4d38bSMark Brown 			handle_nested_irq(wm831x->irq_base + i);
4795fb4d38bSMark Brown 		else
4805fb4d38bSMark Brown 			*status &= ~wm831x_irqs[i].mask;
4817d4d0a3eSMark Brown 	}
4827d4d0a3eSMark Brown 
4837d4d0a3eSMark Brown out:
4845fb4d38bSMark Brown 	for (i = 0; i < ARRAY_SIZE(status_regs); i++) {
4855fb4d38bSMark Brown 		if (status_regs[i])
4865fb4d38bSMark Brown 			wm831x_reg_write(wm831x, WM831X_INTERRUPT_STATUS_1 + i,
4875fb4d38bSMark Brown 					 status_regs[i]);
4887d4d0a3eSMark Brown 	}
4897d4d0a3eSMark Brown 
4907d4d0a3eSMark Brown 	return IRQ_HANDLED;
4917d4d0a3eSMark Brown }
4927d4d0a3eSMark Brown 
4937d4d0a3eSMark Brown int wm831x_irq_init(struct wm831x *wm831x, int irq)
4947d4d0a3eSMark Brown {
4955fb4d38bSMark Brown 	struct wm831x_pdata *pdata = wm831x->dev->platform_data;
4965fb4d38bSMark Brown 	int i, cur_irq, ret;
4977d4d0a3eSMark Brown 
49814f572faSMark Brown 	mutex_init(&wm831x->irq_lock);
49914f572faSMark Brown 
5000d7e0e39SMark Brown 	/* Mask the individual interrupt sources */
5010d7e0e39SMark Brown 	for (i = 0; i < ARRAY_SIZE(wm831x->irq_masks_cur); i++) {
5020d7e0e39SMark Brown 		wm831x->irq_masks_cur[i] = 0xffff;
5030d7e0e39SMark Brown 		wm831x->irq_masks_cache[i] = 0xffff;
5040d7e0e39SMark Brown 		wm831x_reg_write(wm831x, WM831X_INTERRUPT_STATUS_1_MASK + i,
5050d7e0e39SMark Brown 				 0xffff);
5060d7e0e39SMark Brown 	}
5070d7e0e39SMark Brown 
5087d4d0a3eSMark Brown 	if (!irq) {
5097d4d0a3eSMark Brown 		dev_warn(wm831x->dev,
5107d4d0a3eSMark Brown 			 "No interrupt specified - functionality limited\n");
5117d4d0a3eSMark Brown 		return 0;
5127d4d0a3eSMark Brown 	}
5137d4d0a3eSMark Brown 
5145fb4d38bSMark Brown 	if (!pdata || !pdata->irq_base) {
5155fb4d38bSMark Brown 		dev_err(wm831x->dev,
5165fb4d38bSMark Brown 			"No interrupt base specified, no interrupts\n");
5175fb4d38bSMark Brown 		return 0;
5187d4d0a3eSMark Brown 	}
5197d4d0a3eSMark Brown 
520180e4f5fSMark Brown 	/* Try to flag /IRQ as a wake source; there are a number of
521180e4f5fSMark Brown 	 * unconditional wake sources in the PMIC so this isn't
522180e4f5fSMark Brown 	 * conditional but we don't actually care *too* much if it
523180e4f5fSMark Brown 	 * fails.
524180e4f5fSMark Brown 	 */
525180e4f5fSMark Brown 	ret = enable_irq_wake(irq);
526180e4f5fSMark Brown 	if (ret != 0) {
527180e4f5fSMark Brown 		dev_warn(wm831x->dev, "Can't enable IRQ as wake source: %d\n",
528180e4f5fSMark Brown 			 ret);
529180e4f5fSMark Brown 	}
530180e4f5fSMark Brown 
5317d4d0a3eSMark Brown 	wm831x->irq = irq;
5325fb4d38bSMark Brown 	wm831x->irq_base = pdata->irq_base;
5337d4d0a3eSMark Brown 
5345fb4d38bSMark Brown 	/* Register them with genirq */
5355fb4d38bSMark Brown 	for (cur_irq = wm831x->irq_base;
5365fb4d38bSMark Brown 	     cur_irq < ARRAY_SIZE(wm831x_irqs) + wm831x->irq_base;
5375fb4d38bSMark Brown 	     cur_irq++) {
5385fb4d38bSMark Brown 		set_irq_chip_data(cur_irq, wm831x);
5395fb4d38bSMark Brown 		set_irq_chip_and_handler(cur_irq, &wm831x_irq_chip,
5405fb4d38bSMark Brown 					 handle_edge_irq);
5415fb4d38bSMark Brown 		set_irq_nested_thread(cur_irq, 1);
5427d4d0a3eSMark Brown 
5435fb4d38bSMark Brown 		/* ARM needs us to explicitly flag the IRQ as valid
5445fb4d38bSMark Brown 		 * and will set them noprobe when we do so. */
5455fb4d38bSMark Brown #ifdef CONFIG_ARM
5465fb4d38bSMark Brown 		set_irq_flags(cur_irq, IRQF_VALID);
5475fb4d38bSMark Brown #else
5485fb4d38bSMark Brown 		set_irq_noprobe(cur_irq);
5495fb4d38bSMark Brown #endif
5505fb4d38bSMark Brown 	}
5515fb4d38bSMark Brown 
5525fb4d38bSMark Brown 	ret = request_threaded_irq(irq, NULL, wm831x_irq_thread,
5535fb4d38bSMark Brown 				   IRQF_TRIGGER_LOW | IRQF_ONESHOT,
5547d4d0a3eSMark Brown 				   "wm831x", wm831x);
5557d4d0a3eSMark Brown 	if (ret != 0) {
5567d4d0a3eSMark Brown 		dev_err(wm831x->dev, "Failed to request IRQ %d: %d\n",
5577d4d0a3eSMark Brown 			irq, ret);
5587d4d0a3eSMark Brown 		return ret;
5597d4d0a3eSMark Brown 	}
5607d4d0a3eSMark Brown 
5615fb4d38bSMark Brown 	/* Enable top level interrupts, we mask at secondary level */
5625fb4d38bSMark Brown 	wm831x_reg_write(wm831x, WM831X_SYSTEM_INTERRUPTS_MASK, 0);
5635fb4d38bSMark Brown 
5647d4d0a3eSMark Brown 	return 0;
5657d4d0a3eSMark Brown }
5667d4d0a3eSMark Brown 
5677d4d0a3eSMark Brown void wm831x_irq_exit(struct wm831x *wm831x)
5687d4d0a3eSMark Brown {
5697d4d0a3eSMark Brown 	if (wm831x->irq)
5707d4d0a3eSMark Brown 		free_irq(wm831x->irq, wm831x);
5717d4d0a3eSMark Brown }
572