Page MenuHomeFreeBSD

D26311.1777276517.diff
No OneTemporary

Size
15 KB
Referenced Files
None
Subscribers
None

D26311.1777276517.diff

Index: sys/dev/gpio/gpioled_acpi.c
===================================================================
--- /dev/null
+++ sys/dev/gpio/gpioled_acpi.c
@@ -0,0 +1,596 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) Andriy Gapon <avg@FreeBSD.org>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions, and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include "opt_platform.h"
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/bus.h>
+#include <sys/gpio.h>
+#include <sys/kernel.h>
+#include <sys/lock.h>
+#include <sys/malloc.h>
+#include <sys/module.h>
+#include <sys/mutex.h>
+
+#include <contrib/dev/acpica/include/acpi.h>
+#include <contrib/dev/acpica/include/accommon.h>
+
+#include <dev/acpica/acpivar.h>
+#include <dev/gpio/gpiobusvar.h>
+#include <dev/led/led.h>
+
+#include "gpiobus_if.h"
+
+struct gpioled
+{
+ gpio_pin_t pin;
+ struct cdev *leddev;
+};
+
+struct gpioleds_softc
+{
+ device_t sc_dev;
+ struct gpioled *sc_leds;
+ int sc_led_count;
+ int sc_led_size;
+};
+
+/* UUID_DEVICE_PROPERTIES, daffd814-6eba-4d8c-8a91-bc9bbf4aa301. */
+static const uint8_t dev_prop_uuid[] = {
+ 0x14, 0xd8, 0xff, 0xda,
+ 0xba, 0x6e,
+ 0x8c, 0x4d,
+ 0x8a, 0x91, 0xbc, 0x9b, 0xbf, 0x4a, 0xa3, 0x01
+};
+
+static const char * gpio_leds_compat[] = {
+ "gpio-leds",
+ NULL
+};
+
+static void gpioled_control(void *priv, int onoff);
+
+typedef int (*dsd_property_cb)(const char *name, const ACPI_OBJECT *obj,
+ void *arg);
+
+static int
+acpi_dsd_walk(device_t dev, const uint8_t *uuid, dsd_property_cb cb, void *arg)
+{
+ ACPI_HANDLE h;
+ ACPI_BUFFER buf;
+ ACPI_OBJECT *dsd_data, *dsd_uuid;
+ ACPI_OBJECT *bp;
+ ACPI_STATUS status;
+ int error;
+
+ h = acpi_get_handle(dev);
+ if (h == NULL)
+ return (ENXIO);
+
+ buf.Pointer = NULL;
+ buf.Length = ACPI_ALLOCATE_BUFFER;
+ status = AcpiEvaluateObjectTyped(h, METHOD_NAME__DSD, NULL, &buf,
+ ACPI_TYPE_PACKAGE);
+ if (status == AE_NOT_FOUND)
+ return (ENOENT);
+ if (ACPI_FAILURE(status))
+ return (ENXIO);
+
+ error = 0;
+ bp = buf.Pointer;
+ if (bp->Package.Count % 2 != 0) {
+ error = EINVAL;
+ goto out;
+ }
+
+ for (int i = 0; i < bp->Package.Count; i += 2) {
+ dsd_uuid = &bp->Package.Elements[i];
+ dsd_data = &bp->Package.Elements[i + 1];
+
+ if (dsd_uuid->Type != ACPI_TYPE_BUFFER ||
+ dsd_uuid->Buffer.Length != UUID_BUFFER_LENGTH ||
+ dsd_data->Type != ACPI_TYPE_PACKAGE) {
+ error = EINVAL;
+ goto out;
+ }
+
+ if (memcmp(uuid, dsd_uuid->Buffer.Pointer,
+ UUID_BUFFER_LENGTH) != 0)
+ continue;
+
+ for (int p = 0; p < dsd_data->Package.Count; p++) {
+ ACPI_OBJECT *prop;
+ ACPI_OBJECT *name;
+ ACPI_OBJECT *val;
+
+ prop = &dsd_data->Package.Elements[p];
+ if (prop->Type != ACPI_TYPE_PACKAGE ||
+ prop->Package.Count != 2) {
+ error = EINVAL;
+ goto out;
+ }
+
+ name = &prop->Package.Elements[0];
+ val = &prop->Package.Elements[1];
+ if (name->Type != ACPI_TYPE_STRING) {
+ error = EINVAL;
+ goto out;
+ }
+
+ switch (val->Type) {
+ case ACPI_TYPE_PACKAGE:
+ /* Validate types used in the package. */
+ for (int e = 0; e < val->Package.Count; e++) {
+ ACPI_OBJECT *element;
+
+ element = &val->Package.Elements[e];
+ switch (element->Type) {
+ case ACPI_TYPE_STRING:
+ case ACPI_TYPE_INTEGER:
+ case ACPI_TYPE_LOCAL_REFERENCE:
+ break;
+ default:
+ error = EINVAL;
+ goto out;
+ }
+ }
+ /* FALLTHROUGH */
+ case ACPI_TYPE_STRING:
+ case ACPI_TYPE_INTEGER:
+ case ACPI_TYPE_LOCAL_REFERENCE:
+ /* Allow the callback to abort the walk. */
+ error = cb(name->String.Pointer, val, arg);
+ if (error != 0) {
+ if (error == EJUSTRETURN)
+ error = 0;
+ goto out;
+ }
+ break;
+ default:
+ error = EINVAL;
+ goto out;
+ }
+ }
+ }
+
+out:
+ AcpiOsFree(buf.Pointer);
+ return (error);
+}
+
+struct compat_check_arg
+{
+ const char * const * const strings;
+ int index;
+ bool found;
+};
+
+static int
+compat_check_cb(const char *name, const ACPI_OBJECT *obj, void *arg)
+{
+ struct compat_check_arg *cca = arg;
+
+ if (strcmp(name, "compatible") != 0)
+ return (0); /* keep going */
+ if (obj->Type != ACPI_TYPE_STRING)
+ return (EINVAL);
+ for (int i = 0; cca->strings[i] != NULL; i++) {
+ if (strcmp(cca->strings[i], obj->String.Pointer) == 0) {
+ cca->index = i;
+ cca->found = true;
+ break;
+ }
+ }
+ return (EJUSTRETURN); /* a single instance is expected */
+}
+
+static int
+acpi_dsd_check_compat(device_t dev, const char *strings[])
+{
+ struct compat_check_arg cca = {
+ .strings = strings,
+ .index = -1,
+ .found = false,
+ };
+ int error;
+
+ error = acpi_dsd_walk(dev, dev_prop_uuid, compat_check_cb, &cca);
+ if (error != 0)
+ return (error);
+ if (!cca.found)
+ return (ENXIO);
+ return (0);
+}
+
+struct acpi_gpio_iopin
+{
+ ACPI_HANDLE handle;
+ int pin_num;
+ int config;
+ int io_restriction;
+};
+
+struct crs_iopin_walk
+{
+ struct acpi_gpio_iopin *iopin;
+ ACPI_HANDLE handle;
+ int crs_index;
+ int pos;
+ int pin_index;
+};
+
+static ACPI_STATUS
+acpi_gpio_io_cb(ACPI_RESOURCE *res, void *arg)
+{
+ struct crs_iopin_walk *ctx = arg;
+ ACPI_RESOURCE_GPIO *gpio_res;
+ ACPI_HANDLE controller;
+ ACPI_STATUS status;
+
+ if (res->Type != ACPI_RESOURCE_TYPE_GPIO)
+ return (AE_OK);
+
+ if (ctx->pos++ != ctx->crs_index)
+ return (AE_OK);
+
+ gpio_res = &res->Data.Gpio;
+ if (gpio_res->ConnectionType == ACPI_RESOURCE_GPIO_TYPE_IO &&
+ gpio_res->ProducerConsumer == ACPI_CONSUMER) {
+ if (ctx->pin_index >= gpio_res->PinTableLength)
+ return (AE_ERROR);
+ ctx->iopin->pin_num = gpio_res->PinTable[ctx->pin_index];
+
+ /* Allow both relative and absolute paths. */
+ status = AcpiGetHandle(ctx->handle,
+ gpio_res->ResourceSource.StringPtr, &controller);
+ if (ACPI_FAILURE(status)) {
+ printf("failed to resolve GPIO resource source %s\n",
+ gpio_res->ResourceSource.StringPtr);
+ return (status);
+ }
+ ctx->iopin->handle = controller;
+
+ ctx->iopin->config = gpio_res->PinConfig;
+ ctx->iopin->io_restriction = gpio_res->IoRestriction;
+ return (AE_CTRL_TERMINATE);
+ }
+ return (AE_ERROR);
+}
+
+static int
+acpi_gpio_get_iopin(ACPI_HANDLE handle, int crs_index, int pin_index,
+ struct acpi_gpio_iopin *iopin)
+{
+ struct crs_iopin_walk ctx;
+ ACPI_STATUS status;
+
+ ctx.handle = handle;
+ ctx.iopin = iopin;
+ ctx.crs_index = crs_index;
+ ctx.pin_index = pin_index;
+ ctx.pos = 0;
+
+ status = AcpiWalkResources(handle, "_CRS", acpi_gpio_io_cb, &ctx);
+ if (ACPI_FAILURE(status))
+ return (ENXIO);
+ return (0);
+}
+
+static ACPI_STATUS
+gpioled_count_leds_cb(ACPI_HANDLE handle, UINT32 level, void *context,
+ void **status)
+{
+ struct gpioleds_softc *sc = context;
+
+ sc->sc_led_size++;
+ return (AE_OK);
+}
+
+struct led_props_arg
+{
+ struct gpioleds_softc *leds_sc;
+ gpio_pin_t pin;
+ char *name;
+ int state;
+};
+
+static void
+gpios_prop_cb(device_t dev, const ACPI_OBJECT *obj, struct led_props_arg *lpa)
+{
+ static char path[256];
+ ACPI_BUFFER buf;
+ struct acpi_gpio_iopin iopin;
+ device_t gpio_dev;
+ device_t bus_dev;
+ ACPI_HANDLE ref;
+ int crs_index, pin_index;
+ int active_low;
+ int err;
+
+ if (obj->Type != ACPI_TYPE_PACKAGE) {
+ device_printf(dev, "gpios property is not a package, "
+ "type = %d\n", obj->Type);
+ return;
+ }
+
+ if (obj->Package.Count != 4 ||
+ obj->Package.Elements[0].Type != ACPI_TYPE_LOCAL_REFERENCE ||
+ obj->Package.Elements[1].Type != ACPI_TYPE_INTEGER ||
+ obj->Package.Elements[2].Type != ACPI_TYPE_INTEGER ||
+ obj->Package.Elements[3].Type != ACPI_TYPE_INTEGER) {
+ device_printf(dev, "gpios package has incorrect structure\n");
+ return;
+ }
+
+ crs_index = obj->Package.Elements[1].Integer.Value;
+ pin_index = obj->Package.Elements[2].Integer.Value;
+ active_low = obj->Package.Elements[3].Integer.Value;
+
+ ref = acpi_GetReference(NULL, &obj->Package.Elements[0]);
+ if (ref == NULL) {
+ device_printf(dev, "failed to resolve _CRS device reference\n");
+ return;
+ }
+
+ err = acpi_gpio_get_iopin(ref, crs_index, pin_index, &iopin);
+ if (err != 0) {
+ buf.Length = sizeof(path);
+ buf.Pointer = path;
+ AcpiGetName(ref, ACPI_FULL_PATHNAME, &buf);
+ device_printf(dev, "failed to get GpioIo resource, "
+ "device = %s, CRS crs_index = %d, err = %d\n",
+ path, crs_index, err);
+ return;
+ }
+
+ /* Stash controller's path, we may need it later. */
+ buf.Length = sizeof(path);
+ buf.Pointer = path;
+ AcpiGetName(iopin.handle, ACPI_FULL_PATHNAME, &buf);
+
+ gpio_dev = acpi_get_device(iopin.handle);
+ if (gpio_dev == NULL || !device_is_attached(gpio_dev)) {
+ device_printf(dev, "GPIO controller device %s is not "
+ "attached\n", path);
+ return;
+ }
+
+ bus_dev = GPIO_GET_BUS(gpio_dev);
+ err = gpio_pin_get_by_bus_pinnum(bus_dev, iopin.pin_num, &lpa->pin);
+ if (err != 0) {
+ device_printf(dev, "failed to acquire pin %d from %s: %d\n",
+ iopin.pin_num, device_get_nameunit(bus_dev), err);
+ return;
+ }
+
+ /* TODO expose GPIO_ACTIVE_LOW. */
+ lpa->pin->flags = active_low ? 1 : 0;
+
+ if (bootverbose) {
+ device_printf(dev, "using pin %d of %s [%s] (active %s)\n",
+ iopin.pin_num, device_get_nameunit(gpio_dev), path,
+ active_low ? "low" : "high");
+
+ }
+}
+
+static int
+led_props_cb(const char *name, const ACPI_OBJECT *obj, void *arg)
+{
+ struct led_props_arg *lpa = arg;
+ struct gpioleds_softc *sc;
+ device_t dev;
+
+ sc = lpa->leds_sc;
+ dev = sc->sc_dev;
+
+ if (strcmp(name, "label") == 0) {
+ if (obj->Type == ACPI_TYPE_STRING)
+ lpa->name = strdup(obj->String.Pointer, M_DEVBUF);
+ else
+ device_printf(dev, "label is not a string, type = %d\n",
+ obj->Type);
+ } else if (strcmp(name, "default-state") == 0) {
+ if (obj->Type != ACPI_TYPE_STRING) {
+ device_printf(dev, "default-state is not a string, "
+ "type = %d\n", obj->Type);
+ } else if (strcasecmp(obj->String.Pointer, "on") == 0) {
+ lpa->state = 1;
+ } else if (strcasecmp(obj->String.Pointer, "off") == 0) {
+ lpa->state = 0;
+ } else if (strcasecmp(obj->String.Pointer, "keep") == 0) {
+ lpa->state = -1;
+ } else {
+ lpa->state = -1;
+ device_printf(dev, "unknown default-state value %s\n",
+ obj->String.Pointer);
+ }
+ } else if (strcmp(name, "gpios") == 0) {
+ gpios_prop_cb(dev, obj, lpa);
+ } else if (bootverbose) {
+ device_printf(dev, "ignored unknown property %s\n", name);
+ }
+ return (0);
+}
+
+static ACPI_STATUS
+gpioleds_attach_led_cb(ACPI_HANDLE handle, UINT32 level, void *context,
+ void **status)
+{
+ struct led_props_arg lpa;
+ struct gpioleds_softc *sc = context;
+ struct gpioled *led;
+ int error;
+
+ lpa.leds_sc = sc;
+ lpa.pin = NULL;
+ lpa.name = NULL;
+ lpa.state = 0;
+ error = acpi_dsd_walk(acpi_get_device(handle), dev_prop_uuid,
+ led_props_cb, &lpa);
+
+ if (error != 0)
+ goto out;
+ if (lpa.pin == NULL)
+ goto out;
+ if (lpa.name == NULL) {
+ gpio_pin_release(lpa.pin);
+ goto out;
+ }
+
+ led = &sc->sc_leds[sc->sc_led_count];
+ led->pin = lpa.pin;
+ gpio_pin_setflags(led->pin, GPIO_PIN_OUTPUT);
+ led->leddev = led_create_state(gpioled_control, led, lpa.name,
+ lpa.state);
+ sc->sc_led_count++;
+
+ if (bootverbose)
+ device_printf(sc->sc_dev, "added LED %s\n", lpa.name);
+
+out:
+ free(lpa.name, M_DEVBUF);
+ return (AE_OK);
+}
+
+static void
+gpioled_control(void *priv, int onoff)
+{
+ struct gpioled *led;
+
+ led = (struct gpioled *)priv;
+ gpio_pin_set_active(led->pin, onoff);
+}
+
+static void
+gpioleds_detach_led(struct gpioled *led)
+{
+
+ if (led->leddev != NULL)
+ led_destroy(led->leddev);
+
+ if (led->pin)
+ gpio_pin_release(led->pin);
+}
+
+static int
+gpioleds_probe(device_t dev)
+{
+ if (acpi_dsd_check_compat(dev, gpio_leds_compat) != 0)
+ return (ENXIO);
+
+ device_set_desc(dev, "ACPI GPIO LEDs");
+ return (BUS_PROBE_DEFAULT);
+}
+
+static int
+gpioleds_attach(device_t dev)
+{
+ ACPI_HANDLE handle;
+ struct gpioleds_softc *sc;
+
+ handle = acpi_get_handle(dev);
+ sc = device_get_softc(dev);
+ sc->sc_dev = dev;
+
+ AcpiWalkNamespace(ACPI_TYPE_DEVICE, handle, 1,
+ gpioled_count_leds_cb, NULL, sc, NULL);
+ if (sc->sc_led_size == 0)
+ return (ENXIO);
+
+ sc->sc_leds = malloc(sizeof(struct gpioled) * sc->sc_led_size,
+ M_DEVBUF, M_WAITOK | M_ZERO);
+
+ AcpiWalkNamespace(ACPI_TYPE_DEVICE, handle, 1,
+ gpioleds_attach_led_cb, NULL, sc, NULL);
+
+ return (0);
+}
+
+static int
+gpioleds_detach(device_t dev)
+{
+ struct gpioleds_softc *sc;
+ int i;
+
+ sc = device_get_softc(dev);
+
+ for (i = 0; i < sc->sc_led_count; i++)
+ gpioleds_detach_led(&sc->sc_leds[i]);
+
+ free(sc->sc_leds, M_DEVBUF);
+
+ return (0);
+}
+
+/*
+ * Turn off all LEDs.
+ * TODO support for retain-state-shutdown, retain-state-suspended.
+ * TODO support for restoring LED state after resume.
+ */
+static int
+gpioleds_shutdown(device_t dev)
+{
+ struct gpioleds_softc *sc;
+ int i;
+
+ sc = device_get_softc(dev);
+
+ for (i = 0; i < sc->sc_led_count; i++)
+ gpioled_control(&sc->sc_leds[i], 0);
+
+ return (0);
+}
+
+static devclass_t acpi_gpioleds_devclass;
+
+static device_method_t acpi_gpioleds_methods[] = {
+ /* Device interface */
+ DEVMETHOD(device_probe, gpioleds_probe),
+ DEVMETHOD(device_attach, gpioleds_attach),
+ DEVMETHOD(device_detach, gpioleds_detach),
+ DEVMETHOD(device_shutdown, gpioleds_shutdown),
+
+ DEVMETHOD_END
+};
+
+static driver_t acpi_gpioleds_driver = {
+ "gpioleds",
+ acpi_gpioleds_methods,
+ sizeof(struct gpioleds_softc),
+};
+
+DRIVER_MODULE(gpioleds, acpi, acpi_gpioleds_driver, acpi_gpioleds_devclass,
+ NULL, NULL);
+MODULE_DEPEND(gpioleds, acpi, 1, 1, 1);
+MODULE_DEPEND(gpioleds, gpiobus, 1, 1, 1);
+MODULE_VERSION(gpioleds, 1);
Index: sys/modules/gpio/gpioled/Makefile
===================================================================
--- sys/modules/gpio/gpioled/Makefile
+++ sys/modules/gpio/gpioled/Makefile
@@ -32,12 +32,20 @@
.PATH: ${SRCTOP}/sys/dev/gpio/
KMOD= gpioled
+
.if !empty(OPT_FDT)
SRCS= gpioled_fdt.c
+SRCS+= ofw_bus_if.h
.else
SRCS= gpioled.c
.endif
-SRCS+= device_if.h bus_if.h gpio_if.h gpiobus_if.h opt_platform.h ofw_bus_if.h
+
+.if ${MACHINE_CPUARCH} == "aarch64" || ${MACHINE_CPUARCH} == "amd64" || \
+ ${MACHINE_CPUARCH} == "i386"
+SRCS+= gpioled_acpi.c opt_acpi.h acpi_if.h
+.endif
+
+SRCS+= device_if.h bus_if.h gpio_if.h gpiobus_if.h opt_platform.h
CFLAGS+= -I. -I${SRCTOP}/sys/dev/gpio/

File Metadata

Mime Type
text/plain
Expires
Mon, Apr 27, 7:55 AM (8 h, 45 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
28437464
Default Alt Text
D26311.1777276517.diff (15 KB)

Event Timeline