Fix gpio specifiers decoding (respect #gpio-cells)

Luiz Otavio O Souza loos.br at gmail.com
Wed Apr 23 14:16:10 UTC 2014


I've been working to reduce the changes we need to decode a linux DTS
file (from the GPIO point of view).

The attached patch now respects the GPIO controller #gpio-cells (which
previously was only able to decode gpio specifiers if #gpio-cells =
3). This now make possible read linux like gpio specifiers
(#gpio-cells = 2) without any change to code.

Following linux, it also adds a new ofw_bus method that allows the
GPIO controller to implement its own mapping to gpio specifier,
allowing the specifier to be GPIO controller specific (rk30xx_gpio.c
comes to my mind...).

This patch also passes the gpio specifier flag as an ivar variable
which is visible to child that can now act upon.

Thoughts ?

Luiz
-------------- next part --------------
Index: sys/dev/gpio/gpiobusvar.h
===================================================================
--- sys/dev/gpio/gpiobusvar.h	(revision 264550)
+++ sys/dev/gpio/gpiobusvar.h	(working copy)
@@ -60,10 +60,10 @@
 	int		*sc_pins_mapped; /* mark mapped pins */
 };
 
-
 struct gpiobus_ivar
 {
 	uint32_t	npins;	/* pins total */
+	uint32_t	*flags;	/* pins flags */
 	uint32_t	*pins;	/* pins map */
 };
 
Index: sys/dev/gpio/ofw_gpiobus.c
===================================================================
--- sys/dev/gpio/ofw_gpiobus.c	(revision 264550)
+++ sys/dev/gpio/ofw_gpiobus.c	(working copy)
@@ -83,10 +83,37 @@
 }
 
 static int
+ofw_gpiobus_alloc_ivars(struct gpiobus_ivar *dinfo)
+{
+
+	/* Allocate pins and flags memory. */
+	dinfo->pins = malloc(sizeof(uint32_t) * dinfo->npins, M_DEVBUF,
+	    M_NOWAIT | M_ZERO);
+	if (dinfo->pins == NULL)
+		return (ENOMEM);
+	dinfo->flags = malloc(sizeof(uint32_t) * dinfo->npins, M_DEVBUF,
+	    M_NOWAIT | M_ZERO);
+	if (dinfo->flags == NULL) {
+		free(dinfo->pins, M_DEVBUF);
+		return (ENOMEM);
+	}
+
+	return (0);
+}
+
+static void
+ofw_gpiobus_free_ivars(struct gpiobus_ivar *dinfo)
+{
+
+	free(dinfo->flags, M_DEVBUF);
+	free(dinfo->pins, M_DEVBUF);
+}
+
+static int
 ofw_gpiobus_parse_gpios(struct gpiobus_softc *sc, struct gpiobus_ivar *dinfo,
 	phandle_t child)
 {
-	int i, len;
+	int cells, i, j, len;
 	pcell_t *gpios;
 	phandle_t gpio;
 
@@ -102,44 +129,81 @@
 	}
 
 	/*
-	 * Each 'gpios' entry must contain 4 pcells.
-	 * The first one is the GPIO controller phandler.
-	 * Then the last three are the GPIO pin, the GPIO pin direction and
-	 * the GPIO pin flags.
+	 * The gpio-specifier is controller independent, but the first pcell
+	 * has the reference to the GPIO controller phandler.
+	 * One the first pass we count the number of encoded gpio-specifiers.
 	 */
-	if ((len / sizeof(pcell_t)) % 4) {
+	i = 0;
+	len /= sizeof(pcell_t);
+	while (i < len) {
+		/* Allow NULL specifiers. */
+		if (gpios[i] == 0) {
+			dinfo->npins++;
+			i++;
+			continue;
+		}
+		gpio = OF_xref_phandle(gpios[i]);
+		/* Verify if we're attaching to the correct GPIO controller. */
+		if (!OF_hasprop(gpio, "gpio-controller") ||
+		    gpio != ofw_bus_get_node(sc->sc_dev)) {
+			free(gpios, M_DEVBUF);
+			return (EINVAL);
+		}
+		/* Read gpio-cells property for this GPIO controller. */
+		if (OF_getencprop(gpio, "#gpio-cells", &cells,
+		    sizeof(cells)) < 0) {
+			free(gpios, M_DEVBUF);
+			return (EINVAL);
+		}
+		dinfo->npins++;
+		i += cells + 1;
+	}
+
+	if (dinfo->npins == 0) {
 		free(gpios, M_DEVBUF);
 		return (EINVAL);
 	}
-	dinfo->npins = len / (sizeof(pcell_t) * 4);
-	dinfo->pins = malloc(sizeof(uint32_t) * dinfo->npins, M_DEVBUF,
-	    M_NOWAIT | M_ZERO);
-	if (dinfo->pins == NULL) {
+
+	/* Allocate the child resources. */
+	if (ofw_gpiobus_alloc_ivars(dinfo) != 0) {
 		free(gpios, M_DEVBUF);
 		return (ENOMEM);
 	}
 
-	for (i = 0; i < dinfo->npins; i++) {
+	/* Decode the gpio specifier on the second pass. */
+	i = 0;
+	j = 0;
+	while (i < len) {
+		/* Allow NULL specifiers. */
+		if (gpios[i] == 0) {
+			i++;
+			j++;
+			continue;
+		}
 
-		/* Verify if we're attaching to the correct gpio controller. */
-		gpio = OF_xref_phandle(gpios[i * 4 + 0]);
-		if (!OF_hasprop(gpio, "gpio-controller") ||
-		    gpio != ofw_bus_get_node(sc->sc_dev)) {
-			free(dinfo->pins, M_DEVBUF);
+		gpio = OF_xref_phandle(gpios[i]);
+		/* Read gpio-cells property for this GPIO controller. */
+		if (OF_getencprop(gpio, "#gpio-cells", &cells,
+		    sizeof(cells)) < 0) {
+			ofw_gpiobus_free_ivars(dinfo);
 			free(gpios, M_DEVBUF);
 			return (EINVAL);
 		}
 
-		/* Get the GPIO pin number. */
-		dinfo->pins[i] = gpios[i * 4 + 1];
-		/* gpios[i * 4 + 2] - GPIO pin direction */
-		/* gpios[i * 4 + 3] - GPIO pin flags */
+		/* Get the GPIO pin number and flags. */
+		if (ofw_bus_map_gpios(sc->sc_dev, child, gpio, cells,
+		    &gpios[i + 1], &dinfo->pins[j], &dinfo->flags[j]) != 0) {
+			ofw_gpiobus_free_ivars(dinfo);
+			free(gpios, M_DEVBUF);
+			return (EINVAL);
+		}
 
-		if (dinfo->pins[i] > sc->sc_npins) {
+		/* Consistency check. */
+		if (dinfo->pins[j] > sc->sc_npins) {
 			device_printf(sc->sc_busdev,
 			    "invalid pin %d, max: %d\n",
-			    dinfo->pins[i], sc->sc_npins - 1);
-			free(dinfo->pins, M_DEVBUF);
+			    dinfo->pins[j], sc->sc_npins - 1);
+			ofw_gpiobus_free_ivars(dinfo);
 			free(gpios, M_DEVBUF);
 			return (EINVAL);
 		}
@@ -147,15 +211,18 @@
 		/*
 		 * Mark pin as mapped and give warning if it's already mapped.
 		 */
-		if (sc->sc_pins_mapped[dinfo->pins[i]]) {
+		if (sc->sc_pins_mapped[dinfo->pins[j]]) {
 			device_printf(sc->sc_busdev,
 			    "warning: pin %d is already mapped\n",
-			    dinfo->pins[i]);
-			free(dinfo->pins, M_DEVBUF);
+			    dinfo->pins[j]);
+			ofw_gpiobus_free_ivars(dinfo);
 			free(gpios, M_DEVBUF);
 			return (EINVAL);
 		}
-		sc->sc_pins_mapped[dinfo->pins[i]] = 1;
+		sc->sc_pins_mapped[dinfo->pins[j]] = 1;
+
+		i += cells + 1;
+		j++;
 	}
 
 	free(gpios, M_DEVBUF);
Index: sys/dev/ofw/ofw_bus.h
===================================================================
--- sys/dev/ofw/ofw_bus.h	(revision 264550)
+++ sys/dev/ofw/ofw_bus.h	(working copy)
@@ -76,4 +76,12 @@
 	return (OFW_BUS_MAP_INTR(dev, dev, iparent, icells, intr));
 }
 
+static __inline int
+ofw_bus_map_gpios(device_t bus, phandle_t dev, phandle_t gparent, int gcells,
+    pcell_t *gpios, uint32_t *pin, uint32_t *flags)
+{
+	return (OFW_BUS_MAP_GPIOS(bus, dev, gparent, gcells, gpios, pin,
+	    flags));
+}
+
 #endif /* !_DEV_OFW_OFW_BUS_H_ */
Index: sys/dev/ofw/ofw_bus_if.m
===================================================================
--- sys/dev/ofw/ofw_bus_if.m	(revision 264550)
+++ sys/dev/ofw/ofw_bus_if.m	(working copy)
@@ -58,6 +58,7 @@
 	static ofw_bus_get_node_t ofw_bus_default_get_node;
 	static ofw_bus_get_type_t ofw_bus_default_get_type;
 	static ofw_bus_map_intr_t ofw_bus_default_map_intr;
+	static ofw_bus_map_gpios_t ofw_bus_default_map_gpios;
 
 	static const struct ofw_bus_devinfo *
 	ofw_bus_default_get_devinfo(device_t bus, device_t dev)
@@ -113,6 +114,24 @@
 		/* If that fails, then assume a one-domain system */
 		return (interrupt[0]);
 	}
+
+	int
+	ofw_bus_default_map_gpios(device_t bus, phandle_t dev,
+	    phandle_t gparent, int gcells, pcell_t *gpios, uint32_t *pin,
+	    uint32_t *flags)
+	{
+		/* Propagate up the bus hierarchy until someone handles it. */	
+		if (device_get_parent(bus) != NULL)
+			return OFW_BUS_MAP_GPIOS(device_get_parent(bus), dev,
+			    gparent, gcells, gpios, pin, flags);
+
+		/* If that fails, then assume the FreeBSD defaults. */
+		*pin = gpios[0];
+		if (gcells == 2 || gcells == 3)
+			*flags = gpios[gcells - 1];
+
+		return (0);
+	}
 };
 
 # Get the ofw_bus_devinfo struct for the device dev on the bus. Used for bus
@@ -170,4 +189,13 @@
 	pcell_t *interrupt;
 } DEFAULT ofw_bus_default_map_intr;
 
-
+# Map the GPIO controller specific gpio-specifier to GPIO pin and flags.
+METHOD int map_gpios {
+	device_t bus;
+	phandle_t dev;
+	phandle_t gparent;
+	int gcells;
+	pcell_t *gpios;
+	uint32_t *pin;
+	uint32_t *flags;
+} DEFAULT ofw_bus_default_map_gpios;


More information about the freebsd-embedded mailing list