git: 3706af423ff2 - main - Add support for Lewisburg GPIO through P2SB

Wojciech Macek wma at FreeBSD.org
Thu Aug 26 09:16:16 UTC 2021


The branch main has been updated by wma:

URL: https://cgit.FreeBSD.org/src/commit/?id=3706af423ff257540dfe88bcb91fb0ab54e60946

commit 3706af423ff257540dfe88bcb91fb0ab54e60946
Author:     Arnaud Ysmal <arnaud.ysmal at stormshield.eu>
AuthorDate: 2021-06-29 13:54:07 +0000
Commit:     Wojciech Macek <wma at FreeBSD.org>
CommitDate: 2021-08-26 09:00:39 +0000

    Add support for Lewisburg GPIO through P2SB
    
    Obtained from:          Semihalf
    Sponsored by:           Stormshield
    Differential revision:  https://reviews.freebsd.org/D31269
---
 sys/conf/files.amd64            |   3 +
 sys/dev/p2sb/lewisburg_gpio.c   | 269 ++++++++++++++++++++++++++++++
 sys/dev/p2sb/lewisburg_gpiocm.c | 351 ++++++++++++++++++++++++++++++++++++++++
 sys/dev/p2sb/lewisburg_gpiocm.h |  12 ++
 sys/dev/p2sb/p2sb.c             | 215 ++++++++++++++++++++++++
 sys/dev/p2sb/p2sb.h             |  11 ++
 sys/modules/Makefile            |   3 +
 sys/modules/p2sb/Makefile       |   7 +
 8 files changed, 871 insertions(+)

diff --git a/sys/conf/files.amd64 b/sys/conf/files.amd64
index d58cf7927206..1bcfaa05d0ba 100644
--- a/sys/conf/files.amd64
+++ b/sys/conf/files.amd64
@@ -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
diff --git a/sys/dev/p2sb/lewisburg_gpio.c b/sys/dev/p2sb/lewisburg_gpio.c
new file mode 100644
index 000000000000..d0b08520eb87
--- /dev/null
+++ b/sys/dev/p2sb/lewisburg_gpio.c
@@ -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);
diff --git a/sys/dev/p2sb/lewisburg_gpiocm.c b/sys/dev/p2sb/lewisburg_gpiocm.c
new file mode 100644
index 000000000000..4a046a50aed4
--- /dev/null
+++ b/sys/dev/p2sb/lewisburg_gpiocm.c
@@ -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);
diff --git a/sys/dev/p2sb/lewisburg_gpiocm.h b/sys/dev/p2sb/lewisburg_gpiocm.h
new file mode 100644
index 000000000000..7fcbf919949b
--- /dev/null
+++ b/sys/dev/p2sb/lewisburg_gpiocm.h
@@ -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__ */
diff --git a/sys/dev/p2sb/p2sb.c b/sys/dev/p2sb/p2sb.c
new file mode 100644
index 000000000000..80836cbb2f59
--- /dev/null
+++ b/sys/dev/p2sb/p2sb.c
@@ -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);
diff --git a/sys/dev/p2sb/p2sb.h b/sys/dev/p2sb/p2sb.h
new file mode 100644
index 000000000000..7c060a02fcf9
--- /dev/null
+++ b/sys/dev/p2sb/p2sb.h
@@ -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__ */
diff --git a/sys/modules/Makefile b/sys/modules/Makefile
index 7ef3c5a477ce..585e2bdc80ca 100644
--- a/sys/modules/Makefile
+++ b/sys/modules/Makefile
@@ -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}
diff --git a/sys/modules/p2sb/Makefile b/sys/modules/p2sb/Makefile
new file mode 100644
index 000000000000..8f4d604f73d5
--- /dev/null
+++ b/sys/modules/p2sb/Makefile
@@ -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>


More information about the dev-commits-src-all mailing list