git: 3d20f8a02243 - main - jh7110_gpio: driver enhancements

From: Mitchell Horne <mhorne_at_FreeBSD.org>
Date: Fri, 21 Aug 2026 16:55:35 UTC
The branch main has been updated by mhorne:

URL: https://cgit.FreeBSD.org/src/commit/?id=3d20f8a022435e8ed37e2c0ce8f66d4c1203ec1c

commit 3d20f8a022435e8ed37e2c0ce8f66d4c1203ec1c
Author:     Brian Scott <bscott@bunyatech.com.au>
AuthorDate: 2026-08-21 16:27:00 +0000
Commit:     Mitchell Horne <mhorne@FreeBSD.org>
CommitDate: 2026-08-21 16:55:24 +0000

    jh7110_gpio: driver enhancements
    
    Fix reporting of state and capabilities by the gpioctl command.
    
    Support selection of pull-up and pull-down resistors.
    
    Support second gpio device (AON - always on power domain) to allow
    attaching gpioled device to visionfive2 status LED or querying boot
    selection switches.
    
    Reviewed by:    mhorne
    MFC after:      1 week
    Differential Revision:  https://reviews.freebsd.org/D58693
---
 sys/riscv/starfive/jh7110_gpio.c | 149 +++++++++++++++++++++++++++++----------
 1 file changed, 112 insertions(+), 37 deletions(-)

diff --git a/sys/riscv/starfive/jh7110_gpio.c b/sys/riscv/starfive/jh7110_gpio.c
index 93de49e79ace..fdf40e5c147d 100644
--- a/sys/riscv/starfive/jh7110_gpio.c
+++ b/sys/riscv/starfive/jh7110_gpio.c
@@ -25,24 +25,39 @@
 
 #include "gpio_if.h"
 
+#define	PINCTRL_SYS		1
+#define	PINCTRL_AON		2
+
 #define	GPIO_PINS		64
+#define	AON_GPIO_PINS		4
 #define	GPIO_REGS		2
 
 #define	GP0_DOEN_CFG		0x0
+#define	AON_DOEN_CFG		0x0
 #define	GP0_DOUT_CFG		0x40
+#define	AON_DOUT_CFG		0x4
 #define	GPIOEN			0xdc
+#define	AON_GPIOEN		0xc
 #define	GPIOE_0			0x100
 #define	GPIOE_1			0x104
+#define	GPIOE_AON		0x20
 #define	GPIO_DIN_LOW		0x118
 #define	GPIO_DIN_HIGH		0x11c
+#define	GPIO_DIN_AON		0x2c
 #define	IOMUX_SYSCFG_288	0x120
+#define	IOMUX_AONCFG_52		0x34
 
 #define	PAD_INPUT_EN		(1 << 0)
+#define	SHIFT_DRIVESTRENGTH	1
+#define	PAD_DRIVESTRENGTH	(0x3 << SHIFT_DRIVESTRENGTH)
 #define	PAD_PULLUP		(1 << 3)
 #define	PAD_PULLDOWN		(1 << 4)
+#define	PAD_SLEW		(1 << 5)
 #define	PAD_HYST		(1 << 6)
+#define	PAD_POWERONSTART	(1 << 7)
 
 #define	ENABLE_MASK		0x3f
+#define	OUTPUT_MASK		0x01
 #define	DATA_OUT_MASK		0x7f
 #define	DIROUT_DISABLE		1
 
@@ -52,10 +67,17 @@ struct jh7110_gpio_softc {
 	struct mtx		mtx;
 	struct resource		*res;
 	clk_t			clk;
+	int			pinctrl; /* which pinctrl controller */
+	uint32_t		maxpin; /* pins on this controller */
+	/* location of variable position fields for the two controllers */
+	uint32_t		iomuxcfg;
+	uint32_t		doutcfg;
+	uint32_t		doencfg;
 };
 
 static struct ofw_compat_data compat_data[] = {
-	{"starfive,jh7110-sys-pinctrl", 1},
+	{"starfive,jh7110-sys-pinctrl",	PINCTRL_SYS},
+	{"starfive,jh7110-aon-pinctrl",	PINCTRL_AON},
 	{NULL,				0}
 };
 
@@ -66,7 +88,7 @@ static struct resource_spec jh7110_gpio_spec[] = {
 
 #define	GPIO_RW_OFFSET(_val)		(_val & ~3)
 #define	GPIO_SHIFT(_val)		((_val & 3) * 8)
-#define	PAD_OFFSET(_val)		(_val * 4)
+#define	PAD_OFFSET(_val)		(_val * 4) /* 32 bits per pin (even though only 8 used) */
 
 #define	JH7110_GPIO_LOCK(_sc)		mtx_lock(&(_sc)->mtx)
 #define	JH7110_GPIO_UNLOCK(_sc)		mtx_unlock(&(_sc)->mtx)
@@ -87,7 +109,11 @@ jh7110_gpio_get_bus(device_t dev)
 static int
 jh7110_gpio_pin_max(device_t dev, int *maxpin)
 {
-	*maxpin = GPIO_PINS - 1;
+	struct jh7110_gpio_softc *sc;
+
+	sc = device_get_softc(dev);
+
+	*maxpin = sc->maxpin;
 
 	return (0);
 }
@@ -100,11 +126,14 @@ jh7110_gpio_pin_get(device_t dev, uint32_t pin, uint32_t *val)
 
 	sc = device_get_softc(dev);
 
-	if (pin >= GPIO_PINS)
+	if (pin > sc->maxpin)
 		return (EINVAL);
 
 	JH7110_GPIO_LOCK(sc);
-	if (pin < GPIO_PINS / GPIO_REGS) {
+	if (sc->pinctrl == PINCTRL_AON) {
+		reg = RD4(sc, GPIO_DIN_AON);
+		*val = (reg >> pin) & 0x1;
+	} else if (pin < GPIO_PINS / GPIO_REGS) {
 		reg = RD4(sc, GPIO_DIN_LOW);
 		*val = (reg >> pin) & 0x1;
 	} else {
@@ -124,15 +153,15 @@ jh7110_gpio_pin_set(device_t dev, uint32_t pin, uint32_t val)
 
 	sc = device_get_softc(dev);
 
-	if (pin >= GPIO_PINS)
+	if (pin > sc->maxpin)
 		return (EINVAL);
 
 	JH7110_GPIO_LOCK(sc);
-	reg = RD4(sc, GP0_DOUT_CFG + GPIO_RW_OFFSET(pin));
+	reg = RD4(sc, sc->doutcfg + GPIO_RW_OFFSET(pin));
 	reg &= ~(DATA_OUT_MASK << GPIO_SHIFT(pin));
 	if (val != 0)
 		reg |= 0x1 << GPIO_SHIFT(pin);
-	WR4(sc, GP0_DOUT_CFG + GPIO_RW_OFFSET(pin), reg);
+	WR4(sc, sc->doutcfg + GPIO_RW_OFFSET(pin), reg);
 	JH7110_GPIO_UNLOCK(sc);
 
 	return (0);
@@ -146,18 +175,18 @@ jh7110_gpio_pin_toggle(device_t dev, uint32_t pin)
 
 	sc = device_get_softc(dev);
 
-	if (pin >= GPIO_PINS)
+	if (pin > sc->maxpin)
 		return (EINVAL);
 
 	JH7110_GPIO_LOCK(sc);
-	reg = RD4(sc, GP0_DOUT_CFG + GPIO_RW_OFFSET(pin));
+	reg = RD4(sc, sc->doutcfg + GPIO_RW_OFFSET(pin));
 	if ((reg & 0x1 << GPIO_SHIFT(pin)) != 0) {
 		reg &= ~(DATA_OUT_MASK << GPIO_SHIFT(pin));
 	} else {
 		reg &= ~(DATA_OUT_MASK << GPIO_SHIFT(pin));
 		reg |= 0x1 << GPIO_SHIFT(pin);
 	}
-	WR4(sc, GP0_DOUT_CFG + GPIO_RW_OFFSET(pin), reg);
+	WR4(sc, sc->doutcfg + GPIO_RW_OFFSET(pin), reg);
 	JH7110_GPIO_UNLOCK(sc);
 
 	return (0);
@@ -166,10 +195,14 @@ jh7110_gpio_pin_toggle(device_t dev, uint32_t pin)
 static int
 jh7110_gpio_pin_getcaps(device_t dev, uint32_t pin, uint32_t *caps)
 {
-	if (pin >= GPIO_PINS)
+	struct jh7110_gpio_softc *sc;
+
+	sc = device_get_softc(dev);
+
+	if (pin > sc->maxpin)
 		return (EINVAL);
 
-	*caps = (GPIO_PIN_INPUT | GPIO_PIN_OUTPUT);
+	*caps = (GPIO_PIN_INPUT | GPIO_PIN_OUTPUT | GPIO_PIN_PULLUP | GPIO_PIN_PULLDOWN);
 
 	return (0);
 }
@@ -177,10 +210,15 @@ jh7110_gpio_pin_getcaps(device_t dev, uint32_t pin, uint32_t *caps)
 static int
 jh7110_gpio_pin_getname(device_t dev, uint32_t pin, char *name)
 {
-	if (pin >= GPIO_PINS)
+	struct jh7110_gpio_softc *sc;
+
+	sc = device_get_softc(dev);
+
+	if (pin > sc->maxpin)
 		return (EINVAL);
 
-	snprintf(name, GPIOMAXNAME, "GPIO%d", pin);
+	snprintf(name, GPIOMAXNAME, "%sGPIO%d", sc->pinctrl == PINCTRL_SYS ?
+	  "" : "R", pin);
 
 	return (0);
 }
@@ -193,16 +231,22 @@ jh7110_gpio_pin_getflags(device_t dev, uint32_t pin, uint32_t *flags)
 
 	sc = device_get_softc(dev);
 
-	if (pin >= GPIO_PINS)
+	if (pin > sc->maxpin)
 		return (EINVAL);
 
 	/* Reading the direction */
+	*flags = 0;
 	JH7110_GPIO_LOCK(sc);
-	reg = RD4(sc, GP0_DOEN_CFG + GPIO_RW_OFFSET(pin));
+	reg = RD4(sc, sc->doencfg + GPIO_RW_OFFSET(pin));
 	if ((reg & ENABLE_MASK << GPIO_SHIFT(pin)) == 0)
 		*flags |= GPIO_PIN_OUTPUT;
 	else
 		*flags |= GPIO_PIN_INPUT;
+	reg = RD4(sc, sc->iomuxcfg + PAD_OFFSET(pin));
+	if (reg & PAD_PULLUP)
+		*flags |= GPIO_PIN_PULLUP;
+	if (reg & PAD_PULLDOWN)
+		*flags |= GPIO_PIN_PULLDOWN;
 	JH7110_GPIO_UNLOCK(sc);
 
 	return (0);
@@ -216,7 +260,7 @@ jh7110_gpio_pin_setflags(device_t dev, uint32_t pin, uint32_t flags)
 
 	sc = device_get_softc(dev);
 
-	if (pin >= GPIO_PINS)
+	if (pin > sc->maxpin)
 		return (EINVAL);
 
 	/* Setting the direction, enable or disable output, configuring pads */
@@ -224,27 +268,39 @@ jh7110_gpio_pin_setflags(device_t dev, uint32_t pin, uint32_t flags)
 	JH7110_GPIO_LOCK(sc);
 
 	if ((flags & GPIO_PIN_INPUT) != 0) {
-		reg = RD4(sc, IOMUX_SYSCFG_288 + PAD_OFFSET(pin));
+		reg = RD4(sc, sc->iomuxcfg + PAD_OFFSET(pin));
 		reg |= (PAD_INPUT_EN | PAD_HYST);
-		WR4(sc, IOMUX_SYSCFG_288 + PAD_OFFSET(pin), reg);
+		reg &= ~(PAD_DRIVESTRENGTH | PAD_SLEW);
+		if ((flags & GPIO_PIN_PULLUP) != 0)
+			reg |= PAD_PULLUP;
+		else
+			reg &= ~PAD_PULLUP;
+		if ((flags & GPIO_PIN_PULLDOWN) != 0)
+			reg |= PAD_PULLDOWN;
+		else
+			reg &= ~PAD_PULLDOWN;
+		WR4(sc, sc->iomuxcfg + PAD_OFFSET(pin), reg);
 	}
 
-	reg = RD4(sc, GP0_DOEN_CFG + GPIO_RW_OFFSET(pin));
+	reg = RD4(sc, sc->doencfg + GPIO_RW_OFFSET(pin));
 	reg &= ~(ENABLE_MASK << GPIO_SHIFT(pin));
 	if ((flags & GPIO_PIN_INPUT) != 0) {
 		reg |= DIROUT_DISABLE << GPIO_SHIFT(pin);
 	}
-	WR4(sc, GP0_DOEN_CFG + GPIO_RW_OFFSET(pin), reg);
+	WR4(sc, sc->doencfg + GPIO_RW_OFFSET(pin), reg);
 
 	if ((flags & GPIO_PIN_OUTPUT) != 0) {
-		reg = RD4(sc, GP0_DOUT_CFG + GPIO_RW_OFFSET(pin));
-		reg &= ~(ENABLE_MASK << GPIO_SHIFT(pin));
-		reg |= 0x1 << GPIO_SHIFT(pin);
-		WR4(sc, GP0_DOUT_CFG + GPIO_RW_OFFSET(pin), reg);
-
-		reg = RD4(sc, IOMUX_SYSCFG_288 + PAD_OFFSET(pin));
-		reg &= ~(PAD_INPUT_EN | PAD_PULLUP | PAD_PULLDOWN | PAD_HYST);
-		WR4(sc, IOMUX_SYSCFG_288 + PAD_OFFSET(pin), reg);
+		reg = RD4(sc, sc->doutcfg + GPIO_RW_OFFSET(pin));
+		/*
+		 * Clear the output selection but maintain the current (from
+		 * last time as output) state.
+		 */
+		reg &= ~((ENABLE_MASK - OUTPUT_MASK) << GPIO_SHIFT(pin));
+		WR4(sc, sc->doutcfg + GPIO_RW_OFFSET(pin), reg);
+
+		reg = RD4(sc, sc->iomuxcfg + PAD_OFFSET(pin));
+		reg &= ~(PAD_PULLUP | PAD_PULLDOWN | PAD_HYST);
+		WR4(sc, sc->iomuxcfg + PAD_OFFSET(pin), reg);
 	}
 
 	JH7110_GPIO_UNLOCK(sc);
@@ -255,10 +311,14 @@ jh7110_gpio_pin_setflags(device_t dev, uint32_t pin, uint32_t flags)
 static int
 jh7110_gpio_probe(device_t dev)
 {
+	struct jh7110_gpio_softc *sc;
+
 	if (!ofw_bus_status_okay(dev))
 		return (ENXIO);
 
-	if (ofw_bus_search_compatible(dev, compat_data)->ocd_data == 0)
+	sc = device_get_softc(dev);
+
+	if ((sc->pinctrl = ofw_bus_search_compatible(dev, compat_data)->ocd_data) == 0)
 		return (ENXIO);
 
 	device_set_desc(dev, "StarFive JH7110 GPIO controller");
@@ -299,23 +359,38 @@ jh7110_gpio_attach(device_t dev)
 		return (ENXIO);
 	}
 
-	if (clk_get_by_ofw_index(dev, 0, 0, &sc->clk) != 0) {
+	sc->clk = NULL;
+	if (clk_get_by_ofw_index(dev, 0, 0, &sc->clk) != 0 &&
+	    sc->pinctrl != PINCTRL_AON) {
 		device_printf(dev, "Cannot get clock\n");
 		jh7110_gpio_detach(dev);
 		return (ENXIO);
 	}
 
-	if (clk_enable(sc->clk) != 0) {
+	if (sc->clk != NULL && clk_enable(sc->clk) != 0) {
 		device_printf(dev, "Could not enable clock %s\n",
 		    clk_get_name(sc->clk));
 		jh7110_gpio_detach(dev);
 		return (ENXIO);
 	}
 
-	/* Reseting GPIO interrupts */
-	WR4(sc, GPIOE_0, 0);
-	WR4(sc, GPIOE_1, 0);
-	WR4(sc, GPIOEN, 1);
+	/* Reset GPIO interrupts and set register offsets. */
+	if (sc->pinctrl == PINCTRL_SYS) {
+		WR4(sc, GPIOE_0, 0);
+		WR4(sc, GPIOE_1, 0);
+		WR4(sc, GPIOEN, 1);
+		sc->maxpin = GPIO_PINS - 1;
+		sc->iomuxcfg = IOMUX_SYSCFG_288;
+		sc->doutcfg = GP0_DOUT_CFG;
+		sc->doencfg = GP0_DOEN_CFG;
+	} else {
+		WR4(sc, GPIOE_AON, 0);
+		WR4(sc, AON_GPIOEN, 1);
+		sc->maxpin = AON_GPIO_PINS - 1;
+		sc->iomuxcfg = IOMUX_AONCFG_52;
+		sc->doutcfg = AON_DOUT_CFG;
+		sc->doencfg = AON_DOEN_CFG;
+	}
 
 	sc->busdev = gpiobus_add_bus(dev);
 	if (sc->busdev == NULL) {