In certain cases it makes sense to create cascaded GPIO which
are not real GPIOs, merely point to the real backend GPIO chip.
In order to support this, cascaded of_xlate lookup need to be
performed.
For example let's take a connector that we want to abstract
having two GPIO pins from different GPIO controllers, connector
pin #0 connected to gpioA controller with offset 10 and gpioB
with 4.
In pseudo DT form this is analogous to:
gpioA: gpioa@80000 {
compatible = "foocorp,gpio";
...
};
gpioB: gpiob@80800 {
compatible = "foocorp,gpio";
....
};
gpioC: controller_gpio {
compatible = "cascaded-gpio";
gpios = <&gpioA 10>, <&gpioB 5>;
};
For example a user of gpioC (let's take a led driver) will have a
reference to gpioC like this:
leds {
compatible = "gpio-leds";
led@0 {
gpios = <&gpioC 0 GPIO_ACTIVE_HIGH>;
...
};
led@1 {
gpios = <&gpioC 1 GPIO_ACTIVE_HIGH>;
..
};
};
We want the matches for gpioC to instead refer to gpioA & gpioB.
This is accomplished by a new method of_gpiochip_find() which
is an extension of the standard gpiochip_find() method.
A cascaded GPIO controller can modify the gpiospec node pointer
and arg[0] to point to the next GPIO in sequence and return -EAGAIN.
of_gpiochip_find() will restart the match using the new data
until either the final real GPIO is found or a maximum iteration
limit is reached.
In our example the cascaded-gpio driver can have a of_xlate method that
will point to gpioA/10 for gpioC/0 and gpioB/5 for gpioC/1.
Note that the cascade-gpio driver needs not to define any other member
methods since no actual reference will ever be made to it.
Signed-off-by: Pantelis Antoniou <[email protected]>
---
drivers/gpio/gpiolib-of.c | 16 ++++----------
drivers/gpio/gpiolib.c | 54 +++++++++++++++++++++++++++++++++++++++++++++++
drivers/gpio/gpiolib.h | 14 ++++++++++++
3 files changed, 72 insertions(+), 12 deletions(-)
diff --git a/drivers/gpio/gpiolib-of.c b/drivers/gpio/gpiolib-of.c
index 50cb787..771060f 100644
--- a/drivers/gpio/gpiolib-of.c
+++ b/drivers/gpio/gpiolib-of.c
@@ -26,18 +26,10 @@
#include "gpiolib.h"
-/* Private data structure for of_gpiochip_find_and_xlate */
-struct gg_data {
- enum of_gpio_flags *flags;
- struct of_phandle_args gpiospec;
-
- struct gpio_desc *out_gpio;
-};
-
/* Private function for resolving node pointer to gpio_chip */
-static int of_gpiochip_find_and_xlate(struct gpio_chip *gc, void *data)
+static int of_gpiochip_find_and_xlate(struct gpio_chip *gc,
+ struct gg_data *gg_data)
{
- struct gg_data *gg_data = data;
int ret;
if ((gc->of_node != gg_data->gpiospec.np) ||
@@ -95,7 +87,7 @@ struct gpio_desc *of_get_named_gpiod_flags(struct device_node
*np,
return ERR_PTR(ret);
}
- gpiochip_find(&gg_data, of_gpiochip_find_and_xlate);
+ of_gpiochip_find(&gg_data, of_gpiochip_find_and_xlate);
of_node_put(gg_data.gpiospec.np);
pr_debug("%s: parsed '%s' property of node '%s[%d]' - status (%d)\n",
@@ -166,7 +158,7 @@ static struct gpio_desc *of_parse_own_gpio(struct
device_node *np,
return ERR_PTR(ret);
}
- gpiochip_find(&gg_data, of_gpiochip_find_and_xlate);
+ of_gpiochip_find(&gg_data, of_gpiochip_find_and_xlate);
if (!gg_data.out_gpio) {
if (np->parent == np)
return ERR_PTR(-ENXIO);
diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c
index 24f60d2..e719499 100644
--- a/drivers/gpio/gpiolib.c
+++ b/drivers/gpio/gpiolib.c
@@ -884,6 +884,60 @@ struct gpio_chip *gpiochip_find(void *data,
}
EXPORT_SYMBOL_GPL(gpiochip_find);
+#ifdef CONFIG_OF_GPIO
+
+/* allow a maximum of 10 GPIO cascades (should be enough) */
+#define OF_GPIOCHIP_RETRY_COUNT_MAX 10
+
+/**
+ * of_gpiochip_find() - iterator for locating a specific gpio_chip
+ * @gg_data: data to pass to match function
+ * @callback: Callback function to check gpio_chip
+ *
+ * Similar to bus_find_device. It returns a reference to a gpio_chip as
+ * determined by a user supplied @match callback. The callback should return
+ * 0 if the device doesn't match and non-zero if it does. If the callback is
+ * non-zero, this function will return to the caller and not iterate over any
+ * more gpio_chips.
+ */
+struct gpio_chip *of_gpiochip_find(struct gg_data *gg_data,
+ int (*match)(struct gpio_chip *chip, struct gg_data *gg_data))
+{
+ struct gpio_device *gdev;
+ struct gpio_chip *chip;
+ unsigned long flags;
+ int i;
+
+ spin_lock_irqsave(&gpio_lock, flags);
+ /* always start with defer */
+ gg_data->out_gpio = ERR_PTR(-EPROBE_DEFER);
+ for (i = 0; gg_data->out_gpio == ERR_PTR(-EPROBE_DEFER) &&
+ i < OF_GPIOCHIP_RETRY_COUNT_MAX; i++) {
+
+ list_for_each_entry(gdev, &gpio_devices, list) {
+ if (match(gdev->chip, gg_data))
+ break;
+ /* again? cascaded; try agan */
+ if (gg_data->out_gpio == ERR_PTR(-EAGAIN)) {
+ /* defer is the default again */
+ gg_data->out_gpio = ERR_PTR(-EPROBE_DEFER);
+ break;
+ }
+ }
+ }
+
+ /* no match or maximum retry limit? */
+ if (&gdev->list == &gpio_devices || i >= OF_GPIOCHIP_RETRY_COUNT_MAX)
+ chip = NULL;
+ else
+ chip = gdev->chip;
+ spin_unlock_irqrestore(&gpio_lock, flags);
+
+ return chip;
+}
+EXPORT_SYMBOL_GPL(of_gpiochip_find);
+#endif
+
static int gpiochip_match_name(struct gpio_chip *chip, void *data)
{
const char *name = data;
diff --git a/drivers/gpio/gpiolib.h b/drivers/gpio/gpiolib.h
index 2d9ea5e..58cbf75 100644
--- a/drivers/gpio/gpiolib.h
+++ b/drivers/gpio/gpiolib.h
@@ -236,4 +236,18 @@ static inline void gpiochip_sysfs_unregister(struct
gpio_device *gdev)
#endif /* CONFIG_GPIO_SYSFS */
+#ifdef CONFIG_OF_GPIO
+
+/* Private data structure for of_gpiochip_find_and_xlate */
+struct gg_data {
+ enum of_gpio_flags *flags;
+ struct of_phandle_args gpiospec;
+ struct gpio_desc *out_gpio;
+};
+
+extern struct gpio_chip *of_gpiochip_find(struct gg_data *gg_data,
+ int (*match)(struct gpio_chip *chip, struct gg_data *gg_data));
+
+#endif
+
#endif /* GPIOLIB_H */
--
1.7.12