From nobody Thu Aug 6 06:42:45 2026 X-Original-To: dev-commits-src-all@mlmmj.nyi.freebsd.org Received: from mx1.freebsd.org (mx1.freebsd.org [IPv6:2610:1c1:1:606c::19:1]) by mlmmj.nyi.freebsd.org (Postfix) with ESMTP id 4hFyQF4FP1z6nxx5 for ; Thu, 06 Aug 2026 06:42:45 +0000 (UTC) (envelope-from git@FreeBSD.org) Received: from mxrelay.nyi.freebsd.org (mxrelay.nyi.freebsd.org [IPv6:2610:1c1:1:606c::19:3]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits) server-digest SHA256 client-signature RSA-PSS (4096 bits) client-digest SHA256) (Client CN "mxrelay.nyi.freebsd.org", Issuer "YR1" (not verified)) by mx1.freebsd.org (Postfix) with ESMTPS id 4hFyQF1qXZz45xP for ; Thu, 06 Aug 2026 06:42:45 +0000 (UTC) (envelope-from git@FreeBSD.org) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=freebsd.org; s=dkim; t=1785998565; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding; bh=a1h6E/gxIlBrINqJY2WK+tCStpqHcDmVpJJ/eKbMUIQ=; b=qF4+mhtrzsqsr3OqBLxEFPxayYILWHrWkmBiQgY0rCVepe9JOXWe3ElAJk2rskUGdwiKXH zQKE57OTld5bMqPZXhutKCfvS6GlLuRv4Aujbe38KQ58LTHsePz99/lL1LfD7tv1W9Z/G8 rLGONCBSLWN0vX43iF0SoYlKKxmEmCuiheAA/TdyPvosRZIweQWQY6tlZbOk4i/mQY2PMH cKlso+x6t47xl45Q0pjefPyAZasHXO/FaQUHixBLOhSDNRSJaTFLJomm0j8yxw9yN95GQj HH0zt5GTcLjeO0mUgqy9OGg0VAnfmi2ZD3BeG/7AmokXvdlIvh0MQf1Joftc1g== ARC-Seal: i=1; s=dkim; d=freebsd.org; t=1785998565; a=rsa-sha256; cv=none; b=kPQAO6j3N0k+wKJKF2mwO6uKMYjN1tb4lEKQ2K7vS4nla/up4WZivWU3tuL5rmx6L+AUpJ /jUA05F7+HLCoYu8qe1aecl4nAeNNcs0QdiZ3HwEnlWkkmYSYT1BnSUNctA3vTNoWXXgAm vuxVbEM/67KCqkRO+w36VH0XLxM5ECZSdphODNC+Y44uhk2JvQYjCnmq3AW6s20wHySL1/ 72HwzHSBsZMTTufp1MSl4gGnhBn2DaTjSyaQdmZCdEeeUOuEHTEtQ6cGrXdLJ/qGUc3ltP 142qHoa+WeF3XT7/dA80BknJ3wK9NR2t6e49jsbAYJOrqahSjaICf2zSUFmdrw== ARC-Authentication-Results: i=1; mx1.freebsd.org; none ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=freebsd.org; s=dkim; t=1785998565; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding; bh=a1h6E/gxIlBrINqJY2WK+tCStpqHcDmVpJJ/eKbMUIQ=; b=BTolM0znOx4xU2Cqym4C/7N/zvVc0job4b7z46R/wtXLDgIGefrRHeT3Sk8DzbXoYB2+uR mBu8CePn/FjDEbcnxqEX3mNvyUIijKb1AkWQqhK1BCJMqkEQidrmDNVdHJGmszQGsRHs4B TQZ/DBsvf4TgoGuKwsNmq23KRfd2WVmmneAj1rQXepHy59/k6WqPs4RLLdiniZ6+py3XMm ntwgqM5v8GAdt5BMVlUVbTsKR79ZbHGy5FjAOJMbNDMQyPGLVDJVCIIWoVyqRg4w15pupL wWSY5mKZTe7aD/uiThtyWAOzf6qlFUwzx+oQSDDVprYaGMHYLZAX/HUgpYpLoQ== Received: from gitrepo.freebsd.org (gitrepo.freebsd.org [IPv6:2610:1c1:1:6068::e6a:5]) by mxrelay.nyi.freebsd.org (Postfix) with ESMTP id 4hFyQF0pNsz18Hv for ; Thu, 06 Aug 2026 06:42:45 +0000 (UTC) (envelope-from git@FreeBSD.org) Received: from git (uid 1279) (envelope-from git@FreeBSD.org) id 2777c by gitrepo.freebsd.org (DragonFly Mail Agent v0.13+ on gitrepo.freebsd.org); Thu, 06 Aug 2026 06:42:45 +0000 To: src-committers@FreeBSD.org, dev-commits-src-all@FreeBSD.org, dev-commits-src-main@FreeBSD.org Cc: YannickV From: Corvi=?utf-8?Q?n K=C3=B6h?=ne Subject: git: 5349a46babc0 - main - gpio: add generic Intel GPIO pin controller framework List-Id: Commit messages for all branches of the src repository List-Archive: https://lists.freebsd.org/archives/dev-commits-src-all List-Help: List-Post: List-Subscribe: List-Unsubscribe: X-BeenThere: dev-commits-src-all@freebsd.org Sender: owner-dev-commits-src-all@FreeBSD.org List-Id: List-Post: List-Help: List-Subscribe: List-Unsubscribe: List-Owner: Precedence: list MIME-Version: 1.0 Content-Type: text/plain; charset=utf-8 Content-Transfer-Encoding: 8bit X-Git-Committer: corvink X-Git-Repository: src X-Git-Refname: refs/heads/main X-Git-Reftype: branch X-Git-Commit: 5349a46babc03453b0e6679bf867272e7ef7ea22 Auto-Submitted: auto-generated Date: Thu, 06 Aug 2026 06:42:45 +0000 Message-Id: <6a742ce5.2777c.172065cd@gitrepo.freebsd.org> The branch main has been updated by corvink: URL: https://cgit.FreeBSD.org/src/commit/?id=5349a46babc03453b0e6679bf867272e7ef7ea22 commit 5349a46babc03453b0e6679bf867272e7ef7ea22 Author: YannickV AuthorDate: 2026-04-29 12:56:14 +0000 Commit: Corvin Köhne 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 +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include + +#include +#include + +#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 +#include +#include +#include + +#include + +#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_ */