Add the GPIO driver for the ADI Engineering RCC-VE and RCC-DFF/DFFv2.

This driver allows read the software reset switch state and control the
status LEDs.

The GPIO pins have their direction (input/output) locked down to prevent
possible short circuits.

Note that most people get a reset button that is a hardware reset.  The
software reset button is available on boards from Netgate.

Sponsored by:	Rubicon Communications (Netgate)
This commit is contained in:
Luiz Otavio O Souza 2015-08-18 21:05:56 +00:00
parent 337776f858
commit 3df058ffaf
Notes: svn2git 2020-12-20 02:59:44 +00:00
svn path=/head/; revision=286908
4 changed files with 440 additions and 0 deletions

63
share/man/man4/rccgpio.4 Normal file
View File

@ -0,0 +1,63 @@
.\" Copyright (c) 2015, Rubicon Communications, LLC (Netgate)
.\" 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 August 18, 2015
.Dt RCCGPIO 4
.Os
.Sh NAME
.Nm rccgpio
.Nd ADI Engineering RCC-VE and RCC-DFF/DFFv2 GPIO controller
.Sh SYNOPSIS
.Cd "device rccgpio"
.Cd "device gpio"
.Cd "device gpioled"
.Sh DESCRIPTION
The
.Nm
provides a simple interface to read the reset switch state and control the
status LEDs.
.Pp
The software controlled reset switch is known to be available on boards from
Netgate.
Most people get a button that is a hardware reset.
.Pp
All the GPIO pins are locked in their intended setup to disallow any harmful
settings (the ones that can cause short circuits).
.Sh SEE ALSO
.Xr gpio 3 ,
.Xr gpio 4 ,
.Xr gpioled 4 ,
.Xr gpioctl 8
.Sh HISTORY
The
.Nm
manual page first appeared in
.Fx 11.0 .
.Sh AUTHORS
The
.Nm
driver was written by
.An Luiz Otavio O Souza Aq Mt loos@FreeBSD.org .

View File

@ -2323,6 +2323,7 @@ dev/random/fortuna.c optional random !random_yarrow !random_loadable
dev/random/hash.c optional random random_yarrow | \
random !random_yarrow !random_loadable
dev/rc/rc.c optional rc
dev/rccgpio/rccgpio.c optional rccgpio gpio
dev/re/if_re.c optional re
dev/rl/if_rl.c optional rl pci
dev/rndtest/rndtest.c optional rndtest

368
sys/dev/rccgpio/rccgpio.c Normal file
View File

@ -0,0 +1,368 @@
/*-
* Copyright (c) 2015 Rubicon Communications, LLC (Netgate)
* 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.
*/
#include <sys/cdefs.h>
__FBSDID("$FreeBSD$");
/*
* GPIO driver for the ADI Engineering RCC-VE and RCC-DFF/DFFv2.
*/
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/bus.h>
#include <sys/gpio.h>
#include <sys/kernel.h>
#include <sys/module.h>
#include <sys/rman.h>
#include <machine/bus.h>
#include <dev/gpio/gpiobusvar.h>
#include <isa/isavar.h>
#include "gpio_if.h"
#define RCC_GPIO_BASE 0x500
#define RCC_GPIO_USE_SEL 0x00
#define RCC_GPIO_IO_SEL 0x04
#define RCC_GPIO_GP_LVL 0x08
struct rcc_gpio_pin {
uint32_t pin;
const char *name;
uint32_t caps;
};
static struct rcc_gpio_pin rcc_pins[] = {
{ .pin = 11, .name = "reset switch", .caps = GPIO_PIN_INPUT },
{ .pin = 15, .name = "red LED", .caps = GPIO_PIN_OUTPUT },
{ .pin = 17, .name = "green LED", .caps = GPIO_PIN_OUTPUT },
#if 0
{ .pin = 16, .name = "HD1 LED", .caps = GPIO_PIN_OUTPUT },
{ .pin = 18, .name = "HD2 LED", .caps = GPIO_PIN_OUTPUT },
#endif
};
struct rcc_gpio_softc {
device_t sc_dev;
device_t sc_busdev;
struct mtx sc_mtx;
struct resource *sc_io_res;
bus_space_tag_t sc_bst;
bus_space_handle_t sc_bsh;
uint32_t sc_output;
int sc_io_rid;
int sc_gpio_npins;
};
#define RCC_GPIO_LOCK(_sc) mtx_lock(&(_sc)->sc_mtx)
#define RCC_GPIO_UNLOCK(_sc) mtx_unlock(&(_sc)->sc_mtx)
#define RCC_WRITE(_sc, _off, _val) \
bus_space_write_4((_sc)->sc_bst, (_sc)->sc_bsh, _off, _val)
#define RCC_READ(_sc, _off) \
bus_space_read_4((_sc)->sc_bst, (_sc)->sc_bsh, _off)
static void
rcc_gpio_modify_bits(struct rcc_gpio_softc *sc, uint32_t reg, uint32_t mask,
uint32_t bit)
{
uint32_t value;
RCC_GPIO_LOCK(sc);
value = RCC_READ(sc, reg);
value &= ~(1 << mask);
value |= (1 << bit);
RCC_WRITE(sc, reg, value);
RCC_GPIO_UNLOCK(sc);
}
static device_t
rcc_gpio_get_bus(device_t dev)
{
struct rcc_gpio_softc *sc;
sc = device_get_softc(dev);
return (sc->sc_busdev);
}
static int
rcc_gpio_pin_max(device_t dev, int *maxpin)
{
struct rcc_gpio_softc *sc;
sc = device_get_softc(dev);
*maxpin = sc->sc_gpio_npins - 1;
return (0);
}
static int
rcc_gpio_pin_getcaps(device_t dev, uint32_t pin, uint32_t *caps)
{
struct rcc_gpio_softc *sc;
sc = device_get_softc(dev);
if (pin > sc->sc_gpio_npins)
return (EINVAL);
*caps = rcc_pins[pin].caps;
return (0);
}
static int
rcc_gpio_pin_getflags(device_t dev, uint32_t pin, uint32_t *flags)
{
struct rcc_gpio_softc *sc;
sc = device_get_softc(dev);
if (pin > sc->sc_gpio_npins)
return (EINVAL);
/* Flags cannot be changed. */
*flags = rcc_pins[pin].caps;
return (0);
}
static int
rcc_gpio_pin_getname(device_t dev, uint32_t pin, char *name)
{
struct rcc_gpio_softc *sc;
sc = device_get_softc(dev);
if (pin > sc->sc_gpio_npins)
return (EINVAL);
memcpy(name, rcc_pins[pin].name, GPIOMAXNAME);
return (0);
}
static int
rcc_gpio_pin_setflags(device_t dev, uint32_t pin, uint32_t flags)
{
struct rcc_gpio_softc *sc;
sc = device_get_softc(dev);
if (pin > sc->sc_gpio_npins)
return (EINVAL);
/* Flags cannot be changed - risk of short-circuit!!! */
return (0);
}
static int
rcc_gpio_pin_set(device_t dev, uint32_t pin, unsigned int value)
{
struct rcc_gpio_softc *sc;
sc = device_get_softc(dev);
if (pin > sc->sc_gpio_npins)
return (EINVAL);
RCC_GPIO_LOCK(sc);
if (value)
sc->sc_output |= (1 << rcc_pins[pin].pin);
else
sc->sc_output &= ~(1 << rcc_pins[pin].pin);
RCC_WRITE(sc, RCC_GPIO_GP_LVL, sc->sc_output);
RCC_GPIO_UNLOCK(sc);
return (0);
}
static int
rcc_gpio_pin_get(device_t dev, uint32_t pin, unsigned int *val)
{
struct rcc_gpio_softc *sc;
uint32_t value;
sc = device_get_softc(dev);
if (pin > sc->sc_gpio_npins)
return (EINVAL);
RCC_GPIO_LOCK(sc);
if (rcc_pins[pin].caps & GPIO_PIN_INPUT)
value = RCC_READ(sc, RCC_GPIO_GP_LVL);
else
value = sc->sc_output;
RCC_GPIO_UNLOCK(sc);
*val = (value & (1 << rcc_pins[pin].pin)) ? 1 : 0;
return (0);
}
static int
rcc_gpio_pin_toggle(device_t dev, uint32_t pin)
{
struct rcc_gpio_softc *sc;
sc = device_get_softc(dev);
if (pin > sc->sc_gpio_npins)
return (EINVAL);
RCC_GPIO_LOCK(sc);
if ((sc->sc_output & (1 << rcc_pins[pin].pin)) == 0)
sc->sc_output |= (1 << rcc_pins[pin].pin);
else
sc->sc_output &= ~(1 << rcc_pins[pin].pin);
RCC_WRITE(sc, RCC_GPIO_GP_LVL, sc->sc_output);
RCC_GPIO_UNLOCK(sc);
return (0);
}
static int
rcc_gpio_probe(device_t dev)
{
char *prod;
int port;
/*
* We don't know of any PnP ID's for this GPIO controller.
*/
if (isa_get_logicalid(dev) != 0)
return (ENXIO);
/*
* We have to have an IO port hint that is valid.
*/
port = isa_get_port(dev);
if (port != RCC_GPIO_BASE)
return (ENXIO);
prod = kern_getenv("smbios.system.product");
if (prod == NULL ||
(strcmp(prod, "RCC-VE") != 0 && strcmp(prod, "RCC-DFF") != 0))
return (ENXIO);
device_set_desc(dev, "RCC-VE/DFF GPIO controller");
return (BUS_PROBE_DEFAULT);
}
static int
rcc_gpio_attach(device_t dev)
{
int i;
struct rcc_gpio_softc *sc;
sc = device_get_softc(dev);
sc->sc_dev = dev;
/* Allocate IO resources. */
sc->sc_io_rid = 0;
sc->sc_io_res = bus_alloc_resource_any(dev, SYS_RES_IOPORT,
&sc->sc_io_rid, RF_ACTIVE);
if (sc->sc_io_res == NULL) {
device_printf(dev, "cannot allocate memory window\n");
return (ENXIO);
}
sc->sc_bst = rman_get_bustag(sc->sc_io_res);
sc->sc_bsh = rman_get_bushandle(sc->sc_io_res);
mtx_init(&sc->sc_mtx, "rcc-gpio", "gpio", MTX_DEF);
/* Initialize the pins. */
sc->sc_gpio_npins = nitems(rcc_pins);
for (i = 0; i < sc->sc_gpio_npins; i++) {
/* Enable it for GPIO. */
rcc_gpio_modify_bits(sc, RCC_GPIO_USE_SEL, 0, rcc_pins[i].pin);
/* Set the pin as input or output. */
if (rcc_pins[i].caps & GPIO_PIN_OUTPUT)
rcc_gpio_modify_bits(sc, RCC_GPIO_IO_SEL,
rcc_pins[i].pin, 0);
else
rcc_gpio_modify_bits(sc, RCC_GPIO_IO_SEL,
0, rcc_pins[i].pin);
}
RCC_WRITE(sc, RCC_GPIO_GP_LVL, sc->sc_output);
/* Attach the gpiobus. */
sc->sc_busdev = gpiobus_attach_bus(dev);
if (sc->sc_busdev == NULL) {
bus_release_resource(dev, SYS_RES_IOPORT, sc->sc_io_rid,
sc->sc_io_res);
mtx_destroy(&sc->sc_mtx);
return (ENXIO);
}
return (0);
}
static int
rcc_gpio_detach(device_t dev)
{
int i;
struct rcc_gpio_softc *sc;
sc = device_get_softc(dev);
gpiobus_detach_bus(dev);
/* Disable the GPIO function. */
for (i = 0; i < sc->sc_gpio_npins; i++)
rcc_gpio_modify_bits(sc, RCC_GPIO_USE_SEL, rcc_pins[i].pin, 0);
if (sc->sc_io_res != NULL)
bus_release_resource(dev, SYS_RES_IOPORT, sc->sc_io_rid,
sc->sc_io_res);
mtx_destroy(&sc->sc_mtx);
return (0);
}
static device_method_t rcc_gpio_methods[] = {
/* Device interface */
DEVMETHOD(device_probe, rcc_gpio_probe),
DEVMETHOD(device_attach, rcc_gpio_attach),
DEVMETHOD(device_detach, rcc_gpio_detach),
/* GPIO protocol */
DEVMETHOD(gpio_get_bus, rcc_gpio_get_bus),
DEVMETHOD(gpio_pin_max, rcc_gpio_pin_max),
DEVMETHOD(gpio_pin_getname, rcc_gpio_pin_getname),
DEVMETHOD(gpio_pin_getflags, rcc_gpio_pin_getflags),
DEVMETHOD(gpio_pin_getcaps, rcc_gpio_pin_getcaps),
DEVMETHOD(gpio_pin_setflags, rcc_gpio_pin_setflags),
DEVMETHOD(gpio_pin_get, rcc_gpio_pin_get),
DEVMETHOD(gpio_pin_set, rcc_gpio_pin_set),
DEVMETHOD(gpio_pin_toggle, rcc_gpio_pin_toggle),
DEVMETHOD_END
};
static devclass_t rcc_gpio_devclass;
static driver_t rcc_gpio_driver = {
"gpio",
rcc_gpio_methods,
sizeof(struct rcc_gpio_softc),
};
DRIVER_MODULE(rcc_gpio, isa, rcc_gpio_driver, rcc_gpio_devclass, 0, 0);
MODULE_DEPEND(rcc_gpio, gpiobus, 1, 1, 1);

View File

@ -0,0 +1,8 @@
# $FreeBSD$
.PATH: ${.CURDIR}/../../dev/rccgpio
KMOD= rccgpio
SRCS= rccgpio.c
SRCS+= device_if.h bus_if.h isa_if.h gpio_if.h opt_platform.h
.include <bsd.kmod.mk>