Skip site navigation (1)Skip section navigation (2)
Date:      Thu, 12 Dec 2013 17:19:52 -0200
From:      Luiz Otavio O Souza <loos.br@gmail.com>
To:        freebsd-arch@FreeBSD.org
Cc:        freebsd-arm@freebsd.org
Subject:   Re: FDT Support for GPIO (gpiobus and friends)
Message-ID:  <598EB94E-817C-47E9-BBA5-F6E6D5A1B2BA@gmail.com>
In-Reply-To: <7DBC8BB4-8CEF-48E0-BB6B-40C00F71284A@gmail.com>
References:  <BEB9A0F8-560B-4937-8707-653988A26D85@gmail.com> <20121205.060056.592894859995638978.hrs@allbsd.org> <B97B1170-69AD-4AA2-A111-1B9539C71BC3@gmail.com> <7DBC8BB4-8CEF-48E0-BB6B-40C00F71284A@gmail.com>

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

[-- Attachment #1 --]
On Nov 26, 2013, at 4:44 PM, Luiz Otavio O Souza <loos.br@gmail.com> wrote:

> Right, so after playing a little bit with IIC and SPI on FDT i have now a patch, which i believe, is more clean and complete to support an OFW GPIO bus in the same way as it has been done with IIC and SPI.
> 
> The first patch (001-ofw-gpiobus.diff) implements the OFW GPIO bus and all the changes to make possible to the available gpio devices (gpioled and gpioiic) attach to it.
> 
> The second patch (002-iicbb-ofw-iicbus.diff) implements the changes to make the OFW IIC bus attach to a gpioiic bit-banging (iicbb(4)) kind of driver.
> 
> The third patch (006-ofw-gpio-man.diff) has the manual changes for gpioiic and gpioled.
> 
> I would appreciate if you people could take a look and give me some feedback.


Okay, so here are the updated patches (updated to apply clean after the last changes).

I’ve added one missing patch (000-gpio-node.diff) which gives access to FDT node of the gpio controller to GPIO OFW bus.

For now, this last patch is only for RPi and BBB. Both are tested and known to work with gpioled(4) and gpioiic(4).

There is an additional patch (000-bbb-gpioled.diff) which shows what is needed to setup gpioled on BBB.

In the next days, i’ll check the changes for regressions with some non FDT boards and if everything is okay and nobody objects, i’ll ask for permission to commit it.

Comments ?

Luiz





[-- Attachment #2 --]
Index: sys/conf/files
===================================================================
--- sys/conf/files	(revision 259130)
+++ sys/conf/files	(working copy)
@@ -1436,6 +1436,7 @@
 dev/gpio/gpioled.c		optional gpioled
 dev/gpio/gpio_if.m		optional gpio
 dev/gpio/gpiobus_if.m		optional gpio
+dev/gpio/ofw_gpiobus.c		optional fdt gpio
 dev/hatm/if_hatm.c		optional hatm pci
 dev/hatm/if_hatm_intr.c		optional hatm pci
 dev/hatm/if_hatm_ioctl.c	optional hatm pci
Index: sys/dev/gpio/gpiobus.c
===================================================================
--- sys/dev/gpio/gpiobus.c	(revision 259130)
+++ sys/dev/gpio/gpiobus.c	(working copy)
@@ -46,7 +46,6 @@
 #include "gpio_if.h"
 #include "gpiobus_if.h"
 
-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);
 static int gpiobus_attach(device_t);
@@ -73,17 +72,7 @@
 static int gpiobus_pin_get(device_t, device_t, uint32_t, unsigned int*);
 static int gpiobus_pin_toggle(device_t, device_t, uint32_t);
 
-#define	GPIOBUS_LOCK(_sc) mtx_lock(&(_sc)->sc_mtx)
-#define	GPIOBUS_UNLOCK(_sc) mtx_unlock(&(_sc)->sc_mtx)
-#define	GPIOBUS_LOCK_INIT(_sc) \
-	mtx_init(&_sc->sc_mtx, device_get_nameunit(_sc->sc_dev), \
-	    "gpiobus", MTX_DEF)
-#define	GPIOBUS_LOCK_DESTROY(_sc) mtx_destroy(&_sc->sc_mtx);
-#define	GPIOBUS_ASSERT_LOCKED(_sc) mtx_assert(&_sc->sc_mtx, MA_OWNED);
-#define	GPIOBUS_ASSERT_UNLOCKED(_sc) mtx_assert(&_sc->sc_mtx, MA_NOTOWNED);
-
-
-static void
+void
 gpiobus_print_pins(struct gpiobus_ivar *devi)
 {
 	int range_start, range_stop, need_coma;
Index: sys/dev/gpio/gpiobusvar.h
===================================================================
--- sys/dev/gpio/gpiobusvar.h	(revision 259130)
+++ sys/dev/gpio/gpiobusvar.h	(working copy)
@@ -30,13 +30,26 @@
 #ifndef	__GPIOBUS_H__
 #define	__GPIOBUS_H__
 
+#include "opt_platform.h"
+
 #include <sys/param.h>
 #include <sys/lock.h>
 #include <sys/mutex.h>
 
-#define GPIOBUS_IVAR(d) (struct gpiobus_ivar *) device_get_ivars(d)
-#define GPIOBUS_SOFTC(d) (struct gpiobus_softc *) device_get_softc(d)
+#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)
+#define	GPIOBUS_LOCK(_sc) mtx_lock(&(_sc)->sc_mtx)
+#define	GPIOBUS_UNLOCK(_sc) mtx_unlock(&(_sc)->sc_mtx)
+#define	GPIOBUS_LOCK_INIT(_sc) mtx_init(&_sc->sc_mtx,			\
+	    device_get_nameunit(_sc->sc_dev), "gpiobus", MTX_DEF)
+#define	GPIOBUS_LOCK_DESTROY(_sc) mtx_destroy(&_sc->sc_mtx)
+#define	GPIOBUS_ASSERT_LOCKED(_sc) mtx_assert(&_sc->sc_mtx, MA_OWNED)
+#define	GPIOBUS_ASSERT_UNLOCKED(_sc) mtx_assert(&_sc->sc_mtx, MA_NOTOWNED)
+
 struct gpiobus_softc
 {
 	struct mtx	sc_mtx;		/* bus mutex */
@@ -54,4 +67,11 @@
 	uint32_t	*pins;	/* pins map */
 };
 
+void gpiobus_print_pins(struct gpiobus_ivar *);
+#ifdef FDT
+device_t ofw_gpiobus_add_fdt_child(device_t, phandle_t);
+#endif
+
+extern driver_t gpiobus_driver;
+
 #endif	/* __GPIOBUS_H__ */
Index: sys/dev/gpio/gpioiic.c
===================================================================
--- sys/dev/gpio/gpioiic.c	(revision 259130)
+++ sys/dev/gpio/gpioiic.c	(working copy)
@@ -28,6 +28,8 @@
 #include <sys/cdefs.h>
 __FBSDID("$FreeBSD$");
 
+#include "opt_platform.h"
+
 #include <sys/param.h>
 #include <sys/systm.h>
 #include <sys/bus.h>
@@ -38,6 +40,12 @@
 #include <sys/gpio.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
+
 #include <dev/iicbus/iiconf.h>
 #include <dev/iicbus/iicbus.h>
 
@@ -71,6 +79,10 @@
 gpioiic_probe(device_t dev)
 {
 
+#ifdef FDT
+	if (!ofw_bus_is_compatible(dev, "gpioiic"))
+		return (ENXIO);
+#endif
 	device_set_desc(dev, "GPIO I2C bit-banging driver");
 
 	return (0);
@@ -81,6 +93,10 @@
 {
 	struct gpioiic_softc	*sc = device_get_softc(dev);
 	device_t		bitbang;
+#ifdef FDT
+	phandle_t		node;
+	pcell_t			pin;
+#endif
 
 	sc->sc_dev = dev;
 	sc->sc_busdev = device_get_parent(dev);
@@ -91,6 +107,15 @@
 		device_get_unit(dev), "sda", &sc->sda_pin))
 		sc->sda_pin = SDA_PIN_DEFAULT;
 
+#ifdef FDT
+	if ((node = ofw_bus_get_node(dev)) == -1)
+		return (ENXIO);
+	if (OF_getencprop(node, "scl", &pin, sizeof(pin)) > 0)
+		sc->scl_pin = (int)pin;
+	if (OF_getencprop(node, "sda", &pin, sizeof(pin)) > 0)
+		sc->sda_pin = (int)pin;
+#endif
+
 	/* add generic bit-banging code */
 	bitbang = device_add_child(dev, "iicbb", -1);
 	device_probe_and_attach(bitbang);
@@ -209,6 +234,16 @@
 	return (IIC_ENOADDR);
 }
 
+#ifdef FDT
+static phandle_t
+gpioiic_get_node(device_t bus, device_t dev)
+{
+
+	/* We only have one child, the iicbb, which needs our own node. */
+	return (ofw_bus_get_node(bus));
+}
+#endif
+
 static devclass_t gpioiic_devclass;
 
 static device_method_t gpioiic_methods[] = {
@@ -225,6 +260,11 @@
 	DEVMETHOD(iicbb_getscl,		gpioiic_getscl),
 	DEVMETHOD(iicbb_reset,		gpioiic_reset),
 
+#ifdef FDT
+	/* OFW bus interface */
+	DEVMETHOD(ofw_bus_get_node,	gpioiic_get_node),
+#endif
+
 	{ 0, 0 }
 };
 
Index: sys/dev/gpio/gpioled.c
===================================================================
--- sys/dev/gpio/gpioled.c	(revision 259130)
+++ sys/dev/gpio/gpioled.c	(working copy)
@@ -27,6 +27,8 @@
 #include <sys/cdefs.h>
 __FBSDID("$FreeBSD$");
 
+#include "opt_platform.h"
+
 #include <sys/param.h>
 #include <sys/systm.h>
 #include <sys/bio.h>
@@ -39,6 +41,12 @@
 #include <sys/module.h>
 #include <sys/mutex.h>
 
+#ifdef FDT
+#include <dev/fdt/fdt_common.h>
+#include <dev/gpio/gpiobusvar.h>
+#include <dev/ofw/ofw_bus.h>
+#endif
+
 #include <dev/led/led.h>
 #include <sys/gpio.h>
 #include "gpiobus_if.h"
@@ -84,10 +92,65 @@
 	GPIOLED_UNLOCK(sc);
 }
 
+#ifdef FDT
+static void
+gpioled_identify(driver_t *driver, device_t bus)
+{
+	phandle_t child, leds, root;
+
+	root = OF_finddevice("/");
+	if (root == 0)
+		return;
+	leds = fdt_find_compatible(root, "gpio-leds", 1);
+	if (leds == 0)
+		return;
+
+	/* Traverse the 'gpio-leds' node and add its children. */
+	for (child = OF_child(leds); child != 0; child = OF_peer(child))
+		if (ofw_gpiobus_add_fdt_child(bus, child) == NULL)
+			continue;
+}
+#endif
+
 static int
 gpioled_probe(device_t dev)
 {
+#ifdef FDT
+	int match;
+	phandle_t node;
+	char *compat;
+
+	/*
+	 * We can match against our own node compatible string and also against
+	 * our parent node compatible string.  The first is normally used to
+	 * describe leds on a gpiobus and the later when there is a common node
+	 * compatible with 'gpio-leds' which is used to concentrate all the
+	 * leds nodes on the dts.
+	 */
+	match = 0;
+	if (ofw_bus_is_compatible(dev, "gpioled"))
+		match = 1;
+
+	if (match == 0) {
+		if ((node = ofw_bus_get_node(dev)) == -1)
+			return (ENXIO);
+		if ((node = OF_parent(node)) == -1)
+			return (ENXIO);
+		if (OF_getprop_alloc(node, "compatible", 1,
+		    (void **)&compat) == -1)
+			return (ENXIO);
+
+		if (strcasecmp(compat, "gpio-leds") == 0)
+			match = 1;
+
+		free(compat, M_OFWPROP);
+	}
+
+	if (match == 0)
+		return (ENXIO);
+#endif
 	device_set_desc(dev, "GPIO led");
+
 	return (0);
 }
 
@@ -95,18 +158,35 @@
 gpioled_attach(device_t dev)
 {
 	struct gpioled_softc *sc;
+#ifdef FDT
+	phandle_t node;
+	char *name;
+#else
 	const char *name;
+#endif
 
 	sc = device_get_softc(dev);
 	sc->sc_dev = dev;
 	sc->sc_busdev = device_get_parent(dev);
 	GPIOLED_LOCK_INIT(sc);
+#ifdef FDT
+	name = NULL;
+	if ((node = ofw_bus_get_node(dev)) == -1)
+		return (ENXIO);
+	if (OF_getprop_alloc(node, "label", 1, (void **)&name) == -1)
+		OF_getprop_alloc(node, "name", 1, (void **)&name);
+#else
 	if (resource_string_value(device_get_name(dev), 
 	    device_get_unit(dev), "name", &name))
 		name = NULL;
+#endif
 
 	sc->sc_leddev = led_create(gpioled_control, sc, name ? name :
 	    device_get_nameunit(dev));
+#ifdef FDT
+	if (name != NULL)
+		free(name, M_OFWPROP);
+#endif
 
 	return (0);
 }
@@ -129,6 +209,9 @@
 
 static device_method_t gpioled_methods[] = {
 	/* Device interface */
+#ifdef FDT
+	DEVMETHOD(device_identify,	gpioled_identify),
+#endif
 	DEVMETHOD(device_probe,		gpioled_probe),
 	DEVMETHOD(device_attach,	gpioled_attach),
 	DEVMETHOD(device_detach,	gpioled_detach),
--- /dev/null	2013-12-12 17:00:00.000000000 -0200
+++ sys/dev/gpio/ofw_gpiobus.c	2013-11-25 10:15:00.322070491 -0200
@@ -0,0 +1,342 @@
+/*-
+ * Copyright (c) 2009, Nathan Whitehorn <nwhitehorn@FreeBSD.org>
+ * Copyright (c) 2013, Luiz Otavio O Souza <loos@FreeBSD.org>
+ * Copyright (c) 2013 The FreeBSD Foundation
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice unmodified, this list of conditions, and the following
+ *    disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/bus.h>
+#include <sys/gpio.h>
+#include <sys/kernel.h>
+#include <sys/libkern.h>
+#include <sys/lock.h>
+#include <sys/module.h>
+#include <sys/mutex.h>
+
+#include <dev/fdt/fdt_common.h>
+#include <dev/gpio/gpiobusvar.h>
+#include <dev/ofw/ofw_bus.h>
+#include <dev/ofw/openfirm.h>
+
+#include <machine/resource.h>
+
+#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,
+    phandle_t);
+static void ofw_gpiobus_destroy_devinfo(struct ofw_gpiobus_devinfo *);
+
+device_t
+ofw_gpiobus_add_fdt_child(device_t bus, phandle_t child)
+{
+	struct ofw_gpiobus_devinfo *dinfo;
+	device_t childdev;
+
+	/*
+	 * Set up the GPIO child and OFW bus layer devinfo and add it to bus.
+	 */
+	dinfo = ofw_gpiobus_setup_devinfo(bus, child);
+	if (dinfo == NULL)
+		return (NULL);
+	childdev = device_add_child(bus, NULL, -1);
+	if (childdev == NULL) {
+		device_printf(bus, "could not add child: %s\n",
+		    dinfo->opd_obdinfo.obd_name);
+		ofw_gpiobus_destroy_devinfo(dinfo);
+		return (NULL);
+	}
+	device_set_ivars(childdev, dinfo);
+
+	return (childdev);
+}
+
+static int
+ofw_gpiobus_parse_gpios(struct gpiobus_softc *sc, struct gpiobus_ivar *dinfo,
+	phandle_t child)
+{
+	int i, len;
+	pcell_t *gpios;
+	phandle_t gpio;
+
+	/* Check and process 'status' property. */
+	if (!(fdt_is_enabled(child)))
+		return (ENXIO);
+
+	/* Retrieve the gpios property. */
+	if ((len = OF_getproplen(child, "gpios")) < 0)
+		return (EINVAL);
+	gpios = malloc(len, M_DEVBUF, M_NOWAIT | M_ZERO);
+	if (gpios == NULL)
+		return (ENOMEM);
+	if (OF_getencprop(child, "gpios", gpios, len) < 0) {
+		free(gpios, M_DEVBUF);
+		return (EINVAL);
+	}
+
+	/*
+	 * 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.
+	 */
+	if ((len / sizeof(pcell_t)) % 4) {
+		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) {
+		free(gpios, M_DEVBUF);
+		return (ENOMEM);
+	}
+
+	for (i = 0; i < dinfo->npins; i++) {
+
+		/* 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);
+			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 */
+
+		if (dinfo->pins[i] > 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);
+			free(gpios, M_DEVBUF);
+			return (EINVAL);
+		}
+
+		/*
+		 * Mark pin as mapped and give warning if it's already mapped.
+		 */
+		if (sc->sc_pins_mapped[dinfo->pins[i]]) {
+			device_printf(sc->sc_busdev,
+			    "warning: pin %d is already mapped\n",
+			    dinfo->pins[i]);
+			free(dinfo->pins, M_DEVBUF);
+			free(gpios, M_DEVBUF);
+			return (EINVAL);
+		}
+		sc->sc_pins_mapped[dinfo->pins[i]] = 1;
+	}
+
+	free(gpios, M_DEVBUF);
+
+	return (0);
+}
+
+static struct ofw_gpiobus_devinfo *
+ofw_gpiobus_setup_devinfo(device_t dev, phandle_t node)
+{
+	struct gpiobus_softc *sc;
+	struct ofw_gpiobus_devinfo *dinfo;
+
+	sc = device_get_softc(dev);
+	dinfo = malloc(sizeof(*dinfo), M_DEVBUF, M_NOWAIT | M_ZERO);
+	if (dinfo == NULL)
+		return (NULL);
+	if (ofw_bus_gen_setup_devinfo(&dinfo->opd_obdinfo, node) != 0) {
+		free(dinfo, M_DEVBUF);
+		return (NULL);
+	}
+
+	/* Parse the gpios property for the child. */
+	if (ofw_gpiobus_parse_gpios(sc, &dinfo->opd_dinfo, node) != 0) {
+		ofw_bus_gen_destroy_devinfo(&dinfo->opd_obdinfo);
+		free(dinfo, M_DEVBUF);
+		return (NULL);
+	}
+
+	return (dinfo);
+}
+
+static void
+ofw_gpiobus_destroy_devinfo(struct ofw_gpiobus_devinfo *dinfo)
+{
+
+	ofw_bus_gen_destroy_devinfo(&dinfo->opd_obdinfo);
+	free(dinfo, M_DEVBUF);
+}
+
+static int
+ofw_gpiobus_probe(device_t dev)
+{
+
+	if (ofw_bus_get_node(dev) == -1)
+		return (ENXIO);
+	device_set_desc(dev, "OFW GPIO bus");
+
+	return (0);
+}
+
+static int
+ofw_gpiobus_attach(device_t dev)
+{
+	struct gpiobus_softc *sc;
+	phandle_t child;
+
+	sc = GPIOBUS_SOFTC(dev);
+	sc->sc_busdev = dev;
+	sc->sc_dev = device_get_parent(dev);
+
+	/* Read the pin max. value */
+	if (GPIO_PIN_MAX(sc->sc_dev, &sc->sc_npins) != 0)
+		return (ENXIO);
+
+	KASSERT(sc->sc_npins != 0, ("GPIO device with no pins"));
+
+	/*
+	 * Increase to get number of pins.
+	 */
+	sc->sc_npins++;
+
+	sc->sc_pins_mapped = malloc(sizeof(int) * sc->sc_npins, M_DEVBUF, 
+	    M_NOWAIT | M_ZERO);
+
+	if (!sc->sc_pins_mapped)
+		return (ENOMEM);
+
+	/* Init the bus lock. */
+	GPIOBUS_LOCK_INIT(sc);
+
+	bus_generic_probe(dev);
+	bus_enumerate_hinted_children(dev);
+
+	/*
+	 * Attach the children represented in the device tree.
+	 */
+	for (child = OF_child(ofw_bus_get_node(dev)); child != 0;
+	    child = OF_peer(child))
+		if (ofw_gpiobus_add_fdt_child(dev, child) == NULL)
+			continue;
+
+	return (bus_generic_attach(dev));
+}
+
+static device_t
+ofw_gpiobus_add_child(device_t dev, u_int order, const char *name, int unit)
+{
+	device_t child;
+	struct ofw_gpiobus_devinfo *devi;
+
+	child = device_add_child_ordered(dev, order, name, unit);
+	if (child == NULL)
+		return (child);
+	devi = malloc(sizeof(struct ofw_gpiobus_devinfo), M_DEVBUF,
+	    M_NOWAIT | M_ZERO);
+	if (devi == NULL) {
+		device_delete_child(dev, child);
+		return (0);
+	}
+
+	/*
+	 * NULL all the OFW-related parts of the ivars for non-OFW
+	 * children.
+	 */
+	devi->opd_obdinfo.obd_node = -1;
+	devi->opd_obdinfo.obd_name = NULL;
+	devi->opd_obdinfo.obd_compat = NULL;
+	devi->opd_obdinfo.obd_type = NULL;
+	devi->opd_obdinfo.obd_model = NULL;
+
+	device_set_ivars(child, devi);
+
+	return (child);
+}
+
+static int
+ofw_gpiobus_print_child(device_t dev, device_t child)
+{
+	struct ofw_gpiobus_devinfo *devi;
+	int retval = 0;
+
+	devi = device_get_ivars(child);
+	retval += bus_print_child_header(dev, child);
+	retval += printf(" at pin(s) ");
+	gpiobus_print_pins(&devi->opd_dinfo);
+	retval += bus_print_child_footer(dev, child);
+
+	return (retval);
+}
+
+static const struct ofw_bus_devinfo *
+ofw_gpiobus_get_devinfo(device_t bus, device_t dev)
+{
+	struct ofw_gpiobus_devinfo *dinfo;
+
+	dinfo = device_get_ivars(dev);
+
+	return (&dinfo->opd_obdinfo);
+}
+
+static device_method_t ofw_gpiobus_methods[] = {
+	/* Device interface */
+	DEVMETHOD(device_probe,		ofw_gpiobus_probe),
+	DEVMETHOD(device_attach,	ofw_gpiobus_attach),
+
+	/* Bus interface */
+	DEVMETHOD(bus_child_pnpinfo_str,	ofw_bus_gen_child_pnpinfo_str),
+	DEVMETHOD(bus_print_child,	ofw_gpiobus_print_child),
+	DEVMETHOD(bus_add_child,	ofw_gpiobus_add_child),
+
+	/* ofw_bus interface */
+	DEVMETHOD(ofw_bus_get_devinfo,	ofw_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),
+
+	DEVMETHOD_END
+};
+
+static devclass_t ofwgpiobus_devclass;
+
+DEFINE_CLASS_1(gpiobus, ofw_gpiobus_driver, ofw_gpiobus_methods,
+    sizeof(struct gpiobus_softc), gpiobus_driver);
+DRIVER_MODULE(ofw_gpiobus, gpio, ofw_gpiobus_driver, ofwgpiobus_devclass, 0, 0);
+MODULE_VERSION(ofw_gpiobus, 1);
+MODULE_DEPEND(ofw_gpiobus, gpiobus, 1, 1, 1);

[-- Attachment #3 --]
Index: sys/dev/iicbus/iicbb.c
===================================================================
--- sys/dev/iicbus/iicbb.c	(revision 258138)
+++ sys/dev/iicbus/iicbb.c	(working copy)
@@ -43,6 +43,8 @@
  *
  */
 
+#include "opt_platform.h"
+
 #include <sys/param.h>
 #include <sys/kernel.h>
 #include <sys/systm.h>
@@ -50,6 +52,11 @@
 #include <sys/bus.h>
 #include <sys/uio.h>
 
+#ifdef FDT
+#include <dev/ofw/ofw_bus.h>
+#include <dev/ofw/ofw_bus_subr.h>
+#include <dev/fdt/fdt_common.h>
+#endif
 
 #include <dev/iicbus/iiconf.h>
 #include <dev/iicbus/iicbus.h>
@@ -77,6 +84,9 @@
 static int iicbb_read(device_t, char *, int, int *, int, int);
 static int iicbb_reset(device_t, u_char, u_char, u_char *);
 static int iicbb_transfer(device_t dev, struct iic_msg *msgs, uint32_t nmsgs);
+#ifdef FDT
+static phandle_t iicbb_get_node(device_t, device_t);
+#endif
 
 static device_method_t iicbb_methods[] = {
 	/* device interface */
@@ -98,6 +108,11 @@
 	DEVMETHOD(iicbus_reset,		iicbb_reset),
 	DEVMETHOD(iicbus_transfer,	iicbb_transfer),
 
+#ifdef FDT
+	/* ofw_bus interface */
+	DEVMETHOD(ofw_bus_get_node,	iicbb_get_node),
+#endif
+
 	{ 0, 0 }
 };
 
@@ -154,6 +169,16 @@
 	return (0);
 }
 
+#ifdef FDT
+static phandle_t
+iicbb_get_node(device_t bus, device_t dev)
+{
+
+	/* We only have one child, the I2C bus, which needs our own node. */
+	return (ofw_bus_get_node(bus));
+}
+#endif
+
 static void
 iicbb_child_detached( device_t dev, device_t child )
 {
Index: sys/dev/ofw/ofw_iicbus.c
===================================================================
--- sys/dev/ofw/ofw_iicbus.c	(revision 258138)
+++ sys/dev/ofw/ofw_iicbus.c	(working copy)
@@ -80,6 +80,7 @@
 
 DEFINE_CLASS_1(iicbus, ofw_iicbus_driver, ofw_iicbus_methods,
     sizeof(struct iicbus_softc), iicbus_driver);
+DRIVER_MODULE(ofw_iicbus, iicbb, ofw_iicbus_driver, ofwiicbus_devclass, 0, 0);
 DRIVER_MODULE(ofw_iicbus, iichb, ofw_iicbus_driver, ofwiicbus_devclass, 0, 0);
 MODULE_VERSION(ofw_iicbus, 1);
 MODULE_DEPEND(ofw_iicbus, iicbus, 1, 1, 1);

[-- Attachment #4 --]
Index: share/man/man4/gpioiic.4
===================================================================
--- share/man/man4/gpioiic.4	(revision 258550)
+++ share/man/man4/gpioiic.4	(working copy)
@@ -24,7 +24,7 @@
 .\"
 .\" $FreeBSD$
 .\"
-.Dd November 5, 2013
+.Dd November 26, 2013
 .Dt GPIOIIC 4
 .Os
 .Sh NAME
@@ -73,12 +73,75 @@
 .Va hint.gpioiic.%d.pins
 should be used as the SCLOCK
 source.
+Optional, defaults to 0.
 .It Va hint.gpioiic.%d.sda
 Indicates which bit in the
 .Va hint.gpioiic.%d.pins
 should be used as the SDATA
 source.
+Optional, defaults to 1.
 .El
+.Pp
+On a
+.Xr FDT 4
+based system, like
+.Li ARM , the dts part for a
+.Nm gpioiic
+device usually looks like:
+.Bd -literal
+gpio: gpio {
+
+	gpio-controller;
+	...
+
+	gpioiic0 {
+		compatible = "gpioiic";
+		/*
+		 * Attach to GPIO pins 21 and 22.  Set them
+		 * initially as inputs.
+		 */
+		gpios = <&gpio 21 1 0
+			 &gpio 22 1 0>;
+		scl = <0>;		/* GPIO pin 21 */
+		sda = <1>;		/* GPIO pin 22 */
+
+		/* This is an example of a gpioiic child. */
+		gpioiic-child0 {
+			compatible = "lm75";
+			i2c-address = <0x9e>;
+		};
+	};
+};
+.Ed
+.Pp
+Where:
+.Bl -tag -width ".Va compatible"
+.It Va compatible
+Should always be set to "gpioiic".
+.It Va gpios
+The
+.Va gpios
+property indicates which GPIO pins should be used for SCLOCK and SDATA
+on the GPIO IIC bit-banging bus.
+For more details about the
+.Va gpios
+property, please consult
+.Pa /usr/src/sys/boot/fdt/dts/bindings-gpio.txt .
+.It Va scl
+The
+.Va scl
+option indicates which bit in the
+.Va gpios
+should be used as the SCLOCK source.
+Optional, defaults to 0.
+.It Va sda
+The
+.Va sda
+option indicates which bit in the
+.Va gpios
+should be used as the SDATA source.
+Optional, defaults to 1.
+.El
 .Sh SEE ALSO
 .Xr gpio 4 ,
 .Xr gpioled 4 ,
Index: share/man/man4/gpioled.4
===================================================================
--- share/man/man4/gpioled.4	(revision 258550)
+++ share/man/man4/gpioled.4	(working copy)
@@ -24,7 +24,7 @@
 .\"
 .\" $FreeBSD$
 .\"
-.Dd November 5, 2013
+.Dd November 26, 2013
 .Dt GPIOLED 4
 .Os
 .Sh NAME
@@ -68,6 +68,73 @@
 Please note that this mask should only ever have one bit set
 (any others bits - i.e., pins - will be ignored).
 .El
+.Pp
+On a
+.Xr FDT 4
+based system, like
+.Li ARM , the dts part for a
+.Nm gpioled
+device usually looks like:
+.Bd -literal
+gpio: gpio {
+
+	gpio-controller;
+	...
+
+	led0 {
+		compatible = "gpioled";
+		gpios = <&gpio 16 2 0>;		/* GPIO pin 16. */
+		name = "ok";
+	};
+
+	led1 {
+		compatible = "gpioled";
+		gpios = <&gpio 17 2 0>;		/* GPIO pin 17. */
+		name = "user-led1";
+	};
+};
+.Ed
+.Pp
+And optionally, you can choose combine all the leds under a single
+.Dq gpio-leds
+compatible node:
+.Bd -literal
+simplebus0 {
+
+	...
+
+	leds {
+		compatible = "gpio-leds";
+
+		led0 {
+			gpios = <&gpio 16 2 0>;
+			name = "ok"
+		};
+
+		led1 {
+			gpios = <&gpio 17 2 0>;
+			name = "user-led1"
+		};
+	};
+};
+.Ed
+.Pp
+Both methods are equally supported and it is possible to have the leds
+defined with any sort of mix between the methods.
+The only restriction is that a GPIO pin cannot be mapped by two different
+(gpio)leds.
+.Pp
+For more details about the
+.Va gpios
+property, please consult
+.Pa /usr/src/sys/boot/fdt/dts/bindings-gpio.txt .
+.Pp
+The property
+.Va name
+is the arbitrary name of device in
+.Pa /dev/led/
+to create for
+.Xr led 4 .
 .Sh SEE ALSO
 .Xr gpio 4 ,
 .Xr led 4 ,

[-- Attachment #5 --]
Index: sys/arm/ti/ti_gpio.c
===================================================================
--- sys/arm/ti/ti_gpio.c	(revision 258855)
+++ sys/arm/ti/ti_gpio.c	(working copy)
@@ -788,6 +788,14 @@
 	return(0);
 }
 
+static phandle_t
+ti_gpio_get_node(device_t bus, device_t dev)
+{
+
+	/* We only have one child, the GPIO bus, which needs our own node. */
+	return (ofw_bus_get_node(bus));
+}
+
 static device_method_t ti_gpio_methods[] = {
 	DEVMETHOD(device_probe, ti_gpio_probe),
 	DEVMETHOD(device_attach, ti_gpio_attach),
@@ -802,6 +810,10 @@
 	DEVMETHOD(gpio_pin_get, ti_gpio_pin_get),
 	DEVMETHOD(gpio_pin_set, ti_gpio_pin_set),
 	DEVMETHOD(gpio_pin_toggle, ti_gpio_pin_toggle),
+
+	/* ofw_bus interface */
+	DEVMETHOD(ofw_bus_get_node, ti_gpio_get_node),
+
 	{0, 0},
 };
 
Index: sys/arm/broadcom/bcm2835/bcm2835_gpio.c
===================================================================
--- sys/arm/broadcom/bcm2835/bcm2835_gpio.c	(revision 258855)
+++ sys/arm/broadcom/bcm2835/bcm2835_gpio.c	(working copy)
@@ -762,6 +762,14 @@
 	return (EBUSY);
 }
 
+static phandle_t
+bcm_gpio_get_node(device_t bus, device_t dev)
+{
+
+	/* We only have one child, the GPIO bus, which needs our own node. */
+	return (ofw_bus_get_node(bus));
+}
+
 static device_method_t bcm_gpio_methods[] = {
 	/* Device interface */
 	DEVMETHOD(device_probe,		bcm_gpio_probe),
@@ -778,6 +786,9 @@
 	DEVMETHOD(gpio_pin_set,		bcm_gpio_pin_set),
 	DEVMETHOD(gpio_pin_toggle,	bcm_gpio_pin_toggle),
 
+	/* ofw_bus interface */
+	DEVMETHOD(ofw_bus_get_node,	bcm_gpio_get_node),
+
 	DEVMETHOD_END
 };
 

[-- Attachment #6 --]
Index: sys/arm/conf/BEAGLEBONE
===================================================================
--- sys/arm/conf/BEAGLEBONE	(revision 258798)
+++ sys/arm/conf/BEAGLEBONE	(working copy)
@@ -101,6 +101,7 @@
 
 # GPIO
 device		gpio
+device		gpioled
 
 # USB support
 device		usb
Index: sys/boot/fdt/dts/beaglebone-black.dts
===================================================================
--- sys/boot/fdt/dts/beaglebone-black.dts	(revision 258798)
+++ sys/boot/fdt/dts/beaglebone-black.dts	(working copy)
@@ -147,6 +147,30 @@
 		}
 	};
 
+	leds {
+		compatible = "gpio-leds";
+
+		led1 {
+			gpios = <&GPIO 53 2 0>;
+			name = "led1";
+		};
+
+		led2 {
+			gpios = <&GPIO 54 2 0>;
+			name = "led2";
+		};
+
+		led3 {
+			gpios = <&GPIO 55 2 0>;
+			name = "led3";
+		};
+
+		led4 {
+			gpios = <&GPIO 56 2 0>;
+			name = "led4";
+		};
+	};
+
 	chosen {
 		stdin = "uart0";
 		stdout = "uart0";

Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?598EB94E-817C-47E9-BBA5-F6E6D5A1B2BA>