[PATCH 2/2] gpio: Support cascaded GPIO chip lookup for OF

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 




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 <pantelis.antoniou@xxxxxxxxxxxx>
---
 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

--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html



[Index of Archives]     [Device Tree Compilter]     [Device Tree Spec]     [Linux Driver Backports]     [Video for Linux]     [Linux USB Devel]     [Linux PCI Devel]     [Linux Audio Users]     [Linux Kernel]     [Linux SCSI]     [XFree86]     [Yosemite Backpacking]
  Powered by Linux