summaryrefslogtreecommitdiffstats
path: root/sys
diff options
context:
space:
mode:
authorloos <loos@FreeBSD.org>2014-05-15 01:27:53 +0000
committerloos <loos@FreeBSD.org>2014-05-15 01:27:53 +0000
commit5a10856167781cd8857574a1a33431d2628c8052 (patch)
tree236a0222a248be4cab530db22bd510f8ef0c5a1b /sys
parent62b31c7b8fd3fb76eaf3dbc855263b4e5923563b (diff)
downloadFreeBSD-src-5a10856167781cd8857574a1a33431d2628c8052.zip
FreeBSD-src-5a10856167781cd8857574a1a33431d2628c8052.tar.gz
MFC r258046, r258047, r258050, r259035, r259036, r259037, r261842, r261843,
r261844, r261845, r261846, r262194, r262522, r262559 r258046: Fix a typo on a comment in ofw_bus_if.m, the default method will return -1 when a node doesn't exist. r258047: Move the KASSERT() check to the point before the increase of number of pins. r258050: Fix gpiobus to return BUS_PROBE_GENERIC insted of BUS_PROBE_SPECIFIC (0) so it can be overriden by its OFW/FDT version. Give a chance for GPIO devices that implement the device_identify method to attach. r259035: Remove unnecessary includes and an unused softc variable. While here apply two minor style(9) fixes. r259036: Move the GPIOBUS_SET_PINFLAGS(..., ..., pin, GPIO_PIN_OUTPUT) to led(4) control callback function. This makes gpioled(4) works even if the pin is accidentally set to an input. r259037: Fix the pin value reading on AM335x. Because of the inverted logic it was always returning '0' for all the reads, even for the outputs. It is now known to work with gpioiic(4) and gpioled(4). r261842: Add an OFW GPIO compatible bus. This allows the use of the DTS files to describe GPIO bindings in the system. Move the GPIOBUS lock macros to gpiobusvar.h as they are now shared between the OFW and the non OFW versions of GPIO bus. Export gpiobus_print_pins() so it can also be used on the OFW GPIO bus. r261843: Add OFW support to the in tree gpio compatible devices: gpioiic(4) and gpioled(4). Tested on RPi and BBB (using the hardware I2C controller and gpioiic(4) for the I2C tests). It was also verified for regressions on RSPRO (MIPS/ar71xx) used as reference for a non OFW-based system. Update the gpioled(4) and gpioiic(4) man pages with some details and examples about the FDT/OFW support. Some compatibility details pointed out by imp@ will follow in subsequent commits. r261844: Allow the use of OFW I2C bus together with iicbb(4) on OFW-based systems. This change makes ofw_iicbus attach to iicbb(4) controllers in addition to the already supported i2c host bridges (iichb). On iicbb(4) allow the direct access of the OFW parent node by its children, so they can be directly attached to iicbb(4) node on the DTS without the need of describing the i2c bus. r261845: Allow the use of the OFW GPIO bus for ti_gpio and bcm2835_gpio. With this change the gpio children can be described as directly connected to the GPIO controller without the need of describing the OFW GPIO bus itself on the DTS file. With this commit the OFW GPIO bus is fully functional on BBB and RPi. GPIO controllers which want to use the OFW GPIO bus will need similar changes. r261846: Make the gpioled(4) work out of the box on BBB. Add gpioled(4) to BEAGLEBONE kernel and add the description of the four on-board leds of beaglebone-black to its DTS file. r262194: Remove an unnecessary header. r262522: Fix make depend for iicbus. r262559: Inspired by r262522, fix make depend. This fixes the build of gpio modules.
Diffstat (limited to 'sys')
-rw-r--r--sys/arm/broadcom/bcm2835/bcm2835_gpio.c11
-rw-r--r--sys/arm/conf/BEAGLEBONE1
-rw-r--r--sys/arm/ti/ti_gpio.c16
-rw-r--r--sys/boot/fdt/dts/beaglebone-black.dts24
-rw-r--r--sys/boot/fdt/dts/bindings-gpio.txt2
-rw-r--r--sys/conf/files1
-rw-r--r--sys/dev/gpio/gpiobus.c22
-rw-r--r--sys/dev/gpio/gpiobusvar.h24
-rw-r--r--sys/dev/gpio/gpioiic.c49
-rw-r--r--sys/dev/gpio/gpioled.c88
-rw-r--r--sys/dev/gpio/ofw_gpiobus.c337
-rw-r--r--sys/dev/iicbus/iicbb.c25
-rw-r--r--sys/dev/ofw/ofw_bus_if.m2
-rw-r--r--sys/dev/ofw/ofw_iicbus.c1
-rw-r--r--sys/modules/gpio/gpiobus/Makefile2
-rw-r--r--sys/modules/gpio/gpioiic/Makefile1
-rw-r--r--sys/modules/gpio/gpioled/Makefile2
-rw-r--r--sys/modules/i2c/iicbb/Makefile2
18 files changed, 576 insertions, 34 deletions
diff --git a/sys/arm/broadcom/bcm2835/bcm2835_gpio.c b/sys/arm/broadcom/bcm2835/bcm2835_gpio.c
index 9c5f5cd..7e8ead7 100644
--- a/sys/arm/broadcom/bcm2835/bcm2835_gpio.c
+++ b/sys/arm/broadcom/bcm2835/bcm2835_gpio.c
@@ -762,6 +762,14 @@ bcm_gpio_detach(device_t dev)
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 @@ static device_method_t bcm_gpio_methods[] = {
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
};
diff --git a/sys/arm/conf/BEAGLEBONE b/sys/arm/conf/BEAGLEBONE
index 3c6bebb..5fc02c1 100644
--- a/sys/arm/conf/BEAGLEBONE
+++ b/sys/arm/conf/BEAGLEBONE
@@ -101,6 +101,7 @@ device am335x_pmic # AM335x Power Management IC (TPC65217)
# GPIO
device gpio
+device gpioled
# USB support
device usb
diff --git a/sys/arm/ti/ti_gpio.c b/sys/arm/ti/ti_gpio.c
index 4edb10e..fdbf2c4 100644
--- a/sys/arm/ti/ti_gpio.c
+++ b/sys/arm/ti/ti_gpio.c
@@ -543,9 +543,9 @@ ti_gpio_pin_get(device_t dev, uint32_t pin, unsigned int *value)
/* Read the value on the pin */
if (val & mask)
- *value = (ti_gpio_read_4(sc, bank, TI_GPIO_DATAOUT) & mask) ? 1 : 0;
- else
*value = (ti_gpio_read_4(sc, bank, TI_GPIO_DATAIN) & mask) ? 1 : 0;
+ else
+ *value = (ti_gpio_read_4(sc, bank, TI_GPIO_DATAOUT) & mask) ? 1 : 0;
TI_GPIO_UNLOCK(sc);
@@ -788,6 +788,14 @@ ti_gpio_detach(device_t dev)
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 @@ static device_method_t ti_gpio_methods[] = {
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},
};
diff --git a/sys/boot/fdt/dts/beaglebone-black.dts b/sys/boot/fdt/dts/beaglebone-black.dts
index 2ab5066..5da633f 100644
--- a/sys/boot/fdt/dts/beaglebone-black.dts
+++ b/sys/boot/fdt/dts/beaglebone-black.dts
@@ -153,6 +153,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";
diff --git a/sys/boot/fdt/dts/bindings-gpio.txt b/sys/boot/fdt/dts/bindings-gpio.txt
index 492616e..b13078a 100644
--- a/sys/boot/fdt/dts/bindings-gpio.txt
+++ b/sys/boot/fdt/dts/bindings-gpio.txt
@@ -82,7 +82,7 @@ dir:
flags:
0x0000---- IN_NONE
- 0x0001---- IN_POL_LOW Polarity low (inverted input value.
+ 0x0001---- IN_POL_LOW Polarity low (active-low).
0x0002---- IN_IRQ_EDGE Interrupt, edge triggered.
0x0004---- IN_IRQ_LEVEL Interrupt, level triggered.
diff --git a/sys/conf/files b/sys/conf/files
index 5d78804..ed2dd01 100644
--- a/sys/conf/files
+++ b/sys/conf/files
@@ -1439,6 +1439,7 @@ dev/gpio/gpioiic.c optional gpioiic
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
diff --git a/sys/dev/gpio/gpiobus.c b/sys/dev/gpio/gpiobus.c
index 6abb10c..d8eacc6 100644
--- a/sys/dev/gpio/gpiobus.c
+++ b/sys/dev/gpio/gpiobus.c
@@ -46,7 +46,6 @@ __FBSDID("$FreeBSD$");
#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_set(device_t, device_t, uint32_t, unsigned int);
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;
@@ -175,7 +164,8 @@ static int
gpiobus_probe(device_t dev)
{
device_set_desc(dev, "GPIO bus");
- return (0);
+
+ return (BUS_PROBE_GENERIC);
}
static int
@@ -190,13 +180,13 @@ gpiobus_attach(device_t dev)
if (res)
return (ENXIO);
+ KASSERT(sc->sc_npins != 0, ("GPIO device with no pins"));
+
/*
* Increase to get number of pins
*/
sc->sc_npins++;
- KASSERT(sc->sc_npins != 0, ("GPIO device with no pins"));
-
sc->sc_pins_mapped = malloc(sizeof(int) * sc->sc_npins, M_DEVBUF,
M_NOWAIT | M_ZERO);
@@ -209,7 +199,9 @@ gpiobus_attach(device_t dev)
/*
* Get parent's pins and mark them as unmapped
*/
+ bus_generic_probe(dev);
bus_enumerate_hinted_children(dev);
+
return (bus_generic_attach(dev));
}
diff --git a/sys/dev/gpio/gpiobusvar.h b/sys/dev/gpio/gpiobusvar.h
index 7a79aed..94ae1ad 100644
--- a/sys/dev/gpio/gpiobusvar.h
+++ b/sys/dev/gpio/gpiobusvar.h
@@ -30,12 +30,25 @@
#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
{
@@ -54,4 +67,11 @@ struct gpiobus_ivar
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__ */
diff --git a/sys/dev/gpio/gpioiic.c b/sys/dev/gpio/gpioiic.c
index 2a6d093..d7772b6 100644
--- a/sys/dev/gpio/gpioiic.c
+++ b/sys/dev/gpio/gpioiic.c
@@ -28,21 +28,24 @@
#include <sys/cdefs.h>
__FBSDID("$FreeBSD$");
+#include "opt_platform.h"
+
#include <sys/param.h>
#include <sys/systm.h>
-#include <sys/bio.h>
#include <sys/bus.h>
#include <sys/conf.h>
#include <sys/kernel.h>
-#include <sys/kthread.h>
-#include <sys/lock.h>
-#include <sys/malloc.h>
#include <sys/module.h>
-#include <sys/mutex.h>
#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>
@@ -55,7 +58,6 @@ struct gpioiic_softc
{
device_t sc_dev;
device_t sc_busdev;
- struct cdev *sc_leddev;
int scl_pin;
int sda_pin;
};
@@ -77,7 +79,12 @@ static int
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);
}
@@ -86,6 +93,10 @@ gpioiic_attach(device_t dev)
{
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);
@@ -96,6 +107,15 @@ gpioiic_attach(device_t dev)
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);
@@ -139,7 +159,7 @@ gpioiic_callback(device_t dev, int index, caddr_t data)
error = EINVAL;
}
- return(error);
+ return (error);
}
static void
@@ -214,6 +234,16 @@ gpioiic_reset(device_t dev, u_char speed, u_char addr, u_char *oldaddr)
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[] = {
@@ -230,6 +260,11 @@ static device_method_t gpioiic_methods[] = {
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 }
};
diff --git a/sys/dev/gpio/gpioled.c b/sys/dev/gpio/gpioled.c
index a03c172..1377a14 100644
--- a/sys/dev/gpio/gpioled.c
+++ b/sys/dev/gpio/gpioled.c
@@ -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 @@ __FBSDID("$FreeBSD$");
#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"
@@ -75,6 +83,8 @@ gpioled_control(void *priv, int onoff)
GPIOLED_LOCK(sc);
GPIOBUS_LOCK_BUS(sc->sc_busdev);
GPIOBUS_ACQUIRE_BUS(sc->sc_busdev, sc->sc_dev);
+ GPIOBUS_PIN_SETFLAGS(sc->sc_busdev, sc->sc_dev, GPIOLED_PIN,
+ GPIO_PIN_OUTPUT);
GPIOBUS_PIN_SET(sc->sc_busdev, sc->sc_dev, GPIOLED_PIN,
onoff ? GPIO_PIN_HIGH : GPIO_PIN_LOW);
GPIOBUS_RELEASE_BUS(sc->sc_busdev, sc->sc_dev);
@@ -82,10 +92,65 @@ gpioled_control(void *priv, int onoff)
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);
}
@@ -93,21 +158,35 @@ static int
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;
-
- GPIOBUS_PIN_SETFLAGS(sc->sc_busdev, sc->sc_dev, GPIOLED_PIN,
- GPIO_PIN_OUTPUT);
+#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);
}
@@ -130,6 +209,9 @@ static devclass_t gpioled_devclass;
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),
diff --git a/sys/dev/gpio/ofw_gpiobus.c b/sys/dev/gpio/ofw_gpiobus.c
new file mode 100644
index 0000000..7641b56
--- /dev/null
+++ b/sys/dev/gpio/ofw_gpiobus.c
@@ -0,0 +1,337 @@
+/*-
+ * 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/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;
+
+ /* 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);
diff --git a/sys/dev/iicbus/iicbb.c b/sys/dev/iicbus/iicbb.c
index 44a069f..977d52a 100644
--- a/sys/dev/iicbus/iicbb.c
+++ b/sys/dev/iicbus/iicbb.c
@@ -43,6 +43,8 @@ __FBSDID("$FreeBSD$");
*
*/
+#include "opt_platform.h"
+
#include <sys/param.h>
#include <sys/kernel.h>
#include <sys/systm.h>
@@ -50,6 +52,11 @@ __FBSDID("$FreeBSD$");
#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_write(device_t, const char *, int, int *, int);
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 @@ static device_method_t iicbb_methods[] = {
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 @@ iicbb_detach(device_t dev)
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 )
{
diff --git a/sys/dev/ofw/ofw_bus_if.m b/sys/dev/ofw/ofw_bus_if.m
index e2466b4..0034a15 100644
--- a/sys/dev/ofw/ofw_bus_if.m
+++ b/sys/dev/ofw/ofw_bus_if.m
@@ -158,7 +158,7 @@ METHOD const char * get_name {
} DEFAULT ofw_bus_default_get_name;
# Get the firmware node for the device dev on the bus. The default method will
-# return 0, which signals that there is no such node.
+# return -1, which signals that there is no such node.
METHOD phandle_t get_node {
device_t bus;
device_t dev;
diff --git a/sys/dev/ofw/ofw_iicbus.c b/sys/dev/ofw/ofw_iicbus.c
index 965721a..1574f6e 100644
--- a/sys/dev/ofw/ofw_iicbus.c
+++ b/sys/dev/ofw/ofw_iicbus.c
@@ -80,6 +80,7 @@ static devclass_t ofwiicbus_devclass;
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);
diff --git a/sys/modules/gpio/gpiobus/Makefile b/sys/modules/gpio/gpiobus/Makefile
index 1b7afe8..e868cba 100644
--- a/sys/modules/gpio/gpiobus/Makefile
+++ b/sys/modules/gpio/gpiobus/Makefile
@@ -33,7 +33,7 @@
KMOD= gpiobus
SRCS= gpiobus.c
-SRCS+= device_if.h bus_if.h gpio_if.h gpiobus_if.h
+SRCS+= device_if.h bus_if.h gpio_if.h gpiobus_if.h opt_platform.h
CFLAGS+= -I. -I${.CURDIR}/../../../dev/gpio/
diff --git a/sys/modules/gpio/gpioiic/Makefile b/sys/modules/gpio/gpioiic/Makefile
index be59861..abd835e 100644
--- a/sys/modules/gpio/gpioiic/Makefile
+++ b/sys/modules/gpio/gpioiic/Makefile
@@ -34,6 +34,7 @@
KMOD= gpioiic
SRCS= gpioiic.c
SRCS+= device_if.h bus_if.h gpio_if.h gpiobus_if.h iicbus_if.h iicbb_if.h
+SRCS+= opt_platform.h
CFLAGS+= -I. -I${.CURDIR}/../../../dev/gpio/
diff --git a/sys/modules/gpio/gpioled/Makefile b/sys/modules/gpio/gpioled/Makefile
index 0420b1b..cc5f860 100644
--- a/sys/modules/gpio/gpioled/Makefile
+++ b/sys/modules/gpio/gpioled/Makefile
@@ -33,7 +33,7 @@
KMOD= gpioled
SRCS= gpioled.c
-SRCS+= device_if.h bus_if.h gpio_if.h gpiobus_if.h
+SRCS+= device_if.h bus_if.h gpio_if.h gpiobus_if.h opt_platform.h
CFLAGS+= -I. -I${.CURDIR}/../../../dev/gpio/
diff --git a/sys/modules/i2c/iicbb/Makefile b/sys/modules/i2c/iicbb/Makefile
index ea991e0..75d1c83 100644
--- a/sys/modules/i2c/iicbb/Makefile
+++ b/sys/modules/i2c/iicbb/Makefile
@@ -3,6 +3,6 @@
.PATH: ${.CURDIR}/../../../dev/iicbus
KMOD = iicbb
SRCS = device_if.h bus_if.h iicbus_if.h \
- iicbb_if.h iicbb_if.c iicbb.c
+ iicbb_if.h iicbb_if.c iicbb.c opt_platform.h
.include <bsd.kmod.mk>
OpenPOWER on IntegriCloud