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 @@ -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 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 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\n", + b, pin_num, group, bit); + else + NCT_VERBOSE_PRINTF(sc->dev, + "read %d from output pin %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, 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\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);