The branch main has been updated by corvink:

URL: 
https://cgit.FreeBSD.org/src/commit/?id=5349a46babc03453b0e6679bf867272e7ef7ea22

commit 5349a46babc03453b0e6679bf867272e7ef7ea22
Author:     YannickV <[email protected]>
AuthorDate: 2026-04-29 12:56:14 +0000
Commit:     Corvin Köhne <[email protected]>
CommitDate: 2026-08-06 06:41:33 +0000

    gpio: add generic Intel GPIO pin controller framework
    
    Add a platform-independent driver framework for Intel GPIO pin controllers
    found on modern Intel SoCs. The driver accesses GPIO pad registers through
    ACPI-provided memory-mapped resources and implements the gpio interface [1]
    including pin enumeration, capability reporting, configuration, and
    read/write/toggle operations. A common data model of communities and pad 
groups
    allows individual SoC-specific drivers to supply their own pad tables and 
ACPI
    hardware IDs while sharing all register-level logic.
    
    [1] https://wiki.freebsd.org/GPIO
    
    Reviewed by:            vexeduxr
    MFC after:              1 week
    Sponsored by:           Beckhoff Automation GmbH & Co. KG
    Pull Request:           https://github.com/freebsd/freebsd-src/pull/2205
---
 sys/dev/gpio/intel/intelgpio.c | 415 +++++++++++++++++++++++++++++++++++++++++
 sys/dev/gpio/intel/intelgpio.h |  64 +++++++
 2 files changed, 479 insertions(+)

diff --git a/sys/dev/gpio/intel/intelgpio.c b/sys/dev/gpio/intel/intelgpio.c
new file mode 100644
index 000000000000..aba4c12da828
--- /dev/null
+++ b/sys/dev/gpio/intel/intelgpio.c
@@ -0,0 +1,415 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2026 Beckhoff Automation GmbH & Co. KG
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/bus.h>
+#include <sys/gpio.h>
+#include <sys/kernel.h>
+#include <sys/module.h>
+#include <sys/rman.h>
+
+#include <machine/bus.h>
+#include <machine/resource.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 "intelgpio.h"
+
+#include "gpio_if.h"
+#include "opt_acpi.h"
+
+#define INTELGPIO_LOCK(_sc)   mtx_lock_spin(&(_sc)->sc_mtx)
+#define INTELGPIO_UNLOCK(_sc) mtx_unlock_spin(&(_sc)->sc_mtx)
+#define INTELGPIO_LOCK_INIT(_sc)                                     \
+       mtx_init(&(_sc)->sc_mtx, device_get_nameunit((_sc)->sc_dev), \
+           "intelgpio", MTX_SPIN)
+#define INTELGPIO_LOCK_DESTROY(_sc) mtx_destroy(&(_sc)->sc_mtx)
+
+static int
+intelgpio_gpio_to_pad(struct intelgpio_softc *sc, uint32_t pin,
+    struct resource **res, bus_size_t *pad_off,
+    const struct intelgpio_padgroup **pgp)
+{
+       const struct intelgpio_platform *plat = sc->sc_plat;
+       const struct intelgpio_community *com;
+       const struct intelgpio_padgroup *pg;
+       int i, j, offset, local_pad;
+
+       for (i = 0; i < plat->ncommunities; i++) {
+               com = &plat->communities[i];
+               for (j = 0; j < com->ngroups; j++) {
+                       pg = &com->groups[j];
+                       if (pg->gpio_base == INTELGPIO_GPIO_NOMAP)
+                               continue;
+                       if (pin >= pg->gpio_base &&
+                           pin < pg->gpio_base + pg->npads) {
+                               if (res != NULL)
+                                       *res = sc->sc_mem_res[i];
+                               if (pad_off != NULL) {
+                                       offset = pin - pg->gpio_base;
+                                       local_pad = (pg->first_pad + offset) -
+                                           com->groups[0].first_pad;
+                                       *pad_off = sc->sc_padbar[i] +
+                                           (local_pad * INTELGPIO_PAD_SIZE);
+                               }
+                               if (pgp != NULL)
+                                       *pgp = pg;
+                               return (0);
+                       }
+               }
+       }
+       return (-1);
+}
+
+static device_t
+intelgpio_get_bus(device_t dev)
+{
+       struct intelgpio_softc *sc;
+
+       sc = device_get_softc(dev);
+       return (sc->sc_busdev);
+}
+
+static int
+intelgpio_pin_max(device_t dev, int *maxpin)
+{
+       struct intelgpio_softc *sc;
+       const struct intelgpio_platform *plat;
+       const struct intelgpio_community *com;
+       const struct intelgpio_padgroup *pg;
+       int i, j;
+
+       if (maxpin == NULL)
+               return (EINVAL);
+
+       sc = device_get_softc(dev);
+       plat = sc->sc_plat;
+
+       /* Find the last group with a valid gpio_base, skipping NOMAP */
+       *maxpin = 0;
+       for (i = 0; i < plat->ncommunities; i++) {
+               com = &plat->communities[i];
+               for (j = 0; j < com->ngroups; j++) {
+                       pg = &com->groups[j];
+                       if (pg->gpio_base == INTELGPIO_GPIO_NOMAP)
+                               continue;
+                       *maxpin = pg->gpio_base + (pg->npads - 1);
+               }
+       }
+       return (0);
+}
+
+static int
+intelgpio_pin_getname(device_t dev, uint32_t pin, char *name)
+{
+       struct intelgpio_softc *sc;
+       const struct intelgpio_padgroup *pg;
+       int offset;
+
+       sc = device_get_softc(dev);
+       if (intelgpio_gpio_to_pad(sc, pin, NULL, NULL, &pg) != 0)
+               return (EINVAL);
+
+       offset = pin - pg->gpio_base;
+       snprintf(name, GPIOMAXNAME, "%s%d", pg->name, offset);
+       return (0);
+}
+
+static int
+intelgpio_pin_getcaps(device_t dev, uint32_t pin, uint32_t *caps)
+{
+       struct intelgpio_softc *sc;
+
+       sc = device_get_softc(dev);
+
+       /*
+        * The getcaps function is expected to return the capabilities
+        * of the pin. Validate that the pin number maps to an existing
+        * pad first.
+        */
+       if (intelgpio_gpio_to_pad(sc, pin, NULL, NULL, NULL) != 0)
+               return (EINVAL);
+
+       *caps = GPIO_PIN_INPUT | GPIO_PIN_OUTPUT;
+       return (0);
+}
+
+static int
+intelgpio_pin_getflags(device_t dev, uint32_t pin, uint32_t *flags)
+{
+       struct intelgpio_softc *sc;
+       struct resource *res;
+       bus_size_t pad_off;
+       uint32_t val;
+
+       sc = device_get_softc(dev);
+       if (intelgpio_gpio_to_pad(sc, pin, &res, &pad_off, NULL) != 0)
+               return (EINVAL);
+       if (res == NULL)
+               return (EINVAL);
+
+       *flags = 0;
+
+       INTELGPIO_LOCK(sc);
+       val = bus_read_4(res, pad_off);
+       INTELGPIO_UNLOCK(sc);
+
+       /*
+        * If the pad is not in GPIO mode (i.e. configured for a native
+        * function like PCIe, UART, I2C, etc.), report no flags. The pin
+        * cannot be used as GPIO without first switching it via setflags.
+        */
+       if ((val & INTELGPIO_PADCFG0_PMODE_MASK) !=
+           INTELGPIO_PADCFG0_PMODE_GPIO)
+               return (0);
+
+       /*
+        * Report OUTPUT only if TX is explicitly enabled. Default to INPUT
+        * otherwise: a non-driving pin is considered an input from the user's
+        * perspective.
+        */
+       if (!(val & INTELGPIO_PADCFG0_GPIOTXDIS))
+               *flags = GPIO_PIN_OUTPUT;
+       else
+               *flags = GPIO_PIN_INPUT;
+
+       return (0);
+}
+
+static int
+intelgpio_pin_setflags(device_t dev, uint32_t pin, uint32_t flags)
+{
+       struct intelgpio_softc *sc;
+       struct resource *res;
+       bus_size_t pad_off;
+       uint32_t val;
+
+       sc = device_get_softc(dev);
+       if (intelgpio_gpio_to_pad(sc, pin, &res, &pad_off, NULL) != 0)
+               return (EINVAL);
+       if (res == NULL)
+               return (EINVAL);
+
+       /*
+        * We currently only support input and output, not both simultaneously
+        */
+       if (flags & ~(GPIO_PIN_INPUT | GPIO_PIN_OUTPUT))
+               return (EINVAL);
+       if ((flags & GPIO_PIN_INPUT) && (flags & GPIO_PIN_OUTPUT))
+               return (EINVAL);
+
+       INTELGPIO_LOCK(sc);
+       val = bus_read_4(res, pad_off);
+
+       /*
+        * Switch the pad to GPIO mode. Intel pads can operate in different
+        * modes (native function 1-15 or GPIO). Since setflags is only called
+        * when userspace or a consumer explicitly wants to use the pin as a
+        * GPIO, we force the pad into GPIO mode here so the direction bits
+        * (TXDIS/RXDIS) below take effect.
+        */
+       val &= ~INTELGPIO_PADCFG0_PMODE_MASK;
+       val |= INTELGPIO_PADCFG0_PMODE_GPIO;
+
+       if (flags & GPIO_PIN_INPUT)
+               val &= ~INTELGPIO_PADCFG0_GPIORXDIS;
+       else
+               val |= INTELGPIO_PADCFG0_GPIORXDIS;
+
+       if (flags & GPIO_PIN_OUTPUT)
+               val &= ~INTELGPIO_PADCFG0_GPIOTXDIS;
+       else
+               val |= INTELGPIO_PADCFG0_GPIOTXDIS;
+
+       bus_write_4(res, pad_off, val);
+       INTELGPIO_UNLOCK(sc);
+
+       return (0);
+}
+
+static int
+intelgpio_pin_get(device_t dev, uint32_t pin, unsigned int *value)
+{
+       struct intelgpio_softc *sc;
+       struct resource *res;
+       bus_size_t pad_off;
+       uint32_t val;
+
+       sc = device_get_softc(dev);
+       if (intelgpio_gpio_to_pad(sc, pin, &res, &pad_off, NULL) != 0)
+               return (EINVAL);
+       if (res == NULL)
+               return (EINVAL);
+
+       INTELGPIO_LOCK(sc);
+       val = bus_read_4(res, pad_off);
+       INTELGPIO_UNLOCK(sc);
+
+       *value = (val & INTELGPIO_PADCFG0_GPIORXSTATE) ?
+           GPIO_PIN_HIGH : GPIO_PIN_LOW;
+
+       return (0);
+}
+
+static int
+intelgpio_pin_set(device_t dev, uint32_t pin, unsigned int value)
+{
+       struct intelgpio_softc *sc;
+       struct resource *res;
+       bus_size_t pad_off;
+       uint32_t val;
+
+       sc = device_get_softc(dev);
+       if (intelgpio_gpio_to_pad(sc, pin, &res, &pad_off, NULL) != 0)
+               return (EINVAL);
+       if (res == NULL)
+               return (EINVAL);
+
+       INTELGPIO_LOCK(sc);
+       val = bus_read_4(res, pad_off);
+       if (value == GPIO_PIN_LOW)
+               val &= ~INTELGPIO_PADCFG0_GPIOTXSTATE;
+       else
+               val |= INTELGPIO_PADCFG0_GPIOTXSTATE;
+       bus_write_4(res, pad_off, val);
+       INTELGPIO_UNLOCK(sc);
+
+       return (0);
+}
+
+static int
+intelgpio_pin_toggle(device_t dev, uint32_t pin)
+{
+       struct intelgpio_softc *sc;
+       struct resource *res;
+       bus_size_t pad_off;
+       uint32_t val;
+
+       sc = device_get_softc(dev);
+       if (intelgpio_gpio_to_pad(sc, pin, &res, &pad_off, NULL) != 0)
+               return (EINVAL);
+       if (res == NULL)
+               return (EINVAL);
+
+       INTELGPIO_LOCK(sc);
+       val = bus_read_4(res, pad_off);
+       val ^= INTELGPIO_PADCFG0_GPIOTXSTATE;
+       bus_write_4(res, pad_off, val);
+       INTELGPIO_UNLOCK(sc);
+
+       return (0);
+}
+
+int
+intelgpio_probe(device_t dev, const struct intelgpio_platform *plat)
+{
+       int rv;
+
+       if (acpi_disabled("intelgpio"))
+               return (ENXIO);
+
+       rv = ACPI_ID_PROBE(device_get_parent(dev), dev, plat->hids, NULL);
+       if (rv <= 0)
+               device_set_desc(dev, plat->desc);
+       return (rv);
+}
+
+static int
+intelgpio_detach(device_t dev)
+{
+       struct intelgpio_softc *sc;
+       int i;
+
+       sc = device_get_softc(dev);
+
+       if (sc->sc_busdev != NULL)
+               gpiobus_detach_bus(dev);
+
+       for (i = 0; i < sc->sc_plat->ncommunities; i++) {
+               if (sc->sc_mem_res[i] != NULL)
+                       bus_release_resource(dev, SYS_RES_MEMORY, i,
+                           sc->sc_mem_res[i]);
+       }
+
+       INTELGPIO_LOCK_DESTROY(sc);
+       return (0);
+}
+
+int
+intelgpio_attach(device_t dev, const struct intelgpio_platform *plat)
+{
+       struct intelgpio_softc *sc;
+       bool have_res;
+       int i;
+
+       sc = device_get_softc(dev);
+       sc->sc_dev = dev;
+       sc->sc_plat = plat;
+
+       KASSERT(plat->ncommunities <= INTELGPIO_MAX_COMMUNITIES,
+           ("too many communities: %d > %d", plat->ncommunities,
+               INTELGPIO_MAX_COMMUNITIES));
+
+       INTELGPIO_LOCK_INIT(sc);
+
+       have_res = false;
+       for (i = 0; i < plat->ncommunities; i++) {
+               sc->sc_mem_res[i] = bus_alloc_resource_any(dev, SYS_RES_MEMORY,
+                   &i, RF_ACTIVE);
+               if (sc->sc_mem_res[i] == NULL) {
+                       device_printf(dev,
+                           "failed to allocate memory resource "
+                           "for community %d\n",
+                           i);
+                       continue;
+               }
+               sc->sc_padbar[i] = bus_read_4(sc->sc_mem_res[i],
+                   INTELGPIO_PADBAR_REG);
+               have_res = true;
+       }
+
+       if (!have_res) {
+               device_printf(dev, "no memory resources found\n");
+               INTELGPIO_LOCK_DESTROY(sc);
+               return (ENXIO);
+       }
+
+       sc->sc_busdev = gpiobus_add_bus(dev);
+       if (sc->sc_busdev == NULL) {
+               device_printf(dev, "failed to add gpiobus\n");
+               intelgpio_detach(dev);
+               return (ENXIO);
+       }
+
+       bus_attach_children(dev);
+
+       return (0);
+}
+
+
+static device_method_t intelgpio_methods[] = {
+       DEVMETHOD(device_detach, intelgpio_detach),
+
+       DEVMETHOD(gpio_get_bus, intelgpio_get_bus),
+       DEVMETHOD(gpio_pin_max, intelgpio_pin_max),
+       DEVMETHOD(gpio_pin_getname, intelgpio_pin_getname),
+       DEVMETHOD(gpio_pin_getcaps, intelgpio_pin_getcaps),
+       DEVMETHOD(gpio_pin_getflags, intelgpio_pin_getflags),
+       DEVMETHOD(gpio_pin_setflags, intelgpio_pin_setflags),
+       DEVMETHOD(gpio_pin_get, intelgpio_pin_get),
+       DEVMETHOD(gpio_pin_set, intelgpio_pin_set),
+       DEVMETHOD(gpio_pin_toggle, intelgpio_pin_toggle),
+
+       DEVMETHOD_END
+};
+
+DEFINE_CLASS_0(intelgpio, intelgpio_driver, intelgpio_methods,
+    sizeof(struct intelgpio_softc));
diff --git a/sys/dev/gpio/intel/intelgpio.h b/sys/dev/gpio/intel/intelgpio.h
new file mode 100644
index 000000000000..54aecc9b76d8
--- /dev/null
+++ b/sys/dev/gpio/intel/intelgpio.h
@@ -0,0 +1,64 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2026 Beckhoff Automation GmbH & Co. KG
+ */
+
+#ifndef _DEV_GPIO_INTEL_INTELGPIO_H_
+#define _DEV_GPIO_INTEL_INTELGPIO_H_
+
+#include <sys/param.h>
+#include <sys/bus.h>
+#include <sys/gpio.h>
+#include <sys/kernel.h>
+
+#include <contrib/dev/acpica/include/acpi.h>
+
+#define INTELGPIO_PADBAR_REG         0x00c
+#define INTELGPIO_PAD_SIZE           16
+
+#define INTELGPIO_PADCFG0_GPIOTXSTATE (1 << 0)
+#define INTELGPIO_PADCFG0_GPIORXSTATE (1 << 1)
+#define INTELGPIO_PADCFG0_GPIOTXDIS   (1 << 8)
+#define INTELGPIO_PADCFG0_GPIORXDIS   (1 << 9)
+#define INTELGPIO_PADCFG0_PMODE_MASK  (0x7 << 10)
+#define INTELGPIO_PADCFG0_PMODE_GPIO  0
+
+#define INTELGPIO_GPIO_NOMAP         (-1)
+#define INTELGPIO_MAX_COMMUNITIES     8
+
+struct intelgpio_padgroup {
+       int first_pad;
+       int npads;
+       int gpio_base;
+       const char *name;
+};
+
+struct intelgpio_community {
+       int ngroups;
+       const struct intelgpio_padgroup *groups;
+};
+
+struct intelgpio_platform {
+       const struct intelgpio_community *communities;
+       int ncommunities;
+       char **hids;
+       const char *desc;
+};
+
+struct intelgpio_softc {
+       device_t sc_dev;
+       device_t sc_busdev;
+       struct mtx sc_mtx;
+       const struct intelgpio_platform *sc_plat;
+
+       struct resource *sc_mem_res[INTELGPIO_MAX_COMMUNITIES];
+       uint32_t sc_padbar[INTELGPIO_MAX_COMMUNITIES];
+};
+
+DECLARE_CLASS(intelgpio_driver);
+
+int intelgpio_probe(device_t dev, const struct intelgpio_platform *plat);
+int intelgpio_attach(device_t dev, const struct intelgpio_platform *plat);
+
+#endif /* _DEV_GPIO_INTEL_INTELGPIO_H_ */

Reply via email to