Skip site navigation (1)Skip section navigation (2)
Date:      Sun, 27 Apr 2014 12:11:01 +0000 (UTC)
From:      Luiz Otavio O Souza <loos@FreeBSD.org>
To:        src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-head@freebsd.org
Subject:   svn commit: r265012 - in head/sys/dev: gpio ofw
Message-ID:  <201404271211.s3RCB1wI018791@svn.freebsd.org>

next in thread | raw e-mail | index | archive | help
Author: loos
Date: Sun Apr 27 12:11:00 2014
New Revision: 265012
URL: http://svnweb.freebsd.org/changeset/base/265012

Log:
  Fix the gpio-specifier decoding by respecting the GPIO controller's
  #gpio-cells property.
  
  Add a new ofw_bus method (OFW_BUS_MAP_GPIOS()) that allows the GPIO
  controller to implement its own mapping to deal with gpio-specifiers,
  allowing the decoding of gpio-specifiers to be controller specific.
  
  The default ofw_bus_map_gpios() decodes the linux standard (#gpio-cells =
  <2>) and the FreeBSD standard (#gpio-cells = <3>).
  
  It pass the gpio-specifier flag field to the children as an ivar variable so
  they can act upon.

Modified:
  head/sys/dev/gpio/gpiobusvar.h
  head/sys/dev/gpio/ofw_gpiobus.c
  head/sys/dev/ofw/ofw_bus.h
  head/sys/dev/ofw/ofw_bus_if.m

Modified: head/sys/dev/gpio/gpiobusvar.h
==============================================================================
--- head/sys/dev/gpio/gpiobusvar.h	Sun Apr 27 10:43:48 2014	(r265011)
+++ head/sys/dev/gpio/gpiobusvar.h	Sun Apr 27 12:11:00 2014	(r265012)
@@ -60,17 +60,22 @@ struct gpiobus_softc
 	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 */
 };
 
-void gpiobus_print_pins(struct gpiobus_ivar *);
 #ifdef FDT
+struct ofw_gpiobus_devinfo {
+	struct gpiobus_ivar	opd_dinfo;
+	struct ofw_bus_devinfo	opd_obdinfo;
+};
+
 device_t ofw_gpiobus_add_fdt_child(device_t, phandle_t);
 #endif
+void gpiobus_print_pins(struct gpiobus_ivar *);
 
 extern driver_t gpiobus_driver;
 

Modified: head/sys/dev/gpio/ofw_gpiobus.c
==============================================================================
--- head/sys/dev/gpio/ofw_gpiobus.c	Sun Apr 27 10:43:48 2014	(r265011)
+++ head/sys/dev/gpio/ofw_gpiobus.c	Sun Apr 27 12:11:00 2014	(r265012)
@@ -47,11 +47,6 @@ __FBSDID("$FreeBSD$");
 #include "gpio_if.h"
 #include "gpiobus_if.h"
 
-struct ofw_gpiobus_devinfo {
-	struct gpiobus_ivar	opd_dinfo;
-	struct ofw_bus_devinfo	opd_obdinfo;
-};
-
 static int ofw_gpiobus_parse_gpios(struct gpiobus_softc *,
     struct gpiobus_ivar *, phandle_t);
 static struct ofw_gpiobus_devinfo *ofw_gpiobus_setup_devinfo(device_t,
@@ -83,10 +78,37 @@ ofw_gpiobus_add_fdt_child(device_t bus, 
 }
 
 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 +124,81 @@ ofw_gpiobus_parse_gpios(struct gpiobus_s
 	}
 
 	/*
-	 * 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 +206,18 @@ ofw_gpiobus_parse_gpios(struct gpiobus_s
 		/*
 		 * 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);

Modified: head/sys/dev/ofw/ofw_bus.h
==============================================================================
--- head/sys/dev/ofw/ofw_bus.h	Sun Apr 27 10:43:48 2014	(r265011)
+++ head/sys/dev/ofw/ofw_bus.h	Sun Apr 27 12:11:00 2014	(r265012)
@@ -76,4 +76,12 @@ ofw_bus_map_intr(device_t dev, phandle_t
 	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_ */

Modified: head/sys/dev/ofw/ofw_bus_if.m
==============================================================================
--- head/sys/dev/ofw/ofw_bus_if.m	Sun Apr 27 10:43:48 2014	(r265011)
+++ head/sys/dev/ofw/ofw_bus_if.m	Sun Apr 27 12:11:00 2014	(r265012)
@@ -58,6 +58,7 @@ CODE {
 	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 @@ CODE {
 		/* 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 @@ METHOD int map_intr {
 	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;



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?201404271211.s3RCB1wI018791>