aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/mfd/wm8994-irq.c
diff options
context:
space:
mode:
authorMark Brown <broonie@opensource.wolfsonmicro.com>2013-05-06 11:12:56 -0400
committerSamuel Ortiz <sameo@linux.intel.com>2013-06-11 13:27:52 -0400
commit7c8844481a1c16c10fa9be4ce95be5725aed6ce3 (patch)
tree7533110930172ae82fa38ee8531a70ecd50ed8ca /drivers/mfd/wm8994-irq.c
parentfc83f586adf3b86ff7046478497b4a53b2220be8 (diff)
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>
Diffstat (limited to 'drivers/mfd/wm8994-irq.c')
-rw-r--r--drivers/mfd/wm8994-irq.c100
1 files changed, 96 insertions, 4 deletions
diff --git a/drivers/mfd/wm8994-irq.c b/drivers/mfd/wm8994-irq.c
index a050e56a9bbd..d3a184a240f5 100644
--- a/drivers/mfd/wm8994-irq.c
+++ b/drivers/mfd/wm8994-irq.c
@@ -14,10 +14,12 @@
14 14
15#include <linux/kernel.h> 15#include <linux/kernel.h>
16#include <linux/module.h> 16#include <linux/module.h>
17#include <linux/gpio.h>
17#include <linux/i2c.h> 18#include <linux/i2c.h>
18#include <linux/irq.h> 19#include <linux/irq.h>
19#include <linux/mfd/core.h> 20#include <linux/mfd/core.h>
20#include <linux/interrupt.h> 21#include <linux/interrupt.h>
22#include <linux/irqdomain.h>
21#include <linux/regmap.h> 23#include <linux/regmap.h>
22 24
23#include <linux/mfd/wm8994/core.h> 25#include <linux/mfd/wm8994/core.h>
@@ -138,6 +140,55 @@ static struct regmap_irq_chip wm8994_irq_chip = {
138 .runtime_pm = true, 140 .runtime_pm = true,
139}; 141};
140 142
143static void wm8994_edge_irq_enable(struct irq_data *data)
144{
145}
146
147static void wm8994_edge_irq_disable(struct irq_data *data)
148{
149}
150
151static struct irq_chip wm8994_edge_irq_chip = {
152 .name = "wm8994_edge",
153 .irq_disable = wm8994_edge_irq_disable,
154 .irq_enable = wm8994_edge_irq_enable,
155};
156
157static irqreturn_t wm8994_edge_irq(int irq, void *data)
158{
159 struct wm8994 *wm8994 = data;
160
161 while (gpio_get_value_cansleep(wm8994->pdata.irq_gpio))
162 handle_nested_irq(irq_create_mapping(wm8994->edge_irq, 0));
163
164 return IRQ_HANDLED;
165}
166
167static int wm8994_edge_irq_map(struct irq_domain *h, unsigned int virq,
168 irq_hw_number_t hw)
169{
170 struct wm8994 *wm8994 = h->host_data;
171
172 irq_set_chip_data(virq, wm8994);
173 irq_set_chip_and_handler(virq, &wm8994_edge_irq_chip, handle_edge_irq);
174 irq_set_nested_thread(virq, 1);
175
176 /* ARM needs us to explicitly flag the IRQ as valid
177 * and will set them noprobe when we do so. */
178#ifdef CONFIG_ARM
179 set_irq_flags(virq, IRQF_VALID);
180#else
181 irq_set_noprobe(virq);
182#endif
183
184 return 0;
185}
186
187static struct irq_domain_ops wm8994_edge_irq_ops = {
188 .map = wm8994_edge_irq_map,
189 .xlate = irq_domain_xlate_twocell,
190};
191
141int wm8994_irq_init(struct wm8994 *wm8994) 192int wm8994_irq_init(struct wm8994 *wm8994)
142{ 193{
143 int ret; 194 int ret;
@@ -156,10 +207,51 @@ int wm8994_irq_init(struct wm8994 *wm8994)
156 if (pdata->irq_flags) 207 if (pdata->irq_flags)
157 irqflags = pdata->irq_flags; 208 irqflags = pdata->irq_flags;
158 209
159 ret = regmap_add_irq_chip(wm8994->regmap, wm8994->irq, 210 /* use a GPIO for edge triggered controllers */
160 irqflags, 211 if (irqflags & (IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING)) {
161 wm8994->irq_base, &wm8994_irq_chip, 212 if (gpio_to_irq(pdata->irq_gpio) != wm8994->irq) {
162 &wm8994->irq_data); 213 dev_warn(wm8994->dev, "IRQ %d is not GPIO %d (%d)\n",
214 wm8994->irq, pdata->irq_gpio,
215 gpio_to_irq(pdata->irq_gpio));
216 wm8994->irq = gpio_to_irq(pdata->irq_gpio);
217 }
218
219 ret = devm_gpio_request_one(wm8994->dev, pdata->irq_gpio,
220 GPIOF_IN, "WM8994 IRQ");
221
222 if (ret != 0) {
223 dev_err(wm8994->dev, "Failed to get IRQ GPIO: %d\n",
224 ret);
225 return ret;
226 }
227
228 wm8994->edge_irq = irq_domain_add_linear(NULL, 1,
229 &wm8994_edge_irq_ops,
230 wm8994);
231
232 ret = regmap_add_irq_chip(wm8994->regmap,
233 irq_create_mapping(wm8994->edge_irq,
234 0),
235 IRQF_ONESHOT,
236 wm8994->irq_base, &wm8994_irq_chip,
237 &wm8994->irq_data);
238 if (ret != 0) {
239 dev_err(wm8994->dev, "Failed to get IRQ: %d\n",
240 ret);
241 return ret;
242 }
243
244 ret = request_threaded_irq(wm8994->irq,
245 NULL, wm8994_edge_irq,
246 irqflags,
247 "WM8994 edge", wm8994);
248 } else {
249 ret = regmap_add_irq_chip(wm8994->regmap, wm8994->irq,
250 irqflags,
251 wm8994->irq_base, &wm8994_irq_chip,
252 &wm8994->irq_data);
253 }
254
163 if (ret != 0) { 255 if (ret != 0) {
164 dev_err(wm8994->dev, "Failed to register IRQ chip: %d\n", ret); 256 dev_err(wm8994->dev, "Failed to register IRQ chip: %d\n", ret);
165 return ret; 257 return ret;