commit 526ed7b13acbbb9288353c9afc8baa6f24d9e655 Author: Kevin Bowling Date: Mon Jul 6 23:21:45 2026 -0700 octgpio: OCTEON GPIO controller driver Port of OpenBSD octgpio(4) (Visa Hankala) onto the NetBSD fdtbus gpio controller API. Deviations from the OpenBSD original, per the SDK CSR definitions: CN63XX/CN68XX and CN38/52/56/58XX have 16 pins and no XBIT_CFG registers. The OUTPUT_SEL output mux is cleared only on CN70XX and newer, where it exists. Attach at pass 3 so consumers (octmmc cd-gpios) find the controller registered. Primary motivation is SD card-detect on boards with a real MMC slot but could drive LEDs and other features later. diff --git a/sys/arch/evbmips/conf/OCTEON b/sys/arch/evbmips/conf/OCTEON index bff05aaa17f7..d719981c04ce 100644 --- a/sys/arch/evbmips/conf/OCTEON +++ b/sys/arch/evbmips/conf/OCTEON @@ -142,6 +142,7 @@ simplebus* at fdt? pass 0 octintc* at fdt? pass 1 octcib* at fdt? pass 2 +octgpio* at fdt? pass 3 # GPIO controller (must attach before octmmc) com* at iobus? com* at fdt? diff --git a/sys/arch/mips/cavium/dev/octeon_gpio.c b/sys/arch/mips/cavium/dev/octeon_gpio.c new file mode 100644 index 000000000000..f776446ca955 --- /dev/null +++ b/sys/arch/mips/cavium/dev/octeon_gpio.c @@ -0,0 +1,288 @@ +/* $NetBSD: octeon_gpio.c,v $ */ +/* $OpenBSD: octgpio.c,v 1.2 2019/09/29 04:28:52 visa Exp $ */ + +/* + * Copyright (c) 2019 Visa Hankala + * Copyright (c) 2026 Kevin Bowling + * + * Permission to use, copy, modify, and/or 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. + */ + +/* Driver for OCTEON GPIO controller. */ + +#include +__KERNEL_RCSID(0, "$NetBSD: octeon_gpio.c,v $"); + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include + +#include + +struct octgpio_softc { + device_t sc_dev; + bus_space_tag_t sc_iot; + bus_space_handle_t sc_ioh; + kmutex_t sc_lock; + uint32_t sc_npins; + uint32_t sc_xbit; + bool sc_out_sel; /* BIT_CFG has OUTPUT_SEL */ +}; + +struct octgpio_pin { + struct octgpio_softc *pin_sc; + uint32_t pin_no; + bool pin_actlo; +}; + +#define GPIO_RD_8(sc, reg) \ + bus_space_read_8((sc)->sc_iot, (sc)->sc_ioh, (reg)) +#define GPIO_WR_8(sc, reg, val) \ + bus_space_write_8((sc)->sc_iot, (sc)->sc_ioh, (reg), (val)) + +static int octgpio_match(device_t, cfdata_t, void *); +static void octgpio_attach(device_t, device_t, void *); + +static void *octgpio_acquire(device_t, const void *, size_t, int); +static void octgpio_release(device_t, void *); +static int octgpio_read(device_t, void *, bool); +static void octgpio_write(device_t, void *, int, bool); + +static void octgpio_pin_ctl(struct octgpio_softc *, uint32_t, int); + +CFATTACH_DECL_NEW(octgpio, sizeof(struct octgpio_softc), + octgpio_match, octgpio_attach, NULL, NULL); + +static const struct device_compatible_entry compat_data[] = { + { .compat = "cavium,octeon-3860-gpio" }, + { .compat = "cavium,octeon-7890-gpio" }, + DEVICE_COMPAT_EOL +}; + +static const struct fdtbus_gpio_controller_func octgpio_funcs = { + .acquire = octgpio_acquire, + .release = octgpio_release, + .read = octgpio_read, + .write = octgpio_write, +}; + +static int +octgpio_match(device_t parent, cfdata_t cf, void *aux) +{ + struct fdt_attach_args * const faa = aux; + + return of_compatible_match(faa->faa_phandle, compat_data); +} + +static void +octgpio_attach(device_t parent, device_t self, void *aux) +{ + struct fdt_attach_args * const faa = aux; + struct octgpio_softc *sc = device_private(self); + const int phandle = faa->faa_phandle; + bus_addr_t addr; + bus_size_t size; + + sc->sc_dev = self; + sc->sc_iot = faa->faa_bst; + + if (fdtbus_get_reg(phandle, 0, &addr, &size) != 0) { + aprint_error(": could not get registers\n"); + return; + } + /* + * Device trees give a 0x100 window but the extended pin + * configuration registers (GPIO_XBIT_CFG) live at +0x100. + */ + if (size < GPIO_SIZE) + size = GPIO_SIZE; + if (bus_space_map(sc->sc_iot, addr, size, 0, &sc->sc_ioh) != 0) { + aprint_error(": could not map registers\n"); + return; + } + + /* + * Set the pin count and the register layout per the SDK CSR + * definitions (cvmx-gpio-defs.h). Pins below sc_xbit are + * configured through GPIO_BIT_CFG, the rest through + * GPIO_XBIT_CFG. On CN73XX and CN78XX all pins use the + * GPIO_XBIT_CFG range. The BIT_CFG OUTPUT_SEL output mux + * exists on CN70XX and newer only; on CN61XX-class chips the + * same bits belong to SYNCE_SEL and must not be touched. + */ + switch (MIPS_PRID_IMPL(mips_options.mips_cpu_id)) { + case MIPS_CN30XX: + case MIPS_CN31XX: + case MIPS_CN50XX: + sc->sc_npins = 24; + sc->sc_xbit = 16; + break; + case MIPS_CN38XX: + case MIPS_CN52XX: + case MIPS_CN56XX: + case MIPS_CN58XX: + case MIPS_CN63XX: + case MIPS_CN68XX: + sc->sc_npins = 16; + sc->sc_xbit = 16; + break; + case MIPS_CN61XX: + case MIPS_CN66XX: + case MIPS_CNF71XX: + sc->sc_npins = 20; + sc->sc_xbit = 16; + break; + case MIPS_CN70XX: + sc->sc_npins = 20; + sc->sc_xbit = 16; + sc->sc_out_sel = true; + break; + case MIPS_CN73XX: + case MIPS_CNF75XX: + sc->sc_npins = 32; + sc->sc_xbit = 0; + sc->sc_out_sel = true; + break; + case MIPS_CN78XX: + sc->sc_npins = 20; + sc->sc_xbit = 0; + sc->sc_out_sel = true; + break; + default: + /* Be conservative about unknown models. */ + sc->sc_npins = 16; + sc->sc_xbit = 16; + break; + } + + mutex_init(&sc->sc_lock, MUTEX_DEFAULT, IPL_NONE); + + aprint_naive("\n"); + aprint_normal(": GPIO controller, %u pins\n", sc->sc_npins); + + fdtbus_register_gpio_controller(self, phandle, &octgpio_funcs); +} + +static bus_size_t +octgpio_cfg_reg(struct octgpio_softc *sc, uint32_t pin) +{ + + if (pin >= sc->sc_xbit) + return GPIO_XBIT_CFG16_OFFSET + (pin - sc->sc_xbit) * 8; + return GPIO_BIT_CFG0_OFFSET + pin * 8; +} + +static void +octgpio_pin_ctl(struct octgpio_softc *sc, uint32_t pin, int flags) +{ + uint64_t value; + bus_size_t reg; + + KASSERT(pin < sc->sc_npins); + reg = octgpio_cfg_reg(sc, pin); + + mutex_enter(&sc->sc_lock); + value = GPIO_RD_8(sc, reg); + if (ISSET(flags, GPIO_PIN_OUTPUT)) { + value |= GPIO_BIT_CFG_TX_OE; + /* Select normal GPIO output where the mux exists. */ + if (sc->sc_out_sel) + value &= ~GPIO_BIT_CFG_OUT_SEL; + } else { + value &= ~(GPIO_BIT_CFG_TX_OE | GPIO_BIT_CFG_RX_XOR); + } + /* + * Interrupts are not supported; keep INT_EN clear (on models + * whose XBIT_CFG lacks INT_EN the bit reads as zero anyway). + */ + value &= ~GPIO_BIT_CFG_INT_EN; + GPIO_WR_8(sc, reg, value); + mutex_exit(&sc->sc_lock); +} + +static void * +octgpio_acquire(device_t dev, const void *data, size_t len, int flags) +{ + struct octgpio_softc *sc = device_private(dev); + const u_int *gpio = data; + struct octgpio_pin *pin; + u_int pinno; + bool actlo; + + /* #gpio-cells = <2>: controller phandle, pin, flags. */ + if (len != 12) + return NULL; + + pinno = be32toh(gpio[1]); + actlo = (be32toh(gpio[2]) & 1) != 0; + if (pinno >= sc->sc_npins) + return NULL; + + octgpio_pin_ctl(sc, pinno, flags); + + pin = kmem_zalloc(sizeof(*pin), KM_SLEEP); + pin->pin_sc = sc; + pin->pin_no = pinno; + pin->pin_actlo = actlo; + + return pin; +} + +static void +octgpio_release(device_t dev, void *priv) +{ + struct octgpio_softc *sc = device_private(dev); + struct octgpio_pin *pin = priv; + + octgpio_pin_ctl(sc, pin->pin_no, GPIO_PIN_INPUT); + kmem_free(pin, sizeof(*pin)); +} + +static int +octgpio_read(device_t dev, void *priv, bool raw) +{ + struct octgpio_pin *pin = priv; + struct octgpio_softc *sc = pin->pin_sc; + int val; + + val = (GPIO_RD_8(sc, GPIO_RX_DAT_OFFSET) >> pin->pin_no) & 1; + if (!raw && pin->pin_actlo) + val = !val; + + return val; +} + +static void +octgpio_write(device_t dev, void *priv, int val, bool raw) +{ + struct octgpio_pin *pin = priv; + struct octgpio_softc *sc = pin->pin_sc; + + if (!raw && pin->pin_actlo) + val = !val; + + if (val) + GPIO_WR_8(sc, GPIO_TX_SET_OFFSET, 1ull << pin->pin_no); + else + GPIO_WR_8(sc, GPIO_TX_CLR_OFFSET, 1ull << pin->pin_no); +} diff --git a/sys/arch/mips/cavium/dev/octeon_gpioreg.h b/sys/arch/mips/cavium/dev/octeon_gpioreg.h index e370aa00eebe..26860501e6cb 100644 --- a/sys/arch/mips/cavium/dev/octeon_gpioreg.h +++ b/sys/arch/mips/cavium/dev/octeon_gpioreg.h @@ -64,6 +64,8 @@ #define GPIO_XBIT_CFG22 0x0001070000000930ULL #define GPIO_XBIT_CFG23 0x0001070000000938ULL +/* OUTPUT_SEL (CN70XX and newer): output mux, 0 selects normal GPIO TX. */ +#define GPIO_BIT_CFG_OUT_SEL UINT64_C(0x00000000001f0000) #define GPIO_BIT_CFG_XXX_63_12 UINT64_C(0xfffffffffffff000) #define GPIO_BIT_CFG_FIL_SEL UINT64_C(0x0000000000000f00) #define GPIO_BIT_CFG_FIL_CNT UINT64_C(0x00000000000000f0) diff --git a/sys/arch/mips/conf/files.octeon b/sys/arch/mips/conf/files.octeon index 20a407f2cf14..676e5e9687aa 100644 --- a/sys/arch/mips/conf/files.octeon +++ b/sys/arch/mips/conf/files.octeon @@ -36,6 +36,10 @@ device octcib attach octcib at fdt file arch/mips/cavium/dev/octeon_cib.c octcib +device octgpio: fdt_gpio +attach octgpio at fdt +file arch/mips/cavium/dev/octeon_gpio.c octgpio + attach xhci at fdt with octxhci: fdt_gpio file arch/mips/cavium/dev/octeon_xhci.c octxhci