diff --git a/share/man/man4/Makefile b/share/man/man4/Makefile --- a/share/man/man4/Makefile +++ b/share/man/man4/Makefile @@ -159,6 +159,7 @@ ffclock.4 \ filemon.4 \ firewire.4 \ + ${_ftgpio.4} \ ${_ftwd.4} \ full.4 \ fwe.4 \ @@ -802,6 +803,7 @@ _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 --- /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 --- a/share/man/man4/superio.4 +++ b/share/man/man4/superio.4 @@ -101,7 +101,15 @@ 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 --- a/sys/conf/files.amd64 +++ b/sys/conf/files.amd64 @@ -127,6 +127,7 @@ 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 --- /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 + +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include + +#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 is not configured for output\n", + pin_num, group, index); + return; + } + + FTGPIO_VERBOSE_PRINTF(sc->dev, "set pin %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 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 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 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 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 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 --- a/sys/dev/superio/superio.c +++ b/sys/dev/superio/superio.c @@ -283,6 +283,7 @@ }; 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 @@ .descr = "Fintek F81803", .devices = fintek_devices, }, + { + .vendor = SUPERIO_VENDOR_FINTEK, .devid = 0x0704, + .descr = "Fintek F81865", + .devices = fintek_devices, + }, { 0, 0 } }; @@ -550,8 +556,9 @@ 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 --- a/sys/modules/Makefile +++ b/sys/modules/Makefile @@ -122,6 +122,7 @@ firewire \ firmware \ flash \ + ${_ftgpio} \ ${_ftwd} \ fusefs \ ${_fxp} \ @@ -681,6 +682,7 @@ _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 --- /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