Source-Changes-HG archive

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index][Old Index]

[src/trunk]: src/sys/dev/acpi Add support for GPIO interrupt signaled ACPI ev...



details:   https://anonhg.NetBSD.org/src/rev/e2ee1e1ac904
branches:  trunk
changeset: 994142:e2ee1e1ac904
user:      jmcneill <jmcneill%NetBSD.org@localhost>
date:      Sun Oct 21 18:31:58 2018 +0000

description:
Add support for GPIO interrupt signaled ACPI events.

diffstat:

 sys/dev/acpi/plgpio_acpi.c |  220 ++++++++++++++++++++++++++++++++++++++++++++-
 1 files changed, 216 insertions(+), 4 deletions(-)

diffs (273 lines):

diff -r 09f7f1528f9f -r e2ee1e1ac904 sys/dev/acpi/plgpio_acpi.c
--- a/sys/dev/acpi/plgpio_acpi.c        Sun Oct 21 18:31:14 2018 +0000
+++ b/sys/dev/acpi/plgpio_acpi.c        Sun Oct 21 18:31:58 2018 +0000
@@ -1,4 +1,4 @@
-/* $NetBSD: plgpio_acpi.c,v 1.1 2018/10/15 23:59:16 jmcneill Exp $ */
+/* $NetBSD: plgpio_acpi.c,v 1.2 2018/10/21 18:31:58 jmcneill Exp $ */
 
 /*-
  * Copyright (c) 2018 The NetBSD Foundation, Inc.
@@ -30,7 +30,7 @@
  */
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: plgpio_acpi.c,v 1.1 2018/10/15 23:59:16 jmcneill Exp $");
+__KERNEL_RCSID(0, "$NetBSD: plgpio_acpi.c,v 1.2 2018/10/21 18:31:58 jmcneill Exp $");
 
 #include <sys/param.h>
 #include <sys/bus.h>
@@ -43,11 +43,35 @@
 
 #include <dev/gpio/gpiovar.h>
 #include <dev/ic/pl061var.h>
+#include <dev/ic/pl061reg.h>
+
+struct plgpio_acpi_softc;
+
+struct plgpio_acpi_event {
+       struct plgpio_acpi_softc *ev_sc;
+       u_int                   ev_pin;
+       ACPI_HANDLE             ev_method;
+       bool                    ev_method_evt;
+};
+
+struct plgpio_acpi_softc {
+       struct plgpio_softc     sc_base;
+
+       ACPI_HANDLE             sc_handle;
+       uint32_t                sc_aei_pins;            /* bitmask */
+       ACPI_RESOURCE_GPIO      sc_aei_res[8];
+
+       struct plgpio_acpi_event sc_event[8];
+};
 
 static int     plgpio_acpi_match(device_t, cfdata_t, void *);
 static void    plgpio_acpi_attach(device_t, device_t, void *);
 
-CFATTACH_DECL_NEW(plgpio_acpi, sizeof(struct plgpio_softc), plgpio_acpi_match, plgpio_acpi_attach, NULL, NULL);
+static void    plgpio_acpi_init(struct plgpio_acpi_softc *);
+static void    plgpio_acpi_notify(void *);
+static int     plgpio_acpi_intr(void *);
+
+CFATTACH_DECL_NEW(plgpio_acpi, sizeof(struct plgpio_acpi_softc), plgpio_acpi_match, plgpio_acpi_attach, NULL, NULL);
 
 static const char * const compatible[] = {
        "ARMH0061",
@@ -68,14 +92,18 @@
 static void
 plgpio_acpi_attach(device_t parent, device_t self, void *aux)
 {
-       struct plgpio_softc * const sc = device_private(self);
+       struct plgpio_acpi_softc * const asc = device_private(self);
+       struct plgpio_softc * const sc = &asc->sc_base;
        struct acpi_attach_args *aa = aux;
        struct acpi_resources res;
        struct acpi_mem *mem;
+       struct acpi_irq *irq;
        ACPI_STATUS rv;
        int error;
+       void *ih;
 
        sc->sc_dev = self;
+       asc->sc_handle = aa->aa_node->ad_handle;
 
        rv = acpi_resource_parse(sc->sc_dev, aa->aa_node->ad_handle, "_CRS",
            &res, &acpi_resource_parse_ops_default);
@@ -88,6 +116,12 @@
                goto done;
        }
 
+       irq = acpi_res_irq(&res, 0);
+       if (mem == NULL) {
+               aprint_error_dev(self, "couldn't find irq resource\n");
+               goto done;
+       }
+
        sc->sc_dev = self;
        sc->sc_bst = aa->aa_memt;
        error = bus_space_map(sc->sc_bst, mem->ar_base, mem->ar_length, 0, &sc->sc_bsh);
@@ -98,6 +132,184 @@
 
        plgpio_attach(sc);
 
+       const int type = (irq->ar_type == ACPI_EDGE_SENSITIVE) ? IST_EDGE : IST_LEVEL;
+       ih = intr_establish(irq->ar_irq, IPL_VM, type, plgpio_acpi_intr, asc);
+       if (ih == NULL)
+               aprint_error_dev(self, "couldn't establish interrupt\n");
+
+       plgpio_acpi_init(asc);
+
 done:
        acpi_resource_cleanup(&res);
 }
+
+static ACPI_STATUS
+plgpio_acpi_resource_cb(ACPI_RESOURCE *res, void *ctx)
+{
+       struct plgpio_acpi_softc * const asc = ctx;
+       ACPI_RESOURCE_GPIO *gpio;
+       UINT16 pin;
+
+       if (res->Type != ACPI_RESOURCE_TYPE_GPIO)
+               return AE_OK;
+
+       gpio = &res->Data.Gpio;
+       if (gpio->ConnectionType != ACPI_RESOURCE_GPIO_TYPE_INT ||
+           gpio->PinTableLength != 1)
+               return AE_OK;
+
+       pin = gpio->PinTable[0];
+       if (pin >= __arraycount(asc->sc_aei_res)) {
+               aprint_error_dev(asc->sc_base.sc_dev, "_AEI pin %u out of range\n", pin);
+               return AE_OK;
+       }
+
+       asc->sc_aei_pins |= __BIT(pin);
+       asc->sc_aei_res[pin] = *gpio;
+
+       return AE_OK;
+}
+
+static void
+plgpio_acpi_init(struct plgpio_acpi_softc *asc)
+{
+       struct plgpio_softc * const sc = &asc->sc_base;
+       ACPI_RESOURCE_GPIO *gpio;
+       ACPI_HANDLE handle;
+       char namebuf[5];
+       ACPI_STATUS rv;
+       uint32_t ibe, iev, is, ie;
+       int pin;
+
+       rv = AcpiWalkResources(asc->sc_handle, "_AEI", plgpio_acpi_resource_cb, asc);
+       if (ACPI_FAILURE(rv)) {
+               if (rv != AE_NOT_FOUND)
+                       aprint_error_dev(asc->sc_base.sc_dev, "failed to parse _AEI: %s\n",
+                           AcpiFormatException(rv));
+               return;
+       }
+
+       if (!asc->sc_aei_pins)
+               return;
+
+       aprint_verbose_dev(asc->sc_base.sc_dev, "ACPI event pins: %#x\n", asc->sc_aei_pins);
+
+       sc->sc_reserved_mask = asc->sc_aei_pins;
+
+       /*
+        * For each event pin, find the corresponding event method (_Exx, _Lxx, or _EVT).
+        */
+       for (pin = 0; pin < __arraycount(asc->sc_aei_res); pin++) {
+               if ((asc->sc_aei_pins & __BIT(pin)) == 0)
+                       continue;
+
+               gpio = &asc->sc_aei_res[pin];
+
+               const char trig = gpio->Triggering == ACPI_LEVEL_SENSITIVE ? 'L' : 'E';
+               handle = NULL;
+               snprintf(namebuf, sizeof(namebuf), "_%c%02X", trig, pin);
+               if (ACPI_FAILURE(AcpiGetHandle(asc->sc_handle, namebuf, &handle))) {
+                       (void)AcpiGetHandle(asc->sc_handle, "_EVT", &handle);
+                       if (handle != NULL)
+                               asc->sc_event[pin].ev_method_evt = true;
+               }
+
+               if (handle == NULL)
+                       continue;
+
+               asc->sc_event[pin].ev_sc = asc;
+               asc->sc_event[pin].ev_pin = pin;
+               asc->sc_event[pin].ev_method = handle;
+
+               /*
+                * Configure and enable interrupts for this pin.
+                */
+
+               ibe = PLGPIO_READ(sc, PL061_GPIOIBE_REG);
+               iev = PLGPIO_READ(sc, PL061_GPIOIEV_REG);
+               switch (gpio->Polarity) {
+               case ACPI_ACTIVE_HIGH:
+                       ibe &= ~__BIT(pin);
+                       iev |= __BIT(pin);
+                       break;
+               case ACPI_ACTIVE_LOW:
+                       ibe &= ~__BIT(pin);
+                       iev &= ~__BIT(pin);
+                       break;
+               case ACPI_ACTIVE_BOTH:
+                       ibe |= __BIT(pin);
+                       break;
+               }
+               PLGPIO_WRITE(sc, PL061_GPIOIBE_REG, ibe);
+               PLGPIO_WRITE(sc, PL061_GPIOIEV_REG, iev);
+
+               is = PLGPIO_READ(sc, PL061_GPIOIS_REG);
+               switch (gpio->Triggering) {
+               case ACPI_LEVEL_SENSITIVE:
+                       is |= __BIT(pin);
+                       break;
+               case ACPI_EDGE_SENSITIVE:
+                       is &= ~__BIT(pin);
+                       break;
+               }
+               PLGPIO_WRITE(sc, PL061_GPIOIS_REG, is);
+
+               delay(20);
+
+               PLGPIO_WRITE(sc, PL061_GPIOIC_REG, __BIT(pin));
+
+               ie = PLGPIO_READ(sc, PL061_GPIOIE_REG);
+               ie |= __BIT(pin);
+               PLGPIO_WRITE(sc, PL061_GPIOIE_REG, ie);
+       }
+}
+
+static void
+plgpio_acpi_notify(void *priv)
+{
+       struct plgpio_acpi_event * const ev = priv;
+       struct plgpio_acpi_softc * const asc = ev->ev_sc;
+       struct plgpio_softc * const sc = &asc->sc_base;
+       ACPI_STATUS rv;
+
+       if (ev->ev_method_evt) {
+               ACPI_OBJECT_LIST objs;
+               ACPI_OBJECT obj[1];
+               objs.Count = 1;
+               objs.Pointer = obj;
+               obj[0].Type = ACPI_TYPE_INTEGER;
+               obj[0].Integer.Value = ev->ev_pin;
+               rv = AcpiEvaluateObject(ev->ev_method, NULL, &objs, NULL);
+       } else {
+               rv = AcpiEvaluateObject(ev->ev_method, NULL, NULL, NULL);
+       }
+
+       if (ACPI_FAILURE(rv))
+               device_printf(sc->sc_dev, "failed to handle %s event: %s\n",
+                   acpi_name(ev->ev_method), AcpiFormatException(rv));
+}
+
+static int
+plgpio_acpi_intr(void *priv)
+{
+       struct plgpio_acpi_softc * const asc = priv;
+       struct plgpio_softc * const sc = &asc->sc_base;
+       uint32_t mis;
+       int bit;
+
+       mis = PLGPIO_READ(sc, PL061_GPIOMIS_REG);
+       PLGPIO_WRITE(sc, PL061_GPIOIC_REG, mis);
+
+       while ((bit = __builtin_ffs(mis)) != 0) {
+               const int pin = bit - 1;
+               struct plgpio_acpi_event * const ev = &asc->sc_event[pin];
+
+               KASSERT(ev->ev_method != NULL);
+
+               AcpiOsExecute(OSL_NOTIFY_HANDLER, plgpio_acpi_notify, ev);
+
+               mis &= ~__BIT(pin);
+       }
+
+       return 1;
+}



Home | Main Index | Thread Index | Old Index