Merge branch 'x86-urgent-for-linus' of git://git.kernel.org/pub/scm/linux/kernel...
[firefly-linux-kernel-4.4.55.git] / drivers / gpio / gpiolib-acpi.c
index ba98bb59a58f23c20a1945d72c6849f267f0e91b..c0929d938ced866e343e0230e00fc5c9cda77c0b 100644 (file)
  */
 
 #include <linux/errno.h>
+#include <linux/gpio.h>
 #include <linux/gpio/consumer.h>
 #include <linux/gpio/driver.h>
 #include <linux/export.h>
 #include <linux/acpi.h>
 #include <linux/interrupt.h>
 #include <linux/mutex.h>
+#include <linux/pinctrl/pinctrl.h>
 
 #include "gpiolib.h"
 
@@ -55,6 +57,58 @@ static int acpi_gpiochip_find(struct gpio_chip *gc, void *data)
        return ACPI_HANDLE(gc->dev) == data;
 }
 
+#ifdef CONFIG_PINCTRL
+/**
+ * acpi_gpiochip_pin_to_gpio_offset() - translates ACPI GPIO to Linux GPIO
+ * @chip: GPIO chip
+ * @pin: ACPI GPIO pin number from GpioIo/GpioInt resource
+ *
+ * Function takes ACPI GpioIo/GpioInt pin number as a parameter and
+ * translates it to a corresponding offset suitable to be passed to a
+ * GPIO controller driver.
+ *
+ * Typically the returned offset is same as @pin, but if the GPIO
+ * controller uses pin controller and the mapping is not contigous the
+ * offset might be different.
+ */
+static int acpi_gpiochip_pin_to_gpio_offset(struct gpio_chip *chip, int pin)
+{
+       struct gpio_pin_range *pin_range;
+
+       /* If there are no ranges in this chip, use 1:1 mapping */
+       if (list_empty(&chip->pin_ranges))
+               return pin;
+
+       list_for_each_entry(pin_range, &chip->pin_ranges, node) {
+               const struct pinctrl_gpio_range *range = &pin_range->range;
+               int i;
+
+               if (range->pins) {
+                       for (i = 0; i < range->npins; i++) {
+                               if (range->pins[i] == pin)
+                                       return range->base + i - chip->base;
+                       }
+               } else {
+                       if (pin >= range->pin_base &&
+                           pin < range->pin_base + range->npins) {
+                               unsigned gpio_base;
+
+                               gpio_base = range->base - chip->base;
+                               return gpio_base + pin - range->pin_base;
+                       }
+               }
+       }
+
+       return -EINVAL;
+}
+#else
+static inline int acpi_gpiochip_pin_to_gpio_offset(struct gpio_chip *chip,
+                                                  int pin)
+{
+       return pin;
+}
+#endif
+
 /**
  * acpi_get_gpiod() - Translate ACPI GPIO pin to GPIO descriptor usable with GPIO API
  * @path:      ACPI GPIO controller full path name, (e.g. "\\_SB.GPO1")
@@ -69,6 +123,7 @@ static struct gpio_desc *acpi_get_gpiod(char *path, int pin)
        struct gpio_chip *chip;
        acpi_handle handle;
        acpi_status status;
+       int offset;
 
        status = acpi_get_handle(NULL, path, &handle);
        if (ACPI_FAILURE(status))
@@ -78,10 +133,11 @@ static struct gpio_desc *acpi_get_gpiod(char *path, int pin)
        if (!chip)
                return ERR_PTR(-ENODEV);
 
-       if (pin < 0 || pin > chip->ngpio)
-               return ERR_PTR(-EINVAL);
+       offset = acpi_gpiochip_pin_to_gpio_offset(chip, pin);
+       if (offset < 0)
+               return ERR_PTR(offset);
 
-       return gpiochip_get_desc(chip, pin);
+       return gpiochip_get_desc(chip, offset);
 }
 
 static irqreturn_t acpi_gpio_irq_handler(int irq, void *data)
@@ -153,7 +209,7 @@ static acpi_status acpi_gpiochip_request_interrupt(struct acpi_resource *ares,
 
        gpiod_direction_input(desc);
 
-       ret = gpio_lock_as_irq(chip, pin);
+       ret = gpiochip_lock_as_irq(chip, pin);
        if (ret) {
                dev_err(chip->dev, "Failed to lock GPIO as interrupt\n");
                goto fail_free_desc;
@@ -209,7 +265,7 @@ static acpi_status acpi_gpiochip_request_interrupt(struct acpi_resource *ares,
 fail_free_event:
        kfree(event);
 fail_unlock_irq:
-       gpio_unlock_as_irq(chip, pin);
+       gpiochip_unlock_as_irq(chip, pin);
 fail_free_desc:
        gpiochip_free_own_desc(desc);
 
@@ -280,7 +336,7 @@ void acpi_gpiochip_free_interrupts(struct gpio_chip *chip)
                desc = event->desc;
                if (WARN_ON(IS_ERR(desc)))
                        continue;
-               gpio_unlock_as_irq(chip, event->pin);
+               gpiochip_unlock_as_irq(chip, event->pin);
                gpiochip_free_own_desc(desc);
                list_del(&event->node);
                kfree(event);