Add support for Lewisburg GPIO through P2SB
Obtained from: Semihalf Sponsored by: Stormshield Differential revision: https://reviews.freebsd.org/D31269
This commit is contained in:
parent
3daa8e165c
commit
3706af423f
@ -363,6 +363,9 @@ dev/viawd/viawd.c optional viawd
|
||||
dev/vmd/vmd.c optional vmd
|
||||
dev/vmd/vmd_bus.c optional vmd_bus
|
||||
dev/wbwd/wbwd.c optional wbwd
|
||||
dev/p2sb/p2sb.c optional p2sb pci
|
||||
dev/p2sb/lewisburg_gpiocm.c optional lbggpiocm p2sb
|
||||
dev/p2sb/lewisburg_gpio.c optional lbggpio lbggpiocm
|
||||
isa/syscons_isa.c optional sc
|
||||
isa/vga_isa.c optional vga
|
||||
kern/imgact_aout.c optional compat_aout
|
||||
|
269
sys/dev/p2sb/lewisburg_gpio.c
Normal file
269
sys/dev/p2sb/lewisburg_gpio.c
Normal file
@ -0,0 +1,269 @@
|
||||
/*-
|
||||
* Copyright (c) 2018 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.
|
||||
*/
|
||||
|
||||
#include <sys/param.h> /* defines used in kernel.h */
|
||||
#include <sys/module.h>
|
||||
#include <sys/systm.h>
|
||||
#include <sys/errno.h>
|
||||
#include <sys/kernel.h> /* types used in module initialization */
|
||||
#include <sys/conf.h> /* cdevsw struct */
|
||||
#include <sys/uio.h> /* uio struct */
|
||||
#include <sys/malloc.h>
|
||||
#include <sys/bus.h> /* structs, prototypes for pci bus stuff and DEVMETHOD macros! */
|
||||
#include <sys/gpio.h>
|
||||
|
||||
#include <machine/bus.h>
|
||||
#include <sys/rman.h>
|
||||
#include <machine/resource.h>
|
||||
|
||||
#include <dev/gpio/gpiobusvar.h>
|
||||
|
||||
#include "gpio_if.h"
|
||||
|
||||
#include "lewisburg_gpiocm.h"
|
||||
|
||||
#define P2SB_GROUP_GPIO_MAX_PINS 24
|
||||
struct lbggpio_softc
|
||||
{
|
||||
device_t sc_busdev;
|
||||
int groupid;
|
||||
int pins_off;
|
||||
int npins;
|
||||
char grpname;
|
||||
struct gpio_pin gpio_setup[P2SB_GROUP_GPIO_MAX_PINS];
|
||||
};
|
||||
|
||||
static device_t
|
||||
lbggpio_get_bus(device_t dev)
|
||||
{
|
||||
struct lbggpio_softc *sc;
|
||||
|
||||
sc = device_get_softc(dev);
|
||||
|
||||
return (sc->sc_busdev);
|
||||
}
|
||||
|
||||
static int
|
||||
lbggpio_pin_max(device_t dev, int *maxpin)
|
||||
{
|
||||
struct lbggpio_softc *sc;
|
||||
|
||||
if (maxpin == NULL)
|
||||
return (EINVAL);
|
||||
|
||||
sc = device_get_softc(dev);
|
||||
|
||||
*maxpin = sc->npins - 1;
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
static int
|
||||
lbggpio_pin_getname(device_t dev, uint32_t pin, char *name)
|
||||
{
|
||||
struct lbggpio_softc *sc = device_get_softc(dev);
|
||||
|
||||
if (name == NULL)
|
||||
return (EINVAL);
|
||||
|
||||
if (pin >= sc->npins)
|
||||
return (EINVAL);
|
||||
|
||||
strlcpy(name, sc->gpio_setup[pin].gp_name, GPIOMAXNAME);
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
static int
|
||||
lbggpio_pin_getflags(device_t dev, uint32_t pin, uint32_t *flags)
|
||||
{
|
||||
struct lbggpio_softc *sc = device_get_softc(dev);
|
||||
|
||||
if (flags == NULL)
|
||||
return (EINVAL);
|
||||
|
||||
if (pin >= sc->npins)
|
||||
return (EINVAL);
|
||||
|
||||
*flags = sc->gpio_setup[pin].gp_flags;
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
static int
|
||||
lbggpio_pin_getcaps(device_t dev, uint32_t pin, uint32_t *caps)
|
||||
{
|
||||
struct lbggpio_softc *sc = device_get_softc(dev);
|
||||
|
||||
if (caps == NULL)
|
||||
return (EINVAL);
|
||||
|
||||
if (pin >= sc->npins)
|
||||
return (EINVAL);
|
||||
|
||||
*caps = sc->gpio_setup[pin].gp_caps;
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
static int
|
||||
lbggpio_pin_setflags(device_t dev, uint32_t pin, uint32_t flags)
|
||||
{
|
||||
struct lbggpio_softc *sc = device_get_softc(dev);
|
||||
|
||||
if (pin >= sc->npins)
|
||||
return (EINVAL);
|
||||
|
||||
/* Check for unwanted flags. */
|
||||
if ((flags & sc->gpio_setup[pin].gp_caps) != flags)
|
||||
return (EINVAL);
|
||||
|
||||
lbggpiocm_pin_setflags(device_get_parent(dev), dev, pin, flags);
|
||||
sc->gpio_setup[pin].gp_flags = flags;
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
static int
|
||||
lbggpio_pin_get(device_t dev, uint32_t pin, uint32_t *value)
|
||||
{
|
||||
struct lbggpio_softc *sc = device_get_softc(dev);
|
||||
|
||||
if (value == NULL)
|
||||
return (EINVAL);
|
||||
|
||||
if (pin >= sc->npins)
|
||||
return (EINVAL);
|
||||
|
||||
return (lbggpiocm_pin_get(device_get_parent(dev), dev, pin, value));
|
||||
}
|
||||
|
||||
static int
|
||||
lbggpio_pin_set(device_t dev, uint32_t pin, uint32_t value)
|
||||
{
|
||||
struct lbggpio_softc *sc = device_get_softc(dev);
|
||||
|
||||
if (pin >= sc->npins)
|
||||
return (EINVAL);
|
||||
|
||||
return (lbggpiocm_pin_set(device_get_parent(dev), dev, pin, value));
|
||||
}
|
||||
|
||||
static int
|
||||
lbggpio_pin_toggle(device_t dev, uint32_t pin)
|
||||
{
|
||||
struct lbggpio_softc *sc = device_get_softc(dev);
|
||||
|
||||
if (pin >= sc->npins)
|
||||
return (EINVAL);
|
||||
|
||||
return (lbggpiocm_pin_toggle(device_get_parent(dev), dev, pin));
|
||||
}
|
||||
|
||||
static int
|
||||
lbggpio_probe(device_t dev)
|
||||
{
|
||||
struct lbggpio_softc *sc = device_get_softc(dev);
|
||||
/* X is a placeholder for the actual one letter group name. */
|
||||
static char desc[] = "LewisBurg GPIO Group X";
|
||||
|
||||
sc->npins = lbggpiocm_get_group_npins(device_get_parent(dev), dev);
|
||||
sc->grpname = lbggpiocm_get_group_name(device_get_parent(dev), dev);
|
||||
if (sc->npins <= 0)
|
||||
return (ENXIO);
|
||||
|
||||
desc[sizeof(desc)-2] = sc->grpname;
|
||||
device_set_desc_copy(dev, desc);
|
||||
return (BUS_PROBE_DEFAULT);
|
||||
}
|
||||
|
||||
static int
|
||||
lbggpio_attach(device_t dev)
|
||||
{
|
||||
uint32_t i;
|
||||
struct lbggpio_softc *sc;
|
||||
|
||||
sc = device_get_softc(dev);
|
||||
/* GPIO config */
|
||||
for (i = 0; i < sc->npins; ++i) {
|
||||
sc->gpio_setup[i].gp_pin = i;
|
||||
snprintf(sc->gpio_setup[i].gp_name,
|
||||
sizeof(sc->gpio_setup[i].gp_name),
|
||||
"GPIO %c%u", sc->grpname, i);
|
||||
sc->gpio_setup[i].gp_caps = GPIO_PIN_INPUT | GPIO_PIN_OUTPUT;
|
||||
}
|
||||
|
||||
/* support gpio */
|
||||
sc->sc_busdev = gpiobus_attach_bus(dev);
|
||||
if (sc->sc_busdev == NULL)
|
||||
return (ENXIO);
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
static int
|
||||
lbggpio_detach(device_t dev)
|
||||
{
|
||||
struct lbggpio_softc *sc;
|
||||
|
||||
sc = device_get_softc(dev);
|
||||
|
||||
if (sc->sc_busdev)
|
||||
gpiobus_detach_bus(dev);
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
static device_method_t lbggpio_methods[] = {
|
||||
/* Device interface */
|
||||
DEVMETHOD(device_probe, lbggpio_probe),
|
||||
DEVMETHOD(device_attach, lbggpio_attach),
|
||||
DEVMETHOD(device_detach, lbggpio_detach),
|
||||
|
||||
/* GPIO protocol */
|
||||
DEVMETHOD(gpio_get_bus, lbggpio_get_bus),
|
||||
DEVMETHOD(gpio_pin_max, lbggpio_pin_max),
|
||||
DEVMETHOD(gpio_pin_getcaps, lbggpio_pin_getcaps),
|
||||
DEVMETHOD(gpio_pin_getflags, lbggpio_pin_getflags),
|
||||
DEVMETHOD(gpio_pin_setflags, lbggpio_pin_setflags),
|
||||
DEVMETHOD(gpio_pin_getname, lbggpio_pin_getname),
|
||||
DEVMETHOD(gpio_pin_set, lbggpio_pin_set),
|
||||
DEVMETHOD(gpio_pin_get, lbggpio_pin_get),
|
||||
DEVMETHOD(gpio_pin_toggle, lbggpio_pin_toggle),
|
||||
|
||||
DEVMETHOD_END
|
||||
};
|
||||
|
||||
static driver_t lbggpio_driver = {
|
||||
"gpio",
|
||||
lbggpio_methods,
|
||||
sizeof(struct lbggpio_softc)
|
||||
};
|
||||
|
||||
static devclass_t lbggpio_devclass;
|
||||
|
||||
DRIVER_MODULE(lbggpio, lbggpiocm, lbggpio_driver, lbggpio_devclass, NULL, NULL);
|
||||
MODULE_DEPEND(lbggpio, gpiobus, 1, 1, 1);
|
351
sys/dev/p2sb/lewisburg_gpiocm.c
Normal file
351
sys/dev/p2sb/lewisburg_gpiocm.c
Normal file
@ -0,0 +1,351 @@
|
||||
/*-
|
||||
* Copyright (c) 2018 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.
|
||||
*/
|
||||
|
||||
#include <sys/param.h>
|
||||
#include <sys/module.h>
|
||||
#include <sys/systm.h>
|
||||
#include <sys/errno.h>
|
||||
#include <sys/kernel.h>
|
||||
#include <sys/malloc.h>
|
||||
#include <sys/bus.h>
|
||||
#include <sys/gpio.h>
|
||||
|
||||
#include <machine/bus.h>
|
||||
#include <sys/rman.h>
|
||||
#include <machine/resource.h>
|
||||
|
||||
#include "gpio_if.h"
|
||||
|
||||
#include "lewisburg_gpiocm.h"
|
||||
#include "p2sb.h"
|
||||
|
||||
#define PADBAR 0x00c
|
||||
|
||||
#define PADCFG0_GPIORXDIS (1<<9)
|
||||
#define PADCFG0_GPIOTXDIS (1<<8)
|
||||
#define PADCFG0_GPIORXSTATE (1<<1)
|
||||
#define PADCFG0_GPIOTXSTATE (1<<0)
|
||||
|
||||
#define MAX_PAD_PER_GROUP 24
|
||||
|
||||
#define LBGGPIOCM_READ(sc, reg) p2sb_port_read_4(sc->p2sb, sc->port, reg)
|
||||
#define LBGGPIOCM_WRITE(sc, reg, val) \
|
||||
p2sb_port_write_4(sc->p2sb, sc->port, reg, val)
|
||||
#define LBGGPIOCM_LOCK(sc) p2sb_lock(sc->p2sb)
|
||||
#define LBGGPIOCM_UNLOCK(sc) p2sb_unlock(sc->p2sb)
|
||||
|
||||
struct lbggroup {
|
||||
int groupid;
|
||||
int npins;
|
||||
int pins_off;
|
||||
device_t dev;
|
||||
char grpname;
|
||||
};
|
||||
|
||||
struct lbgcommunity {
|
||||
uint8_t npins;
|
||||
const char *name;
|
||||
uint32_t pad_off;
|
||||
struct lbggroup groups[3];
|
||||
int ngroups;
|
||||
const char *grpnames;
|
||||
};
|
||||
#define LBG_COMMUNITY(n, np, g) \
|
||||
{ \
|
||||
.name = n, \
|
||||
.npins = np, \
|
||||
.grpnames = g, \
|
||||
}
|
||||
|
||||
static struct lbgcommunity lbg_communities[] = {
|
||||
LBG_COMMUNITY("LewisBurg GPIO Community 0", 72, "ABF"),
|
||||
LBG_COMMUNITY("LewisBurg GPIO Community 1", 61, "CDE"),
|
||||
LBG_COMMUNITY("LewisBurg GPIO Community 2", 0, ""),
|
||||
LBG_COMMUNITY("LewisBurg GPIO Community 3", 12, "I"),
|
||||
LBG_COMMUNITY("LewisBurg GPIO Community 4", 36, "JK"),
|
||||
LBG_COMMUNITY("LewisBurg GPIO Community 5", 66, "GHL"),
|
||||
};
|
||||
|
||||
struct lbggpiocm_softc
|
||||
{
|
||||
int port;
|
||||
device_t p2sb;
|
||||
struct lbgcommunity *community;
|
||||
};
|
||||
|
||||
static struct lbggroup *lbggpiocm_get_group(struct lbggpiocm_softc *sc,
|
||||
device_t child);
|
||||
|
||||
static __inline struct lbggroup *
|
||||
lbggpiocm_get_group(struct lbggpiocm_softc *sc, device_t child)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < sc->community->ngroups; ++i)
|
||||
if (sc->community->groups[i].dev == child)
|
||||
return (&sc->community->groups[i]);
|
||||
return (NULL);
|
||||
}
|
||||
|
||||
|
||||
static __inline uint32_t
|
||||
lbggpiocm_getpad(struct lbggpiocm_softc *sc, uint32_t pin)
|
||||
{
|
||||
|
||||
if (pin >= sc->community->npins)
|
||||
return (0);
|
||||
|
||||
return (sc->community->pad_off + 2 * 4 * pin);
|
||||
}
|
||||
|
||||
int
|
||||
lbggpiocm_get_group_npins(device_t dev, device_t child)
|
||||
{
|
||||
struct lbggpiocm_softc *sc = device_get_softc(dev);
|
||||
struct lbggroup *group;
|
||||
|
||||
group = lbggpiocm_get_group(sc, child);
|
||||
if (group != NULL)
|
||||
return (group->npins);
|
||||
return (-1);
|
||||
}
|
||||
|
||||
char
|
||||
lbggpiocm_get_group_name(device_t dev, device_t child)
|
||||
{
|
||||
struct lbggpiocm_softc *sc = device_get_softc(dev);
|
||||
struct lbggroup *group;
|
||||
|
||||
group = lbggpiocm_get_group(sc, child);
|
||||
if (group != NULL)
|
||||
return (group->grpname);
|
||||
return ('\0');
|
||||
}
|
||||
|
||||
static int
|
||||
lbggpiocm_pin2cpin(struct lbggpiocm_softc *sc, device_t child, uint32_t pin)
|
||||
{
|
||||
struct lbggroup *group;
|
||||
|
||||
group = lbggpiocm_get_group(sc, child);
|
||||
if (group != NULL)
|
||||
return (pin + group->pins_off);
|
||||
return (-1);
|
||||
}
|
||||
|
||||
int
|
||||
lbggpiocm_pin_setflags(device_t dev, device_t child, uint32_t pin, uint32_t flags)
|
||||
{
|
||||
struct lbggpiocm_softc *sc = device_get_softc(dev);
|
||||
uint32_t padreg, padval;
|
||||
int rpin;
|
||||
|
||||
if ((flags & (GPIO_PIN_INPUT | GPIO_PIN_OUTPUT)) ==
|
||||
(GPIO_PIN_INPUT | GPIO_PIN_OUTPUT))
|
||||
return (EINVAL);
|
||||
|
||||
if ((flags & (GPIO_PIN_INPUT | GPIO_PIN_OUTPUT)) == 0)
|
||||
return (EINVAL);
|
||||
|
||||
rpin = lbggpiocm_pin2cpin(sc, child, pin);
|
||||
if (rpin < 0)
|
||||
return (EINVAL);
|
||||
|
||||
padreg = lbggpiocm_getpad(sc, rpin);
|
||||
|
||||
LBGGPIOCM_LOCK(sc);
|
||||
padval = LBGGPIOCM_READ(sc, padreg);
|
||||
|
||||
if (flags & GPIO_PIN_INPUT) {
|
||||
padval &= ~PADCFG0_GPIORXDIS;
|
||||
padval |= PADCFG0_GPIOTXDIS;
|
||||
} else if (flags & GPIO_PIN_OUTPUT) {
|
||||
padval &= ~PADCFG0_GPIOTXDIS;
|
||||
padval |= PADCFG0_GPIORXDIS;
|
||||
}
|
||||
|
||||
LBGGPIOCM_WRITE(sc, padreg, padval);
|
||||
LBGGPIOCM_UNLOCK(sc);
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
int
|
||||
lbggpiocm_pin_get(device_t dev, device_t child, uint32_t pin, uint32_t *value)
|
||||
{
|
||||
struct lbggpiocm_softc *sc = device_get_softc(dev);
|
||||
uint32_t padreg, val;
|
||||
int rpin;
|
||||
|
||||
if (value == NULL)
|
||||
return (EINVAL);
|
||||
|
||||
rpin = lbggpiocm_pin2cpin(sc, child, pin);
|
||||
if (rpin < 0)
|
||||
return (EINVAL);
|
||||
|
||||
padreg = lbggpiocm_getpad(sc, rpin);
|
||||
|
||||
LBGGPIOCM_LOCK(sc);
|
||||
val = LBGGPIOCM_READ(sc, padreg);
|
||||
LBGGPIOCM_UNLOCK(sc);
|
||||
|
||||
if (!(val & PADCFG0_GPIOTXDIS))
|
||||
*value = !!(val & PADCFG0_GPIOTXSTATE);
|
||||
else
|
||||
*value = !!(val & PADCFG0_GPIORXSTATE);
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
int
|
||||
lbggpiocm_pin_set(device_t dev, device_t child, uint32_t pin, uint32_t value)
|
||||
{
|
||||
struct lbggpiocm_softc *sc = device_get_softc(dev);
|
||||
uint32_t padreg, padcfg;
|
||||
int rpin;
|
||||
|
||||
rpin = lbggpiocm_pin2cpin(sc, child, pin);
|
||||
if (rpin < 0)
|
||||
return (EINVAL);
|
||||
|
||||
padreg = lbggpiocm_getpad(sc, rpin);
|
||||
|
||||
LBGGPIOCM_LOCK(sc);
|
||||
|
||||
padcfg = LBGGPIOCM_READ(sc, padreg);
|
||||
if (value)
|
||||
padcfg |= PADCFG0_GPIOTXSTATE;
|
||||
else
|
||||
padcfg &= ~PADCFG0_GPIOTXSTATE;
|
||||
LBGGPIOCM_WRITE(sc, padreg, padcfg);
|
||||
|
||||
LBGGPIOCM_UNLOCK(sc);
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
int
|
||||
lbggpiocm_pin_toggle(device_t dev, device_t child, uint32_t pin)
|
||||
{
|
||||
struct lbggpiocm_softc *sc = device_get_softc(dev);
|
||||
uint32_t padreg, padcfg;
|
||||
int rpin;
|
||||
|
||||
rpin = lbggpiocm_pin2cpin(sc, child, pin);
|
||||
if (rpin < 0)
|
||||
return (EINVAL);
|
||||
|
||||
padreg = lbggpiocm_getpad(sc, rpin);
|
||||
|
||||
LBGGPIOCM_LOCK(sc);
|
||||
padcfg = LBGGPIOCM_READ(sc, padreg);
|
||||
padcfg ^= PADCFG0_GPIOTXSTATE;
|
||||
LBGGPIOCM_WRITE(sc, padreg, padcfg);
|
||||
|
||||
LBGGPIOCM_UNLOCK(sc);
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
static int
|
||||
lbggpiocm_probe(device_t dev)
|
||||
{
|
||||
struct lbggpiocm_softc *sc = device_get_softc(dev);
|
||||
int unit;
|
||||
|
||||
sc->p2sb = device_get_parent(dev);
|
||||
unit = device_get_unit(dev);
|
||||
KASSERT(unit < nitems(lbg_communities), ("Wrong number of devices or communities"));
|
||||
sc->port = p2sb_get_port(sc->p2sb, unit);
|
||||
sc->community = &lbg_communities[unit];
|
||||
if (sc->port < 0)
|
||||
return (ENXIO);
|
||||
|
||||
device_set_desc(dev, sc->community->name);
|
||||
return (BUS_PROBE_DEFAULT);
|
||||
}
|
||||
|
||||
static int
|
||||
lbggpiocm_attach(device_t dev)
|
||||
{
|
||||
uint32_t npins;
|
||||
struct lbggpiocm_softc *sc;
|
||||
struct lbggroup *group;
|
||||
int i;
|
||||
|
||||
sc = device_get_softc(dev);
|
||||
if (sc->community->npins == 0)
|
||||
return (ENXIO);
|
||||
|
||||
LBGGPIOCM_LOCK(sc);
|
||||
sc->community->pad_off = LBGGPIOCM_READ(sc, PADBAR);
|
||||
LBGGPIOCM_UNLOCK(sc);
|
||||
|
||||
npins = sc->community->npins;
|
||||
for (i = 0; i < nitems(sc->community->groups) && npins > 0; ++i) {
|
||||
group = &sc->community->groups[i];
|
||||
|
||||
group->groupid = i;
|
||||
group->grpname = sc->community->grpnames[i];
|
||||
group->pins_off = i * MAX_PAD_PER_GROUP;
|
||||
group->npins = npins < MAX_PAD_PER_GROUP ? npins :
|
||||
MAX_PAD_PER_GROUP;
|
||||
npins -= group->npins;
|
||||
group->dev = device_add_child(dev, "gpio", -1);
|
||||
}
|
||||
sc->community->ngroups = i;
|
||||
return (bus_generic_attach(dev));
|
||||
}
|
||||
|
||||
static int
|
||||
lbggpiocm_detach(device_t dev)
|
||||
{
|
||||
int error;
|
||||
|
||||
error = device_delete_children(dev);
|
||||
if (error)
|
||||
return (error);
|
||||
|
||||
return (bus_generic_detach(dev));
|
||||
}
|
||||
|
||||
static device_method_t lbggpiocm_methods[] = {
|
||||
/* Device interface */
|
||||
DEVMETHOD(device_probe, lbggpiocm_probe),
|
||||
DEVMETHOD(device_attach, lbggpiocm_attach),
|
||||
DEVMETHOD(device_detach, lbggpiocm_detach),
|
||||
|
||||
DEVMETHOD_END
|
||||
};
|
||||
|
||||
static driver_t lbggpiocm_driver = {
|
||||
"lbggpiocm",
|
||||
lbggpiocm_methods,
|
||||
sizeof(struct lbggpiocm_softc)
|
||||
};
|
||||
static devclass_t lbggpiocm_devclass;
|
||||
DRIVER_MODULE(lbggpiocm, p2sb, lbggpiocm_driver, lbggpiocm_devclass, NULL, NULL);
|
12
sys/dev/p2sb/lewisburg_gpiocm.h
Normal file
12
sys/dev/p2sb/lewisburg_gpiocm.h
Normal file
@ -0,0 +1,12 @@
|
||||
#ifndef __LEWISBURG_GPIOCM_H__
|
||||
#define __LEWISBURG_GPIOCM_H__
|
||||
|
||||
int lbggpiocm_get_group_npins(device_t dev, device_t child);
|
||||
char lbggpiocm_get_group_name(device_t dev, device_t child);
|
||||
|
||||
int lbggpiocm_pin_setflags(device_t, device_t, uint32_t, uint32_t);
|
||||
int lbggpiocm_pin_get(device_t, device_t, uint32_t, uint32_t *);
|
||||
int lbggpiocm_pin_set(device_t, device_t, uint32_t, uint32_t);
|
||||
int lbggpiocm_pin_toggle(device_t, device_t, uint32_t);
|
||||
|
||||
#endif /* __LEWISBURG_GPIOCM_H__ */
|
215
sys/dev/p2sb/p2sb.c
Normal file
215
sys/dev/p2sb/p2sb.c
Normal file
@ -0,0 +1,215 @@
|
||||
/*-
|
||||
* Copyright (c) 2018 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.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Implementation of Primary to Sideband bridge (P2SB), the documentation is available here :
|
||||
* https://www.intel.com/content/dam/www/public/us/en/documents/datasheets/c620-series-chipset-datasheet.pdf
|
||||
* section 36.9 P2SB Bridge.
|
||||
* This device exposes a 16MB memory block, this block is composed of 256 64KB blocks called ports.
|
||||
* The indexes of this array (target port ID) can be found on the Table 36-10 of the documentation.
|
||||
*/
|
||||
|
||||
#include <sys/param.h>
|
||||
#include <sys/module.h>
|
||||
#include <sys/systm.h>
|
||||
#include <sys/errno.h>
|
||||
#include <sys/kernel.h>
|
||||
#include <sys/lock.h>
|
||||
#include <sys/malloc.h>
|
||||
#include <sys/mutex.h>
|
||||
#include <sys/bus.h>
|
||||
|
||||
#include <machine/bus.h>
|
||||
#include <sys/rman.h>
|
||||
#include <machine/resource.h>
|
||||
|
||||
#include <dev/pci/pcivar.h>
|
||||
#include <dev/pci/pcireg.h>
|
||||
|
||||
#include "p2sb.h"
|
||||
|
||||
#define PCI_PRODUCT_LEWISBURG_P2SB 0xa1a08086
|
||||
|
||||
#define P2SB_PORT2ADDRESS_SHIFT 16
|
||||
#define P2SB_PORT_ADDRESS(port) ((uint32_t)port << P2SB_PORT2ADDRESS_SHIFT)
|
||||
|
||||
static const uint8_t lbg_communities[] = {
|
||||
0xAF, 0xAE, 0xAD, 0xAC, 0xAB, 0x11
|
||||
};
|
||||
|
||||
/* The softc holds our per-instance data. */
|
||||
struct p2sb_softc {
|
||||
device_t dev;
|
||||
int rid;
|
||||
struct resource *res;
|
||||
struct intel_community *communities;
|
||||
int ncommunities;
|
||||
struct mtx mutex;
|
||||
};
|
||||
|
||||
int
|
||||
p2sb_get_port(device_t dev, int unit)
|
||||
{
|
||||
|
||||
if (unit >= nitems(lbg_communities))
|
||||
return (EINVAL);
|
||||
return (lbg_communities[unit]);
|
||||
}
|
||||
|
||||
uint32_t
|
||||
p2sb_port_read_4(device_t dev, uint8_t port, uint32_t reg)
|
||||
{
|
||||
struct p2sb_softc *sc;
|
||||
|
||||
KASSERT(reg < (1<<P2SB_PORT2ADDRESS_SHIFT), ("register out of port"));
|
||||
sc = device_get_softc(dev);
|
||||
return (bus_read_4(sc->res, P2SB_PORT_ADDRESS(port) + reg));
|
||||
}
|
||||
|
||||
void
|
||||
p2sb_port_write_4(device_t dev, uint8_t port, uint32_t reg, uint32_t val)
|
||||
{
|
||||
struct p2sb_softc *sc;
|
||||
|
||||
KASSERT(reg < (1<<P2SB_PORT2ADDRESS_SHIFT), ("register out of port"));
|
||||
sc = device_get_softc(dev);
|
||||
bus_write_4(sc->res, P2SB_PORT_ADDRESS(port) + reg, val);
|
||||
}
|
||||
|
||||
void
|
||||
p2sb_lock(device_t dev)
|
||||
{
|
||||
struct p2sb_softc *sc;
|
||||
|
||||
sc = device_get_softc(dev);
|
||||
mtx_lock_spin(&sc->mutex);
|
||||
}
|
||||
|
||||
void
|
||||
p2sb_unlock(device_t dev)
|
||||
{
|
||||
struct p2sb_softc *sc;
|
||||
|
||||
sc = device_get_softc(dev);
|
||||
mtx_unlock_spin(&sc->mutex);
|
||||
}
|
||||
|
||||
|
||||
static int
|
||||
p2sb_probe(device_t dev)
|
||||
{
|
||||
|
||||
if (pci_get_devid(dev) == PCI_PRODUCT_LEWISBURG_P2SB) {
|
||||
device_set_desc(dev, "Lewisburg P2SB");
|
||||
return (BUS_PROBE_DEFAULT);
|
||||
}
|
||||
return (ENXIO);
|
||||
}
|
||||
|
||||
/* Attach function is only called if the probe is successful. */
|
||||
|
||||
static int
|
||||
p2sb_attach(device_t dev)
|
||||
{
|
||||
struct p2sb_softc *sc;
|
||||
int i;
|
||||
|
||||
sc = device_get_softc(dev);
|
||||
sc->dev = dev;
|
||||
sc->rid = PCIR_BAR(0);
|
||||
sc->res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &sc->rid, RF_ACTIVE);
|
||||
if (sc->res == NULL) {
|
||||
device_printf(dev, "Could not allocate memory.\n");
|
||||
return (ENXIO);
|
||||
}
|
||||
mtx_init(&sc->mutex, device_get_nameunit(dev), NULL, MTX_SPIN);
|
||||
for (i = 0; i < nitems(lbg_communities); ++i)
|
||||
device_add_child(dev, "lbggpiocm", i);
|
||||
|
||||
return (bus_generic_attach(dev));
|
||||
}
|
||||
|
||||
/* Detach device. */
|
||||
|
||||
static int
|
||||
p2sb_detach(device_t dev)
|
||||
{
|
||||
struct p2sb_softc *sc;
|
||||
|
||||
/* Teardown the state in our softc created in our attach routine. */
|
||||
device_delete_children(dev);
|
||||
sc = device_get_softc(dev);
|
||||
mtx_destroy(&sc->mutex);
|
||||
if (sc->res != NULL)
|
||||
bus_release_resource(dev, SYS_RES_MEMORY, sc->rid, sc->res);
|
||||
return (0);
|
||||
}
|
||||
|
||||
/* Called during system shutdown after sync. */
|
||||
|
||||
static int
|
||||
p2sb_shutdown(device_t dev)
|
||||
{
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
/*
|
||||
* Device suspend routine.
|
||||
*/
|
||||
static int
|
||||
p2sb_suspend(device_t dev)
|
||||
{
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
/*
|
||||
* Device resume routine.
|
||||
*/
|
||||
static int
|
||||
p2sb_resume(device_t dev)
|
||||
{
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
static device_method_t p2sb_methods[] = {
|
||||
/* Device interface */
|
||||
DEVMETHOD(device_probe, p2sb_probe),
|
||||
DEVMETHOD(device_attach, p2sb_attach),
|
||||
DEVMETHOD(device_detach, p2sb_detach),
|
||||
DEVMETHOD(device_shutdown, p2sb_shutdown),
|
||||
DEVMETHOD(device_suspend, p2sb_suspend),
|
||||
DEVMETHOD(device_resume, p2sb_resume),
|
||||
|
||||
DEVMETHOD_END
|
||||
};
|
||||
|
||||
static devclass_t p2sb_devclass;
|
||||
|
||||
DEFINE_CLASS_0(p2sb, p2sb_driver, p2sb_methods, sizeof(struct p2sb_softc));
|
||||
DRIVER_MODULE(p2sb, pci, p2sb_driver, p2sb_devclass, 0, 0);
|
11
sys/dev/p2sb/p2sb.h
Normal file
11
sys/dev/p2sb/p2sb.h
Normal file
@ -0,0 +1,11 @@
|
||||
#ifndef __P2SB_H__
|
||||
#define __P2SB_H__
|
||||
|
||||
void p2sb_lock(device_t dev);
|
||||
void p2sb_unlock(device_t dev);
|
||||
|
||||
uint32_t p2sb_port_read_4(device_t dev, uint8_t port, uint32_t reg);
|
||||
void p2sb_port_write_4(device_t dev, uint8_t port, uint32_t reg, uint32_t val);
|
||||
int p2sb_get_port(device_t dev, int unit);
|
||||
|
||||
#endif /* __P2SB_H__ */
|
@ -358,6 +358,7 @@ SUBDIR= \
|
||||
stge \
|
||||
${_sume} \
|
||||
${_superio} \
|
||||
${_p2sb} \
|
||||
${_sym} \
|
||||
${_syscons} \
|
||||
sysvipc \
|
||||
@ -662,6 +663,7 @@ _safe= safe
|
||||
_speaker= speaker
|
||||
_splash= splash
|
||||
_sppp= sppp
|
||||
_p2sb= p2sb
|
||||
_wbwd= wbwd
|
||||
|
||||
_aac= aac
|
||||
@ -755,6 +757,7 @@ _sfxge= sfxge
|
||||
_sgx= sgx
|
||||
_sgx_linux= sgx_linux
|
||||
_smartpqi= smartpqi
|
||||
_p2sb= p2sb
|
||||
|
||||
.if ${MK_BHYVE} != "no" || defined(ALL_MODULES)
|
||||
.if ${KERN_OPTS:MSMP}
|
||||
|
7
sys/modules/p2sb/Makefile
Normal file
7
sys/modules/p2sb/Makefile
Normal file
@ -0,0 +1,7 @@
|
||||
.PATH: ${.CURDIR}/../../dev/p2sb
|
||||
|
||||
KMOD= p2sb
|
||||
SRCS= p2sb.c lewisburg_gpio.c lewisburg_gpiocm.c
|
||||
SRCS+= device_if.h bus_if.h pci_if.h gpio_if.h
|
||||
|
||||
.include <bsd.kmod.mk>
|
Loading…
Reference in New Issue
Block a user