Port-arm archive

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index][Old Index]

[PATCH NetBSD/rk3588] rk_gpio: Implement GPIO v2



Rockchip SoCs rk3568, rk3588, and probably others contain a slightly
different GPIO controller.  The main difference is, that half of each
(32-bit) DR/DDR register is used by the write-enable flags.
This means that:
 a) The 32 pins are spread over 2 registers,
 b) Read-modify-write cycle (and thus locking) is no longer needed.

With this commit, the applicable version is read from a dedicated
register at attach time.  In case of an unrecognized version a warning
is printed, but otherwise behaviour is unchanged ("version 1" mode is
used).
---
 sys/arch/arm/rockchip/rk_gpio.c | 150 +++++++++++++++++++++++++-------
 1 file changed, 120 insertions(+), 30 deletions(-)

diff --git a/sys/arch/arm/rockchip/rk_gpio.c b/sys/arch/arm/rockchip/rk_gpio.c
index edd3e138fb14..9f5e6a803093 100644
--- a/sys/arch/arm/rockchip/rk_gpio.c
+++ b/sys/arch/arm/rockchip/rk_gpio.c
@@ -55,12 +55,38 @@ __KERNEL_RCSID(0, "$NetBSD: rk_gpio.c,v 1.5 2021/08/07 16:18:45 thorpej Exp $");
 #define	GPIO_PORTA_EOI_REG		0x004c
 #define	GPIO_EXT_PORTA_REG		0x0050
 #define	GPIO_LS_SYNC_REG		0x0060
+#define	GPIO_VER_ID_REG			0x0078
+#define	GPIO_VER_ID_GPIOV2		0x0101157c
+
+
+/*
+ * In "version 2" GPIO controllers, half of each register is used by the
+ * write_enable mask, so the 32 pins are spread over two registers.
+ *
+ * pins  0 - 15 go into the GPIO_SWPORT_*_L register
+ * pins 16 - 31 go into the GPIO_SWPORT_*_H register
+ */
+#define GPIOV2_SWPORT_DR_BASE		0x0000
+#define GPIOV2_SWPORT_DR_REG(pin)	(\
+	GPIOV2_SWPORT_DR_BASE + GPIOV2_REG_OFFSET(pin))
+#define GPIOV2_SWPORT_DDR_BASE		0x0008
+#define GPIOV2_SWPORT_DDR_REG(pin)	(\
+	GPIOV2_SWPORT_DDR_BASE + GPIOV2_REG_OFFSET(pin))
+#define	GPIOV2_EXT_PORT_REG		0x0070
+#define GPIOV2_REG_OFFSET(pin)		(4 * ((pin) / 16))
+#define GPIOV2_DATA_MASK(pin)		(__BIT((pin) % 16))
+#define GPIOV2_WRITE_MASK(pin)		(__BIT(((pin) % 16) + 16))
 
 static const struct device_compatible_entry compat_data[] = {
 	{ .compat = "rockchip,gpio-bank" },
 	DEVICE_COMPAT_EOL
 };
 
+enum rk_gpio_version {
+	GPIOV1 = 1,
+	GPIOV2
+};
+
 struct rk_gpio_softc {
 	device_t sc_dev;
 	bus_space_tag_t sc_bst;
@@ -70,6 +96,7 @@ struct rk_gpio_softc {
 	struct gpio_chipset_tag sc_gp;
 	gpio_pin_t sc_pins[32];
 	device_t sc_gpiodev;
+	enum rk_gpio_version sc_version;
 };
 
 struct rk_gpio_pin {
@@ -93,18 +120,37 @@ CFATTACH_DECL_NEW(rk_gpio, sizeof(struct rk_gpio_softc),
 static int
 rk_gpio_ctl(struct rk_gpio_softc *sc, u_int pin, int flags)
 {
+	uint32_t reg, write_mask;
 	uint32_t ddr;
 
 	KASSERT(mutex_owned(&sc->sc_lock));
 
-	ddr = RD4(sc, GPIO_SWPORTA_DDR_REG);
-	if (flags & GPIO_PIN_INPUT)
-		ddr &= ~__BIT(pin);
-	else if (flags & GPIO_PIN_OUTPUT)
-		ddr |= __BIT(pin);
-	WR4(sc, GPIO_SWPORTA_DDR_REG, ddr);
+	if (sc->sc_version == GPIOV1) {
+		ddr = RD4(sc, GPIO_SWPORTA_DDR_REG);
+		if (flags & GPIO_PIN_INPUT)
+			ddr &= ~__BIT(pin);
+		else if (flags & GPIO_PIN_OUTPUT)
+			ddr |= __BIT(pin);
+		WR4(sc, GPIO_SWPORTA_DDR_REG, ddr);
+
+		return 0;
+	} else if (sc->sc_version == GPIOV2) {
+		reg = GPIOV2_SWPORT_DDR_REG(pin);
+		write_mask = GPIOV2_WRITE_MASK(pin);
+
+		if (flags & GPIO_PIN_INPUT)
+			ddr = 0;
+		else if (flags & GPIO_PIN_OUTPUT)
+			ddr = GPIOV2_DATA_MASK(pin);
+		else
+			return EINVAL;
+
+		WR4(sc, reg, write_mask | ddr);
+
+		return 0;
+	}
 
-	return 0;
+	return ENXIO;
 }
 
 static void *
@@ -158,15 +204,22 @@ rk_gpio_read(device_t dev, void *priv, bool raw)
 {
 	struct rk_gpio_softc * const sc = device_private(dev);
 	struct rk_gpio_pin *pin = priv;
-	uint32_t data;
+	uint32_t data, reg;
 	int val;
 
 	KASSERT(sc == pin->pin_sc);
 
 	const uint32_t data_mask = __BIT(pin->pin_nr);
 
+	if (sc->sc_version == GPIOV1)
+		reg = GPIO_EXT_PORTA_REG;
+	else if (sc->sc_version == GPIOV2)
+		reg = GPIOV2_EXT_PORT_REG;
+	else
+		return 0;
+
 	/* No lock required for reads */
-	data = RD4(sc, GPIO_EXT_PORTA_REG);
+	data = RD4(sc, reg);
 	val = __SHIFTOUT(data, data_mask);
 	if (!raw && pin->pin_actlo)
 		val = !val;
@@ -179,7 +232,7 @@ rk_gpio_write(device_t dev, void *priv, int val, bool raw)
 {
 	struct rk_gpio_softc * const sc = device_private(dev);
 	struct rk_gpio_pin *pin = priv;
-	uint32_t data;
+	uint32_t data, reg, write_mask;
 
 	KASSERT(sc == pin->pin_sc);
 
@@ -188,14 +241,22 @@ rk_gpio_write(device_t dev, void *priv, int val, bool raw)
 	if (!raw && pin->pin_actlo)
 		val = !val;
 
-	mutex_enter(&sc->sc_lock);
-	data = RD4(sc, GPIO_SWPORTA_DR_REG);
-	if (val)
-		data |= data_mask;
-	else
-		data &= ~data_mask;
-	WR4(sc, GPIO_SWPORTA_DR_REG, data);
-	mutex_exit(&sc->sc_lock);
+
+	if (sc->sc_version == GPIOV1) {
+		mutex_enter(&sc->sc_lock);
+		data = RD4(sc, GPIO_SWPORTA_DR_REG);
+		if (val)
+			data |= data_mask;
+		else
+			data &= ~data_mask;
+		WR4(sc, GPIO_SWPORTA_DR_REG, data);
+		mutex_exit(&sc->sc_lock);
+	} else if (sc->sc_version == GPIOV2) {
+		reg = GPIOV2_SWPORT_DR_REG(pin->pin_nr);
+		write_mask = GPIOV2_WRITE_MASK(pin->pin_nr);
+		data = val ? GPIOV2_DATA_MASK(pin->pin_nr) : 0;
+		WR4(sc, reg, write_mask | data);
+	}
 }
 
 static struct fdtbus_gpio_controller_func rk_gpio_funcs = {
@@ -209,15 +270,22 @@ static int
 rk_gpio_pin_read(void *priv, int pin)
 {
 	struct rk_gpio_softc * const sc = priv;
-	uint32_t data;
+	uint32_t data, reg;
 	int val;
 
 	KASSERT(pin < __arraycount(sc->sc_pins));
 
 	const uint32_t data_mask = __BIT(pin);
 
+	if (sc->sc_version == GPIOV1)
+		reg = GPIO_EXT_PORTA_REG;
+	else if (sc->sc_version == GPIOV2)
+		reg = GPIOV2_EXT_PORT_REG;
+	else
+		return 0;
+
 	/* No lock required for reads */
-	data = RD4(sc, GPIO_EXT_PORTA_REG);
+	data = RD4(sc, reg);
 	val = __SHIFTOUT(data, data_mask);
 
 	return val;
@@ -227,20 +295,27 @@ static void
 rk_gpio_pin_write(void *priv, int pin, int val)
 {
 	struct rk_gpio_softc * const sc = priv;
-	uint32_t data;
+	uint32_t data, reg, write_mask;
 
 	KASSERT(pin < __arraycount(sc->sc_pins));
 
 	const uint32_t data_mask = __BIT(pin);
 
-	mutex_enter(&sc->sc_lock);
-	data = RD4(sc, GPIO_SWPORTA_DR_REG);
-	if (val)
-		data |= data_mask;
-	else
-		data &= ~data_mask;
-	WR4(sc, GPIO_SWPORTA_DR_REG, data);
-	mutex_exit(&sc->sc_lock);
+	if (sc->sc_version == GPIOV1) {
+		mutex_enter(&sc->sc_lock);
+		data = RD4(sc, GPIO_SWPORTA_DR_REG);
+		if (val)
+			data |= data_mask;
+		else
+			data &= ~data_mask;
+		WR4(sc, GPIO_SWPORTA_DR_REG, data);
+		mutex_exit(&sc->sc_lock);
+	} else if (sc->sc_version == GPIOV2) {
+		reg = GPIOV2_SWPORT_DR_REG(pin);
+		write_mask = GPIOV2_WRITE_MASK(pin);
+		data = val ? GPIOV2_DATA_MASK(pin) : 0;
+		WR4(sc, reg, write_mask | data);
+	}
 }
 
 static void
@@ -294,6 +369,7 @@ rk_gpio_attach(device_t parent, device_t self, void *aux)
 	struct rk_gpio_softc * const sc = device_private(self);
 	struct fdt_attach_args * const faa = aux;
 	const int phandle = faa->faa_phandle;
+	uint32_t ver_id;
 	struct clk *clk;
 	bus_addr_t addr;
 	bus_size_t size;
@@ -314,10 +390,24 @@ rk_gpio_attach(device_t parent, device_t self, void *aux)
 		aprint_error(": couldn't map registers\n");
 		return;
 	}
+
+	ver_id = RD4(sc, GPIO_VER_ID_REG);
+	/* In version 1, the register is undocumented, but reads as 0. */
+	if (ver_id == 0) {
+		sc->sc_version = GPIOV1;
+	} else if (ver_id == GPIO_VER_ID_GPIOV2) {
+		sc->sc_version = GPIOV2;
+	} else {
+		aprint_normal("warning: unknown version ID: %08" PRIx32
+				", treating as v1\n", ver_id);
+		sc->sc_version = GPIOV1;
+	}
+
 	mutex_init(&sc->sc_lock, MUTEX_DEFAULT, IPL_VM);
 
 	aprint_naive("\n");
-	aprint_normal(": GPIO (%s)\n", fdtbus_get_string(phandle, "name"));
+	aprint_normal(": GPIO v%d (%s)\n", sc->sc_version,
+			fdtbus_get_string(phandle, "name"));
 
 	fdtbus_register_gpio_controller(self, phandle, &rk_gpio_funcs);
 
-- 
2.40.1



Home | Main Index | Thread Index | Old Index