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.

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
> 

Attachment: signature.asc
Description: PGP signature

Reply via email to