summaryrefslogtreecommitdiffstats
path: root/sys/arm/rockchip
diff options
context:
space:
mode:
authorganbold <ganbold@FreeBSD.org>2013-12-09 07:14:59 +0000
committerganbold <ganbold@FreeBSD.org>2013-12-09 07:14:59 +0000
commit5843ce9aaef9d88319c5071ef02dba8d7c9190ce (patch)
treeb3c42d657946acec3db63c47b7e87b221255092f /sys/arm/rockchip
parent1d613bedf47ce75a9c5fd5ca24dd293267e6e921 (diff)
downloadFreeBSD-src-5843ce9aaef9d88319c5071ef02dba8d7c9190ce.zip
FreeBSD-src-5843ce9aaef9d88319c5071ef02dba8d7c9190ce.tar.gz
Add gpio parse routines according to sys/boot/fdt/dts/bindings-gpio.txt.
Reviewed by: stas@
Diffstat (limited to 'sys/arm/rockchip')
-rw-r--r--sys/arm/rockchip/rk30xx_gpio.c146
1 files changed, 146 insertions, 0 deletions
diff --git a/sys/arm/rockchip/rk30xx_gpio.c b/sys/arm/rockchip/rk30xx_gpio.c
index 27a0126..fd8223a 100644
--- a/sys/arm/rockchip/rk30xx_gpio.c
+++ b/sys/arm/rockchip/rk30xx_gpio.c
@@ -86,6 +86,26 @@ struct rk30_gpio_softc {
struct gpio_pin sc_gpio_pins[RK30_GPIO_PINS];
};
+static struct rk30_gpio_softc *rk30_gpio_sc = NULL;
+
+typedef int (*gpios_phandler_t)(phandle_t, pcell_t *, int);
+
+struct gpio_ctrl_entry {
+ const char *compat;
+ gpios_phandler_t handler;
+};
+
+int rk30_gpios_prop_handle(phandle_t ctrl, pcell_t *gpios, int len);
+int platform_gpio_init(void);
+
+struct gpio_ctrl_entry gpio_controllers[] = {
+ { "rockchip,rk30xx-gpio", &rk30_gpios_prop_handle },
+ { "rockchip,rk30xx-gpio", &rk30_gpios_prop_handle },
+ { "rockchip,rk30xx-gpio", &rk30_gpios_prop_handle },
+ { "rockchip,rk30xx-gpio", &rk30_gpios_prop_handle },
+ { NULL, NULL }
+};
+
#define RK30_GPIO_LOCK(_sc) mtx_lock(&_sc->sc_mtx)
#define RK30_GPIO_UNLOCK(_sc) mtx_unlock(&_sc->sc_mtx)
#define RK30_GPIO_LOCK_ASSERT(_sc) mtx_assert(&_sc->sc_mtx, MA_OWNED)
@@ -436,6 +456,9 @@ rk30_gpio_attach(device_t dev)
int i, rid;
phandle_t gpio;
+ if (rk30_gpio_sc)
+ return (ENXIO);
+
sc->sc_dev = dev;
mtx_init(&sc->sc_mtx, "rk30 gpio", "gpio", MTX_DEF);
@@ -480,6 +503,11 @@ rk30_gpio_attach(device_t dev)
device_add_child(dev, "gpioc", device_get_unit(dev));
device_add_child(dev, "gpiobus", device_get_unit(dev));
+
+ rk30_gpio_sc = sc;
+
+ platform_gpio_init();
+
return (bus_generic_attach(dev));
fail:
@@ -525,3 +553,121 @@ static driver_t rk30_gpio_driver = {
};
DRIVER_MODULE(rk30_gpio, simplebus, rk30_gpio_driver, rk30_gpio_devclass, 0, 0);
+
+int
+rk30_gpios_prop_handle(phandle_t ctrl, pcell_t *gpios, int len)
+{
+ struct rk30_gpio_softc *sc;
+ pcell_t gpio_cells;
+ int inc, t, tuples, tuple_size;
+ int dir, flags, pin, i;
+ u_long gpio_ctrl, size;
+
+ sc = rk30_gpio_sc;
+ if (sc == NULL)
+ return ENXIO;
+
+ if (OF_getprop(ctrl, "#gpio-cells", &gpio_cells, sizeof(pcell_t)) < 0)
+ return (ENXIO);
+
+ gpio_cells = fdt32_to_cpu(gpio_cells);
+ if (gpio_cells != 2)
+ return (ENXIO);
+
+ tuple_size = gpio_cells * sizeof(pcell_t) + sizeof(phandle_t);
+ tuples = len / tuple_size;
+
+ if (fdt_regsize(ctrl, &gpio_ctrl, &size))
+ return (ENXIO);
+
+ /*
+ * Skip controller reference, since controller's phandle is given
+ * explicitly (in a function argument).
+ */
+ inc = sizeof(ihandle_t) / sizeof(pcell_t);
+ gpios += inc;
+ for (t = 0; t < tuples; t++) {
+ pin = fdt32_to_cpu(gpios[0]);
+ dir = fdt32_to_cpu(gpios[1]);
+ flags = fdt32_to_cpu(gpios[2]);
+
+ for (i = 0; i < sc->sc_gpio_npins; i++) {
+ if (sc->sc_gpio_pins[i].gp_pin == pin)
+ break;
+ }
+ if (i >= sc->sc_gpio_npins)
+ return (EINVAL);
+
+ rk30_gpio_pin_configure(sc, &sc->sc_gpio_pins[i], flags);
+
+ if (dir == 1) {
+ /* Input. */
+ rk30_gpio_pin_set(sc->sc_dev, pin, RK30_GPIO_INPUT);
+ } else {
+ /* Output. */
+ rk30_gpio_pin_set(sc->sc_dev, pin, RK30_GPIO_OUTPUT);
+ }
+ gpios += gpio_cells + inc;
+ }
+
+ return (0);
+}
+
+#define MAX_PINS_PER_NODE 5
+#define GPIOS_PROP_CELLS 4
+
+int
+platform_gpio_init(void)
+{
+ phandle_t child, parent, root, ctrl;
+ pcell_t gpios[MAX_PINS_PER_NODE * GPIOS_PROP_CELLS];
+ struct gpio_ctrl_entry *e;
+ int len, rv;
+
+ root = OF_finddevice("/");
+ len = 0;
+ parent = root;
+
+ /* Traverse through entire tree to find nodes with 'gpios' prop */
+ for (child = OF_child(parent); child != 0; child = OF_peer(child)) {
+
+ /* Find a 'leaf'. Start the search from this node. */
+ while (OF_child(child)) {
+ parent = child;
+ child = OF_child(child);
+ }
+ if ((len = OF_getproplen(child, "gpios")) > 0) {
+
+ if (len > sizeof(gpios))
+ return (ENXIO);
+
+ /* Get 'gpios' property. */
+ OF_getprop(child, "gpios", &gpios, len);
+
+ e = (struct gpio_ctrl_entry *)&gpio_controllers;
+
+ /* Find and call a handler. */
+ for (; e->compat; e++) {
+ /*
+ * First cell of 'gpios' property should
+ * contain a ref. to a node defining GPIO
+ * controller.
+ */
+ ctrl = OF_xref_phandle(fdt32_to_cpu(gpios[0]));
+
+ if (fdt_is_compatible(ctrl, e->compat))
+ /* Call a handler. */
+ if ((rv = e->handler(ctrl,
+ (pcell_t *)&gpios, len)))
+ return (rv);
+ }
+ }
+
+ if (OF_peer(child) == 0) {
+ /* No more siblings. */
+ child = parent;
+ parent = OF_parent(child);
+ }
+ }
+ return (0);
+}
OpenPOWER on IntegriCloud