Page Menu
Home
FreeBSD
Search
Configure Global Search
Log In
Files
F153395448
D37897.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Flag For Later
Award Token
Size
31 KB
Referenced Files
None
Subscribers
None
D37897.diff
View Options
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
Details
Attached
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)
Attached To
Mode
D37897: nctgpio: Refactor and add support for NCT6779, NCT6116D and Winbond 83627DHG
Attached
Detach File
Event Timeline
Log In to Comment