On Tue, Jun 29, 2021 at 04:22:47AM +0100, Chris Narkiewicz wrote:
> On Wed, Jan 29, 2020 at 10:47:28PM +0800, Nathanael Rensen wrote:
> > The diff below adds gpio(4) support to wbsio(4) for Nuvoton NCT5104D
> > (pcengines APU2).
>
> I'm resurrecting this thread. I was looking for GPIO support for APU2
> board and found this patch in archives.
I solved the problem of controlling the LEDs in the APU2 using
a daemon started from /etc/securelevel and then writing 1-char
commands to it over a FIFO.
I also created a pull request of that solution for PCEngines'
apu_gpio_lib, as a code example, but they have not managed
to prioritize it:
https://github.com/pcengines/apu_gpio_lib/pull/4
/ Raimo Niskanen
>
> Any chance of taking it in?
>
> Patch below:
>
> > It is based on Matt Dainty's diff posted to this list
> > in November 2018:
> >
> > https://marc.info/?l=openbsd-tech&m=154134941027009&w=2
> >
> > A key difference from Matt Dainty's original diff is the use of
> > config_search() rather than config_found() to avoid problems with
> > the lm78 driver mentioned in his post.
> >
> > Nathanael
> >
> > Index: sys/dev/isa/wbsioreg.h
> > ===================================================================
> > RCS file: /cvs/src/sys/dev/isa/wbsioreg.h,v
> > retrieving revision 1.5
> > diff -u -p -r1.5 wbsioreg.h
> > --- sys/dev/isa/wbsioreg.h 17 Dec 2019 01:34:59 -0000 1.5
> > +++ sys/dev/isa/wbsioreg.h 29 Jan 2020 14:30:28 -0000
> > @@ -30,8 +30,15 @@
> >
> > /* Configuration Space Registers */
> > #define WBSIO_LDN 0x07 /* Logical Device Number */
> > +#define WBSIO_MF 0x1c /* Multi Function Selection */
> > +#define WBSIO_MF_UARTD (1 << 2)
> > +#define WBSIO_MF_UARTC (1 << 3)
> > +#define WBSIO_MF_GP67 (1 << 4)
> > #define WBSIO_ID 0x20 /* Device ID */
> > #define WBSIO_REV 0x21 /* Device Revision */
> > +#define WBSIO_CR27 0x27 /* Global Option */
> > +#define WBSIO_SF 0x2f /* Strapping Function */
> > +#define WBSIO_LDN_EN 0x30 /* Logical Device Enable */
> >
> > #define WBSIO_ID_W83627HF 0x52
> > #define WBSIO_ID_W83627THF 0x82
> > @@ -52,8 +59,38 @@
> > #define WBSIO_ID_NCT6795D 0xd3
> >
> > /* Logical Device Number (LDN) Assignments */
> > +#define WBSIO_LDN_GPIO1 0x07
> > +#define WBSIO_LDN_WDT 0x08
> > +#define WBSIO_LDN_GPIO2 0x09 /* Not used */
> > #define WBSIO_LDN_HM 0x0b
> > +#define WBSIO_LDN_GPIO3 0x0f
> >
> > /* Hardware Monitor Control Registers (LDN B) */
> > #define WBSIO_HM_ADDR_MSB 0x60 /* Address [15:8] */
> > #define WBSIO_HM_ADDR_LSB 0x61 /* Address [7:0] */
> > +
> > +/* GPIO Control Registers (LDN 7) */
> > +/* GPIOn registers are offset by n*4 bytes */
> > +#define WBSIO_GPIO_IO 0xe0 /* GPIO Direction */
> > +#define WBSIO_GPIO_DATA 0xe1 /* GPIO Data */
> > +#define WBSIO_GPIO_INVERT 0xe2 /* GPIO Invert */
> > +#define WBSIO_GPIO_STATUS 0xe3 /* GPIO Status */
> > +#define WBSIO_GPIO0_EN (1 << 0)
> > +#define WBSIO_GPIO1_EN (1 << 1)
> > +#define WBSIO_GPIO6_EN (1 << 6)
> > +#define WBSIO_GPIO0_NPINS 8
> > +#define WBSIO_GPIO1_NPINS 8
> > +#define WBSIO_GPIO6_NPINS 1
> > +#define WBSIO_GPIO_NPINS (WBSIO_GPIO0_NPINS + WBSIO_GPIO1_NPINS + \
> > + WBSIO_GPIO6_NPINS)
> > +
> > +/* WDT Control Registers (LDN 8) */
> > +#define WBSIO_WDT_ENABLE 0x30
> > +#define WBSIO_WDT_CONTROL 0xf0
> > +#define WBSIO_WDT_COUNTER 0xf1
> > +#define WBSIO_WDT_MINUTE (1 << 3)
> > +#define WBSIO_WDT_FAST (1 << 4)
> > +
> > +/* GPIO Control Registers (LDN F) */
> > +/* GPIOn register is offset by n bytes */
> > +#define WBSIO_GPIO_PP_OD 0xe0 /* GPIO Push-Pull/Open Drain */
> > Index: sys/dev/isa/wbsio.c
> > ===================================================================
> > RCS file: /cvs/src/sys/dev/isa/wbsio.c,v
> > retrieving revision 1.11
> > diff -u -p -r1.11 wbsio.c
> > --- sys/dev/isa/wbsio.c 17 Dec 2019 01:34:59 -0000 1.11
> > +++ sys/dev/isa/wbsio.c 29 Jan 2020 14:30:28 -0000
> > @@ -23,11 +23,13 @@
> > #include <sys/device.h>
> > #include <sys/kernel.h>
> > #include <sys/systm.h>
> > +#include <sys/gpio.h>
> >
> > #include <machine/bus.h>
> >
> > #include <dev/isa/isavar.h>
> > #include <dev/isa/wbsioreg.h>
> > +#include <dev/gpio/gpiovar.h>
> >
> > #ifdef WBSIO_DEBUG
> > #define DPRINTF(x) printf x
> > @@ -40,12 +42,22 @@ struct wbsio_softc {
> >
> > bus_space_tag_t sc_iot;
> > bus_space_handle_t sc_ioh;
> > +
> > + struct gpio_chipset_tag sc_gpio_gc;
> > + gpio_pin_t sc_gpio_pins[WBSIO_GPIO_NPINS];
> > };
> >
> > int wbsio_probe(struct device *, void *, void *);
> > void wbsio_attach(struct device *, struct device *, void *);
> > +int wbsio_search_cb(struct device *, void *, void *);
> > int wbsio_print(void *, const char *);
> >
> > +int wbsio_gpio_pin_to_group(struct wbsio_softc *, int);
> > +int wbsio_gpio_pin_read(struct wbsio_softc *, int);
> > +int wbsio_gpio_read(void *, int);
> > +void wbsio_gpio_write(void *, int, int);
> > +void wbsio_gpio_ctl(void *, int, int);
> > +
> > struct cfattach wbsio_ca = {
> > sizeof(struct wbsio_softc),
> > wbsio_probe,
> > @@ -56,6 +68,11 @@ struct cfdriver wbsio_cd = {
> > NULL, "wbsio", DV_DULL
> > };
> >
> > +struct wbsio_aux {
> > + struct isa_attach_args ia;
> > + struct gpiobus_attach_args gba;
> > +};
> > +
> > static __inline void
> > wbsio_conf_enable(bus_space_tag_t iot, bus_space_handle_t ioh)
> > {
> > @@ -132,9 +149,10 @@ wbsio_attach(struct device *parent, stru
> > {
> > struct wbsio_softc *sc = (void *)self;
> > struct isa_attach_args *ia = aux;
> > - struct isa_attach_args nia;
> > - u_int8_t devid, reg, reg0, reg1;
> > + struct wbsio_aux wbsio_aux;
> > + u_int8_t devid, reg, reg0, reg1, mf;
> > u_int16_t iobase;
> > + int i, npins = 0, group, gpin;
> >
> > /* Map ISA I/O space */
> > sc->sc_iot = ia->ia_iot;
> > @@ -219,17 +237,123 @@ wbsio_attach(struct device *parent, stru
> >
> > printf("\n");
> >
> > + sc->sc_gpio_gc.gp_cookie = sc;
> > + sc->sc_gpio_gc.gp_pin_read = wbsio_gpio_read;
> > + sc->sc_gpio_gc.gp_pin_write = wbsio_gpio_write;
> > + sc->sc_gpio_gc.gp_pin_ctl = wbsio_gpio_ctl;
> > +
> > + if (devid == WBSIO_ID_NCT5104D) {
> > + /* Read Multi Function Register */
> > + mf = wbsio_conf_read(sc->sc_iot, sc->sc_ioh, WBSIO_MF);
> > +
> > + /* Select GPIO logical device */
> > + wbsio_conf_write(sc->sc_iot, sc->sc_ioh,
> > + WBSIO_LDN, WBSIO_LDN_GPIO1);
> > + reg = wbsio_conf_read(sc->sc_iot, sc->sc_ioh, WBSIO_LDN_EN);
> > +
> > + /* UART C is inactive, use the pins as GPIO instead */
> > + if ((mf & WBSIO_MF_UARTC) == 0) {
> > + reg |= WBSIO_GPIO0_EN;
> > + npins += WBSIO_GPIO0_NPINS;
> > + }
> > +
> > + /* UART D is inactive, use the pins as GPIO instead */
> > + if ((mf & WBSIO_MF_UARTD) == 0) {
> > + reg |= WBSIO_GPIO1_EN;
> > + npins += WBSIO_GPIO1_NPINS;
> > + }
> > +
> > + /* GP67 is available, use it as a GPIO pin */
> > + if (((wbsio_conf_read(sc->sc_iot, sc->sc_ioh, WBSIO_CR27) &
> > + (1 << 2)) == 0) && (mf & WBSIO_MF_GP67)) {
> > + reg |= WBSIO_GPIO6_EN;
> > + npins += WBSIO_GPIO6_NPINS;
> > + }
> > + }
> > +
> > + /* Enable the GPIO pins */
> > + if (npins > 0)
> > + wbsio_conf_write(sc->sc_iot, sc->sc_ioh, WBSIO_LDN_EN, reg);
> > +
> > + for (i = 0; i < npins; i++) {
> > + sc->sc_gpio_pins[i].pin_num = i;
> > + sc->sc_gpio_pins[i].pin_caps = GPIO_PIN_INPUT |
> > + GPIO_PIN_OUTPUT | GPIO_PIN_OPENDRAIN | GPIO_PIN_PUSHPULL |
> > + GPIO_PIN_INVIN | GPIO_PIN_INVOUT;
> > +
> > + /* Work out the GPIO owning this pin */
> > + group = wbsio_gpio_pin_to_group(sc, i);
> > +
> > + /* Read existing I/O direction */
> > + reg = wbsio_conf_read(sc->sc_iot, sc->sc_ioh,
> > + WBSIO_GPIO_IO + (group << 2));
> > + gpin = reg & 1 << (i % 8);
> > + sc->sc_gpio_pins[i].pin_flags = gpin ?
> > + GPIO_PIN_INPUT : GPIO_PIN_OUTPUT;
> > +
> > + /* Read existing inversion status */
> > + reg = wbsio_conf_read(sc->sc_iot, sc->sc_ioh,
> > + WBSIO_GPIO_INVERT + (group << 2));
> > + sc->sc_gpio_pins[i].pin_flags |= (reg & (1 << (i % 8))) ?
> > + (gpin ? GPIO_PIN_INVIN : GPIO_PIN_INVOUT) : 0;
> > +
> > + /* Read existing push-pull/open drain. Note the logical
> > + * device change to read this
> > + */
> > + wbsio_conf_write(sc->sc_iot, sc->sc_ioh, WBSIO_LDN,
> > + WBSIO_LDN_GPIO3);
> > + reg = wbsio_conf_read(sc->sc_iot, sc->sc_ioh,
> > + WBSIO_GPIO_PP_OD + group);
> > + sc->sc_gpio_pins[i].pin_flags |= (reg & (1 << (i % 8))) ?
> > + GPIO_PIN_OPENDRAIN : GPIO_PIN_PUSHPULL;
> > +
> > + /* Read existing pin state switching back to the original
> > + * logical device again
> > + */
> > + wbsio_conf_write(sc->sc_iot, sc->sc_ioh, WBSIO_LDN,
> > + WBSIO_LDN_GPIO1);
> > + sc->sc_gpio_pins[i].pin_state = wbsio_gpio_pin_read(sc, i);
> > + }
> > +
> > + bzero(&wbsio_aux, sizeof(wbsio_aux));
> > + wbsio_aux.ia = *ia;
> > + wbsio_aux.ia.ia_iobase = iobase;
> > + /* pass devid to wb_match() */
> > + wbsio_aux.ia.ia_aux = (void *)(u_long)devid;
> > + wbsio_aux.gba.gba_name = "gpio";
> > + wbsio_aux.gba.gba_gc = &sc->sc_gpio_gc;
> > + wbsio_aux.gba.gba_pins = sc->sc_gpio_pins;
> > + wbsio_aux.gba.gba_npins = npins;
> > +
> > + config_search(wbsio_search_cb, self, &wbsio_aux);
> > +
> > /* Escape from configuration mode */
> > wbsio_conf_disable(sc->sc_iot, sc->sc_ioh);
> > +}
> >
> > - if (iobase == 0)
> > - return;
> > +int
> > +wbsio_search_cb(struct device *parent, void *match, void *aux)
> > +{
> > + struct wbsio_aux *wbaux = aux;
> > + struct cfdata *cf = match;
> > + cfprint_t print = NULL;
> > +
> > + aux = NULL;
> > +
> > + if (strcmp(wbaux->gba.gba_name, cf->cf_driver->cd_name) == 0) {
> > + if (wbaux->gba.gba_npins > 0) {
> > + aux = &wbaux->gba;
> > + print = gpiobus_print;
> > + }
> > + } else if (wbaux->ia.ia_iobase != 0) {
> > + aux = &wbaux->ia;
> > + print = wbsio_print;
> > + }
> >
> > - nia = *ia;
> > - nia.ia_iobase = iobase;
> > - nia.ia_aux = (void *)(u_long)devid; /* pass devid down to wb_match() */
> > + if (aux != NULL && cf->cf_attach->ca_match(parent, match, aux))
> > + config_attach(parent, match, aux, print);
> >
> > - config_found(self, &nia, wbsio_print);
> > + return 0;
> > }
> >
> > int
> > @@ -244,4 +368,150 @@ wbsio_print(void *aux, const char *pnp)
> > if (ia->ia_iosize > 1)
> > printf("/%d", ia->ia_iosize);
> > return (UNCONF);
> > +}
> > +
> > +/* Assumes the chip is unlocked and the correct logical device is selected
> > */
> > +int
> > +wbsio_gpio_pin_to_group(struct wbsio_softc *sc, int pin)
> > +{
> > + u_int8_t reg;
> > + int n = pin / 8;
> > + int group;
> > +
> > + /* Read the enabled GPIO group, mask out any other potential bits */
> > + reg = wbsio_conf_read(sc->sc_iot, sc->sc_ioh, WBSIO_LDN_EN) &
> > + (WBSIO_GPIO0_EN | WBSIO_GPIO1_EN | WBSIO_GPIO6_EN);
> > +
> > + for (group = 0; group < 8; group++) {
> > + if ((reg & 1 << group) == 0)
> > + continue;
> > + if (n == 0)
> > + break;
> > + n--;
> > + }
> > + return group;
> > +}
> > +
> > +int
> > +wbsio_gpio_pin_read(struct wbsio_softc *sc, int pin)
> > +{
> > + return (wbsio_conf_read(sc->sc_iot, sc->sc_ioh,
> > + WBSIO_GPIO_DATA + (wbsio_gpio_pin_to_group(sc, pin) << 2)) &
> > + (1 << (pin % 8))) ? GPIO_PIN_HIGH : GPIO_PIN_LOW;
> > +}
> > +
> > +int
> > +wbsio_gpio_read(void *arg, int pin)
> > +{
> > + struct wbsio_softc *sc = arg;
> > + int value;
> > +
> > + /* Enter configuration mode */
> > + wbsio_conf_enable(sc->sc_iot, sc->sc_ioh);
> > +
> > + /* Select GPIO logical device */
> > + wbsio_conf_write(sc->sc_iot, sc->sc_ioh, WBSIO_LDN, WBSIO_LDN_GPIO1);
> > +
> > + /* Read pin state */
> > + value = wbsio_gpio_pin_read(sc, pin);
> > +
> > + /* Escape from configuration mode */
> > + wbsio_conf_disable(sc->sc_iot, sc->sc_ioh);
> > +
> > + return (value);
> > +}
> > +
> > +void
> > +wbsio_gpio_write(void *arg, int pin, int value)
> > +{
> > + struct wbsio_softc *sc = arg;
> > + u_int8_t data;
> > + int group;
> > +
> > + /* Enter configuration mode */
> > + wbsio_conf_enable(sc->sc_iot, sc->sc_ioh);
> > +
> > + /* Select GPIO logical device */
> > + wbsio_conf_write(sc->sc_iot, sc->sc_ioh, WBSIO_LDN, WBSIO_LDN_GPIO1);
> > +
> > + /* Work out the GPIO owning this pin */
> > + group = wbsio_gpio_pin_to_group(sc, pin);
> > +
> > + /* Write pin state */
> > + data = wbsio_conf_read(sc->sc_iot, sc->sc_ioh,
> > + WBSIO_GPIO_DATA + (group << 2));
> > +
> > + if (value == GPIO_PIN_LOW)
> > + data &= ~(1 << (pin % 8));
> > + else if (value == GPIO_PIN_HIGH)
> > + data |= (1 << (pin % 8));
> > +
> > + wbsio_conf_write(sc->sc_iot, sc->sc_ioh, WBSIO_GPIO_DATA + (group << 2),
> > + data);
> > +
> > + /* Escape from configuration mode */
> > + wbsio_conf_disable(sc->sc_iot, sc->sc_ioh);
> > +}
> > +
> > +void
> > +wbsio_gpio_ctl(void *arg, int pin, int flags)
> > +{
> > + struct wbsio_softc *sc = arg;
> > + u_int8_t data;
> > + int group, gpin;
> > +
> > + /* Enter configuration mode */
> > + wbsio_conf_enable(sc->sc_iot, sc->sc_ioh);
> > +
> > + /* Select GPIO logical device */
> > + wbsio_conf_write(sc->sc_iot, sc->sc_ioh, WBSIO_LDN, WBSIO_LDN_GPIO1);
> > +
> > + /* Work out the GPIO group owning this pin */
> > + group = wbsio_gpio_pin_to_group(sc, pin);
> > +
> > + /* Set I/O direction */
> > + data = wbsio_conf_read(sc->sc_iot, sc->sc_ioh,
> > + WBSIO_GPIO_IO + (group << 2));
> > +
> > + gpin = flags & GPIO_PIN_INPUT;
> > + if (gpin)
> > + data |= (1 << (pin % 8));
> > + else
> > + data &= ~(1 << (pin % 8));
> > +
> > + wbsio_conf_write(sc->sc_iot, sc->sc_ioh, WBSIO_GPIO_IO + (group << 2),
> > + data);
> > +
> > + /* Set inversion */
> > + data = wbsio_conf_read(sc->sc_iot, sc->sc_ioh,
> > + WBSIO_GPIO_INVERT + (group << 2));
> > +
> > + if ((gpin && flags & GPIO_PIN_INVIN) ||
> > + (!gpin && flags & GPIO_PIN_INVOUT))
> > + data |= (1 << (pin % 8));
> > + else
> > + data &= ~(1 << (pin % 8));
> > +
> > + wbsio_conf_write(sc->sc_iot, sc->sc_ioh,
> > + WBSIO_GPIO_INVERT + (group << 2), data);
> > +
> > + /* Set push-pull/open drain */
> > + wbsio_conf_write(sc->sc_iot, sc->sc_ioh, WBSIO_LDN, WBSIO_LDN_GPIO3);
> > +
> > + data = wbsio_conf_read(sc->sc_iot, sc->sc_ioh,
> > + WBSIO_GPIO_PP_OD + group);
> > +
> > + if (flags & GPIO_PIN_OPENDRAIN)
> > + data |= (1 << (pin % 8));
> > + if (flags & GPIO_PIN_PUSHPULL)
> > + data &= ~(1 << (pin % 8));
> > +
> > + wbsio_conf_write(sc->sc_iot, sc->sc_ioh, WBSIO_GPIO_PP_OD + group,
> > + data);
> > +
> > + wbsio_conf_write(sc->sc_iot, sc->sc_ioh, WBSIO_LDN, WBSIO_LDN_GPIO1);
> > + sc->sc_gpio_pins[pin].pin_state = wbsio_gpio_pin_read(sc, pin);
> > +
> > + /* Escape from configuration mode */
> > + wbsio_conf_disable(sc->sc_iot, sc->sc_ioh);
> > }
> > Index: share/man/man4/wbsio.4
> > ===================================================================
> > RCS file: /cvs/src/share/man/man4/wbsio.4,v
> > retrieving revision 1.3
> > diff -u -p -r1.3 wbsio.4
> > --- share/man/man4/wbsio.4 16 Jul 2013 16:05:49 -0000 1.3
> > +++ share/man/man4/wbsio.4 29 Jan 2020 14:30:28 -0000
> > @@ -27,7 +27,8 @@
> > The
> > .Nm
> > driver provides support for the Winbond LPC Super I/O ICs.
> > -Only the hardware monitoring function is currently supported.
> > +GPIO is supported for NCT5104D. For other revisions only the
> > +hardware monitoring function is currently supported.
> > .Pp
> > Support for the hardware monitor function is provided through the
> > .Xr lm 4
> >
>
--
/ Raimo Niskanen, Erlang/OTP, Ericsson AB