The diff below adds gpio(4) support to wbsio(4) for Nuvoton NCT5104D
(pcengines APU2). 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

Reply via email to