From 8a4ba03859e731b2cf4179deb1d3f6bbcb673108 Mon Sep 17 00:00:00 2001 From: Michal Meloun Date: Tue, 1 Mar 2016 16:10:15 +0000 Subject: [PATCH] OFW_GPIOBUS: Add utility functions for easier handling of OFW GPIO pins. Reviewed by: ian, loos (paritaly) --- sys/dev/gpio/gpiobusvar.h | 12 +++ sys/dev/gpio/ofw_gpiobus.c | 160 +++++++++++++++++++++++++++++++++++++ 2 files changed, 172 insertions(+) diff --git a/sys/dev/gpio/gpiobusvar.h b/sys/dev/gpio/gpiobusvar.h index 6a614cf8a6af..2ed97315aa76 100644 --- a/sys/dev/gpio/gpiobusvar.h +++ b/sys/dev/gpio/gpiobusvar.h @@ -38,6 +38,7 @@ #ifdef FDT #include +#include #endif #include "gpio_if.h" @@ -83,6 +84,7 @@ struct gpiobus_pin uint32_t flags; /* pin flags */ uint32_t pin; /* pin number */ }; +typedef struct gpiobus_pin *gpio_pin_t; struct gpiobus_ivar { @@ -109,6 +111,16 @@ device_t ofw_gpiobus_add_fdt_child(device_t, const char *, phandle_t); int ofw_gpiobus_parse_gpios(device_t, char *, struct gpiobus_pin **); void ofw_gpiobus_register_provider(device_t); void ofw_gpiobus_unregister_provider(device_t); + +/* Consumers interface. */ +int gpio_pin_get_by_ofw_name(device_t consumer, char *name, gpio_pin_t *gpio); +int gpio_pin_get_by_ofw_idx(device_t consumer, int idx, gpio_pin_t *gpio); +int gpio_pin_get_by_ofw_property(device_t consumer, char *name, + gpio_pin_t *gpio); +void gpio_pin_release(gpio_pin_t gpio); +int gpio_pin_is_active(gpio_pin_t pin, bool *active); +int gpio_pin_set_active(gpio_pin_t pin, bool active); +int gpio_pin_setflags(gpio_pin_t pin, uint32_t flags); #endif int gpio_check_flags(uint32_t, uint32_t); device_t gpiobus_attach_bus(device_t); diff --git a/sys/dev/gpio/ofw_gpiobus.c b/sys/dev/gpio/ofw_gpiobus.c index 1dbb5262ae65..be5a74793a90 100644 --- a/sys/dev/gpio/ofw_gpiobus.c +++ b/sys/dev/gpio/ofw_gpiobus.c @@ -47,6 +47,166 @@ static void ofw_gpiobus_destroy_devinfo(device_t, struct ofw_gpiobus_devinfo *); static int ofw_gpiobus_parse_gpios_impl(device_t, phandle_t, char *, struct gpiobus_softc *, struct gpiobus_pin **); +/* + * Utility functions for easier handling of OFW GPIO pins. + * + * !!! BEWARE !!! + * GPIOBUS uses children's IVARs, so we cannot use this interface for cross + * tree consumers. + * + */ +static int +gpio_pin_get_by_ofw_impl(device_t consumer_dev, char *prop_name, int idx, + gpio_pin_t *out_pin) +{ + phandle_t cnode, xref; + pcell_t *cells; + device_t busdev; + struct gpiobus_pin pin; + int ncells, rv; + + cnode = ofw_bus_get_node(consumer_dev); + if (cnode <= 0) { + device_printf(consumer_dev, + "%s called on not ofw based device\n", __func__); + return (ENXIO); + } + + rv = ofw_bus_parse_xref_list_alloc(cnode, prop_name, "#gpio-cells", + idx, &xref, &ncells, &cells); + if (rv != 0) + return (rv); + + /* Translate provider to device. */ + pin.dev = OF_device_from_xref(xref); + if (pin.dev == NULL) { + free(cells, M_OFWPROP); + return (ENODEV); + } + + /* Test if GPIO bus already exist. */ + busdev = GPIO_GET_BUS(pin.dev); + if (busdev == NULL) { + free(cells, M_OFWPROP); + return (ENODEV); + } + + /* Map GPIO pin. */ + rv = gpio_map_gpios(pin.dev, cnode, OF_node_from_xref(xref), ncells, + cells, &pin.pin, &pin.flags); + free(cells, M_OFWPROP); + if (rv != 0) { + device_printf(consumer_dev, "Cannot map the gpio property.\n"); + return (ENXIO); + } + + /* Reserve GPIO pin. */ + rv = gpiobus_map_pin(busdev, pin.pin); + if (rv != 0) { + device_printf(consumer_dev, "Cannot reserve gpio pin.\n"); + return (EBUSY); + } + + *out_pin = malloc(sizeof(struct gpiobus_pin), M_DEVBUF, + M_WAITOK | M_ZERO); + **out_pin = pin; + return (0); +} + +int +gpio_pin_get_by_ofw_idx(device_t consumer_dev, int idx, gpio_pin_t *pin) +{ + + return (gpio_pin_get_by_ofw_impl(consumer_dev, "gpios", idx, pin)); +} + +int +gpio_pin_get_by_ofw_property(device_t consumer_dev, char *name, gpio_pin_t *pin) +{ + + return (gpio_pin_get_by_ofw_impl(consumer_dev, name, 0, pin)); +} + +int +gpio_pin_get_by_ofw_name(device_t consumer_dev, char *name, gpio_pin_t *pin) +{ + int rv, idx; + phandle_t cnode; + + cnode = ofw_bus_get_node(consumer_dev); + if (cnode <= 0) { + device_printf(consumer_dev, + "%s called on not ofw based device\n", __func__); + return (ENXIO); + } + rv = ofw_bus_find_string_index(cnode, "gpio-names", name, &idx); + if (rv != 0) + return (rv); + return (gpio_pin_get_by_ofw_idx(consumer_dev, idx, pin)); +} + +void +gpio_pin_release(gpio_pin_t gpio) +{ + + if (gpio == NULL) + return; + + /* XXXX Unreserve pin. */ + free(gpio, M_DEVBUF); +} + +int +gpio_pin_is_active(gpio_pin_t pin, bool *active) +{ + int rv; + uint32_t tmp; + + KASSERT(pin != NULL, ("GPIO pin is NULL.")); + KASSERT(pin->dev != NULL, ("GPIO pin device is NULL.")); + rv = GPIO_PIN_GET(pin->dev, pin->pin, &tmp); + if (rv != 0) { + return (rv); + } + + *active = tmp != 0; + if (pin->flags & GPIO_ACTIVE_LOW) + *active = !(*active); + return (0); +} + +int +gpio_pin_set_active(gpio_pin_t pin, bool active) +{ + int rv; + uint32_t tmp; + + if (pin->flags & GPIO_ACTIVE_LOW) + tmp = active ? 0 : 1; + else + tmp = active ? 1 : 0; + + KASSERT(pin != NULL, ("GPIO pin is NULL.")); + KASSERT(pin->dev != NULL, ("GPIO pin device is NULL.")); + rv = GPIO_PIN_SET(pin->dev, pin->pin, tmp); + return (rv); +} + +int +gpio_pin_setflags(gpio_pin_t pin, uint32_t flags) +{ + int rv; + + KASSERT(pin != NULL, ("GPIO pin is NULL.")); + KASSERT(pin->dev != NULL, ("GPIO pin device is NULL.")); + + rv = GPIO_PIN_SETFLAGS(pin->dev, pin->pin, flags); + return (rv); +} + +/* + * OFW_GPIOBUS driver. + */ device_t ofw_gpiobus_add_fdt_child(device_t bus, const char *drvname, phandle_t child) {