Changeset View
Changeset View
Standalone View
Standalone View
sys/dev/ftgpio/ftgpio.c
- This file was added.
/*- | |||||
* 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); |