git: 0a91888870e2 - main - arm64: add a GPIO driver for Apple Silicon
- Go to: [ bottom of page ] [ top of archives ] [ this month ]
Date: Thu, 03 Apr 2025 01:11:40 UTC
The branch main has been updated by kevans: URL: https://cgit.FreeBSD.org/src/commit/?id=0a91888870e2227d3b3576aaa949764929550b94 commit 0a91888870e2227d3b3576aaa949764929550b94 Author: Kyle Evans <kevans@FreeBSD.org> AuthorDate: 2025-04-03 01:11:26 +0000 Commit: Kyle Evans <kevans@FreeBSD.org> CommitDate: 2025-04-03 01:11:33 +0000 arm64: add a GPIO driver for Apple Silicon This is a ported version of OpenBSD's work, modulo interrupt functionality. We won't need GPIO interrupts until we start to get closer to audio support, and the existing version is sufficient for, e.g., pcie. Reviewed by: andrew Differential Revision: https://reviews.freebsd.org/D49630 --- sys/arm64/apple/apple_pinctrl.c | 469 ++++++++++++++++++++++++++++++++++++++++ sys/conf/files.arm64 | 1 + 2 files changed, 470 insertions(+) diff --git a/sys/arm64/apple/apple_pinctrl.c b/sys/arm64/apple/apple_pinctrl.c new file mode 100644 index 000000000000..ec2dd5907024 --- /dev/null +++ b/sys/arm64/apple/apple_pinctrl.c @@ -0,0 +1,469 @@ +/* $OpenBSD: aplpinctrl.c,v 1.4 2022/04/06 18:59:26 naddy Exp $ */ +/* + * Copyright (c) 2021 Mark Kettenis <kettenis@openbsd.org> + * Copyright (c) 2022 Kyle Evans <kevans@FreeBSD.org> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/bus.h> +#include <sys/gpio.h> +#include <sys/kernel.h> +#include <sys/malloc.h> +#include <sys/module.h> +#include <sys/mutex.h> + +#include <machine/bus.h> +#include <machine/intr.h> +#include <machine/resource.h> + +#include <dev/gpio/gpiobusvar.h> +#include <dev/ofw/ofw_bus.h> +#include <dev/ofw/ofw_bus_subr.h> +#include <dev/fdt/fdt_pinctrl.h> + +#include "pic_if.h" +#include "gpio_if.h" + +#define APPLE_PIN(pinmux) ((pinmux) & 0xffff) +#define APPLE_FUNC(pinmux) ((pinmux) >> 16) + +#define GPIO_PIN(pin) ((pin) * 4) +#define GPIO_PIN_GROUP_MASK (7 << 16) +#define GPIO_PIN_INPUT_ENABLE (1 << 9) +#define GPIO_PIN_FUNC_MASK (3 << 5) +#define GPIO_PIN_FUNC_SHIFT 5 +#define GPIO_PIN_MODE_MASK (7 << 1) +#define GPIO_PIN_MODE_INPUT (0 << 1) +#define GPIO_PIN_MODE_OUTPUT (1 << 1) +#define GPIO_PIN_MODE_IRQ_HI (2 << 1) +#define GPIO_PIN_MODE_IRQ_LO (3 << 1) +#define GPIO_PIN_MODE_IRQ_UP (4 << 1) +#define GPIO_PIN_MODE_IRQ_DN (5 << 1) +#define GPIO_PIN_MODE_IRQ_ANY (6 << 1) +#define GPIO_PIN_MODE_IRQ_OFF (7 << 1) +#define GPIO_PIN_DATA (1 << 0) +#define GPIO_IRQ(grp, pin) (0x800 + (grp) * 64 + ((pin) >> 5) * 4) + +#define APPLE_PINCTRL_DEFAULT_CAPS \ + (GPIO_PIN_INPUT | GPIO_PIN_OUTPUT) + +#define HREAD4(sc, reg) \ + bus_read_4((sc)->sc_res[APPLE_PINCTRL_MEMRES], reg) +#define HWRITE4(sc, reg, val) \ + bus_write_4((sc)->sc_res[APPLE_PINCTRL_MEMRES], reg, val) +#define HSET4(sc, reg, bits) \ + HWRITE4((sc), (reg), HREAD4((sc), (reg)) | (bits)) +#define HCLR4(sc, reg, bits) \ + HWRITE4((sc), (reg), HREAD4((sc), (reg)) & ~(bits)) + +struct apple_pinctrl_irqsrc { + struct intr_irqsrc isrc; + int irq; + int type; +}; + +enum { + APPLE_PINCTRL_MEMRES = 0, + APPLE_PINCTRL_IRQRES, + APPLE_PINCTRL_NRES, +}; + +struct apple_pinctrl_softc { + device_t sc_dev; + device_t sc_busdev; + struct mtx sc_mtx; + int sc_ngpios; + + void *sc_intrhand; + struct resource *sc_res[APPLE_PINCTRL_NRES]; + struct apple_pinctrl_irqsrc *sc_irqs; +}; + +#define APPLE_PINCTRL_LOCK(sc) mtx_lock_spin(&(sc)->sc_mtx) +#define APPLE_PINCTRL_UNLOCK(sc) mtx_unlock_spin(&(sc)->sc_mtx) +#define APPLE_PINCTRL_LOCK_ASSERT(sc) mtx_assert(&(sc)->sc_mtx, MA_OWNED) + +static struct ofw_compat_data compat_data[] = { + {"apple,pinctrl", 1}, + {NULL, 0}, +}; + +static struct resource_spec apple_pinctrl_res_spec[] = { + { SYS_RES_MEMORY, 0, RF_ACTIVE }, + { SYS_RES_IRQ, 0, RF_ACTIVE }, + { -1, 0, 0 }, +}; + +static int apple_pinctrl_probe(device_t dev); +static int apple_pinctrl_attach(device_t dev); +static int apple_pinctrl_detach(device_t dev); + +static int apple_pinctrl_configure(device_t, phandle_t); +static phandle_t apple_pinctrl_get_node(device_t, device_t); + +static int +apple_pinctrl_probe(device_t dev) +{ + + if (!ofw_bus_status_okay(dev)) + return (ENXIO); + + if (ofw_bus_search_compatible(dev, compat_data)->ocd_data == 0) + return (ENXIO); + + device_set_desc(dev, "Apple Pinmux Controller"); + return (BUS_PROBE_DEFAULT); +} + +static int +apple_pinctrl_attach(device_t dev) +{ + pcell_t gpio_ranges[4]; + phandle_t node; + struct apple_pinctrl_softc *sc; + int error; + + sc = device_get_softc(dev); + sc->sc_dev = dev; + + node = ofw_bus_get_node(dev); + + if (bus_alloc_resources(dev, apple_pinctrl_res_spec, sc->sc_res) != 0) { + device_printf(dev, "cannot allocate device resources\n"); + return (ENXIO); + } + + mtx_init(&sc->sc_mtx, "aapl gpio", "gpio", MTX_SPIN); + + error = OF_getencprop(node, "gpio-ranges", gpio_ranges, + sizeof(gpio_ranges)); + if (error == -1) { + device_printf(dev, "failed to get gpio-ranges\n"); + goto error; + } + + sc->sc_ngpios = gpio_ranges[3]; + if (sc->sc_ngpios == 0) { + device_printf(dev, "no GPIOs\n"); + goto error; + } + + sc->sc_busdev = gpiobus_attach_bus(dev); + if (sc->sc_busdev == NULL) { + device_printf(dev, "failed to attach gpiobus\n"); + goto error; + } + + fdt_pinctrl_register(dev, "pinmux"); + fdt_pinctrl_configure_tree(dev); + + if (!OF_hasprop(node, "interrupt-controller")) + return (0); + + sc->sc_irqs = mallocarray(sc->sc_ngpios, + sizeof(*sc->sc_irqs), M_DEVBUF, M_ZERO | M_WAITOK); + intr_pic_register(dev, OF_xref_from_node(ofw_bus_get_node(dev))); + + return (0); +error: + mtx_destroy(&sc->sc_mtx); + bus_release_resources(dev, apple_pinctrl_res_spec, sc->sc_res); + return (ENXIO); +} + +static int +apple_pinctrl_detach(device_t dev) +{ + + return (EBUSY); +} + +static void +apple_pinctrl_pin_configure(struct apple_pinctrl_softc *sc, uint32_t pin, + uint32_t flags) +{ + uint32_t reg; + + APPLE_PINCTRL_LOCK_ASSERT(sc); + + MPASS(pin < sc->sc_ngpios); + + reg = HREAD4(sc, GPIO_PIN(pin)); + reg &= ~GPIO_PIN_FUNC_MASK; + reg &= ~GPIO_PIN_MODE_MASK; + + if ((flags & GPIO_PIN_PRESET_LOW) != 0) + reg &= ~GPIO_PIN_DATA; + else if ((flags & GPIO_PIN_PRESET_HIGH) != 0) + reg |= GPIO_PIN_DATA; + + if ((flags & GPIO_PIN_INPUT) != 0) + reg |= GPIO_PIN_MODE_INPUT; + else if ((flags & GPIO_PIN_OUTPUT) != 0) + reg |= GPIO_PIN_MODE_OUTPUT; + + HWRITE4(sc, GPIO_PIN(pin), reg); +} + +static device_t +apple_pinctrl_get_bus(device_t dev) +{ + struct apple_pinctrl_softc *sc; + + sc = device_get_softc(dev); + return (sc->sc_busdev); +} + +static int +apple_pinctrl_pin_max(device_t dev, int *maxpin) +{ + struct apple_pinctrl_softc *sc; + + sc = device_get_softc(dev); + *maxpin = sc->sc_ngpios - 1; + return (0); +} + +static int +apple_pinctrl_pin_getname(device_t dev, uint32_t pin, char *name) +{ + struct apple_pinctrl_softc *sc; + + sc = device_get_softc(dev); + if (pin >= sc->sc_ngpios) + return (EINVAL); + + snprintf(name, GPIOMAXNAME - 1, "gpio%c%d", + device_get_unit(dev) + 'a', pin); + + return (0); +} + +static int +apple_pinctrl_pin_getflags(device_t dev, uint32_t pin, uint32_t *flags) +{ + struct apple_pinctrl_softc *sc; + uint32_t reg; + + sc = device_get_softc(dev); + if (pin >= sc->sc_ngpios) + return (EINVAL); + + *flags = 0; + + APPLE_PINCTRL_LOCK(sc); + + reg = HREAD4(sc, GPIO_PIN(pin)); + if ((reg & GPIO_PIN_MODE_INPUT) != 0) + *flags |= GPIO_PIN_INPUT; + else if ((reg & GPIO_PIN_MODE_OUTPUT) != 0) + *flags |= GPIO_PIN_OUTPUT; + + APPLE_PINCTRL_UNLOCK(sc); + + return (0); +} + +static int +apple_pinctrl_pin_getcaps(device_t dev, uint32_t pin, uint32_t *caps) +{ + + *caps = APPLE_PINCTRL_DEFAULT_CAPS; + return (0); +} + +static int +apple_pinctrl_pin_setflags(device_t dev, uint32_t pin, uint32_t flags) +{ + struct apple_pinctrl_softc *sc; + + sc = device_get_softc(dev); + if (pin >= sc->sc_ngpios) + return (EINVAL); + + APPLE_PINCTRL_LOCK(sc); + apple_pinctrl_pin_configure(sc, pin, flags); + APPLE_PINCTRL_UNLOCK(sc); + + return (0); +} + +static int +apple_pinctrl_pin_get(device_t dev, uint32_t pin, unsigned int *val) +{ + struct apple_pinctrl_softc *sc; + uint32_t reg; + + sc = device_get_softc(dev); + if (pin >= sc->sc_ngpios) + return (EINVAL); + + APPLE_PINCTRL_LOCK(sc); + reg = HREAD4(sc, GPIO_PIN(pin)); + *val = !!(reg & GPIO_PIN_DATA); + APPLE_PINCTRL_UNLOCK(sc); + + return (0); +} + +static int +apple_pinctrl_pin_set(device_t dev, uint32_t pin, unsigned int value) +{ + struct apple_pinctrl_softc *sc; + + sc = device_get_softc(dev); + if (pin >= sc->sc_ngpios) + return (EINVAL); + + APPLE_PINCTRL_LOCK(sc); + if (value) + HSET4(sc, GPIO_PIN(pin), GPIO_PIN_DATA); + else + HCLR4(sc, GPIO_PIN(pin), GPIO_PIN_DATA); + device_printf(sc->sc_dev, "set pin %d to %x\n", + pin, HREAD4(sc, GPIO_PIN(pin))); + APPLE_PINCTRL_UNLOCK(sc); + return (0); +} + + +static int +apple_pinctrl_pin_toggle(device_t dev, uint32_t pin) +{ + struct apple_pinctrl_softc *sc; + uint32_t reg; + + sc = device_get_softc(dev); + if (pin >= sc->sc_ngpios) + return (EINVAL); + + APPLE_PINCTRL_LOCK(sc); + reg = HREAD4(sc, GPIO_PIN(pin)); + if ((reg & GPIO_PIN_DATA) == 0) + reg |= GPIO_PIN_DATA; + else + reg &= ~GPIO_PIN_DATA; + HWRITE4(sc, GPIO_PIN(pin), reg); + APPLE_PINCTRL_UNLOCK(sc); + return (0); +} + + +static int +apple_pinctrl_pin_config_32(device_t dev, uint32_t first_pin, uint32_t num_pins, + uint32_t *pin_flags) +{ + struct apple_pinctrl_softc *sc; + uint32_t pin; + + sc = device_get_softc(dev); + if (first_pin >= sc->sc_ngpios) + return (EINVAL); + + /* + * The configuration for a bank of pins is scattered among several + * registers; we cannot g'tee to simultaneously change the state of all + * the pins in the flags array. So just loop through the array + * configuring each pin for now. If there was a strong need, it might + * be possible to support some limited simultaneous config, such as + * adjacent groups of 8 pins that line up the same as the config regs. + */ + APPLE_PINCTRL_LOCK(sc); + for (pin = first_pin; pin < num_pins; ++pin) { + if (pin_flags[pin] & (GPIO_PIN_INPUT | GPIO_PIN_OUTPUT)) + apple_pinctrl_pin_configure(sc, pin, pin_flags[pin]); + } + APPLE_PINCTRL_UNLOCK(sc); + + return (0); +} + +static phandle_t +apple_pinctrl_get_node(device_t dev, device_t bus) +{ + + /* GPIO bus */ + return (ofw_bus_get_node(dev)); +} + +static int +apple_pinctrl_configure(device_t dev, phandle_t cfgxref) +{ + struct apple_pinctrl_softc *sc; + pcell_t *pinmux; + phandle_t node; + ssize_t len; + uint32_t reg; + uint16_t pin, func; + int i; + + sc = device_get_softc(dev); + node = OF_node_from_xref(cfgxref); + + len = OF_getencprop_alloc(node, "pinmux", (void **)&pinmux); + if (len <= 0) + return (-1); + + APPLE_PINCTRL_LOCK(sc); + for (i = 0; i < len / sizeof(pcell_t); i++) { + pin = APPLE_PIN(pinmux[i]); + func = APPLE_FUNC(pinmux[i]); + reg = HREAD4(sc, GPIO_PIN(pin)); + reg &= ~GPIO_PIN_FUNC_MASK; + reg |= (func << GPIO_PIN_FUNC_SHIFT) & GPIO_PIN_FUNC_MASK; + HWRITE4(sc, GPIO_PIN(pin), reg); + } + APPLE_PINCTRL_UNLOCK(sc); + + OF_prop_free(pinmux); + return 0; +} + +static device_method_t apple_pinctrl_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, apple_pinctrl_probe), + DEVMETHOD(device_attach, apple_pinctrl_attach), + DEVMETHOD(device_detach, apple_pinctrl_detach), + + /* GPIO protocol */ + DEVMETHOD(gpio_get_bus, apple_pinctrl_get_bus), + DEVMETHOD(gpio_pin_max, apple_pinctrl_pin_max), + DEVMETHOD(gpio_pin_getname, apple_pinctrl_pin_getname), + DEVMETHOD(gpio_pin_getflags, apple_pinctrl_pin_getflags), + DEVMETHOD(gpio_pin_getcaps, apple_pinctrl_pin_getcaps), + DEVMETHOD(gpio_pin_setflags, apple_pinctrl_pin_setflags), + DEVMETHOD(gpio_pin_get, apple_pinctrl_pin_get), + DEVMETHOD(gpio_pin_set, apple_pinctrl_pin_set), + DEVMETHOD(gpio_pin_toggle, apple_pinctrl_pin_toggle), + DEVMETHOD(gpio_pin_config_32, apple_pinctrl_pin_config_32), + + /* ofw_bus interface */ + DEVMETHOD(ofw_bus_get_node, apple_pinctrl_get_node), + + /* fdt_pinctrl interface */ + DEVMETHOD(fdt_pinctrl_configure, apple_pinctrl_configure), + + DEVMETHOD_END +}; + +static driver_t apple_pinctrl_driver = { + "gpio", + apple_pinctrl_methods, + sizeof(struct apple_pinctrl_softc), +}; + +EARLY_DRIVER_MODULE(apple_pinctrl, simplebus, apple_pinctrl_driver, + 0, 0, BUS_PASS_INTERRUPT + BUS_PASS_ORDER_LATE); diff --git a/sys/conf/files.arm64 b/sys/conf/files.arm64 index 74387914043e..901da27e63f2 100644 --- a/sys/conf/files.arm64 +++ b/sys/conf/files.arm64 @@ -547,6 +547,7 @@ arm/annapurna/alpine/alpine_serdes.c optional al_serdes fdt \ # Apple arm64/apple/apple_aic.c optional soc_apple_t8103 fdt +arm64/apple/apple_pinctrl.c optional soc_apple_t8103 fdt arm64/apple/apple_wdog.c optional soc_apple_t8103 fdt arm64/apple/exynos_uart.c optional soc_apple_t8103 fdt