Index: head/sys/arm/mv/gpio.c =================================================================== --- head/sys/arm/mv/gpio.c +++ head/sys/arm/mv/gpio.c @@ -51,6 +51,7 @@ #include #include +#include #include #include #include @@ -58,8 +59,15 @@ #include #include +#include "gpio_if.h" + #define GPIO_MAX_INTR_COUNT 8 #define GPIO_PINS_PER_REG 32 +#define GPIO_GENERIC_CAP (GPIO_PIN_INPUT | GPIO_PIN_OUTPUT | \ + GPIO_PIN_OPENDRAIN | GPIO_PIN_PUSHPULL | \ + GPIO_PIN_TRISTATE | GPIO_PIN_PULLUP | \ + GPIO_PIN_PULLDOWN | GPIO_PIN_INVIN | \ + GPIO_PIN_INVOUT) #define DEBOUNCE_CHECK_MS 1 #define DEBOUNCE_LO_HI_MS 2 @@ -67,6 +75,7 @@ #define DEBOUNCE_CHECK_TICKS ((hz / 1000) * DEBOUNCE_CHECK_MS) struct mv_gpio_softc { + device_t sc_busdev; struct resource * mem_res; int mem_rid; struct resource * irq_res[GPIO_MAX_INTR_COUNT]; @@ -134,6 +143,19 @@ void mv_gpio_out(device_t, uint32_t, uint8_t, uint8_t); uint8_t mv_gpio_in(device_t, uint32_t); +/* + * GPIO interface + */ +static device_t mv_gpio_get_bus(device_t); +static int mv_gpio_pin_max(device_t, int *); +static int mv_gpio_pin_getcaps(device_t, uint32_t, uint32_t *); +static int mv_gpio_pin_getflags(device_t, uint32_t, uint32_t *); +static int mv_gpio_pin_getname(device_t, uint32_t, char *); +static int mv_gpio_pin_setflags(device_t, uint32_t, uint32_t); +static int mv_gpio_pin_set(device_t, uint32_t, unsigned int); +static int mv_gpio_pin_get(device_t, uint32_t, unsigned int *); +static int mv_gpio_pin_toggle(device_t, uint32_t); + #define MV_GPIO_LOCK() mtx_lock_spin(&sc->mutex) #define MV_GPIO_UNLOCK() mtx_unlock_spin(&sc->mutex) #define MV_GPIO_ASSERT_LOCKED() mtx_assert(&sc->mutex, MA_OWNED) @@ -141,7 +163,19 @@ static device_method_t mv_gpio_methods[] = { DEVMETHOD(device_probe, mv_gpio_probe), DEVMETHOD(device_attach, mv_gpio_attach), - { 0, 0 } + + /* GPIO protocol */ + DEVMETHOD(gpio_get_bus, mv_gpio_get_bus), + DEVMETHOD(gpio_pin_max, mv_gpio_pin_max), + DEVMETHOD(gpio_pin_getname, mv_gpio_pin_getname), + DEVMETHOD(gpio_pin_getflags, mv_gpio_pin_getflags), + DEVMETHOD(gpio_pin_getcaps, mv_gpio_pin_getcaps), + DEVMETHOD(gpio_pin_setflags, mv_gpio_pin_setflags), + DEVMETHOD(gpio_pin_get, mv_gpio_pin_get), + DEVMETHOD(gpio_pin_set, mv_gpio_pin_set), + DEVMETHOD(gpio_pin_toggle, mv_gpio_pin_toggle), + + DEVMETHOD_END }; static driver_t mv_gpio_driver = { @@ -152,31 +186,23 @@ static devclass_t mv_gpio_devclass; -DRIVER_MODULE(gpio, simplebus, mv_gpio_driver, mv_gpio_devclass, 0, 0); +DRIVER_MODULE(mv_gpio, simplebus, mv_gpio_driver, mv_gpio_devclass, 0, 0); -typedef int (*gpios_phandler_t)(device_t, phandle_t, pcell_t *, int); - -struct gpio_ctrl_entry { - const char *compat; - gpios_phandler_t handler; -}; - static int mv_handle_gpios_prop(device_t, phandle_t, pcell_t *, int); -int gpio_get_config_from_dt(void); -struct gpio_ctrl_entry gpio_controllers[] = { - { "mrvl,gpio", &mv_handle_gpios_prop }, - { NULL, NULL } +struct ofw_compat_data gpio_controllers[] = { + { "mrvl,gpio", (uintptr_t)&mv_handle_gpios_prop }, + { "marvell,orion-gpio", (uintptr_t)&mv_handle_gpios_prop }, + { NULL, 0 } }; static int mv_gpio_probe(device_t dev) { - if (!ofw_bus_status_okay(dev)) return (ENXIO); - if (!ofw_bus_is_compatible(dev, "mrvl,gpio")) + if (ofw_bus_search_compatible(dev, gpio_controllers)->ocd_data == 0) return (ENXIO); device_set_desc(dev, "Marvell Integrated GPIO Controller"); @@ -188,7 +214,6 @@ { int error, i, size; struct mv_gpio_softc *sc; - uint32_t dev_id, rev_id; pcell_t pincnt = 0; pcell_t irq_cells = 0; phandle_t iparent; @@ -197,34 +222,22 @@ if (sc == NULL) return (ENXIO); - /* Get chip id and revision */ - soc_id(&dev_id, &rev_id); - - if (dev_id == MV_DEV_88F5182 || - dev_id == MV_DEV_88F5281 || - dev_id == MV_DEV_MV78100 || - dev_id == MV_DEV_MV78100_Z0 ) { - sc->pin_num = 32; - sc->irq_num = 4; - - } else if (dev_id == MV_DEV_88F6281 || - dev_id == MV_DEV_88F6282) { - sc->pin_num = 50; - sc->irq_num = 7; - - } else { - if (OF_getencprop(ofw_bus_get_node(dev), "pin-count", &pincnt, - sizeof(pcell_t)) >= 0 || - OF_getencprop(ofw_bus_get_node(dev), "ngpios", &pincnt, - sizeof(pcell_t)) >= 0) { - sc->pin_num = pincnt; + if (OF_getencprop(ofw_bus_get_node(dev), "pin-count", &pincnt, + sizeof(pcell_t)) >= 0 || + OF_getencprop(ofw_bus_get_node(dev), "ngpios", &pincnt, + sizeof(pcell_t)) >= 0) { + sc->pin_num = MIN(pincnt, MV_GPIO_MAX_NPINS); + if (bootverbose) device_printf(dev, "%d pins available\n", sc->pin_num); - } else { - device_printf(dev, "ERROR: no pin-count entry found!\n"); - return (ENXIO); - } + } else { + device_printf(dev, "ERROR: no pin-count or ngpios entry found!\n"); + return (ENXIO); } + /* Assign generic capabilities to every gpio pin */ + for(i = 0; i < sc->pin_num; i++) + sc->gpio_setup[i].gp_caps = GPIO_GENERIC_CAP; + /* Find root interrupt controller */ iparent = ofw_bus_find_iparent(ofw_bus_get_node(dev)); if (iparent == 0) { @@ -315,8 +328,13 @@ /* Clear interrupt status. */ bus_space_write_4(sc->bst, sc->bsh, GPIO_INT_CAUSE, 0); - device_add_child(dev, "gpioc", device_get_unit(dev)); - device_add_child(dev, "gpiobus", device_get_unit(dev)); + sc->sc_busdev = gpiobus_attach_bus(dev); + if (sc->sc_busdev == NULL) { + mtx_destroy(&sc->mutex); + bus_release_resource(dev, SYS_RES_IRQ, + sc->irq_rid[i], sc->irq_res[i]); + return (ENXIO); + } return (0); } @@ -535,6 +553,16 @@ MV_GPIO_LOCK(); + if ((mask & flags) & GPIO_PIN_INPUT) + mv_gpio_out_en(dev, pin, 0); + if ((mask & flags) & GPIO_PIN_OUTPUT) { + if ((flags & mask) & GPIO_PIN_OPENDRAIN) + mv_gpio_value_set(dev, pin, 0); + else + mv_gpio_value_set(dev, pin, 1); + mv_gpio_out_en(dev, pin, 1); + } + if (mask & MV_GPIO_OUT_BLINK) mv_gpio_blink(dev, pin, flags & MV_GPIO_OUT_BLINK); if (mask & MV_GPIO_IN_POL_LOW) @@ -799,7 +827,7 @@ struct mv_gpio_softc *sc; sc = (struct mv_gpio_softc *)device_get_softc(dev); - MV_GPIO_LOCK(); + MV_GPIO_ASSERT_LOCKED(); if (sc->gpio_setup[pin].gp_flags & MV_GPIO_IN_DEBOUNCE) { if (sc->gpio_setup[pin].gp_flags & MV_GPIO_IN_POL_LOW) @@ -814,7 +842,6 @@ } else state = (mv_gpio_value_get(dev, pin, 0) ? 1 : 0); - return (state); } @@ -997,6 +1024,8 @@ struct mv_gpio_softc *sc; sc = (struct mv_gpio_softc *)device_get_softc(dev); + MV_GPIO_ASSERT_LOCKED(); + if (pin >= sc->pin_num) return; @@ -1013,9 +1042,8 @@ { pcell_t gpio_cells, pincnt; int inc, t, tuples, tuple_size; - int dir, flags, pin; + int flags, pin; u_long gpio_ctrl, size; - struct mv_gpio_softc sc; pincnt = 0; if (!OF_hasprop(ctrl, "gpio-controller")) @@ -1024,7 +1052,7 @@ if (OF_getencprop(ctrl, "#gpio-cells", &gpio_cells, sizeof(pcell_t)) < 0) return (ENXIO); - if (gpio_cells != 3) + if (gpio_cells != 2) return (ENXIO); tuple_size = gpio_cells * sizeof(pcell_t) + sizeof(phandle_t); @@ -1033,10 +1061,6 @@ if (fdt_regsize(ctrl, &gpio_ctrl, &size)) return (ENXIO); - if (OF_getencprop(ctrl, "pin-count", &pincnt, sizeof(pcell_t)) < 0) - return (ENXIO); - sc.pin_num = pincnt; - /* * Skip controller reference, since controller's phandle is given * explicitly (in a function argument). @@ -1046,22 +1070,9 @@ for (t = 0; t < tuples; t++) { pin = gpios[0]; - dir = gpios[1]; - flags = gpios[2]; + flags = gpios[1]; mv_gpio_configure(dev, pin, flags, ~0); - - if (dir == 1) - /* Input. */ - mv_gpio_out_en(dev, pin, 0); - else { - /* Output. */ - if (flags & MV_GPIO_OUT_OPEN_DRAIN) - mv_gpio_out(dev, pin, 0, 1); - - if (flags & MV_GPIO_OUT_OPEN_SRC) - mv_gpio_out(dev, pin, 1, 1); - } gpios += gpio_cells + inc; } @@ -1075,12 +1086,13 @@ { phandle_t child, parent, root, ctrl; pcell_t gpios[MAX_PINS_PER_NODE * GPIOS_PROP_CELLS]; - struct gpio_ctrl_entry *e; + struct ofw_compat_data *e; int len, rv; root = OF_finddevice("/"); len = 0; parent = root; + rv = 0; /* Traverse through entire tree to find nodes with 'gpios' prop */ for (child = OF_child(parent); child != 0; child = OF_peer(child)) { @@ -1098,30 +1110,175 @@ /* Get 'gpios' property. */ OF_getencprop(child, "gpios", gpios, len); - e = (struct gpio_ctrl_entry *)&gpio_controllers; + /* + * First cell of 'gpios' property should + * contain a ref. to a node defining GPIO + * controller. + */ + ctrl = OF_node_from_xref(gpios[0]); + if(ctrl != ofw_bus_get_node(dev)) { + /* Not this gpio controller */ + device_printf(dev, "Not this gpio controller ctrl: %x, dev: %x\n", + ctrl, ofw_bus_get_node(dev)); + continue; + } + + e = 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_node_from_xref(gpios[0]); + for (; e->ocd_str; e++) { - if (ofw_bus_node_is_compatible(ctrl, e->compat)) + if (ofw_bus_node_is_compatible(ctrl,e->ocd_str)) { /* Call a handler. */ - if ((rv = e->handler(dev, ctrl, - (pcell_t *)&gpios, len))) - return (rv); + rv |= mv_handle_gpios_prop(dev, ctrl, + (pcell_t *)&gpios, len); + } } } - if (OF_peer(child) == 0) { + while (OF_peer(child) == 0 && parent != root) { /* No more siblings. */ child = parent; parent = OF_parent(child); } } + return (rv); +} + +static int +mv_gpio_pin_max(device_t dev, int *maxpin) +{ + struct mv_gpio_softc *sc; + if (maxpin == NULL) + return (EINVAL); + + sc = device_get_softc(dev); + *maxpin = sc->pin_num; + return (0); +} + +static int +mv_gpio_pin_getcaps(device_t dev, uint32_t pin, uint32_t *caps) +{ + struct mv_gpio_softc *sc = device_get_softc(dev); + if (caps == NULL) + return (EINVAL); + + if (pin >= sc->pin_num) + return (EINVAL); + + MV_GPIO_LOCK(); + *caps = sc->gpio_setup[pin].gp_caps; + MV_GPIO_UNLOCK(); + + return (0); +} + +static int +mv_gpio_pin_getflags(device_t dev, uint32_t pin, uint32_t *flags) +{ + struct mv_gpio_softc *sc = device_get_softc(dev); + if (flags == NULL) + return (EINVAL); + + if (pin >= sc->pin_num) + return (EINVAL); + + MV_GPIO_LOCK(); + *flags = sc->gpio_setup[pin].gp_flags; + MV_GPIO_UNLOCK(); + + return (0); +} + +static int +mv_gpio_pin_getname(device_t dev, uint32_t pin, char *name) +{ + struct mv_gpio_softc *sc = device_get_softc(dev); + if (name == NULL) + return (EINVAL); + + if (pin >= sc->pin_num) + return (EINVAL); + + MV_GPIO_LOCK(); + memcpy(name, sc->gpio_setup[pin].gp_name, GPIOMAXNAME); + MV_GPIO_UNLOCK(); + + return (0); +} + +static int +mv_gpio_pin_setflags(device_t dev, uint32_t pin, uint32_t flags) +{ + int ret; + struct mv_gpio_softc *sc = device_get_softc(dev); + if (pin >= sc->pin_num) + return (EINVAL); + + /* Check for unwanted flags. */ + if ((flags & sc->gpio_setup[pin].gp_caps) != flags) + return (EINVAL); + + ret = mv_gpio_configure(dev, pin, flags, ~0); + + return (ret); +} + +static int +mv_gpio_pin_set(device_t dev, uint32_t pin, unsigned int value) +{ + struct mv_gpio_softc *sc = device_get_softc(dev); + if (pin >= sc->pin_num) + return (EINVAL); + + MV_GPIO_LOCK(); + mv_gpio_value_set(dev, pin, value); + MV_GPIO_UNLOCK(); + + return (0); +} + +static int +mv_gpio_pin_get(device_t dev, uint32_t pin, unsigned int *value) +{ + struct mv_gpio_softc *sc = device_get_softc(dev); + if (value == NULL) + return (EINVAL); + + if (pin >= sc->pin_num) + return (EINVAL); + + MV_GPIO_LOCK(); + *value = mv_gpio_in(dev, pin); + MV_GPIO_UNLOCK(); + + return (0); +} + +static int +mv_gpio_pin_toggle(device_t dev, uint32_t pin) +{ + struct mv_gpio_softc *sc = device_get_softc(dev); + uint32_t value; + if (pin >= sc->pin_num) + return (EINVAL); + + MV_GPIO_LOCK(); + value = mv_gpio_in(dev, pin); + value = (~value) & 1; + mv_gpio_value_set(dev, pin, value); + MV_GPIO_UNLOCK(); + + return (0); +} + +static device_t +mv_gpio_get_bus(device_t dev) +{ + struct mv_gpio_softc *sc = device_get_softc(dev); + + return (sc->sc_busdev); } Index: head/sys/dts/arm/db78100.dts =================================================================== --- head/sys/dts/arm/db78100.dts +++ head/sys/dts/arm/db78100.dts @@ -165,7 +165,7 @@ }; GPIO: gpio@10100 { - #gpio-cells = <3>; + #gpio-cells = <2>; compatible = "mrvl,gpio"; reg = <0x10100 0x20>; gpio-controller; Index: head/sys/dts/arm/db88f5182.dts =================================================================== --- head/sys/dts/arm/db88f5182.dts +++ head/sys/dts/arm/db88f5182.dts @@ -152,7 +152,7 @@ }; GPIO: gpio@10100 { - #gpio-cells = <3>; + #gpio-cells = <2>; compatible = "mrvl,gpio"; reg = <0x10100 0x20>; gpio-controller; Index: head/sys/dts/arm/db88f5281.dts =================================================================== --- head/sys/dts/arm/db88f5281.dts +++ head/sys/dts/arm/db88f5281.dts @@ -152,7 +152,7 @@ }; GPIO: gpio@10100 { - #gpio-cells = <3>; + #gpio-cells = <2>; compatible = "mrvl,gpio"; reg = <0x10100 0x20>; gpio-controller; Index: head/sys/dts/arm/db88f6281.dts =================================================================== --- head/sys/dts/arm/db88f6281.dts +++ head/sys/dts/arm/db88f6281.dts @@ -156,7 +156,7 @@ }; GPIO: gpio@10100 { - #gpio-cells = <3>; + #gpio-cells = <2>; compatible = "mrvl,gpio"; reg = <0x10100 0x20>; gpio-controller; Index: head/sys/dts/arm/dockstar.dts =================================================================== --- head/sys/dts/arm/dockstar.dts +++ head/sys/dts/arm/dockstar.dts @@ -141,7 +141,7 @@ }; GPIO: gpio@10100 { - #gpio-cells = <3>; + #gpio-cells = <2>; compatible = "mrvl,gpio"; reg = <0x10100 0x20>; gpio-controller; Index: head/sys/dts/arm/sheevaplug.dts =================================================================== --- head/sys/dts/arm/sheevaplug.dts +++ head/sys/dts/arm/sheevaplug.dts @@ -153,7 +153,7 @@ }; GPIO: gpio@10100 { - #gpio-cells = <3>; + #gpio-cells = <2>; compatible = "mrvl,gpio"; reg = <0x10100 0x20>; gpio-controller; Index: head/sys/dts/bindings-gpio.txt =================================================================== --- head/sys/dts/bindings-gpio.txt +++ head/sys/dts/bindings-gpio.txt @@ -65,37 +65,42 @@ information like pin number, direction and various flags. Example: - gpios = <&GPIO 0 1 0 /* GPIO[0]: IN, NONE */ - &GPIO 1 2 0>; /* GPIO[1]: OUT, NONE */ + gpios = <&GPIO 0 1 /* GPIO[0]: FLAGS */ + &GPIO 1 2>; /* GPIO[1]: FLAGS */ -3. "mrvl,gpio" controller GPIO specifier +3. GPIO controller specifier pin: 0-MAX GPIO pin number. -dir: - 1 IN Input direction. - 2 OUT Output direction. - flags: - 0x0000---- IN_NONE - 0x0001---- IN_POL_LOW Polarity low (active-low). - 0x0002---- IN_IRQ_EDGE Interrupt, edge triggered. - 0x0004---- IN_IRQ_LEVEL Interrupt, level triggered. - - 0x----0000 OUT_NONE - 0x----0001 OUT_BLINK Blink on the pin. - 0x----0002 OUT_OPEN_DRAIN Open drain output line. - 0x----0004 OUT_OPEN_SRC Open source output line. + Available flags are listed in sys/conf.h. Following combination + can be supported by the controller. For details please refer + to controller's GPIO reference manual. + GPIO_PIN_INPUT 0x0001 Input direction + GPIO_PIN_OUTPUT 0x0002 Output direction + GPIO_PIN_OPENDRAIN 0x0004 Open-drain output + GPIO_PIN_OPENSOURCE 0x0008 Open-source output + GPIO_PIN_PUSHPULL 0x0010 Push-pull output + GPIO_PIN_TRISTATE 0x0020 Output disabled + GPIO_PIN_PULLUP 0x0040 Internal pull-up enabled + GPIO_PIN_PULLDOWN 0x0080 Internal pull-down enabled + GPIO_PIN_INVIN 0x0100 Invert input + GPIO_PIN_INVOUT 0x0200 Invert output + GPIO_PIN_PULSATE 0x0400 Pulsate in hardware + GPIO_PIN_IRQ_POL_EDG 0x0800 IRQ active single edge + GPIO_PIN_IRQ_POL_DBL 0x1000 IRQ active double edge + GPIO_PIN_IRQ_POL_LVL 0x2000 IRQ active level + GPIO_PIN_IRQ_DEBOUNCE 0x4000 Debounce on IRQ pin Example: - gpios = <&GPIO 0 1 0x00000000 /* GPIO[0]: IN */ - &GPIO 1 2 0x00000000 /* GPIO[1]: OUT */ - &GPIO 2 1 0x00020000 /* GPIO[2]: IN, IRQ (edge) */ - &GPIO 3 1 0x00040000 /* GPIO[3]: IN, IRQ (level) */ + gpios = <&GPIO 0 0x00000001 /* GPIO[0]: IN */ + &GPIO 1 0x00000002 /* GPIO[1]: OUT */ + &GPIO 2 0x00000801 /* GPIO[2]: IN, IRQ (edge) */ + &GPIO 3 0x00004001 /* GPIO[3]: IN, IRQ (level) */ ... - &GPIO 10 2 0x00000001>; /* GPIO[10]: OUT, blink */ + &GPIO 10 0x00000401>; /* GPIO[10]: OUT, blink */