OSDN Git Service

gpio: acpi: Override GPIO initialization flags
authorAndy Shevchenko <andriy.shevchenko@linux.intel.com>
Tue, 23 May 2017 17:03:23 +0000 (20:03 +0300)
committerLinus Walleij <linus.walleij@linaro.org>
Mon, 29 May 2017 09:21:44 +0000 (11:21 +0200)
This allows ACPI GPIO code to modify flags based on
ACPI GpioIo() / GpioInt() resources.

Signed-off-by: Andy Shevchenko <andriy.shevchenko@linux.intel.com>
Tested-by: Jarkko Nikula <jarkko.nikula@linux.intel.com>
Reviewed-by: Mika Westerberg <mika.westerberg@linux.intel.com>
Signed-off-by: Linus Walleij <linus.walleij@linaro.org>
drivers/gpio/gpiolib-acpi.c
drivers/gpio/gpiolib.c
drivers/gpio/gpiolib.h

index cb61e11..e431222 100644 (file)
@@ -448,6 +448,34 @@ acpi_gpio_to_gpiod_flags(const struct acpi_resource_gpio *agpio)
        }
 }
 
+int
+acpi_gpio_update_gpiod_flags(enum gpiod_flags *flags, enum gpiod_flags update)
+{
+       int ret = 0;
+
+       /*
+        * Check if the BIOS has IoRestriction with explicitly set direction
+        * and update @flags accordingly. Otherwise use whatever caller asked
+        * for.
+        */
+       if (update & GPIOD_FLAGS_BIT_DIR_SET) {
+               enum gpiod_flags diff = *flags ^ update;
+
+               /*
+                * Check if caller supplied incompatible GPIO initialization
+                * flags.
+                *
+                * Return %-EINVAL to notify that firmware has different
+                * settings and we are going to use them.
+                */
+               if (((*flags & GPIOD_FLAGS_BIT_DIR_SET) && (diff & GPIOD_FLAGS_BIT_DIR_OUT)) ||
+                   ((*flags & GPIOD_FLAGS_BIT_DIR_OUT) && (diff & GPIOD_FLAGS_BIT_DIR_VAL)))
+                       ret = -EINVAL;
+               *flags = update;
+       }
+       return ret;
+}
+
 struct acpi_gpio_lookup {
        struct acpi_gpio_info info;
        int index;
@@ -485,8 +513,11 @@ static int acpi_populate_gpio_lookup(struct acpi_resource *ares, void *data)
                 * - ACPI_ACTIVE_HIGH == GPIO_ACTIVE_HIGH
                 */
                if (lookup->info.gpioint) {
+                       lookup->info.flags = GPIOD_IN;
                        lookup->info.polarity = agpio->polarity;
                        lookup->info.triggering = agpio->triggering;
+               } else {
+                       lookup->info.flags = acpi_gpio_to_gpiod_flags(agpio);
                }
 
        }
@@ -613,13 +644,14 @@ static struct gpio_desc *acpi_get_gpiod_by_index(struct acpi_device *adev,
 struct gpio_desc *acpi_find_gpio(struct device *dev,
                                 const char *con_id,
                                 unsigned int idx,
-                                enum gpiod_flags flags,
+                                enum gpiod_flags *dflags,
                                 enum gpio_lookup_flags *lookupflags)
 {
        struct acpi_device *adev = ACPI_COMPANION(dev);
        struct acpi_gpio_info info;
        struct gpio_desc *desc;
        char propname[32];
+       int err;
        int i;
 
        /* Try first from _DSD */
@@ -650,7 +682,7 @@ struct gpio_desc *acpi_find_gpio(struct device *dev,
        }
 
        if (info.gpioint &&
-           (flags == GPIOD_OUT_LOW || flags == GPIOD_OUT_HIGH)) {
+           (*dflags == GPIOD_OUT_LOW || *dflags == GPIOD_OUT_HIGH)) {
                dev_dbg(dev, "refusing GpioInt() entry when doing GPIOD_OUT_* lookup\n");
                return ERR_PTR(-ENOENT);
        }
@@ -658,6 +690,10 @@ struct gpio_desc *acpi_find_gpio(struct device *dev,
        if (info.polarity == GPIO_ACTIVE_LOW)
                *lookupflags |= GPIO_ACTIVE_LOW;
 
+       err = acpi_gpio_update_gpiod_flags(dflags, info.flags);
+       if (err)
+               dev_dbg(dev, "Override GPIO initialization flags\n");
+
        return desc;
 }
 
@@ -711,12 +747,16 @@ struct gpio_desc *acpi_node_get_gpiod(struct fwnode_handle *fwnode,
  * used to translate from the GPIO offset in the resource to the Linux IRQ
  * number.
  *
+ * The function is idempotent, though each time it runs it will configure GPIO
+ * pin direction according to the flags in GpioInt resource.
+ *
  * Return: Linux IRQ number (>%0) on success, negative errno on failure.
  */
 int acpi_dev_gpio_irq_get(struct acpi_device *adev, int index)
 {
        int idx, i;
        unsigned int irq_flags;
+       int ret;
 
        for (i = 0, idx = 0; idx <= index; i++) {
                struct acpi_gpio_info info;
@@ -729,6 +769,7 @@ int acpi_dev_gpio_irq_get(struct acpi_device *adev, int index)
                        return PTR_ERR(desc);
 
                if (info.gpioint && idx++ == index) {
+                       char label[32];
                        int irq;
 
                        if (IS_ERR(desc))
@@ -738,6 +779,11 @@ int acpi_dev_gpio_irq_get(struct acpi_device *adev, int index)
                        if (irq < 0)
                                return irq;
 
+                       snprintf(label, sizeof(label), "GpioInt() %d", index);
+                       ret = gpiod_configure_flags(desc, label, 0, info.flags);
+                       if (ret < 0)
+                               return ret;
+
                        irq_flags = acpi_dev_get_irq_type(info.triggering,
                                                          info.polarity);
 
index 28629b2..300b1ff 100644 (file)
@@ -3286,7 +3286,7 @@ struct gpio_desc *__must_check gpiod_get_index(struct device *dev,
                        desc = of_find_gpio(dev, con_id, idx, &lookupflags);
                } else if (ACPI_COMPANION(dev)) {
                        dev_dbg(dev, "using ACPI for GPIO lookup\n");
-                       desc = acpi_find_gpio(dev, con_id, idx, flags, &lookupflags);
+                       desc = acpi_find_gpio(dev, con_id, idx, &flags, &lookupflags);
                }
        }
 
@@ -3367,8 +3367,12 @@ struct gpio_desc *fwnode_get_named_gpiod(struct fwnode_handle *fwnode,
                struct acpi_gpio_info info;
 
                desc = acpi_node_get_gpiod(fwnode, propname, index, &info);
-               if (!IS_ERR(desc))
+               if (!IS_ERR(desc)) {
                        active_low = info.polarity == GPIO_ACTIVE_LOW;
+                       ret = acpi_gpio_update_gpiod_flags(&dflags, info.flags);
+                       if (ret)
+                               pr_debug("Override GPIO initialization flags\n");
+               }
        }
 
        if (IS_ERR(desc))
index 2aec8be..a8be286 100644 (file)
@@ -75,11 +75,13 @@ struct gpio_device {
 
 /**
  * struct acpi_gpio_info - ACPI GPIO specific information
+ * @flags: GPIO initialization flags
  * @gpioint: if %true this GPIO is of type GpioInt otherwise type is GpioIo
  * @polarity: interrupt polarity as provided by ACPI
  * @triggering: triggering type as provided by ACPI
  */
 struct acpi_gpio_info {
+       enum gpiod_flags flags;
        bool gpioint;
        int polarity;
        int triggering;
@@ -121,10 +123,13 @@ void acpi_gpiochip_remove(struct gpio_chip *chip);
 void acpi_gpiochip_request_interrupts(struct gpio_chip *chip);
 void acpi_gpiochip_free_interrupts(struct gpio_chip *chip);
 
+int acpi_gpio_update_gpiod_flags(enum gpiod_flags *flags,
+                                enum gpiod_flags update);
+
 struct gpio_desc *acpi_find_gpio(struct device *dev,
                                 const char *con_id,
                                 unsigned int idx,
-                                enum gpiod_flags flags,
+                                enum gpiod_flags *dflags,
                                 enum gpio_lookup_flags *lookupflags);
 struct gpio_desc *acpi_node_get_gpiod(struct fwnode_handle *fwnode,
                                      const char *propname, int index,
@@ -143,9 +148,15 @@ acpi_gpiochip_request_interrupts(struct gpio_chip *chip) { }
 static inline void
 acpi_gpiochip_free_interrupts(struct gpio_chip *chip) { }
 
+static inline int
+acpi_gpio_update_gpiod_flags(enum gpiod_flags *flags, enum gpiod_flags update)
+{
+       return 0;
+}
+
 static inline struct gpio_desc *
 acpi_find_gpio(struct device *dev, const char *con_id,
-              unsigned int idx, enum gpiod_flags flags,
+              unsigned int idx, enum gpiod_flags *dflags,
               enum gpio_lookup_flags *lookupflags)
 {
        return ERR_PTR(-ENOENT);