mfd: arizona: Allow GPIO to be specified for IRQ line
authorMark Brown <broonie@opensource.wolfsonmicro.com>
Sun, 24 Mar 2013 23:05:58 +0000 (23:05 +0000)
committerSamuel Ortiz <sameo@linux.intel.com>
Mon, 8 Apr 2013 13:20:58 +0000 (15:20 +0200)
If a GPIO is specified for the chip IRQ line then request it. This
improves support for systems that do not put pins into input mode when
used as interrupts.

Also use this GPIO when the primary IRQ is in edge triggered mode to
detect if we have handled pending interrupts in order to improve
robustness.

Signed-off-by: Mark Brown <broonie@opensource.wolfsonmicro.com>
Signed-off-by: Samuel Ortiz <sameo@linux.intel.com>
drivers/mfd/arizona-irq.c
include/linux/mfd/arizona/pdata.h

index aa25468..f761cc1 100644 (file)
@@ -94,6 +94,7 @@ static irqreturn_t arizona_ctrlif_err(int irq, void *data)
 static irqreturn_t arizona_irq_thread(int irq, void *data)
 {
        struct arizona *arizona = data;
+       bool poll;
        unsigned int val;
        int ret;
 
@@ -103,20 +104,39 @@ static irqreturn_t arizona_irq_thread(int irq, void *data)
                return IRQ_NONE;
        }
 
-       /* Always handle the AoD domain */
-       handle_nested_irq(irq_find_mapping(arizona->virq, 0));
+       do {
+               poll = false;
+
+               /* Always handle the AoD domain */
+               handle_nested_irq(irq_find_mapping(arizona->virq, 0));
+
+               /*
+                * Check if one of the main interrupts is asserted and only
+                * check that domain if it is.
+                */
+               ret = regmap_read(arizona->regmap, ARIZONA_IRQ_PIN_STATUS,
+                                 &val);
+               if (ret == 0 && val & ARIZONA_IRQ1_STS) {
+                       handle_nested_irq(irq_find_mapping(arizona->virq, 1));
+               } else if (ret != 0) {
+                       dev_err(arizona->dev,
+                               "Failed to read main IRQ status: %d\n", ret);
+               }
 
-       /*
-        * Check if one of the main interrupts is asserted and only
-        * check that domain if it is.
-        */
-       ret = regmap_read(arizona->regmap, ARIZONA_IRQ_PIN_STATUS, &val);
-       if (ret == 0 && val & ARIZONA_IRQ1_STS) {
-               handle_nested_irq(irq_find_mapping(arizona->virq, 1));
-       } else if (ret != 0) {
-               dev_err(arizona->dev, "Failed to read main IRQ status: %d\n",
-                       ret);
-       }
+               /*
+                * Poll the IRQ pin status to see if we're really done
+                * if the interrupt controller can't do it for us.
+                */
+               if (!arizona->pdata.irq_gpio) {
+                       break;
+               } else if (arizona->pdata.irq_flags & IRQF_TRIGGER_RISING &&
+                          gpio_get_value_cansleep(arizona->pdata.irq_gpio)) {
+                       poll = true;
+               } else if (arizona->pdata.irq_flags & IRQF_TRIGGER_FALLING &&
+                          !gpio_get_value_cansleep(arizona->pdata.irq_gpio)) {
+                       poll = true;
+               }
+       } while (poll);
 
        pm_runtime_mark_last_busy(arizona->dev);
        pm_runtime_put_autosuspend(arizona->dev);
@@ -262,6 +282,26 @@ int arizona_irq_init(struct arizona *arizona)
                }
        }
 
+       /* Used to emulate edge trigger and to work around broken pinmux */
+       if (arizona->pdata.irq_gpio) {
+               if (gpio_to_irq(arizona->pdata.irq_gpio) != arizona->irq) {
+                       dev_warn(arizona->dev, "IRQ %d is not GPIO %d (%d)\n",
+                                arizona->irq, arizona->pdata.irq_gpio,
+                                gpio_to_irq(arizona->pdata.irq_gpio));
+                       arizona->irq = gpio_to_irq(arizona->pdata.irq_gpio);
+               }
+
+               ret = devm_gpio_request_one(arizona->dev,
+                                           arizona->pdata.irq_gpio,
+                                           GPIOF_IN, "arizona IRQ");
+               if (ret != 0) {
+                       dev_err(arizona->dev,
+                               "Failed to request IRQ GPIO %d:: %d\n",
+                               arizona->pdata.irq_gpio, ret);
+                       arizona->pdata.irq_gpio = 0;
+               }
+       }
+
        ret = request_threaded_irq(arizona->irq, NULL, arizona_irq_thread,
                                   flags, "arizona", arizona);
 
index 84fefed..8755dd0 100644 (file)
@@ -163,6 +163,9 @@ struct arizona_pdata {
 
        /** Haptic actuator type */
        unsigned int hap_act;
+
+       /** GPIO for primary IRQ (used for edge triggered emulation) */
+       int irq_gpio;
 };
 
 #endif