diff options
author | loos <loos@FreeBSD.org> | 2014-04-27 12:11:00 +0000 |
---|---|---|
committer | loos <loos@FreeBSD.org> | 2014-04-27 12:11:00 +0000 |
commit | 401918349b37ff258742381c027518bcc4f650cc (patch) | |
tree | ab6c2cab6977035917d3b9b3e99633575078fb66 | |
parent | c5479b1c51e46a4e9c407c20e8ec65a986a5c556 (diff) | |
download | FreeBSD-src-401918349b37ff258742381c027518bcc4f650cc.zip FreeBSD-src-401918349b37ff258742381c027518bcc4f650cc.tar.gz |
Fix the gpio-specifier decoding by respecting the GPIO controller's
#gpio-cells property.
Add a new ofw_bus method (OFW_BUS_MAP_GPIOS()) that allows the GPIO
controller to implement its own mapping to deal with gpio-specifiers,
allowing the decoding of gpio-specifiers to be controller specific.
The default ofw_bus_map_gpios() decodes the linux standard (#gpio-cells =
<2>) and the FreeBSD standard (#gpio-cells = <3>).
It pass the gpio-specifier flag field to the children as an ivar variable so
they can act upon.
-rw-r--r-- | sys/dev/gpio/gpiobusvar.h | 9 | ||||
-rw-r--r-- | sys/dev/gpio/ofw_gpiobus.c | 126 | ||||
-rw-r--r-- | sys/dev/ofw/ofw_bus.h | 8 | ||||
-rw-r--r-- | sys/dev/ofw/ofw_bus_if.m | 30 |
4 files changed, 138 insertions, 35 deletions
diff --git a/sys/dev/gpio/gpiobusvar.h b/sys/dev/gpio/gpiobusvar.h index 94ae1ad..3d38710 100644 --- a/sys/dev/gpio/gpiobusvar.h +++ b/sys/dev/gpio/gpiobusvar.h @@ -60,17 +60,22 @@ struct gpiobus_softc int *sc_pins_mapped; /* mark mapped pins */ }; - struct gpiobus_ivar { uint32_t npins; /* pins total */ + uint32_t *flags; /* pins flags */ uint32_t *pins; /* pins map */ }; -void gpiobus_print_pins(struct gpiobus_ivar *); #ifdef FDT +struct ofw_gpiobus_devinfo { + struct gpiobus_ivar opd_dinfo; + struct ofw_bus_devinfo opd_obdinfo; +}; + device_t ofw_gpiobus_add_fdt_child(device_t, phandle_t); #endif +void gpiobus_print_pins(struct gpiobus_ivar *); extern driver_t gpiobus_driver; diff --git a/sys/dev/gpio/ofw_gpiobus.c b/sys/dev/gpio/ofw_gpiobus.c index 7641b56..f4eb37d 100644 --- a/sys/dev/gpio/ofw_gpiobus.c +++ b/sys/dev/gpio/ofw_gpiobus.c @@ -47,11 +47,6 @@ __FBSDID("$FreeBSD$"); #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, @@ -83,10 +78,37 @@ ofw_gpiobus_add_fdt_child(device_t bus, phandle_t child) } static int +ofw_gpiobus_alloc_ivars(struct gpiobus_ivar *dinfo) +{ + + /* Allocate pins and flags memory. */ + dinfo->pins = malloc(sizeof(uint32_t) * dinfo->npins, M_DEVBUF, + M_NOWAIT | M_ZERO); + if (dinfo->pins == NULL) + return (ENOMEM); + dinfo->flags = malloc(sizeof(uint32_t) * dinfo->npins, M_DEVBUF, + M_NOWAIT | M_ZERO); + if (dinfo->flags == NULL) { + free(dinfo->pins, M_DEVBUF); + return (ENOMEM); + } + + return (0); +} + +static void +ofw_gpiobus_free_ivars(struct gpiobus_ivar *dinfo) +{ + + free(dinfo->flags, M_DEVBUF); + free(dinfo->pins, M_DEVBUF); +} + +static int ofw_gpiobus_parse_gpios(struct gpiobus_softc *sc, struct gpiobus_ivar *dinfo, phandle_t child) { - int i, len; + int cells, i, j, len; pcell_t *gpios; phandle_t gpio; @@ -102,44 +124,81 @@ ofw_gpiobus_parse_gpios(struct gpiobus_softc *sc, struct gpiobus_ivar *dinfo, } /* - * 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. + * The gpio-specifier is controller independent, but the first pcell + * has the reference to the GPIO controller phandler. + * One the first pass we count the number of encoded gpio-specifiers. */ - if ((len / sizeof(pcell_t)) % 4) { + i = 0; + len /= sizeof(pcell_t); + while (i < len) { + /* Allow NULL specifiers. */ + if (gpios[i] == 0) { + dinfo->npins++; + i++; + continue; + } + gpio = OF_xref_phandle(gpios[i]); + /* Verify if we're attaching to the correct GPIO controller. */ + if (!OF_hasprop(gpio, "gpio-controller") || + gpio != ofw_bus_get_node(sc->sc_dev)) { + free(gpios, M_DEVBUF); + return (EINVAL); + } + /* Read gpio-cells property for this GPIO controller. */ + if (OF_getencprop(gpio, "#gpio-cells", &cells, + sizeof(cells)) < 0) { + free(gpios, M_DEVBUF); + return (EINVAL); + } + dinfo->npins++; + i += cells + 1; + } + + if (dinfo->npins == 0) { 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) { + + /* Allocate the child resources. */ + if (ofw_gpiobus_alloc_ivars(dinfo) != 0) { free(gpios, M_DEVBUF); return (ENOMEM); } - for (i = 0; i < dinfo->npins; i++) { + /* Decode the gpio specifier on the second pass. */ + i = 0; + j = 0; + while (i < len) { + /* Allow NULL specifiers. */ + if (gpios[i] == 0) { + i++; + j++; + continue; + } - /* 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); + gpio = OF_xref_phandle(gpios[i]); + /* Read gpio-cells property for this GPIO controller. */ + if (OF_getencprop(gpio, "#gpio-cells", &cells, + sizeof(cells)) < 0) { + ofw_gpiobus_free_ivars(dinfo); 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 */ + /* Get the GPIO pin number and flags. */ + if (ofw_bus_map_gpios(sc->sc_dev, child, gpio, cells, + &gpios[i + 1], &dinfo->pins[j], &dinfo->flags[j]) != 0) { + ofw_gpiobus_free_ivars(dinfo); + free(gpios, M_DEVBUF); + return (EINVAL); + } - if (dinfo->pins[i] > sc->sc_npins) { + /* Consistency check. */ + if (dinfo->pins[j] > 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); + dinfo->pins[j], sc->sc_npins - 1); + ofw_gpiobus_free_ivars(dinfo); free(gpios, M_DEVBUF); return (EINVAL); } @@ -147,15 +206,18 @@ ofw_gpiobus_parse_gpios(struct gpiobus_softc *sc, struct gpiobus_ivar *dinfo, /* * Mark pin as mapped and give warning if it's already mapped. */ - if (sc->sc_pins_mapped[dinfo->pins[i]]) { + if (sc->sc_pins_mapped[dinfo->pins[j]]) { device_printf(sc->sc_busdev, "warning: pin %d is already mapped\n", - dinfo->pins[i]); - free(dinfo->pins, M_DEVBUF); + dinfo->pins[j]); + ofw_gpiobus_free_ivars(dinfo); free(gpios, M_DEVBUF); return (EINVAL); } - sc->sc_pins_mapped[dinfo->pins[i]] = 1; + sc->sc_pins_mapped[dinfo->pins[j]] = 1; + + i += cells + 1; + j++; } free(gpios, M_DEVBUF); diff --git a/sys/dev/ofw/ofw_bus.h b/sys/dev/ofw/ofw_bus.h index f7c7295..7f0e50e 100644 --- a/sys/dev/ofw/ofw_bus.h +++ b/sys/dev/ofw/ofw_bus.h @@ -76,4 +76,12 @@ ofw_bus_map_intr(device_t dev, phandle_t iparent, int icells, pcell_t *intr) return (OFW_BUS_MAP_INTR(dev, dev, iparent, icells, intr)); } +static __inline int +ofw_bus_map_gpios(device_t bus, phandle_t dev, phandle_t gparent, int gcells, + pcell_t *gpios, uint32_t *pin, uint32_t *flags) +{ + return (OFW_BUS_MAP_GPIOS(bus, dev, gparent, gcells, gpios, pin, + flags)); +} + #endif /* !_DEV_OFW_OFW_BUS_H_ */ diff --git a/sys/dev/ofw/ofw_bus_if.m b/sys/dev/ofw/ofw_bus_if.m index 6486e96..86c9876 100644 --- a/sys/dev/ofw/ofw_bus_if.m +++ b/sys/dev/ofw/ofw_bus_if.m @@ -58,6 +58,7 @@ CODE { static ofw_bus_get_node_t ofw_bus_default_get_node; static ofw_bus_get_type_t ofw_bus_default_get_type; static ofw_bus_map_intr_t ofw_bus_default_map_intr; + static ofw_bus_map_gpios_t ofw_bus_default_map_gpios; static const struct ofw_bus_devinfo * ofw_bus_default_get_devinfo(device_t bus, device_t dev) @@ -113,6 +114,24 @@ CODE { /* If that fails, then assume a one-domain system */ return (interrupt[0]); } + + int + ofw_bus_default_map_gpios(device_t bus, phandle_t dev, + phandle_t gparent, int gcells, pcell_t *gpios, uint32_t *pin, + uint32_t *flags) + { + /* Propagate up the bus hierarchy until someone handles it. */ + if (device_get_parent(bus) != NULL) + return OFW_BUS_MAP_GPIOS(device_get_parent(bus), dev, + gparent, gcells, gpios, pin, flags); + + /* If that fails, then assume the FreeBSD defaults. */ + *pin = gpios[0]; + if (gcells == 2 || gcells == 3) + *flags = gpios[gcells - 1]; + + return (0); + } }; # Get the ofw_bus_devinfo struct for the device dev on the bus. Used for bus @@ -170,4 +189,13 @@ METHOD int map_intr { pcell_t *interrupt; } DEFAULT ofw_bus_default_map_intr; - +# Map the GPIO controller specific gpio-specifier to GPIO pin and flags. +METHOD int map_gpios { + device_t bus; + phandle_t dev; + phandle_t gparent; + int gcells; + pcell_t *gpios; + uint32_t *pin; + uint32_t *flags; +} DEFAULT ofw_bus_default_map_gpios; |