Skip site navigation (1)Skip section navigation (2)
Date:      Tue, 4 Dec 2012 17:19:06 -0200
From:      Luiz Otavio O Souza <loos.br@gmail.com>
To:        freebsd-arch@freebsd.org
Subject:   FDT Support for GPIO (gpiobus and friends)
Message-ID:  <BEB9A0F8-560B-4937-8707-653988A26D85@gmail.com>

next in thread | raw e-mail | index | archive | help

[-- Attachment #1 --]
Hi,

I've been playing with GPIO on RPi and found the missing support of FDT on gpiobus very annoying.

The following patch (gpio-fdt.diff) adds FDT support to GPIO (gpiobus, gpioc, gpioled).

The bcm2835_gpio.c.fdt.diff will add (a better) support of FDT on RPi GPIO controller and the bcm2835.dts.diff has my changes on the RPi dts for adding support of gpioled on 'ok' led (pin 16).

Comments ?

Thanks,
Luiz


[-- Attachment #2 --]
Index: dev/gpio/gpiobus.c
===================================================================
--- dev/gpio/gpiobus.c	(revision 243614)
+++ dev/gpio/gpiobus.c	(working copy)
@@ -46,6 +46,12 @@
 #include "gpio_if.h"
 #include "gpiobus_if.h"
 
+#ifdef FDT
+#include <dev/ofw/ofw_bus.h>
+#include <dev/ofw/ofw_bus_subr.h>
+#include <dev/fdt/fdt_common.h>
+#endif
+
 static void gpiobus_print_pins(struct gpiobus_ivar *);
 static int gpiobus_parse_pins(struct gpiobus_softc *, device_t, int);
 static int gpiobus_probe(device_t);
@@ -169,9 +175,64 @@
 	return (0);
 }
 
+#ifdef FDT
 static int
+gpiobus_fdt_parse_pins(struct gpiobus_softc *sc, struct gpiobus_ivar *devi,
+    device_t child, phandle_t pchild)
+{
+	int i, len;
+	pcell_t *pins;
+
+	if ((len = OF_getproplen(pchild, "pins")) == -1)
+		return (-1);
+
+	pins = malloc(len, M_DEVBUF, M_NOWAIT | M_ZERO);
+	if (pins == NULL)
+		return (-1);
+
+	if (OF_getprop(pchild, "pins", pins, len) == -1) {
+		free(pins, M_DEVBUF);
+		return (-1);
+	}
+
+	devi->npins = len / sizeof(pcell_t);
+	devi->pins = malloc(sizeof(uint32_t) * devi->npins, M_DEVBUF, 
+	    M_NOWAIT | M_ZERO);
+	if (devi->pins == NULL) {
+		free(pins, M_DEVBUF);
+		return (-1);
+	}
+
+	for (i = 0; i < devi->npins; i++) {
+		devi->pins[i] = fdt32_to_cpu(pins[i]);
+
+		/*
+		 * Mark pin as mapped and give warning if it's already mapped
+		 */
+		if (sc->sc_pins_mapped[devi->pins[i]]) {
+			device_printf(child,
+			    "warning: pin %d is already mapped\n",
+			    devi->pins[i]);
+			free(pins, M_DEVBUF);
+			return (-1);
+		}
+		sc->sc_pins_mapped[devi->pins[i]] = 1;
+	}
+
+	free(pins, M_DEVBUF);
+	return (0);
+}
+#endif
+
+static int
 gpiobus_probe(device_t dev)
 {
+
+#ifdef FDT
+	if (!ofw_bus_is_compatible(dev, "gpiobus"))
+		return (ENXIO);
+#endif
+
 	device_set_desc(dev, "GPIO bus");
 	return (0);
 }
@@ -179,6 +240,11 @@
 static int
 gpiobus_attach(device_t dev)
 {
+#ifdef FDT
+	device_t dev_child;
+	phandle_t dt_node, dt_child;
+	struct gpiobus_ivar *devi;
+#endif
 	struct gpiobus_softc *sc = GPIOBUS_SOFTC(dev);
 	int res;
 
@@ -208,6 +274,53 @@
 	 * Get parent's pins and mark them as unmapped
 	 */
 	bus_enumerate_hinted_children(dev);
+
+#ifdef FDT
+	/*
+	 * Walk gpiobus and add direct subordinates as our children.
+	 */
+	dt_node = ofw_bus_get_node(dev);
+	for (dt_child = OF_child(dt_node); dt_child != 0;
+	    dt_child = OF_peer(dt_child)) {
+
+		/* Check and process 'status' property. */
+		if (!(fdt_is_enabled(dt_child)))
+			continue;
+
+		/* The GPIO pins are mandatory. */
+		if (!OF_hasprop(dt_child, "pins"))
+			continue;
+
+		devi = malloc(sizeof(*devi), M_DEVBUF, M_WAITOK | M_ZERO);
+
+		if (ofw_bus_gen_setup_devinfo(&devi->ofw, dt_child) != 0) {
+			free(devi, M_DEVBUF);
+			device_printf(dev, "could not set up devinfo\n");
+			continue;
+		}
+
+		/* Add newbus device for this FDT node */
+		dev_child = device_add_child(dev, NULL, -1);
+		if (dev_child == NULL) {
+			device_printf(dev, "could not add child: %s\n",
+			    devi->ofw.obd_name);
+			/* XXX should unmap */
+			ofw_bus_gen_destroy_devinfo(&devi->ofw);
+			free(devi, M_DEVBUF);
+			continue;
+		}
+		device_set_ivars(dev_child, devi);
+
+		if (gpiobus_fdt_parse_pins(sc,
+		    devi, dev_child, dt_child) != 0) {
+			device_delete_child(dev, dev_child);
+			ofw_bus_gen_destroy_devinfo(&devi->ofw);
+			free(devi, M_DEVBUF);
+			continue;
+		}
+	}
+#endif
+
 	return (bus_generic_attach(dev));
 }
 
@@ -445,6 +558,17 @@
 	return GPIO_PIN_TOGGLE(sc->sc_dev, devi->pins[pin]);
 }
 
+#ifdef FDT
+static const struct ofw_bus_devinfo *
+gpiobus_get_devinfo(device_t bus, device_t child)
+{
+	struct gpiobus_ivar *devi;
+
+	devi = device_get_ivars(child);
+	return (&devi->ofw);
+}
+#endif
+
 static device_method_t gpiobus_methods[] = {
 	/* Device interface */
 	DEVMETHOD(device_probe,		gpiobus_probe),
@@ -473,6 +597,16 @@
 	DEVMETHOD(gpiobus_pin_set,	gpiobus_pin_set),
 	DEVMETHOD(gpiobus_pin_toggle,	gpiobus_pin_toggle),
 
+#ifdef FDT
+	/* OFW bus interface */
+	DEVMETHOD(ofw_bus_get_devinfo,	gpiobus_get_devinfo),
+	DEVMETHOD(ofw_bus_get_compat,	ofw_bus_gen_get_compat),
+	DEVMETHOD(ofw_bus_get_model,	ofw_bus_gen_get_model),
+	DEVMETHOD(ofw_bus_get_name,	ofw_bus_gen_get_name),
+	DEVMETHOD(ofw_bus_get_node,	ofw_bus_gen_get_node),
+	DEVMETHOD(ofw_bus_get_type,	ofw_bus_gen_get_type),
+#endif
+
 	DEVMETHOD_END
 };
 
Index: dev/gpio/gpiobusvar.h
===================================================================
--- dev/gpio/gpiobusvar.h	(revision 243614)
+++ dev/gpio/gpiobusvar.h	(working copy)
@@ -34,6 +34,12 @@
 #include <sys/lock.h>
 #include <sys/mutex.h>
 
+#include "opt_platform.h"
+
+#ifdef FDT
+#include <dev/ofw/ofw_bus_subr.h>
+#endif
+
 #define GPIOBUS_IVAR(d) (struct gpiobus_ivar *) device_get_ivars(d)
 #define GPIOBUS_SOFTC(d) (struct gpiobus_softc *) device_get_softc(d)
 
@@ -50,6 +56,9 @@
 
 struct gpiobus_ivar
 {
+#ifdef FDT
+	struct ofw_bus_devinfo	ofw;	/* FDT device info */
+#endif
 	uint32_t	npins;	/* pins total */
 	uint32_t	*pins;	/* pins map */
 };
Index: dev/gpio/gpioc.c
===================================================================
--- dev/gpio/gpioc.c	(revision 243614)
+++ dev/gpio/gpioc.c	(working copy)
@@ -44,6 +44,13 @@
 #include <sys/gpio.h>
 #include "gpio_if.h"
 
+#include "opt_platform.h"
+
+#ifdef FDT
+#include <dev/ofw/ofw_bus.h>
+#include <dev/ofw/ofw_bus_subr.h>
+#endif
+
 #undef GPIOC_DEBUG
 #ifdef GPIOC_DEBUG
 #define dprintf printf
@@ -73,6 +80,11 @@
 static int
 gpioc_probe(device_t dev)
 {
+
+#ifdef FDT
+	if (!ofw_bus_is_compatible(dev, "gpio-controller"))
+		return (ENXIO);
+#endif
 	device_set_desc(dev, "GPIO controller");
 	return (0);
 }
Index: dev/gpio/gpioled.c
===================================================================
--- dev/gpio/gpioled.c	(revision 243614)
+++ dev/gpio/gpioled.c	(working copy)
@@ -43,11 +43,20 @@
 #include <sys/gpio.h>
 #include "gpiobus_if.h"
 
+#include "opt_platform.h"
+
+#ifdef FDT
+#include <dev/ofw/ofw_bus.h>
+#include <dev/ofw/ofw_bus_subr.h>
+#endif
+
 /*
  * Only one pin for led
  */
 #define	GPIOLED_PIN	0
 
+#define	GPIOLED_MAXBUF	32
+
 #define GPIOLED_LOCK(_sc)		mtx_lock(&(_sc)->sc_mtx)
 #define	GPIOLED_UNLOCK(_sc)		mtx_unlock(&(_sc)->sc_mtx)
 #define GPIOLED_LOCK_INIT(_sc) \
@@ -85,6 +94,11 @@
 static int
 gpioled_probe(device_t dev)
 {
+
+#ifdef FDT
+	if (!ofw_bus_is_compatible(dev, "gpioled"))
+		return (ENXIO);
+#endif
 	device_set_desc(dev, "GPIO led");
 	return (0);
 }
@@ -92,16 +106,27 @@
 static int
 gpioled_attach(device_t dev)
 {
+#ifdef FDT
+	phandle_t node;
+#endif
 	struct gpioled_softc *sc;
-	const char *name;
+	char *name;
 
 	sc = device_get_softc(dev);
 	sc->sc_dev = dev;
 	sc->sc_busdev = device_get_parent(dev);
 	GPIOLED_LOCK_INIT(sc);
+
+#ifdef FDT
+	node = ofw_bus_get_node(dev);
+	name = malloc(GPIOLED_MAXBUF + 1, M_DEVBUF, M_NOWAIT | M_ZERO);
+	if (OF_getprop(node, "label", name, GPIOLED_MAXBUF) == -1)
+		OF_getprop(node, "name", name, GPIOLED_MAXBUF);
+#else
 	if (resource_string_value(device_get_name(dev), 
 	    device_get_unit(dev), "name", &name))
 		name = NULL;
+#endif
 
 	GPIOBUS_PIN_SETFLAGS(sc->sc_busdev, sc->sc_dev, GPIOLED_PIN,
 	    GPIO_PIN_OUTPUT);
@@ -109,6 +134,9 @@
 	sc->sc_leddev = led_create(gpioled_control, sc, name ? name :
 	    device_get_nameunit(dev));
 
+#ifdef FDT
+	free(name, M_DEVBUF);
+#endif
 	return (0);
 }
 

[-- Attachment #3 --]
--- arm/broadcom/bcm2835/bcm2835_gpio.c.orig	2012-12-03 15:39:23.762869715 -0200
+++ arm/broadcom/bcm2835/bcm2835_gpio.c	2012-12-04 16:22:40.133869246 -0200
@@ -689,9 +689,11 @@
 bcm_gpio_attach(device_t dev)
 {
 	struct bcm_gpio_softc *sc = device_get_softc(dev);
+	struct ofw_bus_devinfo *di_ofw;
+	device_t dev_child;
 	uint32_t func;
 	int i, j, rid;
-	phandle_t gpio;
+	phandle_t child, gpio;
 
 	sc->sc_dev = dev;
 
@@ -747,8 +749,39 @@
 
 	bcm_gpio_sysctl_init(sc);
 
-        device_add_child(dev, "gpioc", device_get_unit(dev));
-        device_add_child(dev, "gpiobus", device_get_unit(dev));
+	/*
+	 * Walkthrough our node and add direct subordinates as our children.
+	 */
+	for (child = OF_child(gpio); child != 0; child = OF_peer(child)) {
+
+		/* Check and process 'status' property. */
+		if (!(fdt_is_enabled(child)))
+			continue;
+
+		if (!OF_hasprop(child, "compatible"))
+			continue;
+
+		di_ofw = malloc(sizeof(*di_ofw), M_DEVBUF, M_WAITOK | M_ZERO);
+
+		if (ofw_bus_gen_setup_devinfo(di_ofw, child) != 0) {
+			free(di_ofw, M_DEVBUF);
+			device_printf(dev, "could not set up devinfo\n");
+			continue;
+		}
+
+		/* Add newbus device for this FDT node */
+		dev_child = device_add_child(dev, NULL, -1);
+		if (dev_child == NULL) {
+			device_printf(dev, "could not add child: %s\n",
+			    di_ofw->obd_name);
+			/* XXX should unmap */
+			ofw_bus_gen_destroy_devinfo(di_ofw);
+			free(di_ofw, M_DEVBUF);
+			continue;
+                }
+		device_set_ivars(dev_child, di_ofw);
+	}
+
 	return (bus_generic_attach(dev));
 
 fail:
@@ -766,6 +799,13 @@
 	return (EBUSY);
 }
 
+static const struct ofw_bus_devinfo *
+bcm_gpio_get_devinfo(device_t bus, device_t child)
+{
+
+	return (device_get_ivars(child));
+}
+
 static device_method_t bcm_gpio_methods[] = {
 	/* Device interface */
 	DEVMETHOD(device_probe,		bcm_gpio_probe),
@@ -782,6 +822,14 @@
 	DEVMETHOD(gpio_pin_set,		bcm_gpio_pin_set),
 	DEVMETHOD(gpio_pin_toggle,	bcm_gpio_pin_toggle),
 
+	/* OFW bus interface */
+	DEVMETHOD(ofw_bus_get_devinfo,	bcm_gpio_get_devinfo),
+	DEVMETHOD(ofw_bus_get_compat,	ofw_bus_gen_get_compat),
+	DEVMETHOD(ofw_bus_get_model,	ofw_bus_gen_get_model),
+	DEVMETHOD(ofw_bus_get_name,	ofw_bus_gen_get_name),
+	DEVMETHOD(ofw_bus_get_node,	ofw_bus_gen_get_node),
+	DEVMETHOD(ofw_bus_get_type,	ofw_bus_gen_get_type),
+
 	DEVMETHOD_END
 };
 

[-- Attachment #4 --]
Index: boot/fdt/dts/bcm2835-rpi-b.dts
===================================================================
--- boot/fdt/dts/bcm2835-rpi-b.dts	(revision 243614)
+++ boot/fdt/dts/bcm2835-rpi-b.dts	(working copy)
@@ -173,6 +173,21 @@
 			 */
 			broadcom,read-only = <46>, <47>, <48>, <49>, <50>, <51>, <52>, <53>;
 
+			gpioc {
+				compatible = "gpio-controller";
+			};
+
+			gpiobus {
+				compatible = "gpiobus";
+
+				/* Ok led */
+				led {
+					compatible = "gpioled";
+					label = "ok";
+					pins = <16>;
+				};
+			};
+
 			/* BSC0 */
 			pins_bsc0_a: bsc0_a {
 				broadcom,pins = <0>, <1>;

Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?BEB9A0F8-560B-4937-8707-653988A26D85>