pinctrl: add pin list based GPIO ranges
authorChristian Ruppert <christian.ruppert@abilis.com>
Thu, 13 Jun 2013 12:55:31 +0000 (14:55 +0200)
committerLinus Walleij <linus.walleij@linaro.org>
Mon, 17 Jun 2013 16:18:28 +0000 (18:18 +0200)
Traditionally, GPIO ranges are based on consecutive ranges of both GPIO
and pin numbers. This patch allows for GPIO ranges with arbitrary lists
of pin numbers.

Signed-off-by: Christian Ruppert <christian.ruppert@abilis.com>
Signed-off-by: Linus Walleij <linus.walleij@linaro.org>
drivers/pinctrl/core.c
include/linux/pinctrl/pinctrl.h

index dca9208f54633dc404956a74a1afd39fe7f09e85..33710b5e7bb5b47f134401cfaec7ca333bab85f5 100644 (file)
@@ -282,6 +282,29 @@ static int pinctrl_register_pins(struct pinctrl_dev *pctldev,
        return 0;
 }
 
+/**
+ * gpio_to_pin() - GPIO range GPIO number to pin number translation
+ * @range: GPIO range used for the translation
+ * @gpio: gpio pin to translate to a pin number
+ *
+ * Finds the pin number for a given GPIO using the specified GPIO range
+ * as a base for translation. The distinction between linear GPIO ranges
+ * and pin list based GPIO ranges is managed correctly by this function.
+ *
+ * This function assumes the gpio is part of the specified GPIO range, use
+ * only after making sure this is the case (e.g. by calling it on the
+ * result of successful pinctrl_get_device_gpio_range calls)!
+ */
+static inline int gpio_to_pin(struct pinctrl_gpio_range *range,
+                               unsigned int gpio)
+{
+       unsigned int offset = gpio - range->base;
+       if (range->pins)
+               return range->pins[offset];
+       else
+               return range->pin_base + offset;
+}
+
 /**
  * pinctrl_match_gpio_range() - check if a certain GPIO pin is in range
  * @pctldev: pin controller device to check
@@ -448,8 +471,14 @@ pinctrl_find_gpio_range_from_pin(struct pinctrl_dev *pctldev,
        /* Loop over the ranges */
        list_for_each_entry(range, &pctldev->gpio_ranges, node) {
                /* Check if we're in the valid range */
-               if (pin >= range->pin_base &&
-                   pin < range->pin_base + range->npins) {
+               if (range->pins) {
+                       int a;
+                       for (a = 0; a < range->npins; a++) {
+                               if (range->pins[a] == pin)
+                                       return range;
+                       }
+               } else if (pin >= range->pin_base &&
+                          pin < range->pin_base + range->npins) {
                        mutex_unlock(&pctldev->mutex);
                        return range;
                }
@@ -529,7 +558,7 @@ int pinctrl_request_gpio(unsigned gpio)
        }
 
        /* Convert to the pin controllers number space */
-       pin = gpio - range->base + range->pin_base;
+       pin = gpio_to_pin(range, gpio);
 
        ret = pinmux_request_gpio(pctldev, range, pin, gpio);
 
@@ -559,7 +588,7 @@ void pinctrl_free_gpio(unsigned gpio)
        mutex_lock(&pctldev->mutex);
 
        /* Convert to the pin controllers number space */
-       pin = gpio - range->base + range->pin_base;
+       pin = gpio_to_pin(range, gpio);
 
        pinmux_free_gpio(pctldev, pin, range);
 
@@ -582,7 +611,7 @@ static int pinctrl_gpio_direction(unsigned gpio, bool input)
        mutex_lock(&pctldev->mutex);
 
        /* Convert to the pin controllers number space */
-       pin = gpio - range->base + range->pin_base;
+       pin = gpio_to_pin(range, gpio);
        ret = pinmux_gpio_direction(pctldev, range, pin, input);
 
        mutex_unlock(&pctldev->mutex);
@@ -1349,11 +1378,21 @@ static int pinctrl_gpioranges_show(struct seq_file *s, void *what)
 
        /* Loop over the ranges */
        list_for_each_entry(range, &pctldev->gpio_ranges, node) {
-               seq_printf(s, "%u: %s GPIOS [%u - %u] PINS [%u - %u]\n",
-                          range->id, range->name,
-                          range->base, (range->base + range->npins - 1),
-                          range->pin_base,
-                          (range->pin_base + range->npins - 1));
+               if (range->pins) {
+                       int a;
+                       seq_printf(s, "%u: %s GPIOS [%u - %u] PINS {",
+                               range->id, range->name,
+                               range->base, (range->base + range->npins - 1));
+                       for (a = 0; a < range->npins - 1; a++)
+                               seq_printf(s, "%u, ", range->pins[a]);
+                       seq_printf(s, "%u}\n", range->pins[a]);
+               }
+               else
+                       seq_printf(s, "%u: %s GPIOS [%u - %u] PINS [%u - %u]\n",
+                               range->id, range->name,
+                               range->base, (range->base + range->npins - 1),
+                               range->pin_base,
+                               (range->pin_base + range->npins - 1));
        }
 
        mutex_unlock(&pctldev->mutex);
index 2c2a9e8d8578310e453776e6668e9b6a915ac2c2..176a6c1b4e03b9683b0d12c84490df1baa375b56 100644 (file)
@@ -49,7 +49,8 @@ struct pinctrl_pin_desc {
  * @name: a name for the chip in this range
  * @id: an ID number for the chip in this range
  * @base: base offset of the GPIO range
- * @pin_base: base pin number of the GPIO range
+ * @pin_base: base pin number of the GPIO range if pins != NULL
+ * @pins: enumeration of pins in GPIO range or NULL
  * @npins: number of pins in the GPIO range, including the base number
  * @gc: an optional pointer to a gpio_chip
  */
@@ -59,6 +60,7 @@ struct pinctrl_gpio_range {
        unsigned int id;
        unsigned int base;
        unsigned int pin_base;
+       unsigned const *pins;
        unsigned int npins;
        struct gpio_chip *gc;
 };