aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorStéphane Rochoy <stephane.rochoy@stormshield.eu>2023-02-28 17:16:46 +0000
committerWarner Losh <imp@FreeBSD.org>2023-02-28 17:17:53 +0000
commit1b10e191f341111fad7be32ead11484dfd09b800 (patch)
tree02dcbacb6674c31e94e1bfdf52c875fca3af7705
parent9ab40bc40d4a07e1a9f3622a7779312ca2466b55 (diff)
downloadsrc-1b10e191f341111fad7be32ead11484dfd09b800.tar.gz
src-1b10e191f341111fad7be32ead11484dfd09b800.zip
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
-rw-r--r--share/man/man4/Makefile2
-rw-r--r--share/man/man4/ftgpio.456
-rw-r--r--share/man/man4/superio.410
-rw-r--r--sys/conf/files.amd641
-rw-r--r--sys/dev/ftgpio/ftgpio.c610
-rw-r--r--sys/dev/superio/superio.c11
-rw-r--r--sys/modules/Makefile2
-rw-r--r--sys/modules/ftgpio/Makefile8
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éphane.rochoy@stormshield.eu .
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>