Page MenuHomeFreeBSD

D37897.diff
No OneTemporary

D37897.diff

diff --git a/share/man/man4/nctgpio.4 b/share/man/man4/nctgpio.4
new file mode 100644
--- /dev/null
+++ b/share/man/man4/nctgpio.4
@@ -0,0 +1,51 @@
+.\" $FreeBSD$
+.\"
+.Dd Jan 11, 2021
+.Dt NCTGPIO 4
+.Os
+.Sh NAME
+.Nm nctgpio
+.Nd GPIO controller on Nuvoton and Winbond Super I/O
+.Sh SYNOPSIS
+.Cd "device gpio"
+.Cd "device nctgpio"
+.Cd "device superio"
+.Sh DESCRIPTION
+The
+.Nm
+is a driver for GPIO controller that can be found in Nuvoton and Winbond Super I/O chips.
+.Pp
+The
+.Nm
+driver supports the following chips:
+.Pp
+.Bl -bullet -compact
+.It
+Nuvoton NCT5104D
+.It
+Nuvoton NCT5104D (PC-Engines APU)
+.It
+Nuvoton NCT5104D (PC-Engines APU3)
+.It
+Nuvoton NCT6112D/NCT6114D/NCT6116D
+.It
+Nuvoton NCT6779D
+.It
+Winbond 83627DHG IC ver. 5
+.El
+
+.Sh SEE ALSO
+.Xr gpio 3 ,
+.Xr gpio 4 ,
+.Xr gpioctl 8
+.Sh HISTORY
+The driver first appeared in
+.Fx 11.0 .
+And the
+manual page first appeared in
+.Fx 13.0 .
+.Sh AUTHORS
+The driver was initially written by
+.An Daniel Wyatt Aq Mt daniel@dewyatt.com .
+This man page was written by
+.An Stéphane Rochoy Aq Mt stephane.rochoy@stormshield.eu .
diff --git a/sys/dev/nctgpio/nctgpio.c b/sys/dev/nctgpio/nctgpio.c
--- a/sys/dev/nctgpio/nctgpio.c
+++ b/sys/dev/nctgpio/nctgpio.c
@@ -29,7 +29,6 @@
/*
* Nuvoton GPIO driver.
- *
*/
#include <sys/cdefs.h>
@@ -51,20 +50,6 @@
#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
@@ -75,15 +60,26 @@
#define NCT_IO_DAT 2
#define NCT_IO_INV 3
-#define NCT_MAX_PIN 15
-#define NCT_IS_VALID_PIN(_p) ((_p) >= 0 && (_p) <= NCT_MAX_PIN)
+#define NCT_MAX_GPIO_FUNCTIONS 4
+#define NCT_MAX_GROUP 8
+#define NCT_MAX_PIN 71
-#define NCT_PIN_BIT(_p) (1 << ((_p) & 7))
+#define NCT_PIN_IS_VALID(_sc, _p) ((_sc)->nctdevp->fpn <= (_p) && \
+ (_p) < (_sc->nctdevp->fpn) + (_sc)->npins)
+#define NCT_PIN_BIT(_p) (1 << ((_p) & 7))
+#define NCT_PIN_GETGROUP(_p) ((_p) >> 3)
+#define NCT_PIN_GETBIT(_p) ((_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_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,33 @@
REG_INV = 2,
} reg_t;
+struct nct_gpio_function {
+ uint8_t enable_ldn;
+ uint8_t enable_reg;
+ uint8_t data_ldn;
+ uint32_t caps;
+ int npins;
+ int ngroups;
+ uint8_t iobase[NCT_MAX_GROUP + 1];
+ int group_base;
+};
+
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 */
};
#define GPIO_LOCK_INIT(_sc) mtx_init(&(_sc)->mtx, \
@@ -123,95 +127,291 @@
#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.
+ *
+ * Except for NCT611x devices.
+ */
+struct nct_device {
+ uint16_t devid;
+ const char *descr;
+ int ngpiof; /* Number of GPIO functions */
+ struct nct_gpio_function gpiof[NCT_MAX_GPIO_FUNCTIONS];
+ uint32_t fpn; /* First pin number */
+} nct_devices[] = {
+ {
+ .devid = 0xa025,
+ .descr = "GPIO on Winbond 83627DHG IC ver. 5",
+ .ngpiof = 2,
+ .gpiof = {
+ { /* GPIO2..GPIO5, there's no GPIO0 and GPIO1 groups */
+ .enable_ldn = 0x09,
+ .enable_reg = 0x30,
+ .data_ldn = 0x09,
+ .caps = NCT_GPIO_CAPS,
+ .npins = 32,
+ .ngroups = 4,
+ .iobase = { 0xe3, 0xf0, 0xf4, 0xe0 },
+ .group_base = 2,
+ },
+ { /* GPIO6 */
+ .enable_ldn = 0x07,
+ .enable_reg = 0x30,
+ .data_ldn = 0x07,
+ .caps = NCT_GPIO_CAPS,
+ .npins = 8,
+ .ngroups = 1,
+ .iobase = { 0xf4 },
+ .group_base = 6,
+ },
+ },
+ .fpn = 16, /* GPIO20 */
+ },
+ {
+ .devid = 0x1061,
+ .descr = "GPIO on Nuvoton NCT5104D",
+ .ngpiof = 1,
+ .gpiof = {
+ {
+ .enable_ldn = 0x07,
+ .enable_reg = 0x30,
+ .data_ldn = 0x07,
+ .caps = NCT_GPIO_CAPS,
+ .npins = 16,
+ .ngroups = 2,
+ .iobase = { 0xe0, 0xe4 },
+ .group_base = 0,
+ },
+ },
+ .fpn = 0, /* GPIO00 */
+ },
+ {
+ .devid = 0xc452, /* FIXME Conflict with Nuvoton NCT6106D. See NetBSD's nct_match. */
+ .descr = "GPIO on Nuvoton NCT5104D (PC-Engines APU)",
+ .ngpiof = 1,
+ .gpiof = {
+ {
+ .enable_ldn = 0x07,
+ .enable_reg = 0x30,
+ .data_ldn = 0x07,
+ .caps = NCT_GPIO_CAPS,
+ .npins = 16,
+ .ngroups = 2,
+ .iobase = { 0xe0, 0xe4 },
+ .group_base = 0,
+ },
+ },
+ .fpn = 0, /* GPIO00 */
+ },
{
- .chip_id = 0x1061,
- .descr = "Nuvoton NCT5104D",
+ .devid = 0xc453,
+ .descr = "GPIO on Nuvoton NCT5104D (PC-Engines APU3)",
+ .ngpiof = 1,
+ .gpiof = {
+ {
+ .enable_ldn = 0x07,
+ .enable_reg = 0x30,
+ .data_ldn = 0x07,
+ .caps = NCT_GPIO_CAPS,
+ .npins = 16,
+ .ngroups = 2,
+ .iobase = { 0xe0, 0xe4 },
+ .group_base = 0,
+ },
+ },
+ .fpn = 0, /* GPIO00 */
},
{
- .chip_id = 0xc452,
- .descr = "Nuvoton NCT5104D (PC-Engines APU)",
+ .devid = 0xc562,
+ .descr = "GPIO on Nuvoton NCT6779D",
+ .ngpiof = 4,
+ .gpiof = {
+ { /* GPIO0 */
+ .enable_ldn = 0x08,
+ .enable_reg = 0x30,
+ .data_ldn = 0x08,
+ .caps = NCT_GPIO_CAPS,
+ .npins = 8,
+ .ngroups = 1,
+ .iobase = { 0xe0 },
+ .group_base = 0,
+ },
+ { /* GPIO1 */
+ .enable_ldn = 0x09,
+ .enable_reg = 0x30,
+ .data_ldn = 0x08,
+ .caps = NCT_GPIO_CAPS,
+ .npins = 8,
+ .ngroups = 1,
+ .iobase = { 0xf0 },
+ .group_base = 1,
+ },
+ { /* GPIO2..GPIO5 */
+ .enable_ldn = 0x09,
+ .enable_reg = 0x30,
+ .data_ldn = 0x09,
+ .caps = NCT_GPIO_CAPS,
+ .npins = 32,
+ .ngroups = 4,
+ .iobase = { 0xe0, 0xe4 /* Only 7 pins in GPIO3 */, 0xf0, 0xf4 },
+ .group_base = 2,
+ },
+ { /* GPIO6..GPIO8 */
+ .enable_ldn = 0x09,
+ .enable_reg = 0x30,
+ .data_ldn = 0x07,
+ .caps = NCT_GPIO_CAPS,
+ .npins = 24,
+ .ngroups = 3,
+ .iobase = { 0xf4, 0xe0 /* Only 7 pins in GPIO7 */, 0xe4 },
+ .group_base = 6,
+ },
+ },
+ .fpn = 0, /* GPIO00 */
},
{
- .chip_id = 0xc453,
- .descr = "Nuvoton NCT5104D (PC-Engines APU3)",
+ .devid = 0xd282,
+ .descr = "GPIO on Nuvoton NCT6112D/NCT6114D/NCT6116D",
+ .ngpiof = 2,
+ .gpiof = {
+ { /* GPIO0..GPIO7 */
+ .enable_ldn = 0x07,
+ .enable_reg = 0x30,
+ .data_ldn = 0x07,
+ .caps = NCT_GPIO_CAPS,
+ .npins = 64,
+ .ngroups = 8,
+ .iobase = { 0xe0, 0xe4, 0xe8, 0xec, 0xf0, 0xf4, 0xf8, 0xfc },
+ .group_base = 0,
+ },
+ { /* GPIO8 */
+ .enable_ldn = 0x09,
+ .enable_reg = 0x30,
+ .data_ldn = 0x09,
+ .caps = NCT_GPIO_CAPS,
+ .npins = 8,
+ .ngroups = 1,
+ .iobase = { 0xf0 },
+ .group_base = 8,
+ },
+ },
+ .fpn = 0, /* GPIO00 */
},
};
-static void
-nct_io_set_group(struct nct_softc *sc, int group)
+static struct nct_gpio_function *
+nct_lookup_function(struct nct_softc *sc, uint8_t group)
{
+ struct nct_gpio_function *fp;
+ int f;
+
+ for (f = 0, fp = sc->nctdevp->gpiof; f < sc->nctdevp->ngpiof; f++, fp++) {
+ if (group < fp->group_base ||
+ fp->group_base + fp->ngroups - 1 < group)
+ continue;
+ return (fp);
+ }
+ return (NULL);
+}
- GPIO_ASSERT_LOCKED(sc);
- if (group != sc->curgrp) {
- bus_write_1(sc->iores, NCT_IO_GSR, group);
- sc->curgrp = 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");
+ default: return ("?");
}
}
+static void
+nct_io_set_group(struct nct_softc *sc, uint8_t group)
+{
+ GPIO_ASSERT_LOCKED(sc);
+
+ if (group == sc->curgrp)
+ return;
+
+ NCT_VERBOSE_PRINTF(sc->dev, "write %s 0x%x ioport %d iores %p\n",
+ io2str(NCT_IO_GSR), group, NCT_IO_GSR, sc->iores);
+ bus_write_1(sc->iores, NCT_IO_GSR, group);
+ sc->curgrp = group;
+}
+
static uint8_t
-nct_io_read(struct nct_softc *sc, int group, uint8_t reg)
+nct_io_read(struct nct_softc *sc, uint8_t group, uint8_t reg)
{
+ uint8_t val;
+
nct_io_set_group(sc, group);
- return (bus_read_1(sc->iores, reg));
+
+ val = bus_read_1(sc->iores, reg);
+ NCT_VERBOSE_PRINTF(sc->dev, "read %s 0x%x ioport %d iores %p\n",
+ io2str(reg), val, reg, sc->iores);
+ 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 group, uint8_t reg, uint8_t val)
{
nct_io_set_group(sc, group);
- return (bus_write_1(sc->iores, reg, val));
+
+ NCT_VERBOSE_PRINTF(sc->dev, "write %s 0x%x ioport %d iores %p\n",
+ io2str(reg), val, reg, sc->iores);
+ 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 group)
{
uint8_t ioreg;
if (sc->iores != NULL)
ioreg = NCT_IO_IOR + reg;
- else if (group == 0)
- ioreg = NCT_LD7_GPIO0_IOR + reg;
- else
- ioreg = NCT_LD7_GPIO1_IOR + reg;
+ else {
+ struct nct_gpio_function *fp;
+
+ fp = nct_lookup_function(sc, group);
+ ioreg = fp->iobase[group - fp->group_base] + reg;
+ }
return (ioreg);
}
+static const char *
+reg2str(reg_t reg)
+{
+ switch (reg) {
+ case REG_IOR: return ("io");
+ case REG_DAT: return ("data");
+ case REG_INV: return ("inv");
+ default: return ("?");
+ }
+}
+
static uint8_t
-nct_read_reg(struct nct_softc *sc, reg_t reg, int group)
+nct_read_reg(struct nct_softc *sc, reg_t reg, uint8_t group)
{
- uint8_t ioreg;
- uint8_t val;
+ struct nct_gpio_function *fp;
+ 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 (nct_io_read(sc, group, ioreg));
+ fp = nct_lookup_function(sc, group);
+ val = superio_ldn_read(sc->dev, fp->data_ldn, ioreg);
+ NCT_VERBOSE_PRINTF(sc->dev, "read %s 0x%x from group GPIO%u ioreg 0x%x<group=%u,reg=%u> iores %p\n",
+ reg2str(reg), val, group, ioreg, group, reg, sc->iores);
return (val);
}
-#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)
-{
- uint8_t bit;
- uint8_t group;
- uint8_t val;
-
- KASSERT(NCT_IS_VALID_PIN(pin_num), ("%s: invalid pin number %d",
- __func__, pin_num));
-
- group = pin_num >> 3;
- bit = pin_num & 7;
- val = nct_read_reg(sc, reg, group);
- return (GET_BIT(val, bit));
-}
-
static int
nct_get_pin_cache(struct nct_softc *sc, uint32_t pin_num, uint8_t *cache)
{
@@ -219,25 +419,33 @@
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_GETGROUP(pin_num);
+ bit = NCT_PIN_GETBIT(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 group, uint8_t val)
{
- uint8_t ioreg;
+ struct nct_gpio_function *fp;
+ uint8_t ioreg;
ioreg = nct_get_ioreg(sc, reg, group);
- if (sc->iores != NULL)
+
+ if (sc->iores != NULL) {
nct_io_write(sc, group, ioreg, val);
- else
- superio_write(sc->dev, ioreg, val);
+ return;
+ }
+
+ fp = nct_lookup_function(sc, group);
+ superio_ldn_write(sc->dev, fp->data_ldn, ioreg, val);
+
+ NCT_VERBOSE_PRINTF(sc->dev, "write %s 0x%x to group GPIO%u ioreg 0x%x<group=%u,reg=%u> iores %p\n",
+ reg2str(reg), val, group, ioreg, group, reg, sc->iores);
}
static void
@@ -249,14 +457,14 @@
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_GETGROUP(pin_num);
+ bit = NCT_PIN_GETBIT(pin_num);
+ mask = (uint8_t)1 << bit;
bitval = (uint8_t)val << bit;
if (reg == REG_IOR)
@@ -317,8 +525,9 @@
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_GETGROUP(pin_num);
+ bit = NCT_PIN_GETBIT(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 +541,35 @@
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_GETGROUP(pin_num);
+ bit = NCT_PIN_GETBIT(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 +580,24 @@
{
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_GETGROUP(pin_num);
+ bit = NCT_PIN_GETBIT(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;
@@ -364,9 +611,7 @@
static uint8_t
nct_outcfg_addr(uint32_t pin_num)
{
- KASSERT(NCT_IS_VALID_PIN(pin_num), ("%s: invalid pin number %d",
- __func__, pin_num));
- if ((pin_num >> 3) == 0)
+ if (NCT_PIN_GETGROUP(pin_num) == 0)
return (NCT_LDF_GPIO0_OUTCFG);
else
return (NCT_LDF_GPIO1_OUTCFG);
@@ -384,9 +629,9 @@
uint8_t outcfg;
reg = nct_outcfg_addr(pin_num);
- outcfg = superio_read(sc->dev_f, reg);
+ outcfg = superio_ldn_read(sc->dev, 0xf, reg);
outcfg |= NCT_PIN_BIT(pin_num);
- superio_write(sc->dev_f, reg, outcfg);
+ superio_ldn_write(sc->dev, 0xf, reg, outcfg);
}
static void
@@ -396,9 +641,9 @@
uint8_t outcfg;
reg = nct_outcfg_addr(pin_num);
- outcfg = superio_read(sc->dev_f, reg);
+ outcfg = superio_ldn_read(sc->dev, 0xf, reg);
outcfg &= ~NCT_PIN_BIT(pin_num);
- superio_write(sc->dev_f, reg, outcfg);
+ superio_ldn_write(sc->dev, 0xf, reg, outcfg);
}
static bool
@@ -408,55 +653,67 @@
uint8_t outcfg;
reg = nct_outcfg_addr(pin_num);
- outcfg = superio_read(sc->dev_f, reg);
+ outcfg = superio_ldn_read(sc->dev, 0xf, reg);
return (outcfg & NCT_PIN_BIT(pin_num));
}
+static struct nct_device *
+nct_lookup_device(device_t dev)
+{
+ struct nct_device *nctdevp;
+ int i;
+ uint16_t devid;
+
+ devid = superio_devid(dev);
+ for (i = 0, nctdevp = nct_devices; i < nitems(nct_devices); i++, nctdevp++) {
+ if (devid == nctdevp->devid)
+ return (nctdevp);
+ }
+ return (NULL);
+}
+
static int
nct_probe(device_t dev)
{
- int j;
- uint16_t chipid;
+ struct nct_device *nctdevp;
+ uint8_t ldn;
+
+ ldn = superio_get_ldn(dev);
- if (superio_vendor(dev) != SUPERIO_VENDOR_NUVOTON)
+ 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;
+ struct nct_gpio_function *fp;
device_t dev_8;
+ uint32_t pin_num;
uint16_t iobase;
+ uint8_t enable, v;
int err;
- int i;
+ int i, f, g;
- 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);
- }
+
+ sc = device_get_softc(dev);
+ sc->dev = dev;
+ sc->nctdevp = nct_lookup_device(dev);
/*
* As strange as it may seem, I/O port base is configured in the
@@ -477,76 +734,97 @@
&sc->iorid, RF_ACTIVE);
if (sc->iores == NULL) {
device_printf(dev, "can't map i/o space, "
- "iobase=0x%04x\n", iobase);
+ "iobase=%#x\n", iobase);
}
} else {
device_printf(dev,
- "failed to set io port resource at 0x%x\n", iobase);
+ "failed to set io port resource at %#x\n", iobase);
}
}
-
- /* Enable gpio0 and gpio1. */
- superio_dev_enable(dev, 0x03);
+ NCT_VERBOSE_PRINTF(dev, "iobase %#x iores %p\n", iobase, sc->iores);
+
+ /* Enable GPIO groups */
+ for (f = 0, fp = sc->nctdevp->gpiof; f < sc->nctdevp->ngpiof; f++, fp++) {
+ NCT_VERBOSE_PRINTF(dev, "function %d: %d groups GPIO%u..GPIO%u\n",
+ f, fp->ngroups, fp->group_base,
+ fp->group_base + fp->ngroups - 1);
+ NCT_VERBOSE_PRINTF(dev, "function %d: %d pins\n", f, fp->npins);
+
+ enable = (1 << fp->ngroups) - 1;
+ NCT_VERBOSE_PRINTF(dev, "function %d: enable 0x%x via ldn 0x%x reg 0x%x\n",
+ f, enable, fp->enable_ldn, fp->enable_reg);
+ v = superio_ldn_read(dev, fp->enable_ldn, fp->enable_reg);
+ v |= enable;
+ superio_ldn_write(dev, fp->enable_ldn, fp->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);
-
- /*
- * Caching input values is meaningless as an input can be changed at any
- * time by an external agent. But outputs are controlled by this
- * driver, so it can cache their state. Also, the hardware remembers
- * the output state of a pin when the pin is switched to input mode and
- * then back to output mode. So, the cache stays valid.
- * The only problem is with pins that are in input mode at the attach
- * time. For them the output state is not known until it is set by the
- * driver for the first time.
- * 'out' and 'out_known' bits form a tri-state output cache:
- * |-----+-----------+---------|
- * | out | out_known | cache |
- * |-----+-----------+---------|
- * | X | 0 | invalid |
- * | 0 | 1 | 0 |
- * | 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];
+ pin_num = sc->nctdevp->fpn;
+ NCT_VERBOSE_PRINTF(sc->dev, "first pin is %d<GPIO%u%u>\n",
+ pin_num, NCT_PIN_GETGROUP(pin_num), NCT_PIN_GETBIT(pin_num));
+
+ sc->npins = 0;
+ for (f = 0, fp = sc->nctdevp->gpiof; f < sc->nctdevp->ngpiof; f++, fp++) {
+
+ /*
+ * Caching input values is meaningless as an input can be changed at any
+ * time by an external agent. But outputs are controlled by this
+ * driver, so it can cache their state. Also, the hardware remembers
+ * the output state of a pin when the pin is switched to input mode and
+ * then back to output mode. So, the cache stays valid.
+ * The only problem is with pins that are in input mode at the attach
+ * time. For them the output state is not known until it is set by the
+ * driver for the first time.
+ * 'out' and 'out_known' bits form a tri-state output cache:
+ * |-----+-----------+---------|
+ * | out | out_known | cache |
+ * |-----+-----------+---------|
+ * | X | 0 | invalid |
+ * | 0 | 1 | 0 |
+ * | 1 | 1 | 1 |
+ * |-----+-----------+---------|
+ */
+ for (g = fp->group_base; g < fp->group_base + fp->ngroups; g++) {
+ sc->cache.inv[g] = nct_read_reg(sc, REG_INV, g);
+ sc->cache.ior[g] = nct_read_reg(sc, REG_IOR, g);
+ sc->cache.out[g] = nct_read_reg(sc, REG_DAT, g);
+ sc->cache.out_known[g] = ~sc->cache.ior[g];
+ }
- for (i = 0; i <= NCT_MAX_PIN; i++) {
- struct gpio_pin *pin;
+ sc->npins += fp->npins;
+ for (i = 0; i < fp->npins; i++, pin_num++) {
+ struct gpio_pin *pin;
- pin = &sc->pins[i];
- pin->gp_pin = i;
- pin->gp_caps = NCT_GPIO_CAPS;
- pin->gp_flags = 0;
+ pin = &sc->pins[pin_num];
+ pin->gp_pin = pin_num;
+ pin->gp_caps = fp->caps;
+ pin->gp_flags = 0;
- snprintf(pin->gp_name, GPIOMAXNAME, "GPIO%02o", i);
- pin->gp_name[GPIOMAXNAME - 1] = '\0';
+ snprintf(pin->gp_name, GPIOMAXNAME, "GPIO%u%u",
+ NCT_PIN_GETGROUP(pin_num), NCT_PIN_GETBIT(pin_num));
- if (nct_pin_is_input(sc, i))
- pin->gp_flags |= GPIO_PIN_INPUT;
- else
- pin->gp_flags |= GPIO_PIN_OUTPUT;
+ 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, i))
- pin->gp_flags |= GPIO_PIN_OPENDRAIN;
- else
- pin->gp_flags |= GPIO_PIN_PUSHPULL;
+ 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, i))
- pin->gp_flags |= (GPIO_PIN_INVIN | GPIO_PIN_INVOUT);
+ 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);
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 +859,12 @@
}
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->nctdevp->fpn + sc->npins - 1;
return (0);
}
@@ -593,10 +873,11 @@
{
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 +894,11 @@
{
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 +912,11 @@
{
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 +938,11 @@
{
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 +956,11 @@
{
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 +974,11 @@
{
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 +993,11 @@
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);
@@ -785,4 +1072,3 @@
MODULE_DEPEND(nctgpio, gpiobus, 1, 1, 1);
MODULE_DEPEND(nctgpio, superio, 1, 1, 1);
MODULE_VERSION(nctgpio, 1);
-
diff --git a/sys/dev/superio/superio.h b/sys/dev/superio/superio.h
--- a/sys/dev/superio/superio.h
+++ b/sys/dev/superio/superio.h
@@ -50,7 +50,9 @@
uint16_t superio_devid(device_t dev);
uint8_t superio_revid(device_t dev);
uint8_t superio_read(device_t dev, uint8_t reg);
+uint8_t superio_ldn_read(device_t dev, uint8_t ldn, uint8_t reg);
void superio_write(device_t dev, uint8_t reg, uint8_t val);
+void superio_ldn_write(device_t dev, uint8_t ldn, uint8_t reg, uint8_t val);
bool superio_dev_enabled(device_t dev, uint8_t mask);
void superio_dev_enable(device_t dev, uint8_t mask);
void superio_dev_disable(device_t dev, uint8_t mask);
diff --git a/sys/dev/superio/superio.c b/sys/dev/superio/superio.c
--- a/sys/dev/superio/superio.c
+++ b/sys/dev/superio/superio.c
@@ -270,6 +270,12 @@
{ .type = SUPERIO_DEV_NONE },
};
+const struct sio_device w83627_devices[] = {
+ { .ldn = 8, .type = SUPERIO_DEV_WDT },
+ { .ldn = 9, .type = SUPERIO_DEV_GPIO },
+ { .type = SUPERIO_DEV_NONE },
+};
+
const struct sio_device nvt_devices[] = {
{ .ldn = 8, .type = SUPERIO_DEV_WDT },
{ .type = SUPERIO_DEV_NONE },
@@ -282,6 +288,18 @@
{ .type = SUPERIO_DEV_NONE },
};
+const struct sio_device nct611x_devices[] = {
+ { .ldn = 0x7, .type = SUPERIO_DEV_GPIO },
+ { .ldn = 0x8, .type = SUPERIO_DEV_WDT },
+ { .type = SUPERIO_DEV_NONE },
+};
+
+const struct sio_device nct6779_devices[] = {
+ { .ldn = 0x8, .type = SUPERIO_DEV_WDT },
+ { .ldn = 0x9, .type = SUPERIO_DEV_GPIO },
+ { .type = SUPERIO_DEV_NONE },
+};
+
const struct sio_device fintek_devices[] = {
{ .ldn = 7, .type = SUPERIO_DEV_WDT },
{ .type = SUPERIO_DEV_NONE },
@@ -374,7 +392,7 @@
{
.vendor = SUPERIO_VENDOR_NUVOTON, .devid = 0xa000, .mask = 0xff,
.descr = "Winbond 83627DHG",
- .devices = nvt_devices,
+ .devices = w83627_devices,
},
{
.vendor = SUPERIO_VENDOR_NUVOTON, .devid = 0xa200, .mask = 0xff,
@@ -412,9 +430,9 @@
.devices = nct5104_devices,
},
{
- .vendor = SUPERIO_VENDOR_NUVOTON, .devid = 0xc500, .mask = 0xff,
- .descr = "Nuvoton NCT6779",
- .devices = nvt_devices,
+ .vendor = SUPERIO_VENDOR_NUVOTON, .devid = 0xc500, .mask = 0xff,
+ .descr = "Nuvoton NCT6779D",
+ .devices = nct6779_devices,
},
{
.vendor = SUPERIO_VENDOR_NUVOTON, .devid = 0xc800, .mask = 0xff,
@@ -431,6 +449,11 @@
.descr = "Nuvoton NCT6793",
.devices = nvt_devices,
},
+ {
+ .vendor = SUPERIO_VENDOR_NUVOTON, .devid = 0xd200, .mask = 0xff,
+ .descr = "Nuvoton NCT6112D/NCT6114D/NCT6116D",
+ .devices = nct611x_devices,
+ },
{
.vendor = SUPERIO_VENDOR_NUVOTON, .devid = 0xd300, .mask = 0xff,
.descr = "Nuvoton NCT6795",
@@ -553,7 +576,7 @@
sc->vendor == SUPERIO_VENDOR_NUVOTON,
("Only ITE and Nuvoton SuperIO-s are supported"));
sc->ldn_reg = 0x07;
- sc->enable_reg = 0x30;
+ sc->enable_reg = 0x30; /* FIXME enable_reg not used by nctgpio(4). */
sc->current_ldn = 0xff; /* no device should have this */
if (superio_table[i].descr != NULL) {
@@ -866,30 +889,44 @@
return (sc->revid);
}
+uint8_t
+superio_ldn_read(device_t dev, uint8_t ldn, uint8_t reg)
+{
+ device_t sio_dev = device_get_parent(dev);
+ struct siosc *sc = device_get_softc(sio_dev);
+ uint8_t v;
+
+ sio_conf_enter(sc);
+ v = sio_ldn_read(sc, ldn, reg);
+ sio_conf_exit(sc);
+ return (v);
+}
+
uint8_t
superio_read(device_t dev, uint8_t reg)
{
- device_t sio_dev = device_get_parent(dev);
- struct siosc *sc = device_get_softc(sio_dev);
struct superio_devinfo *dinfo = device_get_ivars(dev);
- uint8_t v;
+
+ return (superio_ldn_read(dev, dinfo->ldn, reg));
+}
+
+void
+superio_ldn_write(device_t dev, uint8_t ldn, uint8_t reg, uint8_t val)
+{
+ device_t sio_dev = device_get_parent(dev);
+ struct siosc *sc = device_get_softc(sio_dev);
sio_conf_enter(sc);
- v = sio_ldn_read(sc, dinfo->ldn, reg);
+ sio_ldn_write(sc, ldn, reg, val);
sio_conf_exit(sc);
- return (v);
}
void
superio_write(device_t dev, uint8_t reg, uint8_t val)
{
- device_t sio_dev = device_get_parent(dev);
- struct siosc *sc = device_get_softc(sio_dev);
struct superio_devinfo *dinfo = device_get_ivars(dev);
- sio_conf_enter(sc);
- sio_ldn_write(sc, dinfo->ldn, reg, val);
- sio_conf_exit(sc);
+ return (superio_ldn_write(dev, dinfo->ldn, reg, val));
}
bool
@@ -904,7 +941,7 @@
if (sc->vendor == SUPERIO_VENDOR_ITE && dinfo->ldn == 7)
return (true);
- v = superio_read(dev, sc->enable_reg);
+ v = superio_read(dev, sc->enable_reg); /* FIXME enable_reg not used by nctgpio(4). */
return ((v & mask) != 0);
}
diff --git a/usr.sbin/gpioctl/gpioctl.c b/usr.sbin/gpioctl/gpioctl.c
--- a/usr.sbin/gpioctl/gpioctl.c
+++ b/usr.sbin/gpioctl/gpioctl.c
@@ -160,6 +160,9 @@
for (i = 0; i <= maxpin; i++) {
pin = cfgs + i;
pinv = gpio_pin_get(handle, pin->g_pin);
+ if (pinv == -1 && !verbose) /* First pin don't always have number 0. */
+ continue;
+
printf("pin %02d:\t%d\t%s", pin->g_pin, pinv,
pin->g_name);

File Metadata

Mime Type
text/plain
Expires
Tue, Apr 21, 10:07 PM (5 h, 28 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
31914428
Default Alt Text
D37897.diff (31 KB)

Event Timeline