diff options
Diffstat (limited to 'sys/dev/gpio')
| -rw-r--r-- | sys/dev/gpio/acpi_gpiobus.c | 17 | ||||
| -rw-r--r-- | sys/dev/gpio/gpiobus.c | 4 | ||||
| -rw-r--r-- | sys/dev/gpio/gpiobus_internal.h | 1 | ||||
| -rw-r--r-- | sys/dev/gpio/gpioc.c | 34 | ||||
| -rw-r--r-- | sys/dev/gpio/gpioled.c | 106 | ||||
| -rw-r--r-- | sys/dev/gpio/pl061.c | 3 |
6 files changed, 132 insertions, 33 deletions
diff --git a/sys/dev/gpio/acpi_gpiobus.c b/sys/dev/gpio/acpi_gpiobus.c index 0d2455cab399..0c31f4fec16d 100644 --- a/sys/dev/gpio/acpi_gpiobus.c +++ b/sys/dev/gpio/acpi_gpiobus.c @@ -304,6 +304,12 @@ acpi_gpiobus_attach_aei(struct acpi_gpiobus_softc *sc, ACPI_HANDLE handle) devi->gpiobus.pins[i] = pins[i + 1]; free(pins, M_DEVBUF); + status = AcpiAttachData(aei_handle, acpi_fake_objhandler, child); + if (ACPI_FAILURE(status)) { + printf("WARNING: Unable to attach object data to %s - %s\n", + acpi_name(aei_handle), AcpiFormatException(status)); + } + bus_attach_children(sc->super_sc.sc_busdev); } @@ -427,6 +433,16 @@ acpi_gpiobus_child_location(device_t bus, device_t child, struct sbuf *sb) return (0); } +static void +acpi_gpiobus_child_deleted(device_t bus, device_t child) +{ + struct acpi_gpiobus_ivar *devi = device_get_ivars(child); + + if (acpi_get_device(devi->handle) == child) + AcpiDetachData(devi->handle, acpi_fake_objhandler); + gpiobus_child_deleted(bus, child); +} + static device_method_t acpi_gpiobus_methods[] = { /* Device interface */ DEVMETHOD(device_probe, acpi_gpiobus_probe), @@ -437,6 +453,7 @@ static device_method_t acpi_gpiobus_methods[] = { DEVMETHOD(bus_read_ivar, acpi_gpiobus_read_ivar), DEVMETHOD(bus_add_child, acpi_gpiobus_add_child), DEVMETHOD(bus_child_location, acpi_gpiobus_child_location), + DEVMETHOD(bus_child_deleted, acpi_gpiobus_child_deleted), DEVMETHOD_END }; diff --git a/sys/dev/gpio/gpiobus.c b/sys/dev/gpio/gpiobus.c index 698b5e5fdd01..596e468d35f3 100644 --- a/sys/dev/gpio/gpiobus.c +++ b/sys/dev/gpio/gpiobus.c @@ -618,7 +618,7 @@ gpiobus_detach(device_t dev) ("gpiobus mutex not initialized")); GPIOBUS_LOCK_DESTROY(sc); - if ((err = bus_detach_children(dev)) != 0) + if ((err = bus_generic_detach(dev)) != 0) return (err); rman_fini(&sc->sc_intr_rman); @@ -734,7 +734,7 @@ gpiobus_add_child(device_t dev, u_int order, const char *name, int unit) sizeof(struct gpiobus_ivar))); } -static void +void gpiobus_child_deleted(device_t dev, device_t child) { struct gpiobus_ivar *devi; diff --git a/sys/dev/gpio/gpiobus_internal.h b/sys/dev/gpio/gpiobus_internal.h index 58f862343403..be76450b2432 100644 --- a/sys/dev/gpio/gpiobus_internal.h +++ b/sys/dev/gpio/gpiobus_internal.h @@ -43,6 +43,7 @@ int gpiobus_read_ivar(device_t, device_t, int, uintptr_t *); int gpiobus_acquire_pin(device_t, uint32_t); void gpiobus_release_pin(device_t, uint32_t); int gpiobus_child_location(device_t, device_t, struct sbuf *); +void gpiobus_child_deleted(device_t, device_t); device_t gpiobus_add_child_common(device_t, u_int, const char *, int, size_t); int gpiobus_add_gpioc(device_t); diff --git a/sys/dev/gpio/gpioc.c b/sys/dev/gpio/gpioc.c index 5a60f939dc78..517f7752daad 100644 --- a/sys/dev/gpio/gpioc.c +++ b/sys/dev/gpio/gpioc.c @@ -158,7 +158,8 @@ static const struct filterops gpioc_read_filterops = { .f_attach = NULL, .f_detach = gpioc_kqdetach, .f_event = gpioc_kqread, - .f_touch = NULL + .f_touch = NULL, + .f_copy = knote_triv_copy, }; static struct gpioc_pin_event * @@ -704,7 +705,7 @@ gpioc_open(struct cdev *dev, int oflags, int devtype, struct thread *td) * npins isn't a horrible fifo size for that either. */ priv->numevents = priv->sc->sc_npins * 2; - priv->events = malloc(priv->numevents * sizeof(struct gpio_event_detail), + priv->events = malloc(priv->numevents * sizeof(struct gpioc_pin_event), M_GPIOC, M_WAITOK | M_ZERO); priv->evidx_head = priv->evidx_tail = 0; @@ -793,6 +794,7 @@ gpioc_ioctl(struct cdev *cdev, u_long cmd, caddr_t arg, int fflag, struct gpio_access_32 *a32; struct gpio_config_32 *c32; struct gpio_event_config *evcfg; + struct gpioc_pin_event *tmp; uint32_t caps, intrflags; switch (cmd) { @@ -908,27 +910,35 @@ gpioc_ioctl(struct cdev *cdev, u_long cmd, caddr_t arg, int fflag, res = devfs_get_cdevpriv((void **)&priv); if (res != 0) break; - /* If any pins have been configured, changes aren't allowed. */ - if (!SLIST_EMPTY(&priv->pins)) { - res = EINVAL; - break; - } if (evcfg->gp_report_type != GPIO_EVENT_REPORT_DETAIL && evcfg->gp_report_type != GPIO_EVENT_REPORT_SUMMARY) { res = EINVAL; break; } - priv->report_option = evcfg->gp_report_type; /* Reallocate the events buffer if the user wants it bigger. */ - if (priv->report_option == GPIO_EVENT_REPORT_DETAIL && + tmp = NULL; + if (evcfg->gp_report_type == GPIO_EVENT_REPORT_DETAIL && priv->numevents < evcfg->gp_fifo_size) { + tmp = malloc(evcfg->gp_fifo_size * + sizeof(struct gpioc_pin_event), M_GPIOC, + M_WAITOK | M_ZERO); + } + mtx_lock(&priv->mtx); + /* If any pins have been configured, changes aren't allowed. */ + if (!SLIST_EMPTY(&priv->pins)) { + mtx_unlock(&priv->mtx); + free(tmp, M_GPIOC); + res = EINVAL; + break; + } + if (tmp != NULL) { free(priv->events, M_GPIOC); + priv->events = tmp; priv->numevents = evcfg->gp_fifo_size; - priv->events = malloc(priv->numevents * - sizeof(struct gpio_event_detail), M_GPIOC, - M_WAITOK | M_ZERO); priv->evidx_head = priv->evidx_tail = 0; } + priv->report_option = evcfg->gp_report_type; + mtx_unlock(&priv->mtx); break; case FIONBIO: /* diff --git a/sys/dev/gpio/gpioled.c b/sys/dev/gpio/gpioled.c index ba53cb733971..a36c2faef379 100644 --- a/sys/dev/gpio/gpioled.c +++ b/sys/dev/gpio/gpioled.c @@ -55,13 +55,13 @@ device_get_nameunit((_sc)->sc_dev), "gpioled", MTX_DEF) #define GPIOLED_LOCK_DESTROY(_sc) mtx_destroy(&(_sc)->sc_mtx) -struct gpioled_softc +struct gpioled_softc { device_t sc_dev; device_t sc_busdev; struct mtx sc_mtx; struct cdev *sc_leddev; - int sc_invert; + int sc_softinvert; }; static void gpioled_control(void *, int); @@ -69,20 +69,17 @@ static int gpioled_probe(device_t); static int gpioled_attach(device_t); static int gpioled_detach(device_t); -static void +static void gpioled_control(void *priv, int onoff) { struct gpioled_softc *sc; sc = (struct gpioled_softc *)priv; + if (sc->sc_softinvert) + onoff = !onoff; GPIOLED_LOCK(sc); - if (GPIOBUS_PIN_SETFLAGS(sc->sc_busdev, sc->sc_dev, GPIOLED_PIN, - GPIO_PIN_OUTPUT) == 0) { - if (sc->sc_invert) - onoff = !onoff; - GPIOBUS_PIN_SET(sc->sc_busdev, sc->sc_dev, GPIOLED_PIN, - onoff ? GPIO_PIN_HIGH : GPIO_PIN_LOW); - } + GPIOBUS_PIN_SET(sc->sc_busdev, sc->sc_dev, GPIOLED_PIN, + onoff ? GPIO_PIN_HIGH : GPIO_PIN_LOW); GPIOLED_UNLOCK(sc); } @@ -95,26 +92,101 @@ gpioled_probe(device_t dev) } static int +gpioled_inv(device_t dev, uint32_t *pin_flags) +{ + struct gpioled_softc *sc; + int invert; + uint32_t pin_caps; + + sc = device_get_softc(dev); + + if (resource_int_value(device_get_name(dev), + device_get_unit(dev), "invert", &invert)) + invert = 0; + + if (GPIOBUS_PIN_GETCAPS(sc->sc_busdev, sc->sc_dev, GPIOLED_PIN, + &pin_caps) != 0) { + if (bootverbose) + device_printf(sc->sc_dev, "unable to get pin caps\n"); + return (-1); + } + if (pin_caps & GPIO_PIN_INVOUT) + *pin_flags &= ~GPIO_PIN_INVOUT; + sc->sc_softinvert = 0; + if (invert) { + const char *invmode; + + if (resource_string_value(device_get_name(dev), + device_get_unit(dev), "invmode", &invmode)) + invmode = NULL; + + if (invmode) { + if (!strcmp(invmode, "sw")) + sc->sc_softinvert = 1; + else if (!strcmp(invmode, "hw")) { + if (pin_caps & GPIO_PIN_INVOUT) + *pin_flags |= GPIO_PIN_INVOUT; + else { + device_printf(sc->sc_dev, "hardware pin inversion not supported\n"); + return (-1); + } + } else { + if (strcmp(invmode, "auto") != 0) + device_printf(sc->sc_dev, "invalid pin inversion mode\n"); + invmode = NULL; + } + } + /* + * auto inversion mode: use hardware support if available, else fallback to + * software emulation. + */ + if (invmode == NULL) { + if (pin_caps & GPIO_PIN_INVOUT) + *pin_flags |= GPIO_PIN_INVOUT; + else + sc->sc_softinvert = 1; + } + } + MPASS(!invert || + (((*pin_flags & GPIO_PIN_INVOUT) != 0) && !sc->sc_softinvert) || + (((*pin_flags & GPIO_PIN_INVOUT) == 0) && sc->sc_softinvert)); + return (invert); +} + +static int gpioled_attach(device_t dev) { struct gpioled_softc *sc; int state; const char *name; + uint32_t pin_flags; + int invert; sc = device_get_softc(dev); sc->sc_dev = dev; sc->sc_busdev = device_get_parent(dev); GPIOLED_LOCK_INIT(sc); - state = 0; - - if (resource_string_value(device_get_name(dev), + if (resource_string_value(device_get_name(dev), device_get_unit(dev), "name", &name)) name = NULL; - resource_int_value(device_get_name(dev), - device_get_unit(dev), "invert", &sc->sc_invert); - resource_int_value(device_get_name(dev), - device_get_unit(dev), "state", &state); + + if (resource_int_value(device_get_name(dev), + device_get_unit(dev), "state", &state)) + state = 0; + + pin_flags = GPIO_PIN_OUTPUT; + invert = gpioled_inv(dev, &pin_flags); + if (invert < 0) + return (ENXIO); + device_printf(sc->sc_dev, "state %d invert %s\n", + state, (invert ? (sc->sc_softinvert ? "sw" : "hw") : "no")); + if (GPIOBUS_PIN_SETFLAGS(sc->sc_busdev, sc->sc_dev, GPIOLED_PIN, + pin_flags) != 0) { + if (bootverbose) + device_printf(sc->sc_dev, "unable to set pin flags, %#x\n", pin_flags); + return (ENXIO); + } sc->sc_leddev = led_create_state(gpioled_control, sc, name ? name : device_get_nameunit(dev), state); diff --git a/sys/dev/gpio/pl061.c b/sys/dev/gpio/pl061.c index 32109e5982bc..9996b0253c7d 100644 --- a/sys/dev/gpio/pl061.c +++ b/sys/dev/gpio/pl061.c @@ -558,8 +558,7 @@ static device_method_t pl061_methods[] = { /* Bus interface */ DEVMETHOD(bus_setup_intr, bus_generic_setup_intr), - DEVMETHOD(bus_activate_resource, bus_generic_activate_resource), - DEVMETHOD(bus_deactivate_resource, bus_generic_deactivate_resource), + DEVMETHOD(bus_teardown_intr, bus_generic_teardown_intr), /* GPIO protocol */ DEVMETHOD(gpio_get_bus, pl061_get_bus), |
