Skip site navigation (1)Skip section navigation (2)
Date:      Sat, 1 Jul 2023 17:20:46 GMT
From:      Warner Losh <imp@FreeBSD.org>
To:        src-committers@FreeBSD.org, dev-commits-src-all@FreeBSD.org, dev-commits-src-main@FreeBSD.org
Subject:   git: 7f8d2ed03bc6 - main - superio+nctgpio: Prepare to support some new (weird) chips
Message-ID:  <202307011720.361HKkA7066350@gitrepo.freebsd.org>

next in thread | raw e-mail | index | archive | help
The branch main has been updated by imp:

URL: https://cgit.FreeBSD.org/src/commit/?id=7f8d2ed03bc670393d7a8322b0681f46ead745e7

commit 7f8d2ed03bc670393d7a8322b0681f46ead745e7
Author:     Stéphane Rochoy <stephane.rochoy@stormshield.eu>
AuthorDate: 2023-07-01 17:19:44 +0000
Commit:     Warner Losh <imp@FreeBSD.org>
CommitDate: 2023-07-01 17:19:51 +0000

    superio+nctgpio: Prepare to support some new (weird) chips
    
    - Increase verbosity to direct i/o code path (iores != 0)
    - Fix pin inversion configuration
    - Allow forcing the use of indirect access channel via hint.gpio.0.flags=2
    - Document the PREFER_INDIRECT_CHANNEL tunable in nctgpio(4)
    
    Reviewed by: imp
    Pull Request: https://github.com/freebsd/freebsd-src/pull/719
---
 sys/dev/nctgpio/nctgpio.c | 705 ++++++++++++++++++++++++++++++----------------
 1 file changed, 465 insertions(+), 240 deletions(-)

diff --git a/sys/dev/nctgpio/nctgpio.c b/sys/dev/nctgpio/nctgpio.c
index 56f37da45bc3..607a5f3e56cf 100644
--- a/sys/dev/nctgpio/nctgpio.c
+++ b/sys/dev/nctgpio/nctgpio.c
@@ -29,7 +29,6 @@
 
 /*
  * Nuvoton GPIO driver.
- *
  */
 
 #include <sys/cdefs.h>
@@ -51,39 +50,36 @@
 
 #include "gpio_if.h"
 
-/* Logical Device Numbers. */
-#define NCT_LDN_GPIO			0x07
-#define NCT_LDN_GPIO_MODE		0x0f
-
-/* Logical Device 7 */
-#define NCT_LD7_GPIO0_IOR		0xe0
-#define NCT_LD7_GPIO0_DAT		0xe1
-#define NCT_LD7_GPIO0_INV		0xe2
-#define NCT_LD7_GPIO0_DST		0xe3
-#define NCT_LD7_GPIO1_IOR		0xe4
-#define NCT_LD7_GPIO1_DAT		0xe5
-#define NCT_LD7_GPIO1_INV		0xe6
-#define NCT_LD7_GPIO1_DST		0xe7
-
-/* Logical Device F */
-#define NCT_LDF_GPIO0_OUTCFG		0xe0
-#define NCT_LDF_GPIO1_OUTCFG		0xe1
+#define NCT_PPOD_LDN 0xf /* LDN used to select Push-Pull/Open-Drain */
 
-/* Direct I/O port access. */
-#define	NCT_IO_GSR			0
-#define	NCT_IO_IOR			1
-#define	NCT_IO_DAT			2
-#define	NCT_IO_INV			3
+/* Direct access through GPIO register table */
+#define	NCT_IO_GSR			0 /* Group Select */
+#define	NCT_IO_IOR			1 /* I/O */
+#define	NCT_IO_DAT			2 /* Data */
+#define	NCT_IO_INV			3 /* Inversion */
+#define	NCT_IO_DST          4 /* Status */
 
-#define NCT_MAX_PIN			15
-#define NCT_IS_VALID_PIN(_p)	((_p) >= 0 && (_p) <= NCT_MAX_PIN)
+#define NCT_MAX_GROUP   9
+#define NCT_MAX_PIN     75
 
-#define NCT_PIN_BIT(_p)         (1 << ((_p) & 7))
+#define NCT_PIN_IS_VALID(_sc, _p)   ((_p) < (_sc)->npins)
+#define NCT_PIN_GROUP(_sc, _p)      ((_sc)->pinmap[(_p)].group)
+#define NCT_PIN_GRPNUM(_sc, _p)     ((_sc)->pinmap[(_p)].grpnum)
+#define NCT_PIN_BIT(_sc, _p)        ((_sc)->pinmap[(_p)].bit)
+#define NCT_PIN_BITMASK(_p)         (1 << ((_p) & 7))
 
 #define NCT_GPIO_CAPS	(GPIO_PIN_INPUT | GPIO_PIN_OUTPUT | \
 	GPIO_PIN_OPENDRAIN | GPIO_PIN_PUSHPULL | \
 	GPIO_PIN_INVIN | GPIO_PIN_INVOUT)
 
+#define NCT_PREFER_INDIRECT_CHANNEL 2
+
+#define NCT_VERBOSE_PRINTF(dev, ...)            \
+	do {                                        \
+		if (__predict_false(bootverbose))       \
+			device_printf(dev, __VA_ARGS__);    \
+	} while (0)
+
 /*
  * Note that the values are important.
  * They match actual register offsets.
@@ -94,25 +90,43 @@ typedef enum {
 	REG_INV = 2,
 } reg_t;
 
+struct nct_gpio_group {
+ 	uint32_t    caps;
+	uint8_t     enable_ldn;
+	uint8_t     enable_reg;
+	uint8_t     enable_mask;
+	uint8_t     data_ldn;
+	uint8_t     iobase;
+	uint8_t     ppod_reg; /* Push-Pull/Open-Drain */
+	uint8_t     grpnum;
+	uint8_t     pinbits[8];
+	uint8_t     npins;
+};
+
 struct nct_softc {
 	device_t			dev;
-	device_t			dev_f;
 	device_t			busdev;
 	struct mtx			mtx;
 	struct resource			*iores;
 	int				iorid;
 	int				curgrp;
 	struct {
-		/* direction, 1: pin is input */
-		uint8_t			ior[2];
-		/* output value */
-		uint8_t			out[2];
-		/* whether out is valid */
-		uint8_t			out_known[2];
-		/* inversion, 1: pin is inverted */
-		uint8_t			inv[2];
-	} 				cache;
-	struct gpio_pin			pins[NCT_MAX_PIN + 1];
+		uint8_t ior[NCT_MAX_GROUP + 1];       /* direction, 1: input 0: output */
+		uint8_t out[NCT_MAX_GROUP + 1];       /* output value */
+		uint8_t out_known[NCT_MAX_GROUP + 1]; /* whether out is valid */
+		uint8_t inv[NCT_MAX_GROUP + 1];       /* inversion, 1: inverted */
+	} cache;
+	struct gpio_pin				pins[NCT_MAX_PIN + 1];
+	struct nct_device			*nctdevp;
+	int							npins; /* Total number of pins */
+
+	/* Lookup tables */
+	struct {
+		struct nct_gpio_group *group;
+		uint8_t                grpnum;
+		uint8_t                bit;
+	} pinmap[NCT_MAX_PIN+1];
+	struct nct_gpio_group *grpmap[NCT_MAX_GROUP+1];
 };
 
 #define GPIO_LOCK_INIT(_sc)	mtx_init(&(_sc)->mtx,		\
@@ -123,93 +137,204 @@ struct nct_softc {
 #define GPIO_ASSERT_LOCKED(_sc)	mtx_assert(&(_sc)->mtx, MA_OWNED)
 #define GPIO_ASSERT_UNLOCKED(_sc)	mtx_assert(&(_sc)->mtx, MA_NOTOWNED)
 
-struct nuvoton_vendor_device_id {
-	uint16_t		chip_id;
-	const char *		descr;
-} nct_devs[] = {
+#define GET_BIT(v, b)	(((v) >> (b)) & 1)
+
+/*
+ * For most devices there are several GPIO devices, we attach only to one of
+ * them and use the rest without attaching.
+ */
+struct nct_device {
+	uint16_t                  devid;
+	int                       extid;
+	const char               *descr;
+	int                       ngroups;
+	struct nct_gpio_group     groups[NCT_MAX_GROUP + 1];
+} nct_devices[] = {
 	{
-		.chip_id	= 0x1061,
-		.descr		= "Nuvoton NCT5104D",
+		.devid   = 0x1061,
+		.descr   = "GPIO on Nuvoton NCT5104D",
+		.ngroups = 2,
+		.groups  = {
+			{
+				.grpnum      = 0,
+				.pinbits     = { 0, 1, 2, 3, 4, 5, 6, 7 },
+				.enable_ldn  = 0x07,
+				.enable_reg  = 0x30,
+				.enable_mask = 0x01,
+				.data_ldn    = 0x07,
+				.ppod_reg    = 0xe0,
+				.caps        = NCT_GPIO_CAPS,
+				.npins       = 8,
+				.iobase      = 0xe0,
+			},
+			{
+				.grpnum      = 1,
+				.pinbits     = { 0, 1, 2, 3, 4, 5, 6, 7 },
+				.enable_ldn  = 0x07,
+				.enable_reg  = 0x30,
+				.enable_mask = 0x02,
+				.data_ldn    = 0x07,
+				.ppod_reg    = 0xe1,
+				.caps        = NCT_GPIO_CAPS,
+				.npins       = 8,
+				.iobase      = 0xe4,
+			},
+		},
 	},
 	{
-		.chip_id	= 0xc452,
-		.descr		= "Nuvoton NCT5104D (PC-Engines APU)",
+		.devid   = 0xc452,
+		.descr   = "GPIO on Nuvoton NCT5104D (PC-Engines APU)",
+		.ngroups = 2,
+		.groups  = {
+			{
+				.grpnum      = 0,
+				.pinbits     = { 0, 1, 2, 3, 4, 5, 6, 7 },
+				.enable_ldn  = 0x07,
+				.enable_reg  = 0x30,
+				.enable_mask = 0x01,
+				.data_ldn    = 0x07,
+				.ppod_reg    = 0xe0,
+				.caps        = NCT_GPIO_CAPS,
+				.npins       = 8,
+				.iobase      = 0xe0,
+			},
+			{
+				.grpnum      = 1,
+				.pinbits     = { 0, 1, 2, 3, 4, 5, 6, 7 },
+				.enable_ldn  = 0x07,
+				.enable_reg  = 0x30,
+				.enable_mask = 0x02,
+				.data_ldn    = 0x07,
+				.ppod_reg    = 0xe1,
+				.caps        = NCT_GPIO_CAPS,
+				.npins       = 8,
+				.iobase      = 0xe4,
+			},
+		},
 	},
 	{
-		.chip_id	= 0xc453,
-		.descr		= "Nuvoton NCT5104D (PC-Engines APU3)",
+		.devid   = 0xc453,
+		.descr   = "GPIO on Nuvoton NCT5104D (PC-Engines APU3)",
+		.ngroups = 2,
+		.groups  = {
+			{
+				.grpnum      = 0,
+				.pinbits     = { 0, 1, 2, 3, 4, 5, 6, 7 },
+				.enable_ldn  = 0x07,
+				.enable_reg  = 0x30,
+				.enable_mask = 0x01,
+				.data_ldn    = 0x07,
+				.ppod_reg    = 0xe0,
+				.caps        = NCT_GPIO_CAPS,
+				.npins       = 8,
+				.iobase      = 0xe0,
+			},
+			{
+				.grpnum      = 1,
+				.pinbits     = { 0, 1, 2, 3, 4, 5, 6, 7 },
+				.enable_ldn  = 0x07,
+				.enable_reg  = 0x30,
+				.enable_mask = 0x02,
+				.data_ldn    = 0x07,
+				.ppod_reg    = 0xe1,
+				.caps        = NCT_GPIO_CAPS,
+				.npins       = 8,
+				.iobase      = 0xe4,
+			},
+		},
 	},
 };
 
-static void
-nct_io_set_group(struct nct_softc *sc, int group)
+static const char *
+io2str(uint8_t ioport)
 {
+	switch (ioport) {
+	case NCT_IO_GSR: return ("grpsel");
+	case NCT_IO_IOR: return ("io");
+	case NCT_IO_DAT: return ("data");
+	case NCT_IO_INV: return ("inv");
+	case NCT_IO_DST: return ("status");
+	default:         return ("?");
+	}
+}
 
+static void
+nct_io_set_group(struct nct_softc *sc, uint8_t grpnum)
+{
 	GPIO_ASSERT_LOCKED(sc);
-	if (group != sc->curgrp) {
-		bus_write_1(sc->iores, NCT_IO_GSR, group);
-		sc->curgrp = group;
-	}
+
+	if (grpnum == sc->curgrp)
+		return;
+
+	NCT_VERBOSE_PRINTF(sc->dev, "write %s 0x%x ioport %d\n",
+		io2str(NCT_IO_GSR), grpnum, NCT_IO_GSR);
+	bus_write_1(sc->iores, NCT_IO_GSR, grpnum);
+	sc->curgrp = grpnum;
 }
 
 static uint8_t
-nct_io_read(struct nct_softc *sc, int group, uint8_t reg)
+nct_io_read(struct nct_softc *sc, uint8_t grpnum, uint8_t reg)
 {
-	nct_io_set_group(sc, group);
-	return (bus_read_1(sc->iores, reg));
+	uint8_t val;
+
+	nct_io_set_group(sc, grpnum);
+
+	val = bus_read_1(sc->iores, reg);
+	NCT_VERBOSE_PRINTF(sc->dev, "read %s 0x%x ioport %d\n",
+		io2str(reg), val, reg);
+	return (val);
 }
 
 static void
-nct_io_write(struct nct_softc *sc, int group, uint8_t reg, uint8_t val)
+nct_io_write(struct nct_softc *sc, uint8_t grpnum, uint8_t reg, uint8_t val)
 {
-	nct_io_set_group(sc, group);
-	return (bus_write_1(sc->iores, reg, val));
+	nct_io_set_group(sc, grpnum);
+
+	NCT_VERBOSE_PRINTF(sc->dev, "write %s 0x%x ioport %d\n",
+		io2str(reg), val, reg);
+	bus_write_1(sc->iores, reg, val);
 }
 
 static uint8_t
-nct_get_ioreg(struct nct_softc *sc, reg_t reg, int group)
+nct_get_ioreg(struct nct_softc *sc, reg_t reg, uint8_t grpnum)
 {
-	uint8_t ioreg;
+	uint8_t iobase;
 
 	if (sc->iores != NULL)
-		ioreg = NCT_IO_IOR + reg;
-	else if (group == 0)
-		ioreg = NCT_LD7_GPIO0_IOR + reg;
+		iobase = NCT_IO_IOR;
 	else
-		ioreg = NCT_LD7_GPIO1_IOR + reg;
-	return (ioreg);
+		iobase = sc->grpmap[grpnum]->iobase;
+	return (iobase + reg);
 }
 
-static uint8_t
-nct_read_reg(struct nct_softc *sc, reg_t reg, int group)
+static const char *
+reg2str(reg_t reg)
 {
-	uint8_t ioreg;
-	uint8_t val;
-
-	ioreg = nct_get_ioreg(sc, reg, group);
-	if (sc->iores != NULL)
-		val = nct_io_read(sc, group, ioreg);
-	else
-		val = superio_read(sc->dev, ioreg);
-
-	return (val);
+	switch (reg) {
+	case REG_IOR: return ("io");
+	case REG_DAT: return ("data");
+	case REG_INV: return ("inv");
+	default:      return ("?");
+	}
 }
 
-#define GET_BIT(v, b)	(((v) >> (b)) & 1)
-static bool
-nct_get_pin_reg(struct nct_softc *sc, reg_t reg, uint32_t pin_num)
+static uint8_t
+nct_read_reg(struct nct_softc *sc, reg_t reg, uint8_t grpnum)
 {
-	uint8_t bit;
-	uint8_t group;
-	uint8_t val;
+	struct nct_gpio_group *gp;
+	uint8_t                ioreg;
+	uint8_t                val;
 
-	KASSERT(NCT_IS_VALID_PIN(pin_num), ("%s: invalid pin number %d",
-	    __func__, pin_num));
+	ioreg = nct_get_ioreg(sc, reg, grpnum);
 
-	group = pin_num >> 3;
-	bit = pin_num & 7;
-	val = nct_read_reg(sc, reg, group);
-	return (GET_BIT(val, bit));
+	if (sc->iores != NULL)
+		return (nct_io_read(sc, grpnum, ioreg));
+
+	gp  = sc->grpmap[grpnum];
+	val = superio_ldn_read(sc->dev, gp->data_ldn, ioreg);
+	NCT_VERBOSE_PRINTF(sc->dev, "read %s 0x%x from group GPIO%u ioreg 0x%x\n",
+		reg2str(reg), val, grpnum, ioreg);
+	return (val);
 }
 
 static int
@@ -219,25 +344,33 @@ nct_get_pin_cache(struct nct_softc *sc, uint32_t pin_num, uint8_t *cache)
 	uint8_t group;
 	uint8_t val;
 
-	KASSERT(NCT_IS_VALID_PIN(pin_num), ("%s: invalid pin number %d",
+	KASSERT(NCT_PIN_IS_VALID(sc, pin_num), ("%s: invalid pin number %d",
 	    __func__, pin_num));
 
-	group = pin_num >> 3;
-	bit = pin_num & 7;
-	val = cache[group];
+	group = NCT_PIN_GRPNUM(sc, pin_num);
+	bit   = NCT_PIN_BIT(sc, pin_num);
+	val   = cache[group];
 	return (GET_BIT(val, bit));
 }
 
 static void
-nct_write_reg(struct nct_softc *sc, reg_t reg, int group, uint8_t val)
+nct_write_reg(struct nct_softc *sc, reg_t reg, uint8_t grpnum, uint8_t val)
 {
-	uint8_t ioreg;
+	struct nct_gpio_group *gp;
+	uint8_t                ioreg;
 
-	ioreg = nct_get_ioreg(sc, reg, group);
-	if (sc->iores != NULL)
-		nct_io_write(sc, group, ioreg, val);
-	else
-		superio_write(sc->dev, ioreg, val);
+	ioreg = nct_get_ioreg(sc, reg, grpnum);
+
+	if (sc->iores != NULL) {
+		nct_io_write(sc, grpnum, ioreg, val);
+		return;
+	}
+
+	gp = sc->grpmap[grpnum];
+	superio_ldn_write(sc->dev, gp->data_ldn, ioreg, val);
+
+	NCT_VERBOSE_PRINTF(sc->dev, "write %s 0x%x to group GPIO%u ioreg 0x%x\n",
+		reg2str(reg), val, grpnum, ioreg);
 }
 
 static void
@@ -249,14 +382,14 @@ nct_set_pin_reg(struct nct_softc *sc, reg_t reg, uint32_t pin_num, bool val)
 	uint8_t group;
 	uint8_t mask;
 
-	KASSERT(NCT_IS_VALID_PIN(pin_num),
+	KASSERT(NCT_PIN_IS_VALID(sc, pin_num),
 	    ("%s: invalid pin number %d", __func__, pin_num));
 	KASSERT(reg == REG_IOR || reg == REG_INV,
 	    ("%s: unsupported register %d", __func__, reg));
 
-	group = pin_num >> 3;
-	bit = pin_num & 7;
-	mask = (uint8_t)1 << bit;
+	group  = NCT_PIN_GRPNUM(sc, pin_num);
+	bit    = NCT_PIN_BIT(sc, pin_num);
+	mask   = (uint8_t)1 << bit;
 	bitval = (uint8_t)val << bit;
 
 	if (reg == REG_IOR)
@@ -317,8 +450,9 @@ nct_write_pin(struct nct_softc *sc, uint32_t pin_num, bool val)
 	uint8_t group;
 
 	KASSERT(!nct_pin_is_input(sc, pin_num), ("attempt to write input pin"));
-	group = pin_num >> 3;
-	bit = pin_num & 7;
+	group = NCT_PIN_GRPNUM(sc, pin_num);
+	bit   = NCT_PIN_BIT(sc, pin_num);
+
 	if (GET_BIT(sc->cache.out_known[group], bit) &&
 	    GET_BIT(sc->cache.out[group], bit) == val) {
 		/* The pin is already in requested state. */
@@ -332,6 +466,35 @@ nct_write_pin(struct nct_softc *sc, uint32_t pin_num, bool val)
 	nct_write_reg(sc, REG_DAT, group, sc->cache.out[group]);
 }
 
+static bool
+nct_get_pin_reg(struct nct_softc *sc, reg_t reg, uint32_t pin_num)
+{
+	uint8_t            bit;
+	uint8_t            group;
+	uint8_t            val;
+	bool               b;
+
+	KASSERT(NCT_PIN_IS_VALID(sc, pin_num), ("%s: invalid pin number %d",
+			__func__, pin_num));
+
+	group = NCT_PIN_GRPNUM(sc, pin_num);
+	bit   = NCT_PIN_BIT(sc, pin_num);
+	val   = nct_read_reg(sc, reg, group);
+	b     = GET_BIT(val, bit);
+
+	if (__predict_false(bootverbose)) {
+		if (nct_pin_is_input(sc, pin_num))
+			NCT_VERBOSE_PRINTF(sc->dev, "read %d from input pin %u<GPIO%u%u>\n",
+				b, pin_num, group, bit);
+		else
+			NCT_VERBOSE_PRINTF(sc->dev,
+				"read %d from output pin %u<GPIO%u%u>, cache miss\n",
+				b, pin_num, group, bit);
+	}
+
+	return (b);
+}
+
 /*
  * NB: state of an input pin cannot be cached, of course.
  * For an output we can either take the value from the cache if it's valid
@@ -342,15 +505,24 @@ nct_read_pin(struct nct_softc *sc, uint32_t pin_num)
 {
 	uint8_t bit;
 	uint8_t group;
-	bool val;
+	bool    val;
 
-	if (nct_pin_is_input(sc, pin_num))
+	if (nct_pin_is_input(sc, pin_num)) {
 		return (nct_get_pin_reg(sc, REG_DAT, pin_num));
+	}
 
-	group = pin_num >> 3;
-	bit = pin_num & 7;
-	if (GET_BIT(sc->cache.out_known[group], bit))
-		return (GET_BIT(sc->cache.out[group], bit));
+	group = NCT_PIN_GRPNUM(sc, pin_num);
+	bit   = NCT_PIN_BIT(sc, pin_num);
+
+	if (GET_BIT(sc->cache.out_known[group], bit)) {
+		val = GET_BIT(sc->cache.out[group], bit);
+
+		NCT_VERBOSE_PRINTF(sc->dev,
+			"read %d from output pin %u<GPIO%u%u>, cache hit\n",
+			val, pin_num, group, bit);
+
+		return (val);
+	}
 
 	val = nct_get_pin_reg(sc, REG_DAT, pin_num);
 	sc->cache.out_known[group] |= 1 << bit;
@@ -362,14 +534,11 @@ nct_read_pin(struct nct_softc *sc, uint32_t pin_num)
 }
 
 static uint8_t
-nct_outcfg_addr(uint32_t pin_num)
+nct_ppod_reg(struct nct_softc *sc, uint32_t pin_num)
 {
-	KASSERT(NCT_IS_VALID_PIN(pin_num), ("%s: invalid pin number %d",
-	    __func__, pin_num));
-	if ((pin_num >> 3) == 0)
-		return (NCT_LDF_GPIO0_OUTCFG);
-	else
-		return (NCT_LDF_GPIO1_OUTCFG);
+	uint8_t group = NCT_PIN_GRPNUM(sc, pin_num);
+
+	return (sc->grpmap[group]->ppod_reg);
 }
 
 /*
@@ -383,10 +552,10 @@ nct_set_pin_opendrain(struct nct_softc *sc, uint32_t pin_num)
 	uint8_t reg;
 	uint8_t outcfg;
 
-	reg = nct_outcfg_addr(pin_num);
-	outcfg = superio_read(sc->dev_f, reg);
-	outcfg |= NCT_PIN_BIT(pin_num);
-	superio_write(sc->dev_f, reg, outcfg);
+	reg = nct_ppod_reg(sc, pin_num);
+	outcfg = superio_ldn_read(sc->dev, NCT_PPOD_LDN, reg);
+	outcfg |= NCT_PIN_BITMASK(pin_num);
+	superio_ldn_write(sc->dev, 0xf, reg, outcfg);
 }
 
 static void
@@ -395,10 +564,10 @@ nct_set_pin_pushpull(struct nct_softc *sc, uint32_t pin_num)
 	uint8_t reg;
 	uint8_t outcfg;
 
-	reg = nct_outcfg_addr(pin_num);
-	outcfg = superio_read(sc->dev_f, reg);
-	outcfg &= ~NCT_PIN_BIT(pin_num);
-	superio_write(sc->dev_f, reg, outcfg);
+	reg = nct_ppod_reg(sc, pin_num);
+	outcfg = superio_ldn_read(sc->dev, NCT_PPOD_LDN, reg);
+	outcfg &= ~NCT_PIN_BITMASK(pin_num);
+	superio_ldn_write(sc->dev, 0xf, reg, outcfg);
 }
 
 static bool
@@ -407,94 +576,156 @@ nct_pin_is_opendrain(struct nct_softc *sc, uint32_t pin_num)
 	uint8_t reg;
 	uint8_t outcfg;
 
-	reg = nct_outcfg_addr(pin_num);
-	outcfg = superio_read(sc->dev_f, reg);
-	return (outcfg & NCT_PIN_BIT(pin_num));
+	reg = nct_ppod_reg(sc, pin_num);
+	outcfg = superio_ldn_read(sc->dev, NCT_PPOD_LDN, reg);
+	return (outcfg & NCT_PIN_BITMASK(pin_num));
+}
+
+static struct nct_device *
+nct_lookup_device(device_t dev)
+{
+	struct nct_device *nctdevp;
+	uint16_t           devid;
+	int                i, extid;
+
+	devid = superio_devid(dev);
+	extid = superio_extid(dev);
+	for (i = 0, nctdevp = nct_devices; i < nitems(nct_devices); i++, nctdevp++) {
+		if (devid == nctdevp->devid && nctdevp->extid == extid)
+			return (nctdevp);
+	}
+	return (NULL);
 }
 
 static int
 nct_probe(device_t dev)
 {
-	int j;
-	uint16_t chipid;
+	struct nct_device *nctdevp;
+	uint8_t            ldn;
 
-	if (superio_vendor(dev) != SUPERIO_VENDOR_NUVOTON)
+	ldn = superio_get_ldn(dev);
+
+	if (superio_vendor(dev) != SUPERIO_VENDOR_NUVOTON) {
+		NCT_VERBOSE_PRINTF(dev, "ldn 0x%x not a Nuvoton device\n", ldn);
 		return (ENXIO);
-	if (superio_get_type(dev) != SUPERIO_DEV_GPIO)
+	}
+	if (superio_get_type(dev) != SUPERIO_DEV_GPIO) {
+		NCT_VERBOSE_PRINTF(dev, "ldn 0x%x not a GPIO device\n", ldn);
 		return (ENXIO);
+	}
 
-	/*
-	 * There are several GPIO devices, we attach only to one of them
-	 * and use the rest without attaching.
-	 */
-	if (superio_get_ldn(dev) != NCT_LDN_GPIO)
+	nctdevp = nct_lookup_device(dev);
+	if (nctdevp == NULL) {
+		NCT_VERBOSE_PRINTF(dev, "ldn 0x%x not supported\n", ldn);
 		return (ENXIO);
-
-	chipid = superio_devid(dev);
-	for (j = 0; j < nitems(nct_devs); j++) {
-		if (chipid == nct_devs[j].chip_id) {
-			device_set_desc(dev, "Nuvoton GPIO controller");
-			return (BUS_PROBE_DEFAULT);
-		}
 	}
-	return (ENXIO);
+	device_set_desc(dev, nctdevp->descr);
+	return (BUS_PROBE_DEFAULT);
 }
 
 static int
 nct_attach(device_t dev)
 {
 	struct nct_softc *sc;
-	device_t dev_8;
-	uint16_t iobase;
-	int err;
-	int i;
-
-	sc = device_get_softc(dev);
-	sc->dev = dev;
-	sc->dev_f = superio_find_dev(device_get_parent(dev), SUPERIO_DEV_GPIO,
-	    NCT_LDN_GPIO_MODE);
-	if (sc->dev_f == NULL) {
-		device_printf(dev, "failed to find LDN F\n");
-		return (ENXIO);
-	}
-
-	/*
-	 * As strange as it may seem, I/O port base is configured in the
-	 * Logical Device 8 which is primarily used for WDT, but also plays
-	 * a role in GPIO configuration.
-	 */
-	iobase = 0;
-	dev_8 = superio_find_dev(device_get_parent(dev), SUPERIO_DEV_WDT, 8);
-	if (dev_8 != NULL)
-		iobase = superio_get_iobase(dev_8);
-	if (iobase != 0 && iobase != 0xffff) {
-		sc->curgrp = -1;
-		sc->iorid = 0;
-		err = bus_set_resource(dev, SYS_RES_IOPORT, sc->iorid,
-		    iobase, 7);
-		if (err == 0) {
-			sc->iores = bus_alloc_resource_any(dev, SYS_RES_IOPORT,
-			    &sc->iorid, RF_ACTIVE);
-			if (sc->iores == NULL) {
-				device_printf(dev, "can't map i/o space, "
-				    "iobase=0x%04x\n", iobase);
+	struct nct_gpio_group *gp;
+	uint32_t pin_num;
+	uint8_t v;
+	int flags, i, g;
+
+	sc          = device_get_softc(dev);
+	sc->dev     = dev;
+	sc->nctdevp = nct_lookup_device(dev);
+
+	flags = 0;
+	(void)resource_int_value(device_get_name(dev), device_get_unit(dev), "flags", &flags);
+
+	if ((flags & NCT_PREFER_INDIRECT_CHANNEL) == 0) {
+		uint16_t iobase;
+		device_t dev_8;
+
+		/*
+		 * As strange as it may seem, I/O port base is configured in the
+		 * Logical Device 8 which is primarily used for WDT, but also plays
+		 * a role in GPIO configuration.
+		 */
+		iobase = 0;
+		dev_8 = superio_find_dev(device_get_parent(dev), SUPERIO_DEV_WDT, 8);
+		if (dev_8 != NULL)
+			iobase = superio_get_iobase(dev_8);
+		if (iobase != 0 && iobase != 0xffff) {
+			int err;
+
+			NCT_VERBOSE_PRINTF(dev, "iobase %#x\n", iobase);
+			sc->curgrp = -1;
+			sc->iorid = 0;
+			err = bus_set_resource(dev, SYS_RES_IOPORT, sc->iorid,
+				iobase, 7);
+			if (err == 0) {
+				sc->iores = bus_alloc_resource_any(dev, SYS_RES_IOPORT,
+					&sc->iorid, RF_ACTIVE);
+				if (sc->iores == NULL) {
+					device_printf(dev, "can't map i/o space, "
+						"iobase=%#x\n", iobase);
+				}
+			} else {
+				device_printf(dev,
+					"failed to set io port resource at %#x\n", iobase);
 			}
-		} else {
-			device_printf(dev,
-			    "failed to set io port resource at 0x%x\n", iobase);
 		}
 	}
-
-	/* Enable gpio0 and gpio1. */
-	superio_dev_enable(dev, 0x03);
+	NCT_VERBOSE_PRINTF(dev, "iores %p %s channel\n",
+		sc->iores, (sc->iores ? "direct" : "indirect"));
+
+	/* Enable GPIO groups */
+	for (g = 0, gp = sc->nctdevp->groups; g < sc->nctdevp->ngroups; g++, gp++) {
+		NCT_VERBOSE_PRINTF(dev,
+			"GPIO%d: %d pins, enable with mask 0x%x via ldn 0x%x reg 0x%x\n",
+			gp->grpnum, gp->npins, gp->enable_mask, gp->enable_ldn,
+			gp->enable_reg);
+		v = superio_ldn_read(dev, gp->enable_ldn, gp->enable_reg);
+		v |= gp->enable_mask;
+		superio_ldn_write(dev, gp->enable_ldn, gp->enable_reg, v);
+	}
 
 	GPIO_LOCK_INIT(sc);
 	GPIO_LOCK(sc);
 
-	sc->cache.inv[0] = nct_read_reg(sc, REG_INV, 0);
-	sc->cache.inv[1] = nct_read_reg(sc, REG_INV, 1);
-	sc->cache.ior[0] = nct_read_reg(sc, REG_IOR, 0);
-	sc->cache.ior[1] = nct_read_reg(sc, REG_IOR, 1);
+	pin_num   = 0;
+	sc->npins = 0;
+	for (g = 0, gp = sc->nctdevp->groups; g < sc->nctdevp->ngroups; g++, gp++) {
+		sc->npins += gp->npins;
+		for (i = 0; i < gp->npins; i++, pin_num++) {
+			struct gpio_pin *pin;
+
+			sc->pinmap[pin_num].group  = gp;
+			sc->pinmap[pin_num].grpnum = gp->grpnum;
+			sc->pinmap[pin_num].bit    = gp->pinbits[i];
+
+			sc->grpmap[gp->grpnum] = gp;
+
+			pin           = &sc->pins[pin_num];
+			pin->gp_pin   = pin_num;
+			pin->gp_caps  = gp->caps;
+			pin->gp_flags = 0;
+
+			snprintf(pin->gp_name, GPIOMAXNAME, "GPIO%u%u",
+				gp->grpnum, gp->pinbits[i]);
+
+			if (nct_pin_is_input(sc, pin_num))
+				pin->gp_flags |= GPIO_PIN_INPUT;
+			else
+				pin->gp_flags |= GPIO_PIN_OUTPUT;
+
+			if (nct_pin_is_opendrain(sc, pin_num))
+				pin->gp_flags |= GPIO_PIN_OPENDRAIN;
+			else
+				pin->gp_flags |= GPIO_PIN_PUSHPULL;
+
+			if (nct_pin_is_inverted(sc, pin_num))
+				pin->gp_flags |= (GPIO_PIN_INVIN | GPIO_PIN_INVOUT);
+		}
+	}
+	NCT_VERBOSE_PRINTF(dev, "%d pins available\n", sc->npins);
 
 	/*
 	 * Caching input values is meaningless as an input can be changed at any
@@ -514,39 +745,18 @@ nct_attach(device_t dev)
 	 * |   1 |         1 |       1 |
 	 * |-----+-----------+---------|
 	 */
-	sc->cache.out[0] = nct_read_reg(sc, REG_DAT, 0);
-	sc->cache.out[1] = nct_read_reg(sc, REG_DAT, 1);
-	sc->cache.out_known[0] = ~sc->cache.ior[0];
-	sc->cache.out_known[1] = ~sc->cache.ior[1];
-
-	for (i = 0; i <= NCT_MAX_PIN; i++) {
-		struct gpio_pin *pin;
-
-		pin = &sc->pins[i];
-		pin->gp_pin = i;
-		pin->gp_caps = NCT_GPIO_CAPS;
-		pin->gp_flags = 0;
-
-		snprintf(pin->gp_name, GPIOMAXNAME, "GPIO%02o", i);
-		pin->gp_name[GPIOMAXNAME - 1] = '\0';
-
-		if (nct_pin_is_input(sc, i))
-			pin->gp_flags |= GPIO_PIN_INPUT;
-		else
-			pin->gp_flags |= GPIO_PIN_OUTPUT;
-
-		if (nct_pin_is_opendrain(sc, i))
-			pin->gp_flags |= GPIO_PIN_OPENDRAIN;
-		else
-			pin->gp_flags |= GPIO_PIN_PUSHPULL;
-
-		if (nct_pin_is_inverted(sc, i))
-			pin->gp_flags |= (GPIO_PIN_INVIN | GPIO_PIN_INVOUT);
+	for (g = 0, gp = sc->nctdevp->groups; g < sc->nctdevp->ngroups; g++, gp++) {
+		sc->cache.inv[gp->grpnum]       = nct_read_reg(sc, REG_INV, gp->grpnum);
+		sc->cache.ior[gp->grpnum]       = nct_read_reg(sc, REG_IOR, gp->grpnum);
+		sc->cache.out[gp->grpnum]       = nct_read_reg(sc, REG_DAT, gp->grpnum);
+		sc->cache.out_known[gp->grpnum] = ~sc->cache.ior[gp->grpnum];
 	}
+
 	GPIO_UNLOCK(sc);
 
 	sc->busdev = gpiobus_attach_bus(dev);
 	if (sc->busdev == NULL) {
+		device_printf(dev, "failed to attach to gpiobus\n");
 		GPIO_LOCK_DESTROY(sc);
 		return (ENXIO);
 	}
@@ -581,10 +791,12 @@ nct_gpio_get_bus(device_t dev)
 }
 
 static int
-nct_gpio_pin_max(device_t dev, int *npins)
+nct_gpio_pin_max(device_t dev, int *maxpin)
 {
-	*npins = NCT_MAX_PIN;
+	struct nct_softc *sc;
 
+	sc      = device_get_softc(dev);
+	*maxpin = sc->npins - 1;
 	return (0);
 }
 
@@ -593,10 +805,11 @@ nct_gpio_pin_set(device_t dev, uint32_t pin_num, uint32_t pin_value)
 {
 	struct nct_softc *sc;
 
-	if (!NCT_IS_VALID_PIN(pin_num))
+	sc = device_get_softc(dev);
+
+	if (!NCT_PIN_IS_VALID(sc, pin_num))
 		return (EINVAL);
 
-	sc = device_get_softc(dev);
 	GPIO_LOCK(sc);
 	if ((sc->pins[pin_num].gp_flags & GPIO_PIN_OUTPUT) == 0) {
 		GPIO_UNLOCK(sc);
@@ -613,10 +826,11 @@ nct_gpio_pin_get(device_t dev, uint32_t pin_num, uint32_t *pin_value)
 {
 	struct nct_softc *sc;
 
-	if (!NCT_IS_VALID_PIN(pin_num))
+	sc = device_get_softc(dev);
+
+	if (!NCT_PIN_IS_VALID(sc, pin_num))
 		return (EINVAL);
 
-	sc = device_get_softc(dev);
 	GPIO_ASSERT_UNLOCKED(sc);
 	GPIO_LOCK(sc);
 	*pin_value = nct_read_pin(sc, pin_num);
@@ -630,10 +844,11 @@ nct_gpio_pin_toggle(device_t dev, uint32_t pin_num)
 {
 	struct nct_softc *sc;
 
-	if (!NCT_IS_VALID_PIN(pin_num))
+	sc = device_get_softc(dev);
+
+	if (!NCT_PIN_IS_VALID(sc, pin_num))
 		return (EINVAL);
 
-	sc = device_get_softc(dev);
 	GPIO_ASSERT_UNLOCKED(sc);
 	GPIO_LOCK(sc);
 	if ((sc->pins[pin_num].gp_flags & GPIO_PIN_OUTPUT) == 0) {
@@ -655,10 +870,11 @@ nct_gpio_pin_getcaps(device_t dev, uint32_t pin_num, uint32_t *caps)
 {
 	struct nct_softc *sc;
 
-	if (!NCT_IS_VALID_PIN(pin_num))
+	sc = device_get_softc(dev);
+
+	if (!NCT_PIN_IS_VALID(sc, pin_num))
 		return (EINVAL);
 
-	sc = device_get_softc(dev);
 	GPIO_ASSERT_UNLOCKED(sc);
 	GPIO_LOCK(sc);
 	*caps = sc->pins[pin_num].gp_caps;
@@ -672,10 +888,11 @@ nct_gpio_pin_getflags(device_t dev, uint32_t pin_num, uint32_t *flags)
 {
 	struct nct_softc *sc;
 
-	if (!NCT_IS_VALID_PIN(pin_num))
+	sc = device_get_softc(dev);
+
+	if (!NCT_PIN_IS_VALID(sc, pin_num))
 		return (EINVAL);
 
-	sc = device_get_softc(dev);
 	GPIO_ASSERT_UNLOCKED(sc);
 	GPIO_LOCK(sc);
 	*flags = sc->pins[pin_num].gp_flags;
@@ -689,10 +906,11 @@ nct_gpio_pin_getname(device_t dev, uint32_t pin_num, char *name)
 {
 	struct nct_softc *sc;
 
-	if (!NCT_IS_VALID_PIN(pin_num))
+	sc = device_get_softc(dev);
+
+	if (!NCT_PIN_IS_VALID(sc, pin_num))
 		return (EINVAL);
 
-	sc = device_get_softc(dev);
 	GPIO_ASSERT_UNLOCKED(sc);
 	GPIO_LOCK(sc);
 	memcpy(name, sc->pins[pin_num].gp_name, GPIOMAXNAME);
@@ -707,10 +925,11 @@ nct_gpio_pin_setflags(device_t dev, uint32_t pin_num, uint32_t flags)
 	struct nct_softc *sc;
 	struct gpio_pin *pin;
 
-	if (!NCT_IS_VALID_PIN(pin_num))
+	sc = device_get_softc(dev);
+
+	if (!NCT_PIN_IS_VALID(sc, pin_num))
 		return (EINVAL);
 
-	sc = device_get_softc(dev);
 	pin = &sc->pins[pin_num];
 	if ((flags & pin->gp_caps) != flags)
 		return (EINVAL);
@@ -730,17 +949,24 @@ nct_gpio_pin_setflags(device_t dev, uint32_t pin_num, uint32_t flags)
 
 	GPIO_ASSERT_UNLOCKED(sc);
 	GPIO_LOCK(sc);
*** 29 LINES SKIPPED ***



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?202307011720.361HKkA7066350>