1e0a3389aSMark Brown /* 2e0a3389aSMark Brown * wm8350-irq.c -- IRQ support for Wolfson WM8350 3e0a3389aSMark Brown * 4e0a3389aSMark Brown * Copyright 2007, 2008, 2009 Wolfson Microelectronics PLC. 5e0a3389aSMark Brown * 6e0a3389aSMark Brown * Author: Liam Girdwood, Mark Brown 7e0a3389aSMark Brown * 8e0a3389aSMark Brown * This program is free software; you can redistribute it and/or modify it 9e0a3389aSMark Brown * under the terms of the GNU General Public License as published by the 10e0a3389aSMark Brown * Free Software Foundation; either version 2 of the License, or (at your 11e0a3389aSMark Brown * option) any later version. 12e0a3389aSMark Brown * 13e0a3389aSMark Brown */ 14e0a3389aSMark Brown 15e0a3389aSMark Brown #include <linux/kernel.h> 16e0a3389aSMark Brown #include <linux/module.h> 17e0a3389aSMark Brown #include <linux/init.h> 18e0a3389aSMark Brown #include <linux/bug.h> 19e0a3389aSMark Brown #include <linux/device.h> 20e0a3389aSMark Brown #include <linux/interrupt.h> 21760e4518SMark Brown #include <linux/irq.h> 22e0a3389aSMark Brown 23e0a3389aSMark Brown #include <linux/mfd/wm8350/core.h> 24e0a3389aSMark Brown #include <linux/mfd/wm8350/audio.h> 25e0a3389aSMark Brown #include <linux/mfd/wm8350/comparator.h> 26e0a3389aSMark Brown #include <linux/mfd/wm8350/gpio.h> 27e0a3389aSMark Brown #include <linux/mfd/wm8350/pmic.h> 28e0a3389aSMark Brown #include <linux/mfd/wm8350/rtc.h> 29e0a3389aSMark Brown #include <linux/mfd/wm8350/supply.h> 30e0a3389aSMark Brown #include <linux/mfd/wm8350/wdt.h> 31e0a3389aSMark Brown 320c7229f9SMark Brown #define WM8350_INT_OFFSET_1 0 330c7229f9SMark Brown #define WM8350_INT_OFFSET_2 1 340c7229f9SMark Brown #define WM8350_POWER_UP_INT_OFFSET 2 350c7229f9SMark Brown #define WM8350_UNDER_VOLTAGE_INT_OFFSET 3 360c7229f9SMark Brown #define WM8350_OVER_CURRENT_INT_OFFSET 4 370c7229f9SMark Brown #define WM8350_GPIO_INT_OFFSET 5 380c7229f9SMark Brown #define WM8350_COMPARATOR_INT_OFFSET 6 390c7229f9SMark Brown 400c7229f9SMark Brown struct wm8350_irq_data { 410c7229f9SMark Brown int primary; 420c7229f9SMark Brown int reg; 430c7229f9SMark Brown int mask; 440c7229f9SMark Brown int primary_only; 450c7229f9SMark Brown }; 460c7229f9SMark Brown 470c7229f9SMark Brown static struct wm8350_irq_data wm8350_irqs[] = { 480c7229f9SMark Brown [WM8350_IRQ_OC_LS] = { 490c7229f9SMark Brown .primary = WM8350_OC_INT, 500c7229f9SMark Brown .reg = WM8350_OVER_CURRENT_INT_OFFSET, 510c7229f9SMark Brown .mask = WM8350_OC_LS_EINT, 520c7229f9SMark Brown .primary_only = 1, 530c7229f9SMark Brown }, 540c7229f9SMark Brown [WM8350_IRQ_UV_DC1] = { 550c7229f9SMark Brown .primary = WM8350_UV_INT, 560c7229f9SMark Brown .reg = WM8350_UNDER_VOLTAGE_INT_OFFSET, 570c7229f9SMark Brown .mask = WM8350_UV_DC1_EINT, 580c7229f9SMark Brown }, 590c7229f9SMark Brown [WM8350_IRQ_UV_DC2] = { 600c7229f9SMark Brown .primary = WM8350_UV_INT, 610c7229f9SMark Brown .reg = WM8350_UNDER_VOLTAGE_INT_OFFSET, 620c7229f9SMark Brown .mask = WM8350_UV_DC2_EINT, 630c7229f9SMark Brown }, 640c7229f9SMark Brown [WM8350_IRQ_UV_DC3] = { 650c7229f9SMark Brown .primary = WM8350_UV_INT, 660c7229f9SMark Brown .reg = WM8350_UNDER_VOLTAGE_INT_OFFSET, 670c7229f9SMark Brown .mask = WM8350_UV_DC3_EINT, 680c7229f9SMark Brown }, 690c7229f9SMark Brown [WM8350_IRQ_UV_DC4] = { 700c7229f9SMark Brown .primary = WM8350_UV_INT, 710c7229f9SMark Brown .reg = WM8350_UNDER_VOLTAGE_INT_OFFSET, 720c7229f9SMark Brown .mask = WM8350_UV_DC4_EINT, 730c7229f9SMark Brown }, 740c7229f9SMark Brown [WM8350_IRQ_UV_DC5] = { 750c7229f9SMark Brown .primary = WM8350_UV_INT, 760c7229f9SMark Brown .reg = WM8350_UNDER_VOLTAGE_INT_OFFSET, 770c7229f9SMark Brown .mask = WM8350_UV_DC5_EINT, 780c7229f9SMark Brown }, 790c7229f9SMark Brown [WM8350_IRQ_UV_DC6] = { 800c7229f9SMark Brown .primary = WM8350_UV_INT, 810c7229f9SMark Brown .reg = WM8350_UNDER_VOLTAGE_INT_OFFSET, 820c7229f9SMark Brown .mask = WM8350_UV_DC6_EINT, 830c7229f9SMark Brown }, 840c7229f9SMark Brown [WM8350_IRQ_UV_LDO1] = { 850c7229f9SMark Brown .primary = WM8350_UV_INT, 860c7229f9SMark Brown .reg = WM8350_UNDER_VOLTAGE_INT_OFFSET, 870c7229f9SMark Brown .mask = WM8350_UV_LDO1_EINT, 880c7229f9SMark Brown }, 890c7229f9SMark Brown [WM8350_IRQ_UV_LDO2] = { 900c7229f9SMark Brown .primary = WM8350_UV_INT, 910c7229f9SMark Brown .reg = WM8350_UNDER_VOLTAGE_INT_OFFSET, 920c7229f9SMark Brown .mask = WM8350_UV_LDO2_EINT, 930c7229f9SMark Brown }, 940c7229f9SMark Brown [WM8350_IRQ_UV_LDO3] = { 950c7229f9SMark Brown .primary = WM8350_UV_INT, 960c7229f9SMark Brown .reg = WM8350_UNDER_VOLTAGE_INT_OFFSET, 970c7229f9SMark Brown .mask = WM8350_UV_LDO3_EINT, 980c7229f9SMark Brown }, 990c7229f9SMark Brown [WM8350_IRQ_UV_LDO4] = { 1000c7229f9SMark Brown .primary = WM8350_UV_INT, 1010c7229f9SMark Brown .reg = WM8350_UNDER_VOLTAGE_INT_OFFSET, 1020c7229f9SMark Brown .mask = WM8350_UV_LDO4_EINT, 1030c7229f9SMark Brown }, 1040c7229f9SMark Brown [WM8350_IRQ_CHG_BAT_HOT] = { 1050c7229f9SMark Brown .primary = WM8350_CHG_INT, 1060c7229f9SMark Brown .reg = WM8350_INT_OFFSET_1, 1070c7229f9SMark Brown .mask = WM8350_CHG_BAT_HOT_EINT, 1080c7229f9SMark Brown }, 1090c7229f9SMark Brown [WM8350_IRQ_CHG_BAT_COLD] = { 1100c7229f9SMark Brown .primary = WM8350_CHG_INT, 1110c7229f9SMark Brown .reg = WM8350_INT_OFFSET_1, 1120c7229f9SMark Brown .mask = WM8350_CHG_BAT_COLD_EINT, 1130c7229f9SMark Brown }, 1140c7229f9SMark Brown [WM8350_IRQ_CHG_BAT_FAIL] = { 1150c7229f9SMark Brown .primary = WM8350_CHG_INT, 1160c7229f9SMark Brown .reg = WM8350_INT_OFFSET_1, 1170c7229f9SMark Brown .mask = WM8350_CHG_BAT_FAIL_EINT, 1180c7229f9SMark Brown }, 1190c7229f9SMark Brown [WM8350_IRQ_CHG_TO] = { 1200c7229f9SMark Brown .primary = WM8350_CHG_INT, 1210c7229f9SMark Brown .reg = WM8350_INT_OFFSET_1, 1220c7229f9SMark Brown .mask = WM8350_CHG_TO_EINT, 1230c7229f9SMark Brown }, 1240c7229f9SMark Brown [WM8350_IRQ_CHG_END] = { 1250c7229f9SMark Brown .primary = WM8350_CHG_INT, 1260c7229f9SMark Brown .reg = WM8350_INT_OFFSET_1, 1270c7229f9SMark Brown .mask = WM8350_CHG_END_EINT, 1280c7229f9SMark Brown }, 1290c7229f9SMark Brown [WM8350_IRQ_CHG_START] = { 1300c7229f9SMark Brown .primary = WM8350_CHG_INT, 1310c7229f9SMark Brown .reg = WM8350_INT_OFFSET_1, 1320c7229f9SMark Brown .mask = WM8350_CHG_START_EINT, 1330c7229f9SMark Brown }, 1340c7229f9SMark Brown [WM8350_IRQ_CHG_FAST_RDY] = { 1350c7229f9SMark Brown .primary = WM8350_CHG_INT, 1360c7229f9SMark Brown .reg = WM8350_INT_OFFSET_1, 1370c7229f9SMark Brown .mask = WM8350_CHG_FAST_RDY_EINT, 1380c7229f9SMark Brown }, 1390c7229f9SMark Brown [WM8350_IRQ_CHG_VBATT_LT_3P9] = { 1400c7229f9SMark Brown .primary = WM8350_CHG_INT, 1410c7229f9SMark Brown .reg = WM8350_INT_OFFSET_1, 1420c7229f9SMark Brown .mask = WM8350_CHG_VBATT_LT_3P9_EINT, 1430c7229f9SMark Brown }, 1440c7229f9SMark Brown [WM8350_IRQ_CHG_VBATT_LT_3P1] = { 1450c7229f9SMark Brown .primary = WM8350_CHG_INT, 1460c7229f9SMark Brown .reg = WM8350_INT_OFFSET_1, 1470c7229f9SMark Brown .mask = WM8350_CHG_VBATT_LT_3P1_EINT, 1480c7229f9SMark Brown }, 1490c7229f9SMark Brown [WM8350_IRQ_CHG_VBATT_LT_2P85] = { 1500c7229f9SMark Brown .primary = WM8350_CHG_INT, 1510c7229f9SMark Brown .reg = WM8350_INT_OFFSET_1, 1520c7229f9SMark Brown .mask = WM8350_CHG_VBATT_LT_2P85_EINT, 1530c7229f9SMark Brown }, 1540c7229f9SMark Brown [WM8350_IRQ_RTC_ALM] = { 1550c7229f9SMark Brown .primary = WM8350_RTC_INT, 1560c7229f9SMark Brown .reg = WM8350_INT_OFFSET_1, 1570c7229f9SMark Brown .mask = WM8350_RTC_ALM_EINT, 1580c7229f9SMark Brown }, 1590c7229f9SMark Brown [WM8350_IRQ_RTC_SEC] = { 1600c7229f9SMark Brown .primary = WM8350_RTC_INT, 1610c7229f9SMark Brown .reg = WM8350_INT_OFFSET_1, 1620c7229f9SMark Brown .mask = WM8350_RTC_SEC_EINT, 1630c7229f9SMark Brown }, 1640c7229f9SMark Brown [WM8350_IRQ_RTC_PER] = { 1650c7229f9SMark Brown .primary = WM8350_RTC_INT, 1660c7229f9SMark Brown .reg = WM8350_INT_OFFSET_1, 1670c7229f9SMark Brown .mask = WM8350_RTC_PER_EINT, 1680c7229f9SMark Brown }, 1690c7229f9SMark Brown [WM8350_IRQ_CS1] = { 1700c7229f9SMark Brown .primary = WM8350_CS_INT, 1710c7229f9SMark Brown .reg = WM8350_INT_OFFSET_2, 1720c7229f9SMark Brown .mask = WM8350_CS1_EINT, 1730c7229f9SMark Brown }, 1740c7229f9SMark Brown [WM8350_IRQ_CS2] = { 1750c7229f9SMark Brown .primary = WM8350_CS_INT, 1760c7229f9SMark Brown .reg = WM8350_INT_OFFSET_2, 1770c7229f9SMark Brown .mask = WM8350_CS2_EINT, 1780c7229f9SMark Brown }, 1790c7229f9SMark Brown [WM8350_IRQ_SYS_HYST_COMP_FAIL] = { 1800c7229f9SMark Brown .primary = WM8350_SYS_INT, 1810c7229f9SMark Brown .reg = WM8350_INT_OFFSET_2, 1820c7229f9SMark Brown .mask = WM8350_SYS_HYST_COMP_FAIL_EINT, 1830c7229f9SMark Brown }, 1840c7229f9SMark Brown [WM8350_IRQ_SYS_CHIP_GT115] = { 1850c7229f9SMark Brown .primary = WM8350_SYS_INT, 1860c7229f9SMark Brown .reg = WM8350_INT_OFFSET_2, 1870c7229f9SMark Brown .mask = WM8350_SYS_CHIP_GT115_EINT, 1880c7229f9SMark Brown }, 1890c7229f9SMark Brown [WM8350_IRQ_SYS_CHIP_GT140] = { 1900c7229f9SMark Brown .primary = WM8350_SYS_INT, 1910c7229f9SMark Brown .reg = WM8350_INT_OFFSET_2, 1920c7229f9SMark Brown .mask = WM8350_SYS_CHIP_GT140_EINT, 1930c7229f9SMark Brown }, 1940c7229f9SMark Brown [WM8350_IRQ_SYS_WDOG_TO] = { 1950c7229f9SMark Brown .primary = WM8350_SYS_INT, 1960c7229f9SMark Brown .reg = WM8350_INT_OFFSET_2, 1970c7229f9SMark Brown .mask = WM8350_SYS_WDOG_TO_EINT, 1980c7229f9SMark Brown }, 1990c7229f9SMark Brown [WM8350_IRQ_AUXADC_DATARDY] = { 2000c7229f9SMark Brown .primary = WM8350_AUXADC_INT, 2010c7229f9SMark Brown .reg = WM8350_INT_OFFSET_2, 2020c7229f9SMark Brown .mask = WM8350_AUXADC_DATARDY_EINT, 2030c7229f9SMark Brown }, 2040c7229f9SMark Brown [WM8350_IRQ_AUXADC_DCOMP4] = { 2050c7229f9SMark Brown .primary = WM8350_AUXADC_INT, 2060c7229f9SMark Brown .reg = WM8350_INT_OFFSET_2, 2070c7229f9SMark Brown .mask = WM8350_AUXADC_DCOMP4_EINT, 2080c7229f9SMark Brown }, 2090c7229f9SMark Brown [WM8350_IRQ_AUXADC_DCOMP3] = { 2100c7229f9SMark Brown .primary = WM8350_AUXADC_INT, 2110c7229f9SMark Brown .reg = WM8350_INT_OFFSET_2, 2120c7229f9SMark Brown .mask = WM8350_AUXADC_DCOMP3_EINT, 2130c7229f9SMark Brown }, 2140c7229f9SMark Brown [WM8350_IRQ_AUXADC_DCOMP2] = { 2150c7229f9SMark Brown .primary = WM8350_AUXADC_INT, 2160c7229f9SMark Brown .reg = WM8350_INT_OFFSET_2, 2170c7229f9SMark Brown .mask = WM8350_AUXADC_DCOMP2_EINT, 2180c7229f9SMark Brown }, 2190c7229f9SMark Brown [WM8350_IRQ_AUXADC_DCOMP1] = { 2200c7229f9SMark Brown .primary = WM8350_AUXADC_INT, 2210c7229f9SMark Brown .reg = WM8350_INT_OFFSET_2, 2220c7229f9SMark Brown .mask = WM8350_AUXADC_DCOMP1_EINT, 2230c7229f9SMark Brown }, 2240c7229f9SMark Brown [WM8350_IRQ_USB_LIMIT] = { 2250c7229f9SMark Brown .primary = WM8350_USB_INT, 2260c7229f9SMark Brown .reg = WM8350_INT_OFFSET_2, 2270c7229f9SMark Brown .mask = WM8350_USB_LIMIT_EINT, 2280c7229f9SMark Brown .primary_only = 1, 2290c7229f9SMark Brown }, 2300c7229f9SMark Brown [WM8350_IRQ_WKUP_OFF_STATE] = { 2310c7229f9SMark Brown .primary = WM8350_WKUP_INT, 2320c7229f9SMark Brown .reg = WM8350_COMPARATOR_INT_OFFSET, 2330c7229f9SMark Brown .mask = WM8350_WKUP_OFF_STATE_EINT, 2340c7229f9SMark Brown }, 2350c7229f9SMark Brown [WM8350_IRQ_WKUP_HIB_STATE] = { 2360c7229f9SMark Brown .primary = WM8350_WKUP_INT, 2370c7229f9SMark Brown .reg = WM8350_COMPARATOR_INT_OFFSET, 2380c7229f9SMark Brown .mask = WM8350_WKUP_HIB_STATE_EINT, 2390c7229f9SMark Brown }, 2400c7229f9SMark Brown [WM8350_IRQ_WKUP_CONV_FAULT] = { 2410c7229f9SMark Brown .primary = WM8350_WKUP_INT, 2420c7229f9SMark Brown .reg = WM8350_COMPARATOR_INT_OFFSET, 2430c7229f9SMark Brown .mask = WM8350_WKUP_CONV_FAULT_EINT, 2440c7229f9SMark Brown }, 2450c7229f9SMark Brown [WM8350_IRQ_WKUP_WDOG_RST] = { 2460c7229f9SMark Brown .primary = WM8350_WKUP_INT, 2470c7229f9SMark Brown .reg = WM8350_COMPARATOR_INT_OFFSET, 2480c7229f9SMark Brown .mask = WM8350_WKUP_WDOG_RST_EINT, 2490c7229f9SMark Brown }, 2500c7229f9SMark Brown [WM8350_IRQ_WKUP_GP_PWR_ON] = { 2510c7229f9SMark Brown .primary = WM8350_WKUP_INT, 2520c7229f9SMark Brown .reg = WM8350_COMPARATOR_INT_OFFSET, 2530c7229f9SMark Brown .mask = WM8350_WKUP_GP_PWR_ON_EINT, 2540c7229f9SMark Brown }, 2550c7229f9SMark Brown [WM8350_IRQ_WKUP_ONKEY] = { 2560c7229f9SMark Brown .primary = WM8350_WKUP_INT, 2570c7229f9SMark Brown .reg = WM8350_COMPARATOR_INT_OFFSET, 2580c7229f9SMark Brown .mask = WM8350_WKUP_ONKEY_EINT, 2590c7229f9SMark Brown }, 2600c7229f9SMark Brown [WM8350_IRQ_WKUP_GP_WAKEUP] = { 2610c7229f9SMark Brown .primary = WM8350_WKUP_INT, 2620c7229f9SMark Brown .reg = WM8350_COMPARATOR_INT_OFFSET, 2630c7229f9SMark Brown .mask = WM8350_WKUP_GP_WAKEUP_EINT, 2640c7229f9SMark Brown }, 2650c7229f9SMark Brown [WM8350_IRQ_CODEC_JCK_DET_L] = { 2660c7229f9SMark Brown .primary = WM8350_CODEC_INT, 2670c7229f9SMark Brown .reg = WM8350_COMPARATOR_INT_OFFSET, 2680c7229f9SMark Brown .mask = WM8350_CODEC_JCK_DET_L_EINT, 2690c7229f9SMark Brown }, 2700c7229f9SMark Brown [WM8350_IRQ_CODEC_JCK_DET_R] = { 2710c7229f9SMark Brown .primary = WM8350_CODEC_INT, 2720c7229f9SMark Brown .reg = WM8350_COMPARATOR_INT_OFFSET, 2730c7229f9SMark Brown .mask = WM8350_CODEC_JCK_DET_R_EINT, 2740c7229f9SMark Brown }, 2750c7229f9SMark Brown [WM8350_IRQ_CODEC_MICSCD] = { 2760c7229f9SMark Brown .primary = WM8350_CODEC_INT, 2770c7229f9SMark Brown .reg = WM8350_COMPARATOR_INT_OFFSET, 2780c7229f9SMark Brown .mask = WM8350_CODEC_MICSCD_EINT, 2790c7229f9SMark Brown }, 2800c7229f9SMark Brown [WM8350_IRQ_CODEC_MICD] = { 2810c7229f9SMark Brown .primary = WM8350_CODEC_INT, 2820c7229f9SMark Brown .reg = WM8350_COMPARATOR_INT_OFFSET, 2830c7229f9SMark Brown .mask = WM8350_CODEC_MICD_EINT, 2840c7229f9SMark Brown }, 2850c7229f9SMark Brown [WM8350_IRQ_EXT_USB_FB] = { 2860c7229f9SMark Brown .primary = WM8350_EXT_INT, 2870c7229f9SMark Brown .reg = WM8350_COMPARATOR_INT_OFFSET, 2880c7229f9SMark Brown .mask = WM8350_EXT_USB_FB_EINT, 2890c7229f9SMark Brown }, 2900c7229f9SMark Brown [WM8350_IRQ_EXT_WALL_FB] = { 2910c7229f9SMark Brown .primary = WM8350_EXT_INT, 2920c7229f9SMark Brown .reg = WM8350_COMPARATOR_INT_OFFSET, 2930c7229f9SMark Brown .mask = WM8350_EXT_WALL_FB_EINT, 2940c7229f9SMark Brown }, 2950c7229f9SMark Brown [WM8350_IRQ_EXT_BAT_FB] = { 2960c7229f9SMark Brown .primary = WM8350_EXT_INT, 2970c7229f9SMark Brown .reg = WM8350_COMPARATOR_INT_OFFSET, 2980c7229f9SMark Brown .mask = WM8350_EXT_BAT_FB_EINT, 2990c7229f9SMark Brown }, 3000c7229f9SMark Brown [WM8350_IRQ_GPIO(0)] = { 3010c7229f9SMark Brown .primary = WM8350_GP_INT, 3020c7229f9SMark Brown .reg = WM8350_GPIO_INT_OFFSET, 3030c7229f9SMark Brown .mask = WM8350_GP0_EINT, 3040c7229f9SMark Brown }, 3050c7229f9SMark Brown [WM8350_IRQ_GPIO(1)] = { 3060c7229f9SMark Brown .primary = WM8350_GP_INT, 3070c7229f9SMark Brown .reg = WM8350_GPIO_INT_OFFSET, 3080c7229f9SMark Brown .mask = WM8350_GP1_EINT, 3090c7229f9SMark Brown }, 3100c7229f9SMark Brown [WM8350_IRQ_GPIO(2)] = { 3110c7229f9SMark Brown .primary = WM8350_GP_INT, 3120c7229f9SMark Brown .reg = WM8350_GPIO_INT_OFFSET, 3130c7229f9SMark Brown .mask = WM8350_GP2_EINT, 3140c7229f9SMark Brown }, 3150c7229f9SMark Brown [WM8350_IRQ_GPIO(3)] = { 3160c7229f9SMark Brown .primary = WM8350_GP_INT, 3170c7229f9SMark Brown .reg = WM8350_GPIO_INT_OFFSET, 3180c7229f9SMark Brown .mask = WM8350_GP3_EINT, 3190c7229f9SMark Brown }, 3200c7229f9SMark Brown [WM8350_IRQ_GPIO(4)] = { 3210c7229f9SMark Brown .primary = WM8350_GP_INT, 3220c7229f9SMark Brown .reg = WM8350_GPIO_INT_OFFSET, 3230c7229f9SMark Brown .mask = WM8350_GP4_EINT, 3240c7229f9SMark Brown }, 3250c7229f9SMark Brown [WM8350_IRQ_GPIO(5)] = { 3260c7229f9SMark Brown .primary = WM8350_GP_INT, 3270c7229f9SMark Brown .reg = WM8350_GPIO_INT_OFFSET, 3280c7229f9SMark Brown .mask = WM8350_GP5_EINT, 3290c7229f9SMark Brown }, 3300c7229f9SMark Brown [WM8350_IRQ_GPIO(6)] = { 3310c7229f9SMark Brown .primary = WM8350_GP_INT, 3320c7229f9SMark Brown .reg = WM8350_GPIO_INT_OFFSET, 3330c7229f9SMark Brown .mask = WM8350_GP6_EINT, 3340c7229f9SMark Brown }, 3350c7229f9SMark Brown [WM8350_IRQ_GPIO(7)] = { 3360c7229f9SMark Brown .primary = WM8350_GP_INT, 3370c7229f9SMark Brown .reg = WM8350_GPIO_INT_OFFSET, 3380c7229f9SMark Brown .mask = WM8350_GP7_EINT, 3390c7229f9SMark Brown }, 3400c7229f9SMark Brown [WM8350_IRQ_GPIO(8)] = { 3410c7229f9SMark Brown .primary = WM8350_GP_INT, 3420c7229f9SMark Brown .reg = WM8350_GPIO_INT_OFFSET, 3430c7229f9SMark Brown .mask = WM8350_GP8_EINT, 3440c7229f9SMark Brown }, 3450c7229f9SMark Brown [WM8350_IRQ_GPIO(9)] = { 3460c7229f9SMark Brown .primary = WM8350_GP_INT, 3470c7229f9SMark Brown .reg = WM8350_GPIO_INT_OFFSET, 3480c7229f9SMark Brown .mask = WM8350_GP9_EINT, 3490c7229f9SMark Brown }, 3500c7229f9SMark Brown [WM8350_IRQ_GPIO(10)] = { 3510c7229f9SMark Brown .primary = WM8350_GP_INT, 3520c7229f9SMark Brown .reg = WM8350_GPIO_INT_OFFSET, 3530c7229f9SMark Brown .mask = WM8350_GP10_EINT, 3540c7229f9SMark Brown }, 3550c7229f9SMark Brown [WM8350_IRQ_GPIO(11)] = { 3560c7229f9SMark Brown .primary = WM8350_GP_INT, 3570c7229f9SMark Brown .reg = WM8350_GPIO_INT_OFFSET, 3580c7229f9SMark Brown .mask = WM8350_GP11_EINT, 3590c7229f9SMark Brown }, 3600c7229f9SMark Brown [WM8350_IRQ_GPIO(12)] = { 3610c7229f9SMark Brown .primary = WM8350_GP_INT, 3620c7229f9SMark Brown .reg = WM8350_GPIO_INT_OFFSET, 3630c7229f9SMark Brown .mask = WM8350_GP12_EINT, 3640c7229f9SMark Brown }, 3650c7229f9SMark Brown }; 3660c7229f9SMark Brown 367760e4518SMark Brown static inline struct wm8350_irq_data *irq_to_wm8350_irq(struct wm8350 *wm8350, 368760e4518SMark Brown int irq) 369e0a3389aSMark Brown { 370760e4518SMark Brown return &wm8350_irqs[irq - wm8350->irq_base]; 371e0a3389aSMark Brown } 372e0a3389aSMark Brown 373e0a3389aSMark Brown /* 374e0a3389aSMark Brown * This is a threaded IRQ handler so can access I2C/SPI. Since all 375e0a3389aSMark Brown * interrupts are clear on read the IRQ line will be reasserted and 376e0a3389aSMark Brown * the physical IRQ will be handled again if another interrupt is 377e0a3389aSMark Brown * asserted while we run - in the normal course of events this is a 378760e4518SMark Brown * rare occurrence so we save I2C/SPI reads. We're also assuming that 379760e4518SMark Brown * it's rare to get lots of interrupts firing simultaneously so try to 380760e4518SMark Brown * minimise I/O. 381e0a3389aSMark Brown */ 3820c7229f9SMark Brown static irqreturn_t wm8350_irq(int irq, void *irq_data) 383e0a3389aSMark Brown { 3840c7229f9SMark Brown struct wm8350 *wm8350 = irq_data; 3850c7229f9SMark Brown u16 level_one; 3860c7229f9SMark Brown u16 sub_reg[WM8350_NUM_IRQ_REGS]; 3870c7229f9SMark Brown int read_done[WM8350_NUM_IRQ_REGS]; 3880c7229f9SMark Brown struct wm8350_irq_data *data; 3890c7229f9SMark Brown int i; 390e0a3389aSMark Brown 391e0a3389aSMark Brown level_one = wm8350_reg_read(wm8350, WM8350_SYSTEM_INTERRUPTS) 392e0a3389aSMark Brown & ~wm8350_reg_read(wm8350, WM8350_SYSTEM_INTERRUPTS_MASK); 393e0a3389aSMark Brown 3940c7229f9SMark Brown if (!level_one) 3950c7229f9SMark Brown return IRQ_NONE; 396e0a3389aSMark Brown 3970c7229f9SMark Brown memset(&read_done, 0, sizeof(read_done)); 398e0a3389aSMark Brown 3990c7229f9SMark Brown for (i = 0; i < ARRAY_SIZE(wm8350_irqs); i++) { 4000c7229f9SMark Brown data = &wm8350_irqs[i]; 4010c7229f9SMark Brown 4020c7229f9SMark Brown if (!(level_one & data->primary)) 4030c7229f9SMark Brown continue; 4040c7229f9SMark Brown 4050c7229f9SMark Brown if (!read_done[data->reg]) { 4060c7229f9SMark Brown sub_reg[data->reg] = 4070c7229f9SMark Brown wm8350_reg_read(wm8350, WM8350_INT_STATUS_1 + 4080c7229f9SMark Brown data->reg); 409760e4518SMark Brown sub_reg[data->reg] &= ~wm8350->irq_masks[data->reg]; 4100c7229f9SMark Brown read_done[data->reg] = 1; 411e0a3389aSMark Brown } 412e0a3389aSMark Brown 4130c7229f9SMark Brown if (sub_reg[data->reg] & data->mask) 414760e4518SMark Brown handle_nested_irq(wm8350->irq_base + i); 415e0a3389aSMark Brown } 416e0a3389aSMark Brown 417e0a3389aSMark Brown return IRQ_HANDLED; 418e0a3389aSMark Brown } 419e0a3389aSMark Brown 420fdcc475bSMark Brown static void wm8350_irq_lock(struct irq_data *data) 421e0a3389aSMark Brown { 42225a947f8SMark Brown struct wm8350 *wm8350 = irq_data_get_irq_chip_data(data); 423e0a3389aSMark Brown 424760e4518SMark Brown mutex_lock(&wm8350->irq_lock); 425e0a3389aSMark Brown } 426e0a3389aSMark Brown 427fdcc475bSMark Brown static void wm8350_irq_sync_unlock(struct irq_data *data) 428e0a3389aSMark Brown { 42925a947f8SMark Brown struct wm8350 *wm8350 = irq_data_get_irq_chip_data(data); 430760e4518SMark Brown int i; 431e0a3389aSMark Brown 432760e4518SMark Brown for (i = 0; i < ARRAY_SIZE(wm8350->irq_masks); i++) { 433760e4518SMark Brown /* If there's been a change in the mask write it back 434760e4518SMark Brown * to the hardware. */ 435760e4518SMark Brown if (wm8350->irq_masks[i] != 436760e4518SMark Brown wm8350->reg_cache[WM8350_INT_STATUS_1_MASK + i]) 437760e4518SMark Brown WARN_ON(wm8350_reg_write(wm8350, 438760e4518SMark Brown WM8350_INT_STATUS_1_MASK + i, 439760e4518SMark Brown wm8350->irq_masks[i])); 440e0a3389aSMark Brown } 441e0a3389aSMark Brown 442760e4518SMark Brown mutex_unlock(&wm8350->irq_lock); 443760e4518SMark Brown } 444760e4518SMark Brown 445fdcc475bSMark Brown static void wm8350_irq_enable(struct irq_data *data) 446e0a3389aSMark Brown { 44725a947f8SMark Brown struct wm8350 *wm8350 = irq_data_get_irq_chip_data(data); 448fdcc475bSMark Brown struct wm8350_irq_data *irq_data = irq_to_wm8350_irq(wm8350, 449fdcc475bSMark Brown data->irq); 450e0a3389aSMark Brown 451760e4518SMark Brown wm8350->irq_masks[irq_data->reg] &= ~irq_data->mask; 452e0a3389aSMark Brown } 453760e4518SMark Brown 454fdcc475bSMark Brown static void wm8350_irq_disable(struct irq_data *data) 455760e4518SMark Brown { 45625a947f8SMark Brown struct wm8350 *wm8350 = irq_data_get_irq_chip_data(data); 457fdcc475bSMark Brown struct wm8350_irq_data *irq_data = irq_to_wm8350_irq(wm8350, 458fdcc475bSMark Brown data->irq); 459760e4518SMark Brown 460760e4518SMark Brown wm8350->irq_masks[irq_data->reg] |= irq_data->mask; 461760e4518SMark Brown } 462760e4518SMark Brown 463760e4518SMark Brown static struct irq_chip wm8350_irq_chip = { 464760e4518SMark Brown .name = "wm8350", 465fdcc475bSMark Brown .irq_bus_lock = wm8350_irq_lock, 466fdcc475bSMark Brown .irq_bus_sync_unlock = wm8350_irq_sync_unlock, 467fdcc475bSMark Brown .irq_disable = wm8350_irq_disable, 468fdcc475bSMark Brown .irq_enable = wm8350_irq_enable, 469760e4518SMark Brown }; 470e0a3389aSMark Brown 471e0a3389aSMark Brown int wm8350_irq_init(struct wm8350 *wm8350, int irq, 472e0a3389aSMark Brown struct wm8350_platform_data *pdata) 473e0a3389aSMark Brown { 474760e4518SMark Brown int ret, cur_irq, i; 475e0a3389aSMark Brown int flags = IRQF_ONESHOT; 476d1738aefSSascha Hauer int irq_base = -1; 477e0a3389aSMark Brown 478e0a3389aSMark Brown if (!irq) { 479760e4518SMark Brown dev_warn(wm8350->dev, "No interrupt support, no core IRQ\n"); 480760e4518SMark Brown return 0; 481e0a3389aSMark Brown } 482e0a3389aSMark Brown 483760e4518SMark Brown /* Mask top level interrupts */ 484e0a3389aSMark Brown wm8350_reg_write(wm8350, WM8350_SYSTEM_INTERRUPTS_MASK, 0xFFFF); 485e0a3389aSMark Brown 486760e4518SMark Brown /* Mask all individual interrupts by default and cache the 487760e4518SMark Brown * masks. We read the masks back since there are unwritable 488760e4518SMark Brown * bits in the mask registers. */ 489760e4518SMark Brown for (i = 0; i < ARRAY_SIZE(wm8350->irq_masks); i++) { 490760e4518SMark Brown wm8350_reg_write(wm8350, WM8350_INT_STATUS_1_MASK + i, 491760e4518SMark Brown 0xFFFF); 492760e4518SMark Brown wm8350->irq_masks[i] = 493760e4518SMark Brown wm8350_reg_read(wm8350, 494760e4518SMark Brown WM8350_INT_STATUS_1_MASK + i); 495760e4518SMark Brown } 496760e4518SMark Brown 497760e4518SMark Brown mutex_init(&wm8350->irq_lock); 498e0a3389aSMark Brown wm8350->chip_irq = irq; 499760e4518SMark Brown wm8350->irq_base = pdata->irq_base; 500e0a3389aSMark Brown 501d1738aefSSascha Hauer if (pdata && pdata->irq_base > 0) 502d1738aefSSascha Hauer irq_base = pdata->irq_base; 503d1738aefSSascha Hauer 504d1738aefSSascha Hauer wm8350->irq_base = irq_alloc_descs(irq_base, 0, ARRAY_SIZE(wm8350_irqs), 0); 505d1738aefSSascha Hauer if (wm8350->irq_base < 0) { 506d1738aefSSascha Hauer dev_warn(wm8350->dev, "Allocating irqs failed with %d\n", 507d1738aefSSascha Hauer wm8350->irq_base); 508d1738aefSSascha Hauer return 0; 509d1738aefSSascha Hauer } 510d1738aefSSascha Hauer 511d1738aefSSascha Hauer if (pdata && pdata->irq_high) { 512e0a3389aSMark Brown flags |= IRQF_TRIGGER_HIGH; 513e0a3389aSMark Brown 514e0a3389aSMark Brown wm8350_set_bits(wm8350, WM8350_SYSTEM_CONTROL_1, 515e0a3389aSMark Brown WM8350_IRQ_POL); 516e0a3389aSMark Brown } else { 517e0a3389aSMark Brown flags |= IRQF_TRIGGER_LOW; 518e0a3389aSMark Brown 519e0a3389aSMark Brown wm8350_clear_bits(wm8350, WM8350_SYSTEM_CONTROL_1, 520e0a3389aSMark Brown WM8350_IRQ_POL); 521e0a3389aSMark Brown } 522e0a3389aSMark Brown 523760e4518SMark Brown /* Register with genirq */ 524760e4518SMark Brown for (cur_irq = wm8350->irq_base; 525760e4518SMark Brown cur_irq < ARRAY_SIZE(wm8350_irqs) + wm8350->irq_base; 526760e4518SMark Brown cur_irq++) { 527d5bb1221SThomas Gleixner irq_set_chip_data(cur_irq, wm8350); 528d5bb1221SThomas Gleixner irq_set_chip_and_handler(cur_irq, &wm8350_irq_chip, 529760e4518SMark Brown handle_edge_irq); 530d5bb1221SThomas Gleixner irq_set_nested_thread(cur_irq, 1); 531760e4518SMark Brown 532760e4518SMark Brown /* ARM needs us to explicitly flag the IRQ as valid 533760e4518SMark Brown * and will set them noprobe when we do so. */ 534760e4518SMark Brown #ifdef CONFIG_ARM 535760e4518SMark Brown set_irq_flags(cur_irq, IRQF_VALID); 536760e4518SMark Brown #else 537d5bb1221SThomas Gleixner irq_set_noprobe(cur_irq); 538760e4518SMark Brown #endif 539760e4518SMark Brown } 540760e4518SMark Brown 541e0a3389aSMark Brown ret = request_threaded_irq(irq, NULL, wm8350_irq, flags, 542e0a3389aSMark Brown "wm8350", wm8350); 543e0a3389aSMark Brown if (ret != 0) 544e0a3389aSMark Brown dev_err(wm8350->dev, "Failed to request IRQ: %d\n", ret); 545e0a3389aSMark Brown 546760e4518SMark Brown /* Allow interrupts to fire */ 547760e4518SMark Brown wm8350_reg_write(wm8350, WM8350_SYSTEM_INTERRUPTS_MASK, 0); 548760e4518SMark Brown 549e0a3389aSMark Brown return ret; 550e0a3389aSMark Brown } 551e0a3389aSMark Brown 552e0a3389aSMark Brown int wm8350_irq_exit(struct wm8350 *wm8350) 553e0a3389aSMark Brown { 554e0a3389aSMark Brown free_irq(wm8350->chip_irq, wm8350); 555e0a3389aSMark Brown return 0; 556e0a3389aSMark Brown } 557