The branch main has been updated by imp:

URL: 
https://cgit.FreeBSD.org/src/commit/?id=1b10e191f341111fad7be32ead11484dfd09b800

commit 1b10e191f341111fad7be32ead11484dfd09b800
Author:     Stéphane Rochoy <[email protected]>
AuthorDate: 2023-02-28 17:16:46 +0000
Commit:     Warner Losh <[email protected]>
CommitDate: 2023-02-28 17:17:53 +0000

    superio,ftgpio: Add support for Fintek F81865 GPIO
    
    Reviewed by: imp
    Pull Request: https://github.com/freebsd/freebsd-src/pull/674
    Differential Revision: https://reviews.freebsd.org/D37893
---
 share/man/man4/Makefile     |   2 +
 share/man/man4/ftgpio.4     |  56 ++++
 share/man/man4/superio.4    |  10 +-
 sys/conf/files.amd64        |   1 +
 sys/dev/ftgpio/ftgpio.c     | 610 ++++++++++++++++++++++++++++++++++++++++++++
 sys/dev/superio/superio.c   |  11 +-
 sys/modules/Makefile        |   2 +
 sys/modules/ftgpio/Makefile |   8 +
 8 files changed, 697 insertions(+), 3 deletions(-)

diff --git a/share/man/man4/Makefile b/share/man/man4/Makefile
index 897c72d43baf..91c43c87a9bf 100644
--- a/share/man/man4/Makefile
+++ b/share/man/man4/Makefile
@@ -159,6 +159,7 @@ MAN=        aac.4 \
        ffclock.4 \
        filemon.4 \
        firewire.4 \
+       ${_ftgpio.4} \
        ${_ftwd.4} \
        full.4 \
        fwe.4 \
@@ -802,6 +803,7 @@ _chvgpio.4= chvgpio.4
 _coretemp.4=   coretemp.4
 _cpuctl.4=     cpuctl.4
 _dpms.4=       dpms.4
+_ftgpio.4=     ftgpio.4
 _ftwd.4=       ftwd.4
 _hpt27xx.4=    hpt27xx.4
 _hptiop.4=     hptiop.4
diff --git a/share/man/man4/ftgpio.4 b/share/man/man4/ftgpio.4
new file mode 100644
index 000000000000..5c58af8c6833
--- /dev/null
+++ b/share/man/man4/ftgpio.4
@@ -0,0 +1,56 @@
+.\" Copyright (c) 2022, Stormshield
+.\" All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\"    notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\"    notice, this list of conditions and the following disclaimer in the
+.\"    documentation and/or other materials provided with the distribution.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" $FreeBSD$
+.\"
+.Dd December 28, 2022
+.Dt FTGPIO 4
+.Os
+.Sh NAME
+.Nm ftgpio
+.Nd GPIO Controller on Fintek Super I/O",
+.Sh SYNOPSIS
+.Cd "device ftgpio"
+.Cd "device gpio"
+.Cd "device gpioled"
+.Sh DESCRIPTION
+The
+.Nm
+is a driver for the GPIO controller found on Fintek Super I/O chips.
+.Sh SEE ALSO
+.Xr gpio 3 ,
+.Xr gpio 4 ,
+.Xr gpioled 4 ,
+.Xr gpioctl 8
+.Xr superio 4 ,
+.Sh HISTORY
+The
+.Nm
+manual page first appeared in
+.Fx 14.0 .
+.Sh AUTHORS
+The
+.Nm
+driver was partially written by
+.An Stéphane Rochoy Aq Mt sté[email protected] .
diff --git a/share/man/man4/superio.4 b/share/man/man4/superio.4
index e242875e3b09..8a5a4239b345 100644
--- a/share/man/man4/superio.4
+++ b/share/man/man4/superio.4
@@ -101,7 +101,15 @@ controllers and a bus driver for supported devices in 
those controllers.
 The
 .Nm
 driver supports a multitude of Super I/O controllers produced by Nuvoton,
-formerly known as Winbond, and ITE.
+formerly known as Winbond, and ITE. As well as some produced by Fintek, namely:
+
+.Bl -bullet -compact
+.It
+F81803
+.It
+F81865
+.El
+
 .Sh SEE ALSO
 .Xr superio 9
 .Sh HISTORY
diff --git a/sys/conf/files.amd64 b/sys/conf/files.amd64
index ff5138104bf4..d93cd1461936 100644
--- a/sys/conf/files.amd64
+++ b/sys/conf/files.amd64
@@ -127,6 +127,7 @@ dev/enic/vnic_dev.c         optional        enic
 dev/enic/vnic_intr.c           optional        enic
 dev/enic/vnic_rq.c             optional        enic
 dev/enic/vnic_wq.c             optional        enic
+dev/ftgpio/ftgpio.c            optional        ftgpio superio
 dev/hyperv/vmbus/amd64/hyperv_machdep.c                        optional        
hyperv
 dev/hyperv/vmbus/amd64/vmbus_vector.S                  optional        hyperv
 dev/iavf/if_iavf_iflib.c       optional        iavf pci \
diff --git a/sys/dev/ftgpio/ftgpio.c b/sys/dev/ftgpio/ftgpio.c
new file mode 100644
index 000000000000..874f6e832949
--- /dev/null
+++ b/sys/dev/ftgpio/ftgpio.c
@@ -0,0 +1,610 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2016-2023 Stormshield
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+
+#include <sys/param.h>
+#include <sys/systm.h>
+
+#include <sys/bus.h>
+#include <sys/eventhandler.h>
+#include <sys/gpio.h>
+#include <sys/kernel.h>
+#include <sys/lock.h>
+#include <sys/module.h>
+#include <sys/mutex.h>
+
+#include <machine/bus.h>
+
+#include <dev/gpio/gpiobusvar.h>
+#include <dev/superio/superio.h>
+
+#include "gpio_if.h"
+
+#define GPIO_LOCK_INIT(_sc)    mtx_init(&(_sc)->mtx,   \
+               device_get_nameunit(dev), NULL, MTX_DEF)
+#define GPIO_LOCK_DESTROY(_sc)         mtx_destroy(&(_sc)->mtx)
+#define GPIO_LOCK(_sc)         mtx_lock(&(_sc)->mtx)
+#define GPIO_UNLOCK(_sc)       mtx_unlock(&(_sc)->mtx)
+#define GPIO_ASSERT_LOCKED(_sc)        mtx_assert(&(_sc)->mtx, MA_OWNED)
+#define GPIO_ASSERT_UNLOCKED(_sc)      mtx_assert(&(_sc)->mtx, MA_NOTOWNED)
+
+/* Global register set */
+#define GPIO4_ENABLE 0x28
+#define GPIO3_ENABLE 0x29
+#define FULL_UR5_UR6 0x2A
+#define GPIO1_ENABLE 0x2B
+#define GPIO2_ENABLE 0x2C
+
+/* Logical Device Numbers. */
+#define FTGPIO_LDN_GPIO                        0x06
+
+#define FTGPIO_MAX_GROUP 6
+#define FTGPIO_MAX_PIN   52
+
+#define FTGPIO_IS_VALID_PIN(_p)  ((_p) >= 0 && (_p) <= FTGPIO_MAX_PIN)
+#define FTGPIO_PIN_GETINDEX(_p) ((_p) & 7)
+#define FTGPIO_PIN_GETGROUP(_p) ((_p) >> 3)
+
+#define FTGPIO_GPIO_CAPS (GPIO_PIN_INPUT  | GPIO_PIN_OUTPUT    | 
GPIO_PIN_INVIN | \
+                          GPIO_PIN_INVOUT | GPIO_PIN_OPENDRAIN | 
GPIO_PIN_PUSHPULL)
+
+#define GET_BIT(_v, _b) (((_v) >> (_b)) & 1)
+
+#define FTGPIO_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.
+ * See p71 and p72 of F81865's datasheet.
+ */
+#define REG_OUTPUT_ENABLE         0 /* Not for GPIO0 */
+#define REG_OUTPUT_DATA           1
+#define REG_PIN_STATUS            2
+#define REG_DRIVE_ENABLE          3
+#define REG_MODE_SELECT_1         4 /* Only for GPIO0 */
+#define REG_MODE_SELECT_2         5 /* Only for GPIO0 */
+#define REG_PULSE_WIDTH_SELECT_1  6 /* Only for GPIO0 */
+#define REG_PULSE_WIDTH_SELECT_2  7 /* Only for GPIO0 */
+#define REG_INTERRUPT_ENABLE      8 /* Only for GPIO0 */
+#define REG_INTERRUPT_STATUS      9 /* Only for GPIO0 */
+
+struct ftgpio_device {
+       uint16_t    devid;
+       const char *descr;
+} ftgpio_devices[] = {
+       {
+               .devid = 0x0704,
+               .descr = "GPIO Controller on Fintek Super I/O",
+       },
+};
+
+struct ftgpio_softc {
+       device_t                        dev;
+       device_t                        busdev;
+       struct mtx                      mtx;
+       struct gpio_pin         pins[FTGPIO_MAX_PIN + 1];
+};
+
+static uint8_t
+ftgpio_group_get_ioreg(struct ftgpio_softc *sc, uint8_t reg, unsigned group)
+{
+       uint8_t ioreg;
+
+       KASSERT((group == 0 && REG_OUTPUT_DATA <= reg && reg <= 
REG_INTERRUPT_STATUS) || \
+               (group >= 1 && reg <= REG_DRIVE_ENABLE),
+               ("%s: invalid register %u for group %u", __func__, reg, group));
+       ioreg = (((0xf - group) << 4) + reg);
+       return (ioreg);
+}
+
+static uint8_t
+ftgpio_group_get_output(struct ftgpio_softc *sc, unsigned group)
+{
+       uint8_t ioreg, val;
+
+       ioreg = ftgpio_group_get_ioreg(sc, REG_OUTPUT_DATA, group);
+       val   = superio_read(sc->dev, ioreg);
+       FTGPIO_VERBOSE_PRINTF(sc->dev, "group GPIO%u output is 0x%x 
(ioreg=0x%x)\n",
+               group, val, ioreg);
+       return (val);
+}
+
+static void
+ftgpio_group_set_output(struct ftgpio_softc *sc, unsigned group, uint8_t 
group_value)
+{
+       uint8_t ioreg;
+
+       ioreg = ftgpio_group_get_ioreg(sc, REG_OUTPUT_DATA, group);
+       superio_write(sc->dev, ioreg, group_value);
+       FTGPIO_VERBOSE_PRINTF(sc->dev, "set group GPIO%u output to 0x%x 
(ioreg=0x%x)\n",
+               group, group_value, ioreg);
+}
+
+static uint8_t
+ftgpio_group_get_status(struct ftgpio_softc *sc, unsigned group)
+{
+       uint8_t ioreg;
+
+       ioreg = ftgpio_group_get_ioreg(sc, REG_PIN_STATUS, group);
+       return (superio_read(sc->dev, ioreg));
+}
+
+static void
+ftgpio_pin_write(struct ftgpio_softc *sc, uint32_t pin_num, bool pin_value)
+{
+       uint32_t pin_flags;
+       uint8_t  val;
+       unsigned group, index;
+
+       GPIO_ASSERT_LOCKED(sc);
+       index     = FTGPIO_PIN_GETINDEX(pin_num);
+       group     = FTGPIO_PIN_GETGROUP(pin_num);
+       pin_flags = sc->pins[pin_num].gp_flags;
+       if ((pin_flags & (GPIO_PIN_OUTPUT)) == 0) {
+               FTGPIO_VERBOSE_PRINTF(sc->dev, "pin %u<GPIO%u%u> is not 
configured for output\n",
+                       pin_num, group, index);
+               return;
+       }
+
+       FTGPIO_VERBOSE_PRINTF(sc->dev, "set pin %u<GPIO%u%u> to %s\n",
+               pin_num, group, index, (pin_value ? "on" : "off"));
+
+       val = ftgpio_group_get_output(sc, group);
+       if (!pin_value != !(pin_flags & GPIO_PIN_INVOUT))
+               val |=  (1 << index);
+       else
+               val &= ~(1 << index);
+       ftgpio_group_set_output(sc, group, val);
+}
+
+static bool
+ftgpio_pin_read(struct ftgpio_softc *sc, uint32_t pin_num)
+{
+       uint32_t pin_flags;
+       unsigned group, index;
+       uint8_t  val;
+       bool     pin_value;
+
+       GPIO_ASSERT_LOCKED(sc);
+       group     = FTGPIO_PIN_GETGROUP(pin_num);
+       index     = FTGPIO_PIN_GETINDEX(pin_num);
+       pin_flags = sc->pins[pin_num].gp_flags;
+       if ((pin_flags & (GPIO_PIN_OUTPUT | GPIO_PIN_INPUT)) == 0) {
+               FTGPIO_VERBOSE_PRINTF(sc->dev, "pin %u<GPIO%u%u> is not 
configured for input or output\n",
+                       pin_num, group, index);
+               return (false);
+       }
+
+       if (pin_flags & GPIO_PIN_OUTPUT)
+               val = ftgpio_group_get_output(sc, group);
+       else
+               val = ftgpio_group_get_status(sc, group);
+       pin_value = GET_BIT(val, index);
+
+       if (((pin_flags & (GPIO_PIN_OUTPUT|GPIO_PIN_INVOUT)) == 
(GPIO_PIN_OUTPUT|GPIO_PIN_INVOUT)) ||
+           ((pin_flags & (GPIO_PIN_INPUT |GPIO_PIN_INVIN )) == (GPIO_PIN_INPUT 
|GPIO_PIN_INVIN)))
+               pin_value = !pin_value;
+       FTGPIO_VERBOSE_PRINTF(sc->dev, "pin %u<GPIO%u%u> is %s\n",
+               pin_num, group, index, (pin_value ? "on" : "off"));
+
+       return (pin_value);
+}
+
+static void
+ftgpio_pin_set_drive(struct ftgpio_softc *sc, uint32_t pin_num, bool pin_drive)
+{
+       unsigned group, index;
+       uint8_t  group_drive, ioreg;
+
+       index       = FTGPIO_PIN_GETINDEX(pin_num);
+       group       = FTGPIO_PIN_GETGROUP(pin_num);
+       ioreg           = ftgpio_group_get_ioreg(sc, REG_DRIVE_ENABLE, group);
+       group_drive = superio_read(sc->dev, ioreg);
+       FTGPIO_VERBOSE_PRINTF(sc->dev, "group GPIO%u drive is 0x%x 
(ioreg=0x%x)\n",
+               group, group_drive, ioreg);
+
+       if (pin_drive)
+               group_drive |= (1 << index);   /* push pull */
+       else
+               group_drive &= ~(1 << index);  /* open drain */
+       superio_write(sc->dev, ioreg, group_drive);
+}
+
+static bool
+ftgpio_pin_is_pushpull(struct ftgpio_softc *sc, uint32_t pin_num)
+{
+       unsigned group, index;
+       uint8_t  group_drive, ioreg;
+       bool is_pushpull;
+
+       index       = FTGPIO_PIN_GETINDEX(pin_num);
+       group       = FTGPIO_PIN_GETGROUP(pin_num);
+
+       ioreg           = ftgpio_group_get_ioreg(sc, REG_DRIVE_ENABLE, group);
+       group_drive = superio_read(sc->dev, ioreg);
+       FTGPIO_VERBOSE_PRINTF(sc->dev, "group GPIO%u drive is 0x%x 
(ioreg=0x%x)\n",
+               group, group_drive, ioreg);
+
+       is_pushpull = group_drive & (1 << index);
+       FTGPIO_VERBOSE_PRINTF(sc->dev, "pin %u<GPIO%u%u> drive is %s\n",
+               pin_num, group, index, (is_pushpull ? "pushpull" : 
"opendrain"));
+
+       return (is_pushpull);
+}
+
+static void
+ftgpio_pin_set_io(struct ftgpio_softc *sc, uint32_t pin_num, bool pin_io)
+{
+       unsigned group, index;
+       uint8_t  group_io, ioreg;
+
+       index = FTGPIO_PIN_GETINDEX(pin_num);
+       group = FTGPIO_PIN_GETGROUP(pin_num);
+       FTGPIO_VERBOSE_PRINTF(sc->dev, "set pin %u<GPIO%u%u> io to %s\n",
+               pin_num, group, index, (pin_io ? "output" : "input"));
+
+       ioreg    = ftgpio_group_get_ioreg(sc, REG_OUTPUT_ENABLE, group);
+       group_io = superio_read(sc->dev, ioreg);
+       FTGPIO_VERBOSE_PRINTF(sc->dev, "group GPIO%u io is 0x%x (ioreg=0x%x)\n",
+               group, group_io, ioreg);
+       if (pin_io)
+               group_io |=  (1 << index); /* output */
+       else
+               group_io &= ~(1 << index); /* input */
+       superio_write(sc->dev, ioreg, group_io);
+       FTGPIO_VERBOSE_PRINTF(sc->dev, "set group GPIO%u io to 0x%x 
(ioreg=0x%x)\n",
+               group, group_io, ioreg);
+}
+
+static bool
+ftgpio_pin_is_output(struct ftgpio_softc *sc, uint32_t pin_num)
+{
+       unsigned group, index;
+       uint8_t  group_io, ioreg;
+       bool is_output;
+
+       index = FTGPIO_PIN_GETINDEX(pin_num);
+       group = FTGPIO_PIN_GETGROUP(pin_num);
+
+       ioreg    = ftgpio_group_get_ioreg(sc, REG_OUTPUT_ENABLE, group);
+       group_io = superio_read(sc->dev, ioreg);
+       FTGPIO_VERBOSE_PRINTF(sc->dev, "group GPIO%u io is 0x%x (ioreg=0x%x)\n",
+               group, group_io, ioreg);
+
+       is_output = group_io & (1 << index);
+       FTGPIO_VERBOSE_PRINTF(sc->dev, "pin %u<GPIO%u%u> io is %s\n",
+               pin_num, group, index, (is_output ? "output" : "input"));
+       return (is_output);
+}
+
+static int
+ftgpio_pin_setflags(struct ftgpio_softc *sc, uint32_t pin_num, uint32_t 
pin_flags)
+{
+       /* check flags consistency */
+       if ((pin_flags & (GPIO_PIN_INPUT | GPIO_PIN_OUTPUT)) ==
+               (GPIO_PIN_INPUT | GPIO_PIN_OUTPUT))
+               return (EINVAL);
+
+       if ((pin_flags & (GPIO_PIN_OPENDRAIN | GPIO_PIN_PUSHPULL)) ==
+               (GPIO_PIN_OPENDRAIN | GPIO_PIN_PUSHPULL))
+               return (EINVAL);
+
+       if (pin_flags & GPIO_PIN_OPENDRAIN)
+               ftgpio_pin_set_drive(sc, pin_num, 0 /* open drain */);
+       else if (pin_flags & GPIO_PIN_PUSHPULL)
+               ftgpio_pin_set_drive(sc, pin_num, 1 /* push pull */);
+
+       if (pin_flags & GPIO_PIN_INPUT)
+               ftgpio_pin_set_io(sc, pin_num, 0 /* input */);
+       else if (pin_flags & GPIO_PIN_OUTPUT)
+               ftgpio_pin_set_io(sc, pin_num, 1 /* output */);
+
+       sc->pins[pin_num].gp_flags = pin_flags;
+
+       return (0);
+}
+
+static int
+ftgpio_probe(device_t dev)
+{
+       uint16_t devid;
+       int      i;
+
+       if (superio_vendor(dev) != SUPERIO_VENDOR_FINTEK)
+               return (ENXIO);
+       if (superio_get_type(dev) != SUPERIO_DEV_GPIO)
+               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) != FTGPIO_LDN_GPIO)
+               return (ENXIO);
+
+       devid = superio_devid(dev);
+       for (i = 0; i < nitems(ftgpio_devices); i++) {
+               if (devid == ftgpio_devices[i].devid) {
+                       device_set_desc(dev, ftgpio_devices[i].descr);
+                       return (BUS_PROBE_DEFAULT);
+               }
+       }
+       return (ENXIO);
+}
+
+static int
+ftgpio_attach(device_t dev)
+{
+       struct ftgpio_softc *sc;
+       int                  i;
+
+       sc              = device_get_softc(dev);
+       sc->dev = dev;
+
+       GPIO_LOCK_INIT(sc);
+       GPIO_LOCK(sc);
+
+       for (i = 0; i <= FTGPIO_MAX_PIN; i++) {
+               struct gpio_pin *pin;
+
+               pin           = &sc->pins[i];
+               pin->gp_pin   = i;
+               pin->gp_caps  = FTGPIO_GPIO_CAPS;
+               pin->gp_flags = 0;
+
+               if (ftgpio_pin_is_output(sc, i))
+                       pin->gp_flags |= GPIO_PIN_OUTPUT;
+               else
+                       pin->gp_flags |= GPIO_PIN_INPUT;
+
+               if (ftgpio_pin_is_pushpull(sc, i))
+                       pin->gp_flags |= GPIO_PIN_PUSHPULL;
+               else
+                       pin->gp_flags |= GPIO_PIN_OPENDRAIN;
+
+               snprintf(pin->gp_name, GPIOMAXNAME, "GPIO%u%u",
+                       FTGPIO_PIN_GETGROUP(i), FTGPIO_PIN_GETINDEX(i));
+       }
+
+       /* Enable all groups */
+       superio_write(sc->dev, GPIO1_ENABLE, 0xFF);
+       superio_write(sc->dev, GPIO2_ENABLE, 0xFF);
+       superio_write(sc->dev, GPIO3_ENABLE, 0xFF);
+       superio_write(sc->dev, GPIO4_ENABLE, 0xFF);
+       superio_write(sc->dev, FULL_UR5_UR6, 0x0A);
+       FTGPIO_VERBOSE_PRINTF(sc->dev, "groups GPIO1..GPIO6 enabled\n");
+
+       GPIO_UNLOCK(sc);
+       sc->busdev = gpiobus_attach_bus(dev);
+       if (sc->busdev == NULL) {
+               GPIO_LOCK_DESTROY(sc);
+               return (ENXIO);
+       }
+
+       return (0);
+}
+
+static int
+ftgpio_detach(device_t dev)
+{
+       struct ftgpio_softc *sc;
+
+       sc = device_get_softc(dev);
+       gpiobus_detach_bus(dev);
+       GPIO_ASSERT_UNLOCKED(sc);
+       GPIO_LOCK_DESTROY(sc);
+
+       return (0);
+}
+
+static device_t
+ftgpio_gpio_get_bus(device_t dev)
+{
+       struct ftgpio_softc *sc;
+
+       sc = device_get_softc(dev);
+
+       return (sc->busdev);
+}
+
+static int
+ftgpio_gpio_pin_max(device_t dev, int *npins)
+{
+       *npins = FTGPIO_MAX_PIN;
+       return (0);
+}
+
+static int
+ftgpio_gpio_pin_set(device_t dev, uint32_t pin_num, uint32_t pin_value)
+{
+       struct ftgpio_softc *sc;
+
+       if (!FTGPIO_IS_VALID_PIN(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);
+               return (EINVAL);
+       }
+       ftgpio_pin_write(sc, pin_num, pin_value);
+       GPIO_UNLOCK(sc);
+
+       return (0);
+}
+
+static int
+ftgpio_gpio_pin_get(device_t dev, uint32_t pin_num, uint32_t *pin_value)
+{
+       struct ftgpio_softc *sc;
+
+       if (!FTGPIO_IS_VALID_PIN(pin_num))
+               return (EINVAL);
+
+       if (pin_value == NULL)
+               return (EINVAL);
+
+       sc = device_get_softc(dev);
+       GPIO_LOCK(sc);
+       *pin_value = ftgpio_pin_read(sc, pin_num);
+       GPIO_UNLOCK(sc);
+
+       return (0);
+}
+
+static int
+ftgpio_gpio_pin_toggle(device_t dev, uint32_t pin_num)
+{
+       struct ftgpio_softc *sc;
+       bool              pin_value;
+
+       if (!FTGPIO_IS_VALID_PIN(pin_num))
+               return (EINVAL);
+
+       sc = device_get_softc(dev);
+       GPIO_LOCK(sc);
+       pin_value = ftgpio_pin_read(sc, pin_num);
+       ftgpio_pin_write(sc, pin_num, !pin_value);
+       GPIO_UNLOCK(sc);
+
+       return (0);
+}
+
+static int
+ftgpio_gpio_pin_getname(device_t dev, uint32_t pin_num, char *pin_name)
+{
+       struct ftgpio_softc *sc;
+
+       if (pin_name == NULL)
+               return (EINVAL);
+
+       if (!FTGPIO_IS_VALID_PIN(pin_num))
+               return (EINVAL);
+
+       sc = device_get_softc(dev);
+       strlcpy(pin_name, sc->pins[pin_num].gp_name, GPIOMAXNAME);
+
+       return (0);
+}
+
+static int
+ftgpio_gpio_pin_getcaps(device_t dev, uint32_t pin_num, uint32_t *pin_caps)
+{
+       struct ftgpio_softc *sc;
+
+       if (pin_caps == NULL)
+               return (EINVAL);
+
+       if (!FTGPIO_IS_VALID_PIN(pin_num))
+               return (EINVAL);
+
+       sc        = device_get_softc(dev);
+       *pin_caps = sc->pins[pin_num].gp_caps;
+
+       return (0);
+}
+
+static int
+ftgpio_gpio_pin_getflags(device_t dev, uint32_t pin_num, uint32_t *pin_flags)
+{
+       struct ftgpio_softc *sc;
+
+       if (pin_flags == NULL)
+               return (EINVAL);
+
+       if (!FTGPIO_IS_VALID_PIN(pin_num))
+               return (EINVAL);
+
+       sc         = device_get_softc(dev);
+       *pin_flags = sc->pins[pin_num].gp_flags;
+
+       return (0);
+}
+
+static int
+ftgpio_gpio_pin_setflags(device_t dev, uint32_t pin_num, uint32_t pin_flags)
+{
+       struct ftgpio_softc *sc;
+       int               ret;
+
+       if (!FTGPIO_IS_VALID_PIN(pin_num)) {
+               FTGPIO_VERBOSE_PRINTF(dev, "invalid pin number: %u\n", pin_num);
+               return (EINVAL);
+       }
+
+       sc = device_get_softc(dev);
+
+       /* Check for unwanted flags. */
+       if ((pin_flags & sc->pins[pin_num].gp_caps) != pin_flags) {
+               FTGPIO_VERBOSE_PRINTF(dev, "invalid pin flags 0x%x, vs caps 
0x%x\n",
+                       pin_flags, sc->pins[pin_num].gp_caps);
+               return (EINVAL);
+       }
+
+       GPIO_LOCK(sc);
+       ret = ftgpio_pin_setflags(sc, pin_num, pin_flags);
+       GPIO_UNLOCK(sc);
+
+       return (ret);
+}
+
+static device_method_t ftgpio_methods[] = {
+       /* Device interface */
+       DEVMETHOD(device_probe,     ftgpio_probe),
+       DEVMETHOD(device_attach,    ftgpio_attach),
+       DEVMETHOD(device_detach,    ftgpio_detach),
+
+       /* GPIO */
+       DEVMETHOD(gpio_get_bus,         ftgpio_gpio_get_bus),
+       DEVMETHOD(gpio_pin_max,         ftgpio_gpio_pin_max),
+       DEVMETHOD(gpio_pin_set,         ftgpio_gpio_pin_set),
+       DEVMETHOD(gpio_pin_get,         ftgpio_gpio_pin_get),
+       DEVMETHOD(gpio_pin_toggle,      ftgpio_gpio_pin_toggle),
+       DEVMETHOD(gpio_pin_getname,     ftgpio_gpio_pin_getname),
+       DEVMETHOD(gpio_pin_getcaps,     ftgpio_gpio_pin_getcaps),
+       DEVMETHOD(gpio_pin_getflags,    ftgpio_gpio_pin_getflags),
+       DEVMETHOD(gpio_pin_setflags,    ftgpio_gpio_pin_setflags),
+
+       DEVMETHOD_END
+};
+
+static driver_t ftgpio_driver = {
+       "gpio",
+       ftgpio_methods,
+       sizeof(struct ftgpio_softc)
+};
+
+DRIVER_MODULE(ftgpio, superio, ftgpio_driver, NULL,  NULL);
+MODULE_DEPEND(ftgpio, gpiobus, 1, 1, 1);
+MODULE_DEPEND(ftgpio, superio, 1, 1, 1);
+MODULE_VERSION(ftgpio, 1);
diff --git a/sys/dev/superio/superio.c b/sys/dev/superio/superio.c
index 6c7df002f198..37b8d24dd375 100644
--- a/sys/dev/superio/superio.c
+++ b/sys/dev/superio/superio.c
@@ -283,6 +283,7 @@ const struct sio_device nct5104_devices[] = {
 };
 
 const struct sio_device fintek_devices[] = {
+       { .ldn = 6, .type = SUPERIO_DEV_GPIO },
        { .ldn = 7, .type = SUPERIO_DEV_WDT },
        { .type = SUPERIO_DEV_NONE },
 };
@@ -441,6 +442,11 @@ static const struct {
                .descr = "Fintek F81803",
                .devices = fintek_devices,
        },
+       {
+               .vendor = SUPERIO_VENDOR_FINTEK, .devid = 0x0704,
+               .descr = "Fintek F81865",
+               .devices = fintek_devices,
+       },
        { 0, 0 }
 };
 
@@ -550,8 +556,9 @@ superio_detect(device_t dev, bool claim, struct siosc *sc)
        sc->revid = revid;
 
        KASSERT(sc->vendor == SUPERIO_VENDOR_ITE ||
-           sc->vendor == SUPERIO_VENDOR_NUVOTON,
-           ("Only ITE and Nuvoton SuperIO-s are supported"));
+           sc->vendor == SUPERIO_VENDOR_NUVOTON ||
+           sc->vendor == SUPERIO_VENDOR_FINTEK,
+           ("Only ITE, Nuvoton and Fintek SuperIO-s are supported"));
        sc->ldn_reg = 0x07;
        sc->enable_reg = 0x30;
        sc->current_ldn = 0xff; /* no device should have this */
diff --git a/sys/modules/Makefile b/sys/modules/Makefile
index 4397c7beb56e..4aa4a26e6d17 100644
--- a/sys/modules/Makefile
+++ b/sys/modules/Makefile
@@ -122,6 +122,7 @@ SUBDIR=     \
        firewire \
        firmware \
        flash \
+       ${_ftgpio} \
        ${_ftwd} \
        fusefs \
        ${_fxp} \
@@ -681,6 +682,7 @@ _cpufreq=   cpufreq
 _dpms=         dpms
 _em=           em
 _et=           et
+_ftgpio=       ftgpio
 _ftwd=         ftwd
 _exca=         exca
 _igc=          igc
diff --git a/sys/modules/ftgpio/Makefile b/sys/modules/ftgpio/Makefile
new file mode 100644
index 000000000000..bf9b5f0d762b
--- /dev/null
+++ b/sys/modules/ftgpio/Makefile
@@ -0,0 +1,8 @@
+# $FreeBSD$
+
+.PATH: ${SRCTOP}/sys/dev/ftgpio
+KMOD=  ftgpio
+SRCS=  ftgpio.c
+SRCS+= device_if.h bus_if.h isa_if.h gpio_if.h opt_platform.h
+
+.include <bsd.kmod.mk>

Reply via email to