git: 7f8d2ed03bc6 - main - superio+nctgpio: Prepare to support some new (weird) chips
- Go to: [ bottom of page ] [ top of archives ] [ this month ]
Date: Sat, 01 Jul 2023 17:20:46 UTC
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 ***