Nathanael Rensen schreef op 2020-01-29 15:47:
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.

But as before this diff does nothing to make sure it is actually
safe to touch these gpio pins.  Other machines might have the same
chip but use the pins internally to switch power to on-board
components.

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