Skip site navigation (1)Skip section navigation (2)
Date:      Thu, 06 Aug 2026 06:42:45 +0000
From:      Corvi=?utf-8?Q?n K=C3=B6h?=ne <corvink@FreeBSD.org>
To:        src-committers@FreeBSD.org, dev-commits-src-all@FreeBSD.org, dev-commits-src-main@FreeBSD.org
Cc:        YannickV <Y.Vossen@beckhoff.com>
Subject:   git: 5349a46babc0 - main - gpio: add generic Intel GPIO pin controller framework
Message-ID:  <6a742ce5.2777c.172065cd@gitrepo.freebsd.org>

index | next in thread | raw e-mail

The branch main has been updated by corvink:

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

commit 5349a46babc03453b0e6679bf867272e7ef7ea22
Author:     YannickV <Y.Vossen@beckhoff.com>
AuthorDate: 2026-04-29 12:56:14 +0000
Commit:     Corvin Köhne <corvink@FreeBSD.org>
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_ */


home | help

Want to link to this message? Use this
URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?6a742ce5.2777c.172065cd>