diff options
Diffstat (limited to 'sys/dev/gpio')
-rw-r--r-- | sys/dev/gpio/acpi_gpiobus.c | 165 | ||||
-rw-r--r-- | sys/dev/gpio/acpi_gpiobusvar.h | 6 | ||||
-rw-r--r-- | sys/dev/gpio/gpioaei.c | 204 | ||||
-rw-r--r-- | sys/dev/gpio/gpiobus.c | 19 | ||||
-rw-r--r-- | sys/dev/gpio/gpiobus_internal.h | 2 | ||||
-rw-r--r-- | sys/dev/gpio/ofw_gpiobus.c | 14 |
6 files changed, 272 insertions, 138 deletions
diff --git a/sys/dev/gpio/acpi_gpiobus.c b/sys/dev/gpio/acpi_gpiobus.c index 170f23615416..0d2455cab399 100644 --- a/sys/dev/gpio/acpi_gpiobus.c +++ b/sys/dev/gpio/acpi_gpiobus.c @@ -37,6 +37,7 @@ #include <dev/gpio/gpiobusvar.h> #include <dev/gpio/acpi_gpiobusvar.h> #include <dev/gpio/gpiobus_internal.h> +#include <sys/sbuf.h> #include "gpiobus_if.h" @@ -52,12 +53,11 @@ struct acpi_gpiobus_ctx { struct acpi_gpiobus_ivar { - struct gpiobus_ivar gpiobus; /* Must come first */ - ACPI_HANDLE dev_handle; /* ACPI handle for bus */ - uint32_t flags; + struct gpiobus_ivar gpiobus; + ACPI_HANDLE handle; }; -static uint32_t +uint32_t acpi_gpiobus_convflags(ACPI_RESOURCE_GPIO *gpio_res) { uint32_t flags = 0; @@ -150,70 +150,24 @@ acpi_gpiobus_enumerate_res(ACPI_RESOURCE *res, void *context) return (AE_OK); } -static struct acpi_gpiobus_ivar * -acpi_gpiobus_setup_devinfo(device_t bus, device_t child, - ACPI_RESOURCE_GPIO *gpio_res) -{ - struct acpi_gpiobus_ivar *devi; - - devi = malloc(sizeof(*devi), M_DEVBUF, M_NOWAIT | M_ZERO); - if (devi == NULL) - return (NULL); - resource_list_init(&devi->gpiobus.rl); - - devi->flags = acpi_gpiobus_convflags(gpio_res); - if (acpi_quirks & ACPI_Q_AEI_NOPULL) - devi->flags &= ~GPIO_PIN_PULLUP; - - devi->gpiobus.npins = 1; - if (gpiobus_alloc_ivars(&devi->gpiobus) != 0) { - free(devi, M_DEVBUF); - return (NULL); - } - - for (int i = 0; i < devi->gpiobus.npins; i++) - devi->gpiobus.pins[i] = gpio_res->PinTable[i]; - - return (devi); -} - static ACPI_STATUS acpi_gpiobus_enumerate_aei(ACPI_RESOURCE *res, void *context) { ACPI_RESOURCE_GPIO *gpio_res = &res->Data.Gpio; - struct acpi_gpiobus_ctx *ctx = context; - device_t bus = ctx->sc->sc_busdev; - device_t child; - struct acpi_gpiobus_ivar *devi; + uint32_t *npins = context, *pins = npins + 1; - /* Check that we have a GpioInt object. */ + /* + * Check that we have a GpioInt object. + * Note that according to the spec this + * should always be the case. + */ if (res->Type != ACPI_RESOURCE_TYPE_GPIO) return (AE_OK); if (gpio_res->ConnectionType != ACPI_RESOURCE_GPIO_TYPE_INT) return (AE_OK); - /* Add a child. */ - child = device_add_child_ordered(bus, 0, "gpio_aei", DEVICE_UNIT_ANY); - if (child == NULL) - return (AE_OK); - devi = acpi_gpiobus_setup_devinfo(bus, child, gpio_res); - if (devi == NULL) { - device_delete_child(bus, child); - return (AE_OK); - } - device_set_ivars(child, devi); - - for (int i = 0; i < devi->gpiobus.npins; i++) { - if (GPIOBUS_PIN_SETFLAGS(bus, child, 0, devi->flags & - ~GPIO_INTR_MASK)) { - device_delete_child(bus, child); - return (AE_OK); - } - } - - /* Pass ACPI information to children. */ - devi->dev_handle = ctx->dev_handle; - + for (int i = 0; i < gpio_res->PinTableLength; i++) + pins[(*npins)++] = gpio_res->PinTable[i]; return (AE_OK); } @@ -296,6 +250,63 @@ err: return (AE_BAD_PARAMETER); } +static void +acpi_gpiobus_attach_aei(struct acpi_gpiobus_softc *sc, ACPI_HANDLE handle) +{ + struct acpi_gpiobus_ivar *devi; + ACPI_HANDLE aei_handle; + device_t child; + uint32_t *pins; + ACPI_STATUS status; + int err; + + status = AcpiGetHandle(handle, "_AEI", &aei_handle); + if (ACPI_FAILURE(status)) + return; + + /* pins[0] specifies the length of the array. */ + pins = mallocarray(sc->super_sc.sc_npins + 1, + sizeof(uint32_t), M_DEVBUF, M_WAITOK); + pins[0] = 0; + + status = AcpiWalkResources(handle, "_AEI", + acpi_gpiobus_enumerate_aei, pins); + if (ACPI_FAILURE(status)) { + device_printf(sc->super_sc.sc_busdev, + "Failed to enumerate AEI resources\n"); + free(pins, M_DEVBUF); + return; + } + + child = BUS_ADD_CHILD(sc->super_sc.sc_busdev, 0, "gpio_aei", + DEVICE_UNIT_ANY); + if (child == NULL) { + device_printf(sc->super_sc.sc_busdev, + "Failed to add gpio_aei child\n"); + free(pins, M_DEVBUF); + return; + } + + devi = device_get_ivars(child); + devi->gpiobus.npins = pins[0]; + devi->handle = aei_handle; + + err = gpiobus_alloc_ivars(&devi->gpiobus); + if (err != 0) { + device_printf(sc->super_sc.sc_busdev, + "Failed to allocate gpio_aei ivars\n"); + device_delete_child(sc->super_sc.sc_busdev, child); + free(pins, M_DEVBUF); + return; + } + + for (int i = 0; i < pins[0]; i++) + devi->gpiobus.pins[i] = pins[i + 1]; + free(pins, M_DEVBUF); + + bus_attach_children(sc->super_sc.sc_busdev); +} + static int acpi_gpiobus_probe(device_t dev) { @@ -353,13 +364,8 @@ acpi_gpiobus_attach(device_t dev) if (ACPI_FAILURE(status)) device_printf(dev, "Failed to enumerate GPIO resources\n"); - /* Look for AEI children */ - status = AcpiWalkResources(handle, "_AEI", acpi_gpiobus_enumerate_aei, - &ctx); - - if (ACPI_FAILURE(status) && status != AE_NOT_FOUND) - device_printf(dev, "Failed to enumerate AEI resources\n"); - + /* Look for AEI child */ + acpi_gpiobus_attach_aei(sc, handle); return (0); } @@ -390,10 +396,7 @@ acpi_gpiobus_read_ivar(device_t dev, device_t child, int which, switch (which) { case ACPI_GPIOBUS_IVAR_HANDLE: - *result = (uintptr_t)devi->dev_handle; - break; - case ACPI_GPIOBUS_IVAR_FLAGS: - *result = (uintptr_t)devi->flags; + *result = (uintptr_t)devi->handle; break; default: return (gpiobus_read_ivar(dev, child, which, result)); @@ -402,6 +405,28 @@ acpi_gpiobus_read_ivar(device_t dev, device_t child, int which, return (0); } +static device_t +acpi_gpiobus_add_child(device_t dev, u_int order, const char *name, int unit) +{ + return (gpiobus_add_child_common(dev, order, name, unit, + sizeof(struct acpi_gpiobus_ivar))); +} + +static int +acpi_gpiobus_child_location(device_t bus, device_t child, struct sbuf *sb) +{ + struct acpi_gpiobus_ivar *devi; + int err; + + err = gpiobus_child_location(bus, child, sb); + if (err != 0) + return (err); + + devi = device_get_ivars(child); + sbuf_printf(sb, " handle=%s", acpi_name(devi->handle)); + return (0); +} + static device_method_t acpi_gpiobus_methods[] = { /* Device interface */ DEVMETHOD(device_probe, acpi_gpiobus_probe), @@ -410,6 +435,8 @@ static device_method_t acpi_gpiobus_methods[] = { /* Bus interface */ 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_END }; diff --git a/sys/dev/gpio/acpi_gpiobusvar.h b/sys/dev/gpio/acpi_gpiobusvar.h index f8d502eab9d1..288e8bd0f2af 100644 --- a/sys/dev/gpio/acpi_gpiobusvar.h +++ b/sys/dev/gpio/acpi_gpiobusvar.h @@ -33,16 +33,16 @@ #include <contrib/dev/acpica/include/acpi.h> enum acpi_gpiobus_ivars { - ACPI_GPIOBUS_IVAR_HANDLE = 10600, - ACPI_GPIOBUS_IVAR_FLAGS, + ACPI_GPIOBUS_IVAR_HANDLE = 10600 }; #define ACPI_GPIOBUS_ACCESSOR(var, ivar, type) \ __BUS_ACCESSOR(acpi_gpiobus, var, ACPI_GPIOBUS, ivar, type) ACPI_GPIOBUS_ACCESSOR(handle, HANDLE, ACPI_HANDLE) -ACPI_GPIOBUS_ACCESSOR(flags, FLAGS, uint32_t) #undef ACPI_GPIOBUS_ACCESSOR +uint32_t acpi_gpiobus_convflags(ACPI_RESOURCE_GPIO *); + #endif /* __ACPI_GPIOBUS_H__ */ diff --git a/sys/dev/gpio/gpioaei.c b/sys/dev/gpio/gpioaei.c index ecae8ccaf2fa..7b97277aaf61 100644 --- a/sys/dev/gpio/gpioaei.c +++ b/sys/dev/gpio/gpioaei.c @@ -45,13 +45,21 @@ enum gpio_aei_type { ACPI_AEI_TYPE_EVT }; -struct gpio_aei_softc { - ACPI_HANDLE handle; - enum gpio_aei_type type; - int pin; +struct gpio_aei_ctx { + SLIST_ENTRY(gpio_aei_ctx) next; struct resource * intr_res; - int intr_rid; void * intr_cookie; + ACPI_HANDLE handle; + gpio_pin_t gpio; + uint32_t pin; + int intr_rid; + enum gpio_aei_type type; +}; + +struct gpio_aei_softc { + SLIST_HEAD(, gpio_aei_ctx) aei_ctx; + ACPI_HANDLE dev_handle; + device_t dev; }; static int @@ -65,69 +73,157 @@ gpio_aei_probe(device_t dev) static void gpio_aei_intr(void * arg) { - struct gpio_aei_softc * sc = arg; + struct gpio_aei_ctx * ctx = arg; /* Ask ACPI to run the appropriate _EVT, _Exx or _Lxx method. */ - if (sc->type == ACPI_AEI_TYPE_EVT) - acpi_SetInteger(sc->handle, NULL, sc->pin); + if (ctx->type == ACPI_AEI_TYPE_EVT) + acpi_SetInteger(ctx->handle, NULL, ctx->pin); else - AcpiEvaluateObject(sc->handle, NULL, NULL, NULL); + AcpiEvaluateObject(ctx->handle, NULL, NULL, NULL); +} + +static ACPI_STATUS +gpio_aei_enumerate(ACPI_RESOURCE * res, void * context) +{ + ACPI_RESOURCE_GPIO * gpio_res = &res->Data.Gpio; + struct gpio_aei_softc * sc = context; + uint32_t flags, maxpin; + device_t busdev; + int err; + + /* + * Check that we have a GpioInt object. + * Note that according to the spec this + * should always be the case. + */ + if (res->Type != ACPI_RESOURCE_TYPE_GPIO) + return (AE_OK); + if (gpio_res->ConnectionType != ACPI_RESOURCE_GPIO_TYPE_INT) + return (AE_OK); + + flags = acpi_gpiobus_convflags(gpio_res); + if (acpi_quirks & ACPI_Q_AEI_NOPULL) + flags &= ~GPIO_PIN_PULLUP; + + err = GPIO_PIN_MAX(acpi_get_device(sc->dev_handle), &maxpin); + if (err != 0) + return (AE_ERROR); + + busdev = GPIO_GET_BUS(acpi_get_device(sc->dev_handle)); + for (int i = 0; i < gpio_res->PinTableLength; i++) { + struct gpio_aei_ctx * ctx; + uint32_t pin = gpio_res->PinTable[i]; + + if (__predict_false(pin > maxpin)) { + device_printf(sc->dev, + "Invalid pin 0x%x, max: 0x%x (bad ACPI tables?)\n", + pin, maxpin); + continue; + } + + ctx = malloc(sizeof(struct gpio_aei_ctx), M_DEVBUF, M_WAITOK); + ctx->type = ACPI_AEI_TYPE_UNKNOWN; + if (pin <= 255) { + char objname[5]; /* "_EXX" or "_LXX" */ + sprintf(objname, "_%c%02X", + (flags & GPIO_INTR_EDGE_MASK) ? 'E' : 'L', pin); + if (ACPI_SUCCESS(AcpiGetHandle(sc->dev_handle, objname, + &ctx->handle))) + ctx->type = ACPI_AEI_TYPE_ELX; + } + + if (ctx->type == ACPI_AEI_TYPE_UNKNOWN) { + if (ACPI_SUCCESS(AcpiGetHandle(sc->dev_handle, "_EVT", + &ctx->handle))) + ctx->type = ACPI_AEI_TYPE_EVT; + else { + device_printf(sc->dev, + "AEI Device type is unknown for pin 0x%x\n", + pin); + + free(ctx, M_DEVBUF); + continue; + } + } + + err = gpio_pin_get_by_bus_pinnum(busdev, pin, &ctx->gpio); + if (err != 0) { + device_printf(sc->dev, "Cannot acquire pin 0x%x\n", + pin); + + free(ctx, M_DEVBUF); + continue; + } + + err = gpio_pin_setflags(ctx->gpio, flags & ~GPIO_INTR_MASK); + if (err != 0) { + device_printf(sc->dev, + "Cannot set pin flags for pin 0x%x\n", pin); + + gpio_pin_release(ctx->gpio); + free(ctx, M_DEVBUF); + continue; + } + + ctx->intr_rid = 0; + ctx->intr_res = gpio_alloc_intr_resource(sc->dev, + &ctx->intr_rid, RF_ACTIVE, ctx->gpio, + flags & GPIO_INTR_MASK); + if (ctx->intr_res == NULL) { + device_printf(sc->dev, + "Cannot allocate an IRQ for pin 0x%x\n", pin); + + gpio_pin_release(ctx->gpio); + free(ctx, M_DEVBUF); + continue; + } + + err = bus_setup_intr(sc->dev, ctx->intr_res, INTR_TYPE_MISC | + INTR_MPSAFE | INTR_EXCL | INTR_SLEEPABLE, NULL, + gpio_aei_intr, ctx, &ctx->intr_cookie); + if (err != 0) { + device_printf(sc->dev, + "Cannot set up an IRQ for pin 0x%x\n", pin); + + bus_release_resource(sc->dev, ctx->intr_res); + gpio_pin_release(ctx->gpio); + free(ctx, M_DEVBUF); + continue; + } + + ctx->pin = pin; + SLIST_INSERT_HEAD(&sc->aei_ctx, ctx, next); + } + + return (AE_OK); } static int gpio_aei_attach(device_t dev) { struct gpio_aei_softc * sc = device_get_softc(dev); - gpio_pin_t pin; - uint32_t flags; ACPI_HANDLE handle; - int err; + ACPI_STATUS status; /* This is us. */ device_set_desc(dev, "ACPI Event Information Device"); - /* Store parameters needed by gpio_aei_intr. */ handle = acpi_gpiobus_get_handle(dev); - if (gpio_pin_get_by_child_index(dev, 0, &pin) != 0) { - device_printf(dev, "Unable to get the input pin\n"); + status = AcpiGetParent(handle, &sc->dev_handle); + if (ACPI_FAILURE(status)) { + device_printf(dev, "Cannot get parent of %s\n", + acpi_name(handle)); return (ENXIO); } - sc->type = ACPI_AEI_TYPE_UNKNOWN; - sc->pin = pin->pin; - - flags = acpi_gpiobus_get_flags(dev); - if (pin->pin <= 255) { - char objname[5]; /* "_EXX" or "_LXX" */ - sprintf(objname, "_%c%02X", - (flags & GPIO_INTR_EDGE_MASK) ? 'E' : 'L', pin->pin); - if (ACPI_SUCCESS(AcpiGetHandle(handle, objname, &sc->handle))) - sc->type = ACPI_AEI_TYPE_ELX; - } - if (sc->type == ACPI_AEI_TYPE_UNKNOWN) { - if (ACPI_SUCCESS(AcpiGetHandle(handle, "_EVT", &sc->handle))) - sc->type = ACPI_AEI_TYPE_EVT; - } - - if (sc->type == ACPI_AEI_TYPE_UNKNOWN) { - device_printf(dev, "ACPI Event Information Device type is unknown"); - return (ENOTSUP); - } + SLIST_INIT(&sc->aei_ctx); + sc->dev = dev; - /* Set up the interrupt. */ - if ((sc->intr_res = gpio_alloc_intr_resource(dev, &sc->intr_rid, - RF_ACTIVE, pin, flags & GPIO_INTR_MASK)) == NULL) { - device_printf(dev, "Cannot allocate an IRQ\n"); - return (ENOTSUP); - } - err = bus_setup_intr(dev, sc->intr_res, INTR_TYPE_MISC | INTR_MPSAFE | - INTR_EXCL | INTR_SLEEPABLE, NULL, gpio_aei_intr, sc, - &sc->intr_cookie); - if (err != 0) { - device_printf(dev, "Cannot set up IRQ\n"); - bus_release_resource(dev, SYS_RES_IRQ, sc->intr_rid, - sc->intr_res); - return (err); + status = AcpiWalkResources(sc->dev_handle, "_AEI", + gpio_aei_enumerate, sc); + if (ACPI_FAILURE(status)) { + device_printf(dev, "Failed to enumerate AEI resources\n"); + return (ENXIO); } return (0); @@ -137,9 +233,15 @@ static int gpio_aei_detach(device_t dev) { struct gpio_aei_softc * sc = device_get_softc(dev); + struct gpio_aei_ctx * ctx, * tctx; + + SLIST_FOREACH_SAFE(ctx, &sc->aei_ctx, next, tctx) { + bus_teardown_intr(dev, ctx->intr_res, ctx->intr_cookie); + bus_release_resource(dev, ctx->intr_res); + gpio_pin_release(ctx->gpio); + free(ctx, M_DEVBUF); + } - bus_teardown_intr(dev, sc->intr_res, sc->intr_cookie); - bus_release_resource(dev, SYS_RES_IRQ, sc->intr_rid, sc->intr_res); return (0); } diff --git a/sys/dev/gpio/gpiobus.c b/sys/dev/gpio/gpiobus.c index c25c41f43042..5f1f6532a79b 100644 --- a/sys/dev/gpio/gpiobus.c +++ b/sys/dev/gpio/gpiobus.c @@ -57,7 +57,6 @@ static int gpiobus_suspend(device_t); static int gpiobus_resume(device_t); static void gpiobus_probe_nomatch(device_t, device_t); static int gpiobus_print_child(device_t, device_t); -static int gpiobus_child_location(device_t, device_t, struct sbuf *); static device_t gpiobus_add_child(device_t, u_int, const char *, int); static void gpiobus_hinted_child(device_t, const char *, int); @@ -662,7 +661,7 @@ gpiobus_print_child(device_t dev, device_t child) return (retval); } -static int +int gpiobus_child_location(device_t bus, device_t child, struct sbuf *sb) { struct gpiobus_ivar *devi; @@ -674,16 +673,19 @@ gpiobus_child_location(device_t bus, device_t child, struct sbuf *sb) return (0); } -static device_t -gpiobus_add_child(device_t dev, u_int order, const char *name, int unit) +device_t +gpiobus_add_child_common(device_t dev, u_int order, const char *name, int unit, + size_t ivars_size) { device_t child; struct gpiobus_ivar *devi; + KASSERT(ivars_size >= sizeof(struct gpiobus_ivar), + ("child ivars must include gpiobus_ivar as their first member")); child = device_add_child_ordered(dev, order, name, unit); if (child == NULL) return (child); - devi = malloc(sizeof(struct gpiobus_ivar), M_DEVBUF, M_NOWAIT | M_ZERO); + devi = malloc(ivars_size, M_DEVBUF, M_NOWAIT | M_ZERO); if (devi == NULL) { device_delete_child(dev, child); return (NULL); @@ -694,6 +696,13 @@ gpiobus_add_child(device_t dev, u_int order, const char *name, int unit) return (child); } +static device_t +gpiobus_add_child(device_t dev, u_int order, const char *name, int unit) +{ + return (gpiobus_add_child_common(dev, order, name, unit, + sizeof(struct gpiobus_ivar))); +} + static void gpiobus_child_deleted(device_t dev, device_t child) { diff --git a/sys/dev/gpio/gpiobus_internal.h b/sys/dev/gpio/gpiobus_internal.h index de3f57663132..c198e5f79989 100644 --- a/sys/dev/gpio/gpiobus_internal.h +++ b/sys/dev/gpio/gpiobus_internal.h @@ -42,6 +42,8 @@ void gpiobus_free_ivars(struct gpiobus_ivar *); 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 *); +device_t gpiobus_add_child_common(device_t, u_int, const char *, int, size_t); extern driver_t gpiobus_driver; #endif diff --git a/sys/dev/gpio/ofw_gpiobus.c b/sys/dev/gpio/ofw_gpiobus.c index fc5fb03d6824..b12b78fac18c 100644 --- a/sys/dev/gpio/ofw_gpiobus.c +++ b/sys/dev/gpio/ofw_gpiobus.c @@ -451,28 +451,22 @@ ofw_gpiobus_add_child(device_t dev, u_int order, const char *name, int unit) device_t child; struct ofw_gpiobus_devinfo *devi; - child = device_add_child_ordered(dev, order, name, unit); + child = gpiobus_add_child_common(dev, order, name, unit, + sizeof(struct ofw_gpiobus_devinfo)); if (child == NULL) - return (child); - devi = malloc(sizeof(struct ofw_gpiobus_devinfo), M_DEVBUF, - M_NOWAIT | M_ZERO); - if (devi == NULL) { - device_delete_child(dev, child); - return (0); - } + return (NULL); /* * NULL all the OFW-related parts of the ivars for non-OFW * children. */ + devi = device_get_ivars(child); devi->opd_obdinfo.obd_node = -1; devi->opd_obdinfo.obd_name = NULL; devi->opd_obdinfo.obd_compat = NULL; devi->opd_obdinfo.obd_type = NULL; devi->opd_obdinfo.obd_model = NULL; - device_set_ivars(child, devi); - return (child); } |