Index: sys/arm64/rockchip/rk805.c =================================================================== --- sys/arm64/rockchip/rk805.c +++ sys/arm64/rockchip/rk805.c @@ -32,13 +32,17 @@ #include #include #include +#include #include #include #include #include #include +#include #include +#include + #include #include @@ -48,6 +52,8 @@ #include #include +#include + #include #include "clock_if.h" @@ -697,6 +703,317 @@ return (0); } +/* -------------------------------------------------------------------------- */ +/* GPIO, pinctrl and OFW bus. */ + +#define RK805_NPINS 2 + +static struct rk805_pinconf { + int npins; + const char *names[RK805_NPINS]; +} rk805_pinconf = { + RK805_NPINS, + { + "gpio0", + "gpio1", + } +}; + +struct rk805_gpio_softc { + struct sx lock; + device_t dev; + device_t base_dev; + device_t bus_dev; + struct rk805_pinconf *pinconf; +}; + +static device_t +rk805_gpio_get_bus(device_t dev) +{ + struct rk805_gpio_softc *sc; + + sc = device_get_softc(dev); + return (sc->bus_dev); +} + +static int +rk805_gpio_pin_max(device_t dev, int *maxpin) +{ + struct rk805_gpio_softc *sc; + + sc = device_get_softc(dev); + *maxpin = sc->pinconf->npins - 1; + return (0); +} + +static int +rk805_gpio_pin_getcaps(device_t dev, uint32_t pin, uint32_t *caps) +{ + struct rk805_gpio_softc *sc; + + sc = device_get_softc(dev); + if (pin >= sc->pinconf->npins) + return (EINVAL); + + *caps = GPIO_PIN_OUTPUT; + return (0); +} + +static int +rk805_gpio_pin_getflags(device_t dev, uint32_t pin, uint32_t *flags) +{ + struct rk805_gpio_softc *sc; + + sc = device_get_softc(dev); + if (pin >= sc->pinconf->npins) + return (EINVAL); + + *flags = GPIO_PIN_OUTPUT; + return (0); +} + +static int +rk805_gpio_pin_getname(device_t dev, uint32_t pin, char *name) +{ + struct rk805_gpio_softc *sc; + + sc = device_get_softc(dev); + if (pin >= sc->pinconf->npins) + return (EINVAL); + + snprintf(name, GPIOMAXNAME, "%s", sc->pinconf->names[pin]); + return (0); +} + +static int +rk805_gpio_pin_setflags(device_t dev, uint32_t pin, uint32_t flags) +{ + struct rk805_gpio_softc *sc; + + sc = device_get_softc(dev); + if (pin > sc->pinconf->npins) + return (EINVAL); + + if ((flags & ~GPIO_PIN_OUTPUT) != 0) + return (EINVAL); + return (0); +} + +static int +rk805_gpio_pin_set(device_t dev, uint32_t pin, unsigned int value) +{ + struct rk805_gpio_softc *sc; + uint8_t val; + int ret; + + sc = device_get_softc(dev); + if (pin > sc->pinconf->npins) + return (EINVAL); + + sx_xlock(&sc->lock); + ret = rk805_read(sc->base_dev, RK805_OUT, &val, 1); + if (ret == 0) { + if (value) + val |= (1 << pin); + else + val &= ~(1 << pin); + ret = rk805_write(sc->base_dev, RK805_OUT, &val, 1); + } + sx_unlock(&sc->lock); + return (ret); +} + +static int +rk805_gpio_pin_get(device_t dev, uint32_t pin, unsigned int *value) +{ + struct rk805_gpio_softc *sc; + uint8_t val; + int ret; + + sc = device_get_softc(dev); + if (pin > sc->pinconf->npins) + return (EINVAL); + + sx_xlock(&sc->lock); + ret = rk805_read(sc->base_dev, RK805_OUT, &val, 1); + sx_unlock(&sc->lock); + if (ret == 0) + *value = (val & (1 << pin)) != 0; + return (ret); +} + +static int +rk805_gpio_pin_toggle(device_t dev, uint32_t pin) +{ + struct rk805_gpio_softc *sc; + uint8_t val; + int ret; + + sc = device_get_softc(dev); + if (pin > sc->pinconf->npins) + return (EINVAL); + + sx_xlock(&sc->lock); + ret = rk805_read(sc->base_dev, RK805_OUT, &val, 1); + if (ret == 0) { + val ^= (1 << pin); + ret = rk805_write(sc->base_dev, RK805_OUT, &val, 1); + } + sx_unlock(&sc->lock); + return (ret); +} + +static int +rk805_gpio_map_gpios(device_t bus, phandle_t dev, phandle_t gparent, int gcells, + pcell_t *gpios, uint32_t *pin, uint32_t *flags) +{ + if (gcells != 2) + return (EINVAL); + + *pin = gpios[0]; + *flags = gpios[1]; + return (0); +} + +static int +rk805_find_pinnum_by_name(struct rk805_gpio_softc *sc, const char *pinname) +{ + int i; + + for (i = 0; i < sc->pinconf->npins; i++) { + if (strcmp(pinname, sc->pinconf->names[i]) == 0) + return (i); + } + return (-1); +} + +static int +rk805_fdt_configure_pins(device_t dev, phandle_t cfgxref) +{ + struct rk805_gpio_softc *sc; + phandle_t node; + const char **pinlist = NULL; + char *pin_function = NULL; + int pins_nb, pin_num, i, ret; + + sc = device_get_softc(dev); + node = OF_node_from_xref(cfgxref); + ret = 0; + + /* + * Only one function, "gpio", is supported. + * So, just validate the configuration. + */ + pins_nb = ofw_bus_string_list_to_array(node, "pins", &pinlist); + if (pins_nb <= 0) + return (ENOENT); + for (i = 0; i < pins_nb; i++) { + pin_num = rk805_find_pinnum_by_name(sc, pinlist[i]); + if (pin_num == -1) { + ret = ENOENT; + goto out; + } + } + + if (OF_getprop_alloc(node, "function", (void **)&pin_function) != -1) { + ret = ENOENT; + goto out; + } + if (strcmp(pin_function, "gpio") != 0) { + ret = ENOENT; + goto out; + } + + out: + OF_prop_free(pinlist); + OF_prop_free(pin_function); + return (ret); +} + +static phandle_t +rk805_gpio_get_node(device_t bus, device_t dev) +{ + /* Forward up to get the rk805's node. */ + return (ofw_bus_get_node(bus)); +} + +static int +rk805_gpio_probe(device_t dev) +{ + device_set_desc(dev, "RK805 general digital output"); + return (BUS_PROBE_DEFAULT); +} + +static int +rk805_gpio_attach(device_t dev) +{ + struct rk805_gpio_softc *sc; + + sc = device_get_softc(dev); + sc->dev = dev; + sc->base_dev = device_get_parent(dev); + sx_init(&sc->lock, "rk805 gpio"); + sc->pinconf = &rk805_pinconf; + sc->bus_dev = gpiobus_attach_bus(dev); + if (sc->bus_dev != NULL) { + /* Register as a pinctrl device */ + fdt_pinctrl_register(dev, "pins"); + fdt_pinctrl_configure_tree(dev); + return (0); + } else { + device_printf(dev, "Failed to enable GPIO function\n"); + return (ENXIO); + } +} + +static int +rk805_gpio_detach(device_t dev) +{ + struct rk805_gpio_softc *sc; + + sc = device_get_softc(dev); + return (gpiobus_detach_bus(sc->bus_dev)); +} + +static device_method_t rk805_gpio_methods[] = { + DEVMETHOD(device_probe, rk805_gpio_probe), + DEVMETHOD(device_attach, rk805_gpio_attach), + DEVMETHOD(device_detach, rk805_gpio_detach), + + /* GPIO interface */ + DEVMETHOD(gpio_get_bus, rk805_gpio_get_bus), + DEVMETHOD(gpio_pin_max, rk805_gpio_pin_max), + DEVMETHOD(gpio_pin_getname, rk805_gpio_pin_getname), + DEVMETHOD(gpio_pin_getflags, rk805_gpio_pin_getflags), + DEVMETHOD(gpio_pin_getcaps, rk805_gpio_pin_getcaps), + DEVMETHOD(gpio_pin_setflags, rk805_gpio_pin_setflags), + DEVMETHOD(gpio_pin_get, rk805_gpio_pin_get), + DEVMETHOD(gpio_pin_set, rk805_gpio_pin_set), + DEVMETHOD(gpio_pin_toggle, rk805_gpio_pin_toggle), + DEVMETHOD(gpio_map_gpios, rk805_gpio_map_gpios), + + /* ofw_bus interface */ + DEVMETHOD(ofw_bus_get_node, rk805_gpio_get_node), + + /* fdt_pinctrl interface */ + DEVMETHOD(fdt_pinctrl_configure,rk805_fdt_configure_pins), + + DEVMETHOD_END +}; + +static driver_t rk805_gpio_driver = { + "gpio", + rk805_gpio_methods, + sizeof(struct rk805_gpio_softc), +}; + +static devclass_t rk805_gpio_devclass; + +DRIVER_MODULE(rk805_gpio, rk805_pmu, rk805_gpio_driver, rk805_gpio_devclass, + NULL, NULL); +MODULE_DEPEND(rk805_gpio, rk805, 1, 1, 1); +MODULE_VERSION(rk805_gpio, 1); + /* -------------------------------------------------------------------------- */ static int @@ -944,6 +1261,17 @@ SHUTDOWN_PRI_LAST - 2); } + if (sc->type == RK805 && + OF_hasprop(ofw_bus_get_node(dev), "gpio-controller")) { + device_t gpio_dev; + + gpio_dev = device_add_child(dev, "gpio", -1); + if (gpio_dev != NULL) + (void)bus_generic_attach(dev); + else + device_printf(dev, "failed to add gpio child\n"); + } + return (0); } @@ -974,11 +1302,21 @@ return (ERANGE); } +static phandle_t +rk805_get_node(device_t bus, device_t dev) +{ + /* Associate the child gpio device with our node. */ + return (ofw_bus_get_node(bus)); +} + static device_method_t rk805_methods[] = { DEVMETHOD(device_probe, rk805_probe), DEVMETHOD(device_attach, rk805_attach), DEVMETHOD(device_detach, rk805_detach), + /* ofw_bus interface */ + DEVMETHOD(ofw_bus_get_node, rk805_get_node), + /* regdev interface */ DEVMETHOD(regdev_map, rk805_map), Index: sys/arm64/rockchip/rk805reg.h =================================================================== --- sys/arm64/rockchip/rk805reg.h +++ sys/arm64/rockchip/rk805reg.h @@ -97,6 +97,10 @@ #define RK805_DEV_CTRL_OFF (1 << 0) #define RK805_DEV_CTRL_SLP (1 << 1) +#define RK805_OUT 0x52 +#define RK805_OUT_1 (1 << 0) +#define RK805_OUT_2 (1 << 1) + enum rk805_regulator { RK805_DCDC1 = 0, RK805_DCDC2,