svn commit: r357258 - in head/sys: conf dev/xilinx

Ruslan Bukin br at FreeBSD.org
Wed Jan 29 16:52:13 UTC 2020


Author: br
Date: Wed Jan 29 16:52:12 2020
New Revision: 357258
URL: https://svnweb.freebsd.org/changeset/base/357258

Log:
  Add driver for Xilinx XDMA PCIe Bridge found in the U.S. Government
  Furnished Equipment (GFE) riscv cores.
  
  GFE cores are synthesized on the Xilinx Virtex UltraScale+ FPGA VCU118
  Evaluation Kit.
  
  Sponsored by:	DARPA, AFRL
  Differential Revision:	https://reviews.freebsd.org/D23337

Added:
  head/sys/dev/xilinx/xlnx_pcib.c   (contents, props changed)
  head/sys/dev/xilinx/xlnx_pcib.h   (contents, props changed)
Modified:
  head/sys/conf/files.riscv

Modified: head/sys/conf/files.riscv
==============================================================================
--- head/sys/conf/files.riscv	Wed Jan 29 16:24:16 2020	(r357257)
+++ head/sys/conf/files.riscv	Wed Jan 29 16:52:12 2020	(r357258)
@@ -14,6 +14,7 @@ dev/uart/uart_dev_lowrisc.c	optional	uart_lowrisc
 dev/xilinx/axi_quad_spi.c	optional	xilinx_spi
 dev/xilinx/axidma.c		optional	axidma xdma
 dev/xilinx/if_xae.c		optional	xae
+dev/xilinx/xlnx_pcib.c		optional	pci fdt xlnx_pcib
 kern/kern_clocksource.c		standard
 kern/msi_if.m			standard
 kern/pic_if.m			standard

Added: head/sys/dev/xilinx/xlnx_pcib.c
==============================================================================
--- /dev/null	00:00:00 1970	(empty, because file is newly added)
+++ head/sys/dev/xilinx/xlnx_pcib.c	Wed Jan 29 16:52:12 2020	(r357258)
@@ -0,0 +1,794 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2020 Ruslan Bukin <br at bsdpad.com>
+ *
+ * This software was developed by SRI International and the University of
+ * Cambridge Computer Laboratory (Department of Computer Science and
+ * Technology) under DARPA contract HR0011-18-C-0016 ("ECATS"), as part of the
+ * DARPA SSITH research programme.
+ *
+ * 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 "opt_platform.h"
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/malloc.h>
+#include <sys/types.h>
+#include <sys/sysctl.h>
+#include <sys/kernel.h>
+#include <sys/rman.h>
+#include <sys/module.h>
+#include <sys/bus.h>
+#include <sys/endian.h>
+#include <sys/cpuset.h>
+#include <sys/mutex.h>
+#include <sys/proc.h>
+
+#include <machine/intr.h>
+#include <machine/bus.h>
+
+#include <vm/vm.h>
+#include <vm/vm_extern.h>
+#include <vm/vm_kern.h>
+#include <vm/pmap.h>
+
+#include <dev/ofw/openfirm.h>
+#include <dev/ofw/ofw_bus.h>
+#include <dev/ofw/ofw_bus_subr.h>
+
+#include <dev/pci/pcireg.h>
+#include <dev/pci/pcivar.h>
+#include <dev/pci/pci_host_generic.h>
+#include <dev/pci/pci_host_generic_fdt.h>
+#include <dev/pci/pcib_private.h>
+
+#include "xlnx_pcib.h"
+
+#include "ofw_bus_if.h"
+#include "msi_if.h"
+#include "pcib_if.h"
+#include "pic_if.h"
+
+#define	XLNX_PCIB_MAX_MSI	64
+
+static int xlnx_pcib_fdt_attach(device_t);
+static int xlnx_pcib_fdt_probe(device_t);
+static int xlnx_pcib_fdt_get_id(device_t, device_t, enum pci_id_type,
+    uintptr_t *);
+static void xlnx_pcib_msi_mask(device_t dev, struct intr_irqsrc *isrc,
+    bool mask);
+
+struct xlnx_pcib_softc {
+	struct generic_pcie_fdt_softc	fdt_sc;
+	struct resource			*res[4];
+	struct mtx			mtx;
+	vm_offset_t			msi_page;
+	struct xlnx_pcib_irqsrc		*isrcs;
+	device_t			dev;
+	void				*intr_cookie[3];
+};
+
+static struct resource_spec xlnx_pcib_spec[] = {
+	{ SYS_RES_MEMORY,	0,	RF_ACTIVE },
+	{ SYS_RES_IRQ,		0,	RF_ACTIVE },
+	{ SYS_RES_IRQ,		1,	RF_ACTIVE },
+	{ SYS_RES_IRQ,		2,	RF_ACTIVE },
+	{ -1, 0 }
+};
+
+struct xlnx_pcib_irqsrc {
+	struct intr_irqsrc	isrc;
+	u_int			irq;
+#define	XLNX_IRQ_FLAG_USED	(1 << 0)
+	u_int			flags;
+};
+
+static void
+xlnx_pcib_clear_err_interrupts(struct generic_pcie_core_softc *sc)
+{
+	uint32_t reg;
+
+	reg = bus_read_4(sc->res, XLNX_PCIE_RPERRFRR);
+
+	if (reg & RPERRFRR_VALID) {
+		device_printf(sc->dev, "Requested ID: %x\n",
+		    reg & RPERRFRR_REQ_ID_M);
+		bus_write_4(sc->res, XLNX_PCIE_RPERRFRR, ~0U);
+	}
+}
+
+static int
+xlnx_pcib_intr(void *arg)
+{
+	struct generic_pcie_fdt_softc *fdt_sc;
+	struct generic_pcie_core_softc *sc;
+	struct xlnx_pcib_softc *xlnx_sc;
+	uint32_t val, mask, status;
+
+	xlnx_sc = arg;
+	fdt_sc = &xlnx_sc->fdt_sc;
+	sc = &fdt_sc->base;
+
+	val = bus_read_4(sc->res, XLNX_PCIE_IDR);
+	mask = bus_read_4(sc->res, XLNX_PCIE_IMR);
+
+	status = val & mask;
+	if (!status)
+		return (FILTER_HANDLED);
+
+	if (status & IMR_LINK_DOWN)
+		device_printf(sc->dev, "Link down");
+
+	if (status & IMR_HOT_RESET)
+		device_printf(sc->dev, "Hot reset");
+
+	if (status & IMR_CORRECTABLE)
+		xlnx_pcib_clear_err_interrupts(sc);
+
+	if (status & IMR_FATAL)
+		xlnx_pcib_clear_err_interrupts(sc);
+
+	if (status & IMR_NON_FATAL)
+		xlnx_pcib_clear_err_interrupts(sc);
+
+	if (status & IMR_MSI) {
+		device_printf(sc->dev, "MSI interrupt");
+
+		/* FIFO mode MSI not implemented. */
+	}
+
+	if (status & IMR_INTX) {
+		device_printf(sc->dev, "INTx received");
+
+		/* Not implemented. */
+	}
+
+	if (status & IMR_SLAVE_UNSUPP_REQ)
+		device_printf(sc->dev, "Slave unsupported request");
+
+	if (status & IMR_SLAVE_UNEXP_COMPL)
+		device_printf(sc->dev, "Slave unexpected completion");
+
+	if (status & IMR_SLAVE_COMPL_TIMOUT)
+		device_printf(sc->dev, "Slave completion timeout");
+
+	if (status & IMR_SLAVE_ERROR_POISON)
+		device_printf(sc->dev, "Slave error poison");
+
+	if (status & IMR_SLAVE_COMPL_ABORT)
+		device_printf(sc->dev, "Slave completion abort");
+
+	if (status & IMR_SLAVE_ILLEG_BURST)
+		device_printf(sc->dev, "Slave illegal burst");
+
+	if (status & IMR_MASTER_DECERR)
+		device_printf(sc->dev, "Master decode error");
+
+	if (status & IMR_MASTER_SLVERR)
+		device_printf(sc->dev, "Master slave error");
+
+	bus_write_4(sc->res, XLNX_PCIE_IDR, val);
+
+	return (FILTER_HANDLED);
+}
+
+static void
+xlnx_pcib_handle_msi_intr(void *arg, int msireg)
+{ 
+	struct generic_pcie_fdt_softc *fdt_sc;
+	struct generic_pcie_core_softc *sc;
+	struct xlnx_pcib_softc *xlnx_sc;
+	struct xlnx_pcib_irqsrc *xi;
+	struct trapframe *tf;
+	int irq;
+	int reg;
+	int i;
+
+	xlnx_sc = arg;
+	fdt_sc = &xlnx_sc->fdt_sc;
+	sc = &fdt_sc->base;
+	tf = curthread->td_intr_frame;
+
+	do {
+		reg = bus_read_4(sc->res, msireg);
+
+		for (i = 0; i < sizeof(uint32_t) * 8; i++) {
+			if (reg & (1 << i)) {
+				bus_write_4(sc->res, msireg, (1 << i));
+
+				irq = i;
+				if (msireg == XLNX_PCIE_RPMSIID2)
+					irq += 32;
+
+				xi = &xlnx_sc->isrcs[irq];
+				if (intr_isrc_dispatch(&xi->isrc, tf) != 0) {
+					/* Disable stray. */
+					xlnx_pcib_msi_mask(sc->dev,
+					    &xi->isrc, 1);
+					device_printf(sc->dev,
+					    "Stray irq %u disabled\n", irq);
+				}
+			}
+		}
+	} while (reg != 0);
+}
+
+static int
+xlnx_pcib_msi0_intr(void *arg)
+{
+
+	xlnx_pcib_handle_msi_intr(arg, XLNX_PCIE_RPMSIID1);
+
+	return (FILTER_HANDLED);
+}
+
+static int
+xlnx_pcib_msi1_intr(void *arg)
+{
+
+	xlnx_pcib_handle_msi_intr(arg, XLNX_PCIE_RPMSIID2);
+
+	return (FILTER_HANDLED);
+}
+
+static int
+xlnx_pcib_register_msi(struct xlnx_pcib_softc *sc)
+{
+	const char *name;
+	int error;
+	int irq;
+
+	sc->isrcs = malloc(sizeof(*sc->isrcs) * XLNX_PCIB_MAX_MSI, M_DEVBUF,
+	    M_WAITOK | M_ZERO);
+
+	name = device_get_nameunit(sc->dev);
+
+	for (irq = 0; irq < XLNX_PCIB_MAX_MSI; irq++) {
+		sc->isrcs[irq].irq = irq;
+		error = intr_isrc_register(&sc->isrcs[irq].isrc,
+		    sc->dev, 0, "%s,%u", name, irq);
+		if (error != 0)
+			return (error); /* XXX deregister ISRCs */
+	}
+
+	if (intr_msi_register(sc->dev,
+	    OF_xref_from_node(ofw_bus_get_node(sc->dev))) != 0)
+		return (ENXIO);
+
+	return (0);
+}
+
+static void
+xlnx_pcib_init(struct xlnx_pcib_softc *sc)
+{
+	bus_addr_t addr;
+	int reg;
+
+	/* Disable interrupts. */
+	bus_write_4(sc->res[0], XLNX_PCIE_IMR, 0);
+
+	/* Clear pending interrupts.*/
+	reg = bus_read_4(sc->res[0], XLNX_PCIE_IDR);
+	bus_write_4(sc->res[0], XLNX_PCIE_IDR, reg);
+
+	/* Setup an MSI page. */
+	sc->msi_page = kmem_alloc_contig(PAGE_SIZE, M_WAITOK, 0,
+	    BUS_SPACE_MAXADDR, PAGE_SIZE, 0, VM_MEMATTR_DEFAULT);
+	addr = vtophys(sc->msi_page);
+	bus_write_4(sc->res[0], XLNX_PCIE_RPMSIBR1, (addr >> 32));
+	bus_write_4(sc->res[0], XLNX_PCIE_RPMSIBR2, (addr >>  0));
+
+	/* Enable the bridge. */
+	reg = bus_read_4(sc->res[0], XLNX_PCIE_RPSCR);
+	reg |= RPSCR_BE;
+	bus_write_4(sc->res[0], XLNX_PCIE_RPSCR, reg);
+
+	/* Enable interrupts. */
+	reg = IMR_LINK_DOWN
+		| IMR_HOT_RESET
+		| IMR_CFG_COMPL_STATUS_M
+		| IMR_CFG_TIMEOUT
+		| IMR_CORRECTABLE
+		| IMR_NON_FATAL
+		| IMR_FATAL
+		| IMR_INTX
+		| IMR_MSI
+		| IMR_SLAVE_UNSUPP_REQ
+		| IMR_SLAVE_UNEXP_COMPL
+		| IMR_SLAVE_COMPL_TIMOUT
+		| IMR_SLAVE_ERROR_POISON
+		| IMR_SLAVE_COMPL_ABORT
+		| IMR_SLAVE_ILLEG_BURST
+		| IMR_MASTER_DECERR
+		| IMR_MASTER_SLVERR;
+	bus_write_4(sc->res[0], XLNX_PCIE_IMR, reg);
+}
+
+static int
+xlnx_pcib_fdt_probe(device_t dev)
+{
+
+	if (!ofw_bus_status_okay(dev))
+		return (ENXIO);
+
+	if (ofw_bus_is_compatible(dev, "xlnx,xdma-host-3.00")) {
+		device_set_desc(dev, "Xilinx XDMA PCIe Controller");
+		return (BUS_PROBE_DEFAULT);
+	}
+
+	return (ENXIO);
+}
+
+static int
+xlnx_pcib_fdt_attach(device_t dev)
+{
+	struct xlnx_pcib_softc *sc;
+	int error;
+
+	sc = device_get_softc(dev);
+	sc->dev = dev;
+
+	mtx_init(&sc->mtx, "msi_mtx", NULL, MTX_DEF);
+
+	if (bus_alloc_resources(dev, xlnx_pcib_spec, sc->res)) {
+		device_printf(dev, "could not allocate resources\n");
+		return (ENXIO);
+	}
+
+	/* Setup MISC interrupt handler. */
+	error = bus_setup_intr(dev, sc->res[1], INTR_TYPE_MISC | INTR_MPSAFE,
+	    xlnx_pcib_intr, NULL, sc, &sc->intr_cookie[0]);
+	if (error != 0) {
+		device_printf(dev, "could not setup interrupt handler.\n");
+		return (ENXIO);
+	}
+
+	/* Setup MSI0 interrupt handler. */
+	error = bus_setup_intr(dev, sc->res[2], INTR_TYPE_MISC | INTR_MPSAFE,
+	    xlnx_pcib_msi0_intr, NULL, sc, &sc->intr_cookie[1]);
+	if (error != 0) {
+		device_printf(dev, "could not setup interrupt handler.\n");
+		return (ENXIO);
+	}
+
+	/* Setup MSI1 interrupt handler. */
+	error = bus_setup_intr(dev, sc->res[3], INTR_TYPE_MISC | INTR_MPSAFE,
+	    xlnx_pcib_msi1_intr, NULL, sc, &sc->intr_cookie[2]);
+	if (error != 0) {
+		device_printf(dev, "could not setup interrupt handler.\n");
+		return (ENXIO);
+	}
+
+	xlnx_pcib_init(sc);
+
+	/*
+	 * Allow the core driver to map registers.
+	 * We will be accessing the device memory using core_softc.
+	 */
+	bus_release_resources(dev, xlnx_pcib_spec, sc->res);
+
+	error = xlnx_pcib_register_msi(sc);
+	if (error)
+		return (error);
+
+	return (pci_host_generic_attach(dev));
+}
+
+static int
+xlnx_pcib_fdt_get_id(device_t pci, device_t child, enum pci_id_type type,
+    uintptr_t *id)
+{
+	phandle_t node;
+	int bsf;
+
+	if (type != PCI_ID_MSI)
+		return (pcib_get_id(pci, child, type, id));
+
+	node = ofw_bus_get_node(pci);
+	if (OF_hasprop(node, "msi-map"))
+		return (generic_pcie_get_id(pci, child, type, id));
+
+	bsf = pci_get_rid(child);
+	*id = (pci_get_domain(child) << PCI_RID_DOMAIN_SHIFT) | bsf;
+
+	return (0);
+}
+
+static int
+xlnx_pcib_req_valid(struct generic_pcie_core_softc *sc,
+    u_int bus, u_int slot, u_int func, u_int reg)
+{
+	bus_space_handle_t h;
+	bus_space_tag_t t;
+	uint32_t val;
+
+	t = sc->bst;
+	h = sc->bsh;
+
+	if ((bus < sc->bus_start) || (bus > sc->bus_end))
+		return (0);
+	if ((slot > PCI_SLOTMAX) || (func > PCI_FUNCMAX) ||
+	    (reg > PCIE_REGMAX))
+		return (0);
+
+	if (bus == 0 && slot > 0)
+		return (0);
+
+	val = bus_space_read_4(t, h, XLNX_PCIE_PHYSCR);
+	if ((val & PHYSCR_LINK_UP) == 0) {
+		/* Link is down */
+		return (0);
+	}
+
+	/* Valid */
+
+	return (1);
+}
+
+static uint32_t
+xlnx_pcib_read_config(device_t dev, u_int bus, u_int slot,
+    u_int func, u_int reg, int bytes)
+{
+	struct generic_pcie_fdt_softc *fdt_sc;
+	struct xlnx_pcib_softc *xlnx_sc;
+	struct generic_pcie_core_softc *sc;
+	bus_space_handle_t h;
+	bus_space_tag_t t;
+	uint64_t offset;
+	uint32_t data;
+
+	xlnx_sc = device_get_softc(dev);
+	fdt_sc = &xlnx_sc->fdt_sc;
+	sc = &fdt_sc->base;
+
+	if (!xlnx_pcib_req_valid(sc, bus, slot, func, reg))
+		return (~0U);
+
+	offset = PCIE_ADDR_OFFSET(bus - sc->bus_start, slot, func, reg);
+	t = sc->bst;
+	h = sc->bsh;
+
+	data = bus_space_read_4(t, h, offset & ~3);
+
+	switch (bytes) {
+	case 1:
+		data >>= (offset & 3) * 8;
+		data &= 0xff;
+		break;
+	case 2:
+		data >>= (offset & 3) * 8;
+		data = le16toh(data);
+		break;
+	case 4:
+		data = le32toh(data);
+		break;
+	default:
+		return (~0U);
+	}
+
+	return (data);
+}
+
+static void
+xlnx_pcib_write_config(device_t dev, u_int bus, u_int slot,
+    u_int func, u_int reg, uint32_t val, int bytes)
+{
+	struct generic_pcie_fdt_softc *fdt_sc;
+	struct xlnx_pcib_softc *xlnx_sc;
+	struct generic_pcie_core_softc *sc;
+	bus_space_handle_t h;
+	bus_space_tag_t t;
+	uint64_t offset;
+	uint32_t data;
+
+	xlnx_sc = device_get_softc(dev);
+	fdt_sc = &xlnx_sc->fdt_sc;
+	sc = &fdt_sc->base;
+
+	if (!xlnx_pcib_req_valid(sc, bus, slot, func, reg))
+		return;
+
+	offset = PCIE_ADDR_OFFSET(bus - sc->bus_start, slot, func, reg);
+
+	t = sc->bst;
+	h = sc->bsh;
+
+	/*
+	 * 32-bit access used due to a bug in the Xilinx bridge that
+	 * requires to write primary and secondary buses in one blast.
+	 *
+	 * TODO: This is probably wrong on big-endian.
+	 */
+	switch (bytes) {
+	case 1:
+		data = bus_space_read_4(t, h, offset & ~3);
+		data &= ~(0xff << ((offset & 3) * 8));
+		data |= (val & 0xff) << ((offset & 3) * 8);
+		bus_space_write_4(t, h, offset & ~3, htole32(data));
+		break;
+	case 2:
+		data = bus_space_read_4(t, h, offset & ~3);
+		data &= ~(0xffff << ((offset & 3) * 8));
+		data |= (val & 0xffff) << ((offset & 3) * 8);
+		bus_space_write_4(t, h, offset & ~3, htole32(data));
+		break;
+	case 4:
+		bus_space_write_4(t, h, offset, htole32(val));
+		break;
+	default:
+		return;
+	}
+}
+
+static int
+xlnx_pcib_alloc_msi(device_t pci, device_t child, int count, int maxcount,
+    int *irqs)
+{
+	phandle_t msi_parent;
+
+	ofw_bus_msimap(ofw_bus_get_node(pci), pci_get_rid(child), &msi_parent,
+	    NULL);
+	msi_parent = OF_xref_from_node(ofw_bus_get_node(pci));
+	return (intr_alloc_msi(pci, child, msi_parent, count, maxcount,
+	    irqs));
+}
+
+static int
+xlnx_pcib_release_msi(device_t pci, device_t child, int count, int *irqs)
+{
+	phandle_t msi_parent;
+
+	ofw_bus_msimap(ofw_bus_get_node(pci), pci_get_rid(child), &msi_parent,
+	    NULL);
+	msi_parent = OF_xref_from_node(ofw_bus_get_node(pci));
+	return (intr_release_msi(pci, child, msi_parent, count, irqs));
+}
+
+static int
+xlnx_pcib_map_msi(device_t pci, device_t child, int irq, uint64_t *addr,
+    uint32_t *data)
+{
+	phandle_t msi_parent;
+
+	ofw_bus_msimap(ofw_bus_get_node(pci), pci_get_rid(child), &msi_parent,
+	    NULL);
+	msi_parent = OF_xref_from_node(ofw_bus_get_node(pci));
+	return (intr_map_msi(pci, child, msi_parent, irq, addr, data));
+}
+
+static int
+xlnx_pcib_msi_alloc_msi(device_t dev, device_t child, int count, int maxcount,
+    device_t *pic, struct intr_irqsrc **srcs)
+{
+	struct xlnx_pcib_softc *sc;
+	int irq, end_irq, i;
+	bool found;
+
+	sc = device_get_softc(dev);
+
+	mtx_lock(&sc->mtx);
+
+	found = false;
+
+	for (irq = 0; (irq + count - 1) < XLNX_PCIB_MAX_MSI; irq++) {
+
+		/* Assume the range is valid. */
+		found = true;
+
+		/* Check this range is valid. */
+		for (end_irq = irq; end_irq < irq + count; end_irq++) {
+			if (sc->isrcs[end_irq].flags & XLNX_IRQ_FLAG_USED) {
+				/* This is already used. */
+				found = false;
+				break;
+			}
+		}
+
+		if (found)
+			break;
+	}
+
+	if (!found || irq == (XLNX_PCIB_MAX_MSI - 1)) {
+		/* Not enough interrupts were found. */
+		mtx_unlock(&sc->mtx);
+		return (ENXIO);
+	}
+
+	/* Mark the interrupt as used. */
+	for (i = 0; i < count; i++)
+		sc->isrcs[irq + i].flags |= XLNX_IRQ_FLAG_USED;
+
+	mtx_unlock(&sc->mtx);
+
+	for (i = 0; i < count; i++)
+		srcs[i] = (struct intr_irqsrc *)&sc->isrcs[irq + i];
+
+	*pic = device_get_parent(dev);
+
+	return (0);
+}
+
+static int
+xlnx_pcib_msi_release_msi(device_t dev, device_t child, int count,
+    struct intr_irqsrc **isrc)
+{
+	struct xlnx_pcib_softc *sc;
+	struct xlnx_pcib_irqsrc *xi;
+	int i;
+
+	sc = device_get_softc(dev);
+	mtx_lock(&sc->mtx);
+	for (i = 0; i < count; i++) {
+		xi = (struct xlnx_pcib_irqsrc *)isrc[i];
+
+		KASSERT(xi->flags & XLNX_IRQ_FLAG_USED,
+		    ("%s: Releasing an unused MSI interrupt", __func__));
+
+		xi->flags &= ~XLNX_IRQ_FLAG_USED;
+	}
+
+	mtx_unlock(&sc->mtx);
+	return (0);
+}
+
+static int
+xlnx_pcib_msi_map_msi(device_t dev, device_t child, struct intr_irqsrc *isrc,
+    uint64_t *addr, uint32_t *data)
+{
+	struct xlnx_pcib_softc *sc;
+	struct xlnx_pcib_irqsrc *xi;
+
+	sc = device_get_softc(dev);
+	xi = (struct xlnx_pcib_irqsrc *)isrc;
+
+	*addr = vtophys(sc->msi_page);
+	*data = xi->irq;
+
+	return (0);
+}
+
+static void
+xlnx_pcib_msi_mask(device_t dev, struct intr_irqsrc *isrc, bool mask)
+{
+	struct generic_pcie_fdt_softc *fdt_sc;
+	struct generic_pcie_core_softc *sc;
+	struct xlnx_pcib_softc *xlnx_sc;
+	struct xlnx_pcib_irqsrc *xi;
+	uint32_t msireg, irq;
+	uint32_t reg;
+
+	xlnx_sc = device_get_softc(dev);
+	fdt_sc = &xlnx_sc->fdt_sc;
+	sc = &fdt_sc->base;
+
+	xi = (struct xlnx_pcib_irqsrc *)isrc;
+
+	irq = xi->irq;
+	if (irq < 32)
+		msireg = XLNX_PCIE_RPMSIID1_MASK;
+	else
+		msireg = XLNX_PCIE_RPMSIID2_MASK;
+
+	reg = bus_read_4(sc->res, msireg);
+	if (mask)
+		reg &= ~(1 << irq);
+	else
+		reg |= (1 << irq);
+	bus_write_4(sc->res, msireg, reg);
+}
+
+static void
+xlnx_pcib_msi_disable_intr(device_t dev, struct intr_irqsrc *isrc)
+{
+
+	xlnx_pcib_msi_mask(dev, isrc, true);
+}
+
+static void
+xlnx_pcib_msi_enable_intr(device_t dev, struct intr_irqsrc *isrc)
+{
+
+	xlnx_pcib_msi_mask(dev, isrc, false);
+}
+
+static void
+xlnx_pcib_msi_post_filter(device_t dev, struct intr_irqsrc *isrc)
+{
+
+}
+
+static void
+xlnx_pcib_msi_post_ithread(device_t dev, struct intr_irqsrc *isrc)
+{
+
+	xlnx_pcib_msi_mask(dev, isrc, false);
+}
+
+static void
+xlnx_pcib_msi_pre_ithread(device_t dev, struct intr_irqsrc *isrc)
+{
+
+	xlnx_pcib_msi_mask(dev, isrc, true);
+}
+
+static int
+xlnx_pcib_msi_setup_intr(device_t dev, struct intr_irqsrc *isrc,
+    struct resource *res, struct intr_map_data *data)
+{
+
+	return (0);
+}
+
+static int
+xlnx_pcib_msi_teardown_intr(device_t dev, struct intr_irqsrc *isrc,
+    struct resource *res, struct intr_map_data *data)
+{
+
+	return (0);
+}
+
+static device_method_t xlnx_pcib_fdt_methods[] = {
+	/* Device interface */
+	DEVMETHOD(device_probe,		xlnx_pcib_fdt_probe),
+	DEVMETHOD(device_attach,	xlnx_pcib_fdt_attach),
+
+	/* pcib interface */
+	DEVMETHOD(pcib_get_id,		xlnx_pcib_fdt_get_id),
+	DEVMETHOD(pcib_read_config,	xlnx_pcib_read_config),
+	DEVMETHOD(pcib_write_config,	xlnx_pcib_write_config),
+	DEVMETHOD(pcib_alloc_msi,	xlnx_pcib_alloc_msi),
+	DEVMETHOD(pcib_release_msi,	xlnx_pcib_release_msi),
+	DEVMETHOD(pcib_map_msi,		xlnx_pcib_map_msi),
+
+	/* MSI interface */
+	DEVMETHOD(msi_alloc_msi,		xlnx_pcib_msi_alloc_msi),
+	DEVMETHOD(msi_release_msi,		xlnx_pcib_msi_release_msi),
+	DEVMETHOD(msi_map_msi,			xlnx_pcib_msi_map_msi),
+
+	/* Interrupt controller interface */
+	DEVMETHOD(pic_disable_intr,		xlnx_pcib_msi_disable_intr),
+	DEVMETHOD(pic_enable_intr,		xlnx_pcib_msi_enable_intr),
+	DEVMETHOD(pic_setup_intr,		xlnx_pcib_msi_setup_intr),
+	DEVMETHOD(pic_teardown_intr,		xlnx_pcib_msi_teardown_intr),
+	DEVMETHOD(pic_post_filter,		xlnx_pcib_msi_post_filter),
+	DEVMETHOD(pic_post_ithread,		xlnx_pcib_msi_post_ithread),
+	DEVMETHOD(pic_pre_ithread,		xlnx_pcib_msi_pre_ithread),
+
+	/* End */
+	DEVMETHOD_END
+};
+
+DEFINE_CLASS_1(pcib, xlnx_pcib_fdt_driver, xlnx_pcib_fdt_methods,
+    sizeof(struct xlnx_pcib_softc), generic_pcie_fdt_driver);
+
+static devclass_t xlnx_pcib_fdt_devclass;
+
+DRIVER_MODULE(xlnx_pcib, simplebus, xlnx_pcib_fdt_driver,
+    xlnx_pcib_fdt_devclass, 0, 0);
+DRIVER_MODULE(xlnx_pcib, ofwbus, xlnx_pcib_fdt_driver,
+    xlnx_pcib_fdt_devclass, 0, 0);

Added: head/sys/dev/xilinx/xlnx_pcib.h
==============================================================================
--- /dev/null	00:00:00 1970	(empty, because file is newly added)
+++ head/sys/dev/xilinx/xlnx_pcib.h	Wed Jan 29 16:52:12 2020	(r357258)
@@ -0,0 +1,84 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2020 Ruslan Bukin <br at bsdpad.com>
+ *
+ * This software was developed by SRI International and the University of
+ * Cambridge Computer Laboratory (Department of Computer Science and
+ * Technology) under DARPA contract HR0011-18-C-0016 ("ECATS"), as part of the
+ * DARPA SSITH research programme.
+ *
+ * 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$
+ */
+
+#ifndef	_DEV_XILINX_XLNX_PCIB_H_
+#define	_DEV_XILINX_XLNX_PCIB_H_
+
+#define	XLNX_PCIE_VSEC		0x12c
+#define	XLNX_PCIE_BIR		0x130	/* Bridge Info Register */
+#define	XLNX_PCIE_BSCR		0x134	/* Bridge Status and Control */
+#define	XLNX_PCIE_IDR		0x138	/* Interrupt Decode Register */
+#define	XLNX_PCIE_IMR		0x13C	/* Interrupt Mask Register */
+#define	 IMR_LINK_DOWN		(1 << 0)
+#define	 IMR_HOT_RESET		(1 << 3)
+#define	 IMR_CFG_COMPL_STATUS_S	5
+#define	 IMR_CFG_COMPL_STATUS_M	(0x7 << IMR_CFG_COMPL_STATUS_S)
+#define	 IMR_CFG_TIMEOUT	(1 << 8)
+#define	 IMR_CORRECTABLE	(1 << 9)
+#define	 IMR_NON_FATAL		(1 << 10)
+#define	 IMR_FATAL		(1 << 11)
+#define	 IMR_INTX		(1 << 16) /* INTx Interrupt Received */
+#define	 IMR_MSI		(1 << 17) /* MSI Interrupt Received */
+#define	 IMR_SLAVE_UNSUPP_REQ	(1 << 20)
+#define	 IMR_SLAVE_UNEXP_COMPL	(1 << 21)
+#define	 IMR_SLAVE_COMPL_TIMOUT	(1 << 22)
+#define	 IMR_SLAVE_ERROR_POISON	(1 << 23)
+#define	 IMR_SLAVE_COMPL_ABORT	(1 << 24)
+#define	 IMR_SLAVE_ILLEG_BURST	(1 << 25)
+#define	 IMR_MASTER_DECERR	(1 << 26)
+#define	 IMR_MASTER_SLVERR	(1 << 27)
+#define	XLNX_PCIE_BLR		0x140	/* Bus Location Register */
+#define	XLNX_PCIE_PHYSCR	0x144	/* PHY Status/Control Register */
+#define	 PHYSCR_LINK_UP		(1 << 11)	/* Current PHY Link-up state */
+#define	XLNX_PCIE_RPSCR		0x148	/* Root Port Status/Control Register */
+#define	 RPSCR_BE		(1 << 0)	/* Bridge Enable */
+#define	XLNX_PCIE_RPMSIBR1	0x14C	/* Root Port MSI Base Register 1 */
+#define	XLNX_PCIE_RPMSIBR2	0x150	/* Root Port MSI Base Register 2 */
+#define	XLNX_PCIE_RPERRFRR	0x154	/* Root Port Error FIFO Read */
+#define	 RPERRFRR_VALID		(1 << 18) /* Indicates whether read succeeded.*/
+#define	 RPERRFRR_REQ_ID_S	0	/* Requester of the error message. */
+#define	 RPERRFRR_REQ_ID_M	(0xffff << RPERRFRR_REQ_ID_S)
+#define	XLNX_PCIE_RPIFRR1	0x158	/* Root Port Interrupt FIFO Read 1 */
+#define	XLNX_PCIE_RPIFRR2	0x15C	/* Root Port Interrupt FIFO Read 2 */
+#define	XLNX_PCIE_RPID2		0x160	/* Root Port Interrupt Decode 2 */
+#define	XLNX_PCIE_RPID2_MASK	0x164	/* Root Port Interrupt Decode 2 Mask */
+#define	XLNX_PCIE_RPMSIID1	0x170	/* Root Port MSI Interrupt Decode 1 */
+#define	XLNX_PCIE_RPMSIID2	0x174	/* Root Port MSI Interrupt Decode 2 */
+#define	XLNX_PCIE_RPMSIID1_MASK	0x178	/* Root Port MSI Int. Decode 1 Mask */
+#define	XLNX_PCIE_RPMSIID2_MASK	0x17C	/* Root Port MSI Int. Decode 2 Mask */
+#define	XLNX_PCIE_CCR		0x168	/* Configuration Control Register */
+#define	XLNX_PCIE_VSEC_CR	0x200	/* VSEC Capability Register 2 */
+#define	XLNX_PCIE_VSEC_HR	0x204	/* VSEC Header Register 2 */
+
+#endif /* !_DEV_XILINX_XLNX_PCIB_H_ */


More information about the svn-src-head mailing list