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 <b...@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 <b...@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_ */
_______________________________________________
svn-src-head@freebsd.org mailing list
https://lists.freebsd.org/mailman/listinfo/svn-src-head
To unsubscribe, send any mail to "svn-src-head-unsubscr...@freebsd.org"

Reply via email to