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

--Apple-Mail-16-531908476
Content-Transfer-Encoding: quoted-printable
Content-Type: text/plain;
	charset=us-ascii

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


--Apple-Mail-16-531908476
Content-Disposition: attachment;
	filename=gpio-fdt.diff
Content-Type: application/octet-stream;
	name="gpio-fdt.diff"
Content-Transfer-Encoding: 7bit

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);
 }
 

--Apple-Mail-16-531908476
Content-Disposition: attachment;
	filename=bcm2835_gpio.c.fdt.diff
Content-Type: application/octet-stream;
	name="bcm2835_gpio.c.fdt.diff"
Content-Transfer-Encoding: 7bit

--- 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
 };
 

--Apple-Mail-16-531908476
Content-Disposition: attachment;
	filename=bcm2835.dts.diff
Content-Type: application/octet-stream;
	name="bcm2835.dts.diff"
Content-Transfer-Encoding: 7bit

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>;

--Apple-Mail-16-531908476--



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