aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorArnaud Ysmal <arnaud.ysmal@stormshield.eu>2021-06-29 13:54:07 +0000
committerWojciech Macek <wma@FreeBSD.org>2021-08-26 09:00:39 +0000
commit3706af423ff257540dfe88bcb91fb0ab54e60946 (patch)
treeb5b83f0f4f553e4339852ee1c6f1da7b5c9d311c
parent3daa8e165c661c1b45e759f4997f447384c15446 (diff)
downloadsrc-3706af423ff257540dfe88bcb91fb0ab54e60946.tar.gz
src-3706af423ff257540dfe88bcb91fb0ab54e60946.zip
Add support for Lewisburg GPIO through P2SB
Obtained from: Semihalf Sponsored by: Stormshield Differential revision: https://reviews.freebsd.org/D31269
-rw-r--r--sys/conf/files.amd643
-rw-r--r--sys/dev/p2sb/lewisburg_gpio.c269
-rw-r--r--sys/dev/p2sb/lewisburg_gpiocm.c351
-rw-r--r--sys/dev/p2sb/lewisburg_gpiocm.h12
-rw-r--r--sys/dev/p2sb/p2sb.c215
-rw-r--r--sys/dev/p2sb/p2sb.h11
-rw-r--r--sys/modules/Makefile3
-rw-r--r--sys/modules/p2sb/Makefile7
8 files changed, 871 insertions, 0 deletions
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>