diff options
author | loos <loos@FreeBSD.org> | 2015-03-05 02:54:30 +0000 |
---|---|---|
committer | loos <loos@FreeBSD.org> | 2015-03-05 02:54:30 +0000 |
commit | 4655e1ef1928f6b30c6c5cdddd37c93568152498 (patch) | |
tree | 23a27997dbbfb948edc9ff11226105bef50b5ba2 | |
parent | aa6f296074ee25ce0a33226d91cd625ecb67acf2 (diff) | |
download | FreeBSD-src-4655e1ef1928f6b30c6c5cdddd37c93568152498.zip FreeBSD-src-4655e1ef1928f6b30c6c5cdddd37c93568152498.tar.gz |
Change ofw_gpiobus_destroy_devinfo() to unmap the GPIO pins and then
rework the code a little bit to use this function consistently to cleanup
all the changes made as part of the probe phase.
This fixes an issue where a FDT child node without a matching driver could
leave the GPIO pins mapped and prevent the further use of them.
-rw-r--r-- | sys/dev/gpio/ofw_gpiobus.c | 39 |
1 files changed, 24 insertions, 15 deletions
diff --git a/sys/dev/gpio/ofw_gpiobus.c b/sys/dev/gpio/ofw_gpiobus.c index be961ea..3eb6b86 100644 --- a/sys/dev/gpio/ofw_gpiobus.c +++ b/sys/dev/gpio/ofw_gpiobus.c @@ -41,7 +41,7 @@ __FBSDID("$FreeBSD$"); static struct ofw_gpiobus_devinfo *ofw_gpiobus_setup_devinfo(device_t, device_t, phandle_t); -static void ofw_gpiobus_destroy_devinfo(struct ofw_gpiobus_devinfo *); +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 **); @@ -63,7 +63,7 @@ ofw_gpiobus_add_fdt_child(device_t bus, const char *drvname, phandle_t child) return (NULL); } if (device_probe_and_attach(childdev) != 0) { - ofw_gpiobus_destroy_devinfo(dinfo); + ofw_gpiobus_destroy_devinfo(bus, dinfo); device_delete_child(bus, childdev); return (NULL); } @@ -117,41 +117,50 @@ ofw_gpiobus_setup_devinfo(device_t bus, device_t child, phandle_t node) } /* Parse the gpios property for the child. */ npins = ofw_gpiobus_parse_gpios_impl(child, node, "gpios", sc, &pins); - if (npins <= 0) - goto fail; + if (npins <= 0) { + ofw_bus_gen_destroy_devinfo(&dinfo->opd_obdinfo); + free(dinfo, M_DEVBUF); + return (NULL); + } + /* Initialize the irq resource list. */ + resource_list_init(&dinfo->opd_dinfo.rl); + /* Allocate the child ivars and copy the parsed pin data. */ devi = &dinfo->opd_dinfo; devi->npins = (uint32_t)npins; if (gpiobus_alloc_ivars(devi) != 0) { free(pins, M_DEVBUF); - goto fail; + ofw_gpiobus_destroy_devinfo(bus, dinfo); + return (NULL); } for (i = 0; i < devi->npins; i++) { devi->flags[i] = pins[i].flags; devi->pins[i] = pins[i].pin; } free(pins, M_DEVBUF); - /* And now the interrupt resources. */ - resource_list_init(&dinfo->opd_dinfo.rl); + /* Parse the interrupt resources. */ if (ofw_bus_intr_to_rl(bus, node, &dinfo->opd_dinfo.rl) != 0) { - gpiobus_free_ivars(devi); - goto fail; + ofw_gpiobus_destroy_devinfo(bus, dinfo); + return (NULL); } device_set_ivars(child, dinfo); return (dinfo); - -fail: - ofw_bus_gen_destroy_devinfo(&dinfo->opd_obdinfo); - free(dinfo, M_DEVBUF); - return (NULL); } static void -ofw_gpiobus_destroy_devinfo(struct ofw_gpiobus_devinfo *dinfo) +ofw_gpiobus_destroy_devinfo(device_t bus, struct ofw_gpiobus_devinfo *dinfo) { + int i; struct gpiobus_ivar *devi; + struct gpiobus_softc *sc; + sc = device_get_softc(bus); devi = &dinfo->opd_dinfo; + for (i = 0; i < devi->npins; i++) { + if (devi->pins[i] > sc->sc_npins) + continue; + sc->sc_pins_mapped[devi->pins[i]] = 0; + } gpiobus_free_ivars(devi); resource_list_free(&dinfo->opd_dinfo.rl); ofw_bus_gen_destroy_devinfo(&dinfo->opd_obdinfo); |