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
This commit is contained in:
Stéphane Rochoy 2023-02-28 10:16:46 -07:00 committed by Warner Losh
parent 9ab40bc40d
commit 1b10e191f3
8 changed files with 697 additions and 3 deletions

View File

@ -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

56
share/man/man4/ftgpio.4 Normal file
View File

@ -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 .

View File

@ -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

View File

@ -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 \

610
sys/dev/ftgpio/ftgpio.c Normal file
View File

@ -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);

View File

@ -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 */

View File

@ -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

View File

@ -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>