Use ftgpio_group_get_status instead of inlining an imperfect version of it to get correct register and avoid panic: ftgpio_group_get_ioreg: invalid register 0 for group 0 Reviewed by: imp Pull Request: https://github.com/freebsd/freebsd-src/pull/677
605 lines
16 KiB
C
605 lines
16 KiB
C
/*-
|
|
* 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 = "Fintek F81865",
|
|
},
|
|
};
|
|
|
|
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;
|
|
bool is_output;
|
|
|
|
index = FTGPIO_PIN_GETINDEX(pin_num);
|
|
group = FTGPIO_PIN_GETGROUP(pin_num);
|
|
|
|
is_output = ftgpio_group_get_status(sc, group) & (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);
|