1 // SPDX-License-Identifier: GPL-2.0-or-later 2 /* 3 * wm8994-irq.c -- Interrupt controller support for Wolfson WM8994 4 * 5 * Copyright 2010 Wolfson Microelectronics PLC. 6 * 7 * Author: Mark Brown <broonie@opensource.wolfsonmicro.com> 8 */ 9 10 #include <linux/kernel.h> 11 #include <linux/module.h> 12 #include <linux/gpio.h> 13 #include <linux/i2c.h> 14 #include <linux/irq.h> 15 #include <linux/mfd/core.h> 16 #include <linux/interrupt.h> 17 #include <linux/irqdomain.h> 18 #include <linux/regmap.h> 19 20 #include <linux/mfd/wm8994/core.h> 21 #include <linux/mfd/wm8994/pdata.h> 22 #include <linux/mfd/wm8994/registers.h> 23 24 #include <linux/delay.h> 25 26 static const struct regmap_irq wm8994_irqs[] = { 27 [WM8994_IRQ_TEMP_SHUT] = { 28 .reg_offset = 1, 29 .mask = WM8994_TEMP_SHUT_EINT, 30 }, 31 [WM8994_IRQ_MIC1_DET] = { 32 .reg_offset = 1, 33 .mask = WM8994_MIC1_DET_EINT, 34 }, 35 [WM8994_IRQ_MIC1_SHRT] = { 36 .reg_offset = 1, 37 .mask = WM8994_MIC1_SHRT_EINT, 38 }, 39 [WM8994_IRQ_MIC2_DET] = { 40 .reg_offset = 1, 41 .mask = WM8994_MIC2_DET_EINT, 42 }, 43 [WM8994_IRQ_MIC2_SHRT] = { 44 .reg_offset = 1, 45 .mask = WM8994_MIC2_SHRT_EINT, 46 }, 47 [WM8994_IRQ_FLL1_LOCK] = { 48 .reg_offset = 1, 49 .mask = WM8994_FLL1_LOCK_EINT, 50 }, 51 [WM8994_IRQ_FLL2_LOCK] = { 52 .reg_offset = 1, 53 .mask = WM8994_FLL2_LOCK_EINT, 54 }, 55 [WM8994_IRQ_SRC1_LOCK] = { 56 .reg_offset = 1, 57 .mask = WM8994_SRC1_LOCK_EINT, 58 }, 59 [WM8994_IRQ_SRC2_LOCK] = { 60 .reg_offset = 1, 61 .mask = WM8994_SRC2_LOCK_EINT, 62 }, 63 [WM8994_IRQ_AIF1DRC1_SIG_DET] = { 64 .reg_offset = 1, 65 .mask = WM8994_AIF1DRC1_SIG_DET, 66 }, 67 [WM8994_IRQ_AIF1DRC2_SIG_DET] = { 68 .reg_offset = 1, 69 .mask = WM8994_AIF1DRC2_SIG_DET_EINT, 70 }, 71 [WM8994_IRQ_AIF2DRC_SIG_DET] = { 72 .reg_offset = 1, 73 .mask = WM8994_AIF2DRC_SIG_DET_EINT, 74 }, 75 [WM8994_IRQ_FIFOS_ERR] = { 76 .reg_offset = 1, 77 .mask = WM8994_FIFOS_ERR_EINT, 78 }, 79 [WM8994_IRQ_WSEQ_DONE] = { 80 .reg_offset = 1, 81 .mask = WM8994_WSEQ_DONE_EINT, 82 }, 83 [WM8994_IRQ_DCS_DONE] = { 84 .reg_offset = 1, 85 .mask = WM8994_DCS_DONE_EINT, 86 }, 87 [WM8994_IRQ_TEMP_WARN] = { 88 .reg_offset = 1, 89 .mask = WM8994_TEMP_WARN_EINT, 90 }, 91 [WM8994_IRQ_GPIO(1)] = { 92 .mask = WM8994_GP1_EINT, 93 }, 94 [WM8994_IRQ_GPIO(2)] = { 95 .mask = WM8994_GP2_EINT, 96 }, 97 [WM8994_IRQ_GPIO(3)] = { 98 .mask = WM8994_GP3_EINT, 99 }, 100 [WM8994_IRQ_GPIO(4)] = { 101 .mask = WM8994_GP4_EINT, 102 }, 103 [WM8994_IRQ_GPIO(5)] = { 104 .mask = WM8994_GP5_EINT, 105 }, 106 [WM8994_IRQ_GPIO(6)] = { 107 .mask = WM8994_GP6_EINT, 108 }, 109 [WM8994_IRQ_GPIO(7)] = { 110 .mask = WM8994_GP7_EINT, 111 }, 112 [WM8994_IRQ_GPIO(8)] = { 113 .mask = WM8994_GP8_EINT, 114 }, 115 [WM8994_IRQ_GPIO(9)] = { 116 .mask = WM8994_GP8_EINT, 117 }, 118 [WM8994_IRQ_GPIO(10)] = { 119 .mask = WM8994_GP10_EINT, 120 }, 121 [WM8994_IRQ_GPIO(11)] = { 122 .mask = WM8994_GP11_EINT, 123 }, 124 }; 125 126 static const struct regmap_irq_chip wm8994_irq_chip = { 127 .name = "wm8994", 128 .irqs = wm8994_irqs, 129 .num_irqs = ARRAY_SIZE(wm8994_irqs), 130 131 .num_regs = 2, 132 .status_base = WM8994_INTERRUPT_STATUS_1, 133 .mask_base = WM8994_INTERRUPT_STATUS_1_MASK, 134 .ack_base = WM8994_INTERRUPT_STATUS_1, 135 .runtime_pm = true, 136 }; 137 138 static void wm8994_edge_irq_enable(struct irq_data *data) 139 { 140 } 141 142 static void wm8994_edge_irq_disable(struct irq_data *data) 143 { 144 } 145 146 static struct irq_chip wm8994_edge_irq_chip = { 147 .name = "wm8994_edge", 148 .irq_disable = wm8994_edge_irq_disable, 149 .irq_enable = wm8994_edge_irq_enable, 150 }; 151 152 static irqreturn_t wm8994_edge_irq(int irq, void *data) 153 { 154 struct wm8994 *wm8994 = data; 155 156 while (gpio_get_value_cansleep(wm8994->pdata.irq_gpio)) 157 handle_nested_irq(irq_create_mapping(wm8994->edge_irq, 0)); 158 159 return IRQ_HANDLED; 160 } 161 162 static int wm8994_edge_irq_map(struct irq_domain *h, unsigned int virq, 163 irq_hw_number_t hw) 164 { 165 struct wm8994 *wm8994 = h->host_data; 166 167 irq_set_chip_data(virq, wm8994); 168 irq_set_chip_and_handler(virq, &wm8994_edge_irq_chip, handle_edge_irq); 169 irq_set_nested_thread(virq, 1); 170 irq_set_noprobe(virq); 171 172 return 0; 173 } 174 175 static const struct irq_domain_ops wm8994_edge_irq_ops = { 176 .map = wm8994_edge_irq_map, 177 .xlate = irq_domain_xlate_twocell, 178 }; 179 180 int wm8994_irq_init(struct wm8994 *wm8994) 181 { 182 int ret; 183 unsigned long irqflags; 184 struct wm8994_pdata *pdata = &wm8994->pdata; 185 186 if (!wm8994->irq) { 187 dev_warn(wm8994->dev, 188 "No interrupt specified, no interrupts\n"); 189 wm8994->irq_base = 0; 190 return 0; 191 } 192 193 /* select user or default irq flags */ 194 irqflags = IRQF_TRIGGER_HIGH | IRQF_ONESHOT; 195 if (pdata->irq_flags) 196 irqflags = pdata->irq_flags; 197 198 /* use a GPIO for edge triggered controllers */ 199 if (irqflags & (IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING)) { 200 if (gpio_to_irq(pdata->irq_gpio) != wm8994->irq) { 201 dev_warn(wm8994->dev, "IRQ %d is not GPIO %d (%d)\n", 202 wm8994->irq, pdata->irq_gpio, 203 gpio_to_irq(pdata->irq_gpio)); 204 wm8994->irq = gpio_to_irq(pdata->irq_gpio); 205 } 206 207 ret = devm_gpio_request_one(wm8994->dev, pdata->irq_gpio, 208 GPIOF_IN, "WM8994 IRQ"); 209 210 if (ret != 0) { 211 dev_err(wm8994->dev, "Failed to get IRQ GPIO: %d\n", 212 ret); 213 return ret; 214 } 215 216 wm8994->edge_irq = irq_domain_add_linear(NULL, 1, 217 &wm8994_edge_irq_ops, 218 wm8994); 219 220 ret = regmap_add_irq_chip(wm8994->regmap, 221 irq_create_mapping(wm8994->edge_irq, 222 0), 223 IRQF_ONESHOT, 224 wm8994->irq_base, &wm8994_irq_chip, 225 &wm8994->irq_data); 226 if (ret != 0) { 227 dev_err(wm8994->dev, "Failed to get IRQ: %d\n", 228 ret); 229 return ret; 230 } 231 232 ret = request_threaded_irq(wm8994->irq, 233 NULL, wm8994_edge_irq, 234 irqflags, 235 "WM8994 edge", wm8994); 236 } else { 237 ret = regmap_add_irq_chip(wm8994->regmap, wm8994->irq, 238 irqflags, 239 wm8994->irq_base, &wm8994_irq_chip, 240 &wm8994->irq_data); 241 } 242 243 if (ret != 0) { 244 dev_err(wm8994->dev, "Failed to register IRQ chip: %d\n", ret); 245 return ret; 246 } 247 248 /* Enable top level interrupt if it was masked */ 249 wm8994_reg_write(wm8994, WM8994_INTERRUPT_CONTROL, 0); 250 251 return 0; 252 } 253 EXPORT_SYMBOL(wm8994_irq_init); 254 255 void wm8994_irq_exit(struct wm8994 *wm8994) 256 { 257 regmap_del_irq_chip(wm8994->irq, wm8994->irq_data); 258 } 259 EXPORT_SYMBOL(wm8994_irq_exit); 260