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>
