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:
parent
9ab40bc40d
commit
1b10e191f3
@ -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
56
share/man/man4/ftgpio.4
Normal 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 .
|
@ -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
|
||||
|
@ -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
610
sys/dev/ftgpio/ftgpio.c
Normal 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);
|
@ -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 */
|
||||
|
@ -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
|
||||
|
8
sys/modules/ftgpio/Makefile
Normal file
8
sys/modules/ftgpio/Makefile
Normal 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>
|
Loading…
Reference in New Issue
Block a user