Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

mfd: wm8994: Emulate level triggered interrupts if required

The interrupt controller on the wm8994 series of devices requires a level
triggered parent. If one is not available but a GPIO is available for the
interrupt then emulate.

Signed-off-by: Mark Brown <broonie@opensource.wolfsonmicro.com>
Signed-off-by: Samuel Ortiz <sameo@linux.intel.com>

authored by

Mark Brown and committed by
Samuel Ortiz
7c884448 fc83f586

+103 -4
+96 -4
drivers/mfd/wm8994-irq.c
··· 14 14 15 15 #include <linux/kernel.h> 16 16 #include <linux/module.h> 17 + #include <linux/gpio.h> 17 18 #include <linux/i2c.h> 18 19 #include <linux/irq.h> 19 20 #include <linux/mfd/core.h> 20 21 #include <linux/interrupt.h> 22 + #include <linux/irqdomain.h> 21 23 #include <linux/regmap.h> 22 24 23 25 #include <linux/mfd/wm8994/core.h> ··· 140 138 .runtime_pm = true, 141 139 }; 142 140 141 + static void wm8994_edge_irq_enable(struct irq_data *data) 142 + { 143 + } 144 + 145 + static void wm8994_edge_irq_disable(struct irq_data *data) 146 + { 147 + } 148 + 149 + static struct irq_chip wm8994_edge_irq_chip = { 150 + .name = "wm8994_edge", 151 + .irq_disable = wm8994_edge_irq_disable, 152 + .irq_enable = wm8994_edge_irq_enable, 153 + }; 154 + 155 + static irqreturn_t wm8994_edge_irq(int irq, void *data) 156 + { 157 + struct wm8994 *wm8994 = data; 158 + 159 + while (gpio_get_value_cansleep(wm8994->pdata.irq_gpio)) 160 + handle_nested_irq(irq_create_mapping(wm8994->edge_irq, 0)); 161 + 162 + return IRQ_HANDLED; 163 + } 164 + 165 + static int wm8994_edge_irq_map(struct irq_domain *h, unsigned int virq, 166 + irq_hw_number_t hw) 167 + { 168 + struct wm8994 *wm8994 = h->host_data; 169 + 170 + irq_set_chip_data(virq, wm8994); 171 + irq_set_chip_and_handler(virq, &wm8994_edge_irq_chip, handle_edge_irq); 172 + irq_set_nested_thread(virq, 1); 173 + 174 + /* ARM needs us to explicitly flag the IRQ as valid 175 + * and will set them noprobe when we do so. */ 176 + #ifdef CONFIG_ARM 177 + set_irq_flags(virq, IRQF_VALID); 178 + #else 179 + irq_set_noprobe(virq); 180 + #endif 181 + 182 + return 0; 183 + } 184 + 185 + static struct irq_domain_ops wm8994_edge_irq_ops = { 186 + .map = wm8994_edge_irq_map, 187 + .xlate = irq_domain_xlate_twocell, 188 + }; 189 + 143 190 int wm8994_irq_init(struct wm8994 *wm8994) 144 191 { 145 192 int ret; ··· 207 156 if (pdata->irq_flags) 208 157 irqflags = pdata->irq_flags; 209 158 210 - ret = regmap_add_irq_chip(wm8994->regmap, wm8994->irq, 211 - irqflags, 212 - wm8994->irq_base, &wm8994_irq_chip, 213 - &wm8994->irq_data); 159 + /* use a GPIO for edge triggered controllers */ 160 + if (irqflags & (IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING)) { 161 + if (gpio_to_irq(pdata->irq_gpio) != wm8994->irq) { 162 + dev_warn(wm8994->dev, "IRQ %d is not GPIO %d (%d)\n", 163 + wm8994->irq, pdata->irq_gpio, 164 + gpio_to_irq(pdata->irq_gpio)); 165 + wm8994->irq = gpio_to_irq(pdata->irq_gpio); 166 + } 167 + 168 + ret = devm_gpio_request_one(wm8994->dev, pdata->irq_gpio, 169 + GPIOF_IN, "WM8994 IRQ"); 170 + 171 + if (ret != 0) { 172 + dev_err(wm8994->dev, "Failed to get IRQ GPIO: %d\n", 173 + ret); 174 + return ret; 175 + } 176 + 177 + wm8994->edge_irq = irq_domain_add_linear(NULL, 1, 178 + &wm8994_edge_irq_ops, 179 + wm8994); 180 + 181 + ret = regmap_add_irq_chip(wm8994->regmap, 182 + irq_create_mapping(wm8994->edge_irq, 183 + 0), 184 + IRQF_ONESHOT, 185 + wm8994->irq_base, &wm8994_irq_chip, 186 + &wm8994->irq_data); 187 + if (ret != 0) { 188 + dev_err(wm8994->dev, "Failed to get IRQ: %d\n", 189 + ret); 190 + return ret; 191 + } 192 + 193 + ret = request_threaded_irq(wm8994->irq, 194 + NULL, wm8994_edge_irq, 195 + irqflags, 196 + "WM8994 edge", wm8994); 197 + } else { 198 + ret = regmap_add_irq_chip(wm8994->regmap, wm8994->irq, 199 + irqflags, 200 + wm8994->irq_base, &wm8994_irq_chip, 201 + &wm8994->irq_data); 202 + } 203 + 214 204 if (ret != 0) { 215 205 dev_err(wm8994->dev, "Failed to register IRQ chip: %d\n", ret); 216 206 return ret;
+2
include/linux/mfd/wm8994/core.h
··· 29 29 30 30 struct regulator_dev; 31 31 struct regulator_bulk_data; 32 + struct irq_domain; 32 33 33 34 #define WM8994_NUM_GPIO_REGS 11 34 35 #define WM8994_NUM_LDO_REGS 2 ··· 74 73 75 74 int irq; 76 75 struct regmap_irq_chip_data *irq_data; 76 + struct irq_domain *edge_irq; 77 77 78 78 /* Used over suspend/resume */ 79 79 bool suspended;
+5
include/linux/mfd/wm8994/pdata.h
··· 223 223 * lines is mastered. 224 224 */ 225 225 int max_channels_clocked[WM8994_NUM_AIF]; 226 + 227 + /** 228 + * GPIO for the IRQ pin if host only supports edge triggering 229 + */ 230 + int irq_gpio; 226 231 }; 227 232 228 233 #endif