[PATCH 1/4] Make non-linear GPIO ranges accesible from gpiolib

From: Christian Ruppert
Date: Tue Jun 18 2013 - 05:30:20 EST


This patch adds the infrastructure required to register non-linear gpio
ranges through gpiolib and the standard GPIO device tree bindings.

Signed-off-by: Christian Ruppert <christian.ruppert@xxxxxxxxxx>
---
Documentation/devicetree/bindings/gpio/gpio.txt | 44 +++++++++++++++++++++
drivers/gpio/gpiolib-of.c | 31 +++++++++++----
drivers/gpio/gpiolib.c | 47 +++++++++++++++++++++++
drivers/pinctrl/core.c | 23 +++++++++++
include/asm-generic/gpio.h | 10 +++++
include/linux/gpio.h | 9 ++++
include/linux/pinctrl/pinctrl.h | 3 +
7 files changed, 159 insertions(+), 8 deletions(-)

diff --git a/Documentation/devicetree/bindings/gpio/gpio.txt b/Documentation/devicetree/bindings/gpio/gpio.txt
index d933af3..c8afbea 100644
--- a/Documentation/devicetree/bindings/gpio/gpio.txt
+++ b/Documentation/devicetree/bindings/gpio/gpio.txt
@@ -112,3 +112,47 @@ where,

The pinctrl node must have "#gpio-range-cells" property to show number of
arguments to pass with phandle from gpio controllers node.
+
+In addition, named groups of pins can be mapped to pin groups of a given
+pin controller:
+
+ gpio_pio_g: gpio-controller@1480 {
+ #gpio-cells = <2>;
+ compatible = "fsl,qe-pario-bank-e", "fsl,qe-pario-bank";
+ reg = <0x1480 0x18>;
+ gpio-controller;
+ gpio-ranges = <&pinctrl1 0 0 0>, <&pinctrl2 3 0 0>;
+ gpio-ranges-group-names = "foo", "bar";
+ };
+
+where,
+ &pinctrl1 and &pinctrl2 is the phandle to the pinctrl DT node.
+
+ The following value specifies the base GPIO offset of the pin range with
+ respect to the GPIO controller's base. The remaining two values must be
+ 0 to indicate that a named pin group should be used for the respective
+ range. The number of pins in the range is the number of pins in the pin
+ group.
+
+ gpio-ranges-group-names defines the name of each pingroup of the
+ respective pin controller.
+
+The pinctrl node must have a "#gpio-#gpio-range-cells" property set to three
+to define the number of arguments to pass with the phandle.
+
+Both methods can be combined in the same GPIO controller, e.g.
+
+ gpio_pio_i: gpio-controller@14B0 {
+ #gpio-cells = <2>;
+ compatible = "fsl,qe-pario-bank-e", "fsl,qe-pario-bank";
+ reg = <0x1480 0x18>;
+ gpio-controller;
+ gpio-ranges = <&pinctrl1 0 20 10>,
+ <&pinctrl2 10 0 0>,
+ <&pinctrl1 15 0 10>,
+ <&pinctrl2 25 0 0>;
+ gpio-ranges-group-names = "",
+ "foo",
+ "",
+ "bar";
+ };
diff --git a/drivers/gpio/gpiolib-of.c b/drivers/gpio/gpiolib-of.c
index 665f953..e29cc71 100644
--- a/drivers/gpio/gpiolib-of.c
+++ b/drivers/gpio/gpiolib-of.c
@@ -189,6 +189,7 @@ static void of_gpiochip_add_pin_range(struct gpio_chip *chip)
struct of_phandle_args pinspec;
struct pinctrl_dev *pctldev;
int index = 0, ret;
+ const char *name;

if (!np)
return;
@@ -203,14 +204,28 @@ static void of_gpiochip_add_pin_range(struct gpio_chip *chip)
if (!pctldev)
break;

- ret = gpiochip_add_pin_range(chip,
- pinctrl_dev_get_devname(pctldev),
- pinspec.args[0],
- pinspec.args[1],
- pinspec.args[2]);
-
- if (ret)
- break;
+ if (pinspec.args[2]) {
+ /* npins != 0: linear range */
+ ret = gpiochip_add_pin_range(chip,
+ pinctrl_dev_get_devname(pctldev),
+ pinspec.args[0],
+ pinspec.args[1],
+ pinspec.args[2]);
+ if (ret)
+ break;
+ } else {
+ /* npins == 0: pin group based range */
+ ret = of_property_read_string_index(np,
+ "gpio-ranges-group-names",
+ index, &name);
+ if (ret)
+ break;
+
+ ret = gpiochip_add_pingroup_range(chip, pctldev,
+ pinspec.args[0], name);
+ if (ret)
+ break;
+ }
}
}

diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c
index c2534d6..b2dc810 100644
--- a/drivers/gpio/gpiolib.c
+++ b/drivers/gpio/gpiolib.c
@@ -1318,6 +1318,53 @@ EXPORT_SYMBOL_GPL(gpiochip_find);
#ifdef CONFIG_PINCTRL

/**
+ * gpiochip_add_pingroup_range() - add a range for GPIO <-> pin mapping
+ * @chip: the gpiochip to add the range for
+ * @pinctrl: the dev_name() of the pin controller to map to
+ * @gpio_offset: the start offset in the current gpio_chip number space
+ * @pin_group: name of the pin group inside the pin controller
+ */
+int gpiochip_add_pingroup_range(struct gpio_chip *chip,
+ struct pinctrl_dev *pctldev,
+ unsigned int gpio_offset, const char *pin_group)
+{
+ struct gpio_pin_range *pin_range;
+ int ret;
+
+ pin_range = kzalloc(sizeof(*pin_range), GFP_KERNEL);
+ if (!pin_range) {
+ pr_err("%s: GPIO chip: failed to allocate pin ranges\n",
+ chip->label);
+ return -ENOMEM;
+ }
+
+ /* Use local offset as range ID */
+ pin_range->range.id = gpio_offset;
+ pin_range->range.gc = chip;
+ pin_range->range.name = chip->label;
+ pin_range->range.base = chip->base + gpio_offset;
+ pin_range->pctldev = pctldev;
+
+ ret = pinctrl_add_gpio_pingrp_range(pctldev, &pin_range->range,
+ pin_group);
+ if (ret < 0) {
+ pr_err("%s: GPIO chip: could not create pin range %s\n",
+ chip->label, pin_group);
+ return ret;
+ }
+
+ pr_debug("GPIO chip %s: created GPIO range %d->%d ==> %s PINGRP %s\n",
+ chip->label, gpio_offset,
+ gpio_offset + pin_range->range.npins - 1,
+ pinctrl_dev_get_devname(pctldev), pin_group);
+
+ list_add_tail(&pin_range->node, &chip->pin_ranges);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(gpiochip_add_pingroup_range);
+
+/**
* gpiochip_add_pin_range() - add a range for GPIO <-> pin mapping
* @chip: the gpiochip to add the range for
* @pinctrl_name: the dev_name() of the pin controller to map to
diff --git a/drivers/pinctrl/core.c b/drivers/pinctrl/core.c
index 25bb17e..3730c4f 100644
--- a/drivers/pinctrl/core.c
+++ b/drivers/pinctrl/core.c
@@ -452,6 +452,29 @@ struct pinctrl_dev *pinctrl_find_and_add_gpio_range(const char *devname,
}
EXPORT_SYMBOL_GPL(pinctrl_find_and_add_gpio_range);

+int pinctrl_add_gpio_pingrp_range(struct pinctrl_dev *pctldev,
+ struct pinctrl_gpio_range *range,
+ char *pin_group)
+{
+ const struct pinctrl_ops *pctlops = pctldev->desc->pctlops;
+ int group_selector, ret;
+
+ group_selector = pinctrl_get_group_selector(pctldev, pin_group);
+ if (group_selector < 0)
+ return group_selector;
+
+ ret = pctlops->get_group_pins(pctldev, group_selector,
+ &range->pins,
+ &range->npins);
+ if (ret < 0)
+ return ret;
+
+ pinctrl_add_gpio_range(pctldev, range);
+ return 0;
+
+}
+EXPORT_SYMBOL_GPL(pinctrl_add_gpio_pingrp_range);
+
/**
* pinctrl_find_gpio_range_from_pin() - locate the GPIO range for a pin
* @pctldev: the pin controller device to look in
diff --git a/include/asm-generic/gpio.h b/include/asm-generic/gpio.h
index bde6469..523f405 100644
--- a/include/asm-generic/gpio.h
+++ b/include/asm-generic/gpio.h
@@ -228,6 +228,9 @@ struct gpio_pin_range {
int gpiochip_add_pin_range(struct gpio_chip *chip, const char *pinctl_name,
unsigned int gpio_offset, unsigned int pin_offset,
unsigned int npins);
+int gpiochip_add_pingroup_range(struct gpio_chip *chip,
+ struct pinctrl_dev *pctldev,
+ unsigned int gpio_offset, const char *pin_group);
void gpiochip_remove_pin_ranges(struct gpio_chip *chip);

#else
@@ -239,6 +242,13 @@ gpiochip_add_pin_range(struct gpio_chip *chip, const char *pinctl_name,
{
return 0;
}
+static inline int
+gpiochip_add_pingroup_range(struct gpio_chip *chip,
+ struct pinctrl_dev *pctldev,
+ unsigned int gpio_offset, const char *pin_group)
+{
+ return 0;
+}

static inline void
gpiochip_remove_pin_ranges(struct gpio_chip *chip)
diff --git a/include/linux/gpio.h b/include/linux/gpio.h
index 552e3f4..234b32f 100644
--- a/include/linux/gpio.h
+++ b/include/linux/gpio.h
@@ -220,6 +220,15 @@ gpiochip_add_pin_range(struct gpio_chip *chip, const char *pinctl_name,
return -EINVAL;
}

+static inline int
+gpiochip_add_pingroup_range(struct gpio_chip *chip,
+ struct pinctrl_dev *pctldev,
+ unsigned int gpio_offset, const char *pin_group)
+{
+ WARN_ON(1);
+ return -EINVAL;
+}
+
static inline void
gpiochip_remove_pin_ranges(struct gpio_chip *chip)
{
diff --git a/include/linux/pinctrl/pinctrl.h b/include/linux/pinctrl/pinctrl.h
index 5979147..47ab2fb 100644
--- a/include/linux/pinctrl/pinctrl.h
+++ b/include/linux/pinctrl/pinctrl.h
@@ -141,6 +141,9 @@ extern void pinctrl_remove_gpio_range(struct pinctrl_dev *pctldev,

extern struct pinctrl_dev *pinctrl_find_and_add_gpio_range(const char *devname,
struct pinctrl_gpio_range *range);
+extern int pinctrl_add_gpio_pingrp_range(struct pinctrl_dev *pctldev,
+ struct pinctrl_gpio_range *range,
+ char *pin_group);
extern struct pinctrl_gpio_range *
pinctrl_find_gpio_range_from_pin(struct pinctrl_dev *pctldev,
unsigned int pin);
--
1.7.1

--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/