summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMark Brown <broonie@opensource.wolfsonmicro.com>2011-09-16 16:21:47 +0400
committerSamuel Ortiz <sameo@linux.intel.com>2011-10-24 16:09:14 +0400
commit7583a213ec3bde3082547ee37ad96214513bc1cb (patch)
treef70ceb258fa895d2cc5aeea8ef4f3d790ce30d04
parent1f08c1125ed1c9a6ca9bb195a56fe340b2134018 (diff)
downloadlinux-7583a213ec3bde3082547ee37ad96214513bc1cb.tar.xz
mfd: Simulate active high IRQs with wm831x
In order to ease system integration provide a simulation of active high IRQs on the GPIOs by polling the GPIO status when an IRQ is generated. This isn't ideal on several fronts and will miss initially active IRQs in the current implementation but it should work well for most cases. Signed-off-by: Mark Brown <broonie@opensource.wolfsonmicro.com> Signed-off-by: Samuel Ortiz <sameo@linux.intel.com>
-rw-r--r--drivers/mfd/wm831x-irq.c22
-rw-r--r--include/linux/mfd/wm831x/core.h1
2 files changed, 22 insertions, 1 deletions
diff --git a/drivers/mfd/wm831x-irq.c b/drivers/mfd/wm831x-irq.c
index a10937cfff4c..f4747a4a9a93 100644
--- a/drivers/mfd/wm831x-irq.c
+++ b/drivers/mfd/wm831x-irq.c
@@ -420,12 +420,19 @@ static int wm831x_irq_set_type(struct irq_data *data, unsigned int type)
switch (type) {
case IRQ_TYPE_EDGE_BOTH:
wm831x->gpio_update[irq] = 0x10000 | WM831X_GPN_INT_MODE;
+ wm831x->gpio_level[irq] = false;
break;
case IRQ_TYPE_EDGE_RISING:
wm831x->gpio_update[irq] = 0x10000 | WM831X_GPN_POL;
+ wm831x->gpio_level[irq] = false;
break;
case IRQ_TYPE_EDGE_FALLING:
wm831x->gpio_update[irq] = 0x10000;
+ wm831x->gpio_level[irq] = false;
+ break;
+ case IRQ_TYPE_LEVEL_HIGH:
+ wm831x->gpio_update[irq] = 0x10000 | WM831X_GPN_POL;
+ wm831x->gpio_level[irq] = true;
break;
default:
return -EINVAL;
@@ -449,7 +456,7 @@ static irqreturn_t wm831x_irq_thread(int irq, void *data)
{
struct wm831x *wm831x = data;
unsigned int i;
- int primary, status_addr;
+ int primary, status_addr, ret;
int status_regs[WM831X_NUM_IRQ_REGS] = { 0 };
int read[WM831X_NUM_IRQ_REGS] = { 0 };
int *status;
@@ -507,6 +514,19 @@ static irqreturn_t wm831x_irq_thread(int irq, void *data)
if (*status & wm831x_irqs[i].mask)
handle_nested_irq(wm831x->irq_base + i);
+
+ /* Simulate an edge triggered IRQ by polling the input
+ * status. This is sucky but improves interoperability.
+ */
+ if (primary == WM831X_GP_INT &&
+ wm831x->gpio_level[i - WM831X_IRQ_GPIO_1]) {
+ ret = wm831x_reg_read(wm831x, WM831X_GPIO_LEVEL);
+ while (ret & 1 << (i - WM831X_IRQ_GPIO_1)) {
+ handle_nested_irq(wm831x->irq_base + i);
+ ret = wm831x_reg_read(wm831x,
+ WM831X_GPIO_LEVEL);
+ }
+ }
}
out:
diff --git a/include/linux/mfd/wm831x/core.h b/include/linux/mfd/wm831x/core.h
index fb3e84f92e90..272f7fb5f8b8 100644
--- a/include/linux/mfd/wm831x/core.h
+++ b/include/linux/mfd/wm831x/core.h
@@ -385,6 +385,7 @@ struct wm831x {
/* Used by the interrupt controller code to post writes */
int gpio_update[WM831X_NUM_GPIO_REGS];
+ bool gpio_level[WM831X_NUM_GPIO_REGS];
struct mutex auxadc_lock;
struct list_head auxadc_pending;