]> Pileus Git - ~andy/linux/commitdiff
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 aa25468d24aa03b1b6a90fd135377985f123ca15..f761cc119c01a14081930916599cdfc8ff16a76e 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 84fefede3a23dc896b50aeae4b0df872cc6c8988..8755dd05ab785df710683c9c4b13b15ea81a08d2 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