Author: jhibbits
Date: Fri Nov  8 03:36:19 2019
New Revision: 354525
URL: https://svnweb.freebsd.org/changeset/base/354525

Log:
  powerpc/mpc85xx: Add MSI support for Freescale PowerPC SoCs
  
  Freescale SoCs use a set of IRQs at the high end of the OpenPIC IRQ
  list, not counted in the NIRQs of the Feature reporting register.  Some
  SoCs include a MSI inbound window in the PCIe controller configuration
  registers as well, but some don't.  Currently, this only handles the
  SoCs *with* the MSI window.
  
  There are 256 MSIs per MSI bank (32 per MSI IRQ, 8 IRQs per MSI bank).
  The P5020 has 3 banks, yielding up to 768 MSIs; older SoCs have only one
  bank.

Modified:
  head/sys/powerpc/include/openpicvar.h
  head/sys/powerpc/mpc85xx/pci_mpc85xx.c
  head/sys/powerpc/ofw/openpic_ofw.c
  head/sys/powerpc/powerpc/openpic.c

Modified: head/sys/powerpc/include/openpicvar.h
==============================================================================
--- head/sys/powerpc/include/openpicvar.h       Fri Nov  8 03:27:56 2019        
(r354524)
+++ head/sys/powerpc/include/openpicvar.h       Fri Nov  8 03:36:19 2019        
(r354525)
@@ -35,6 +35,7 @@
 #define OPENPIC_IRQMAX 256     /* h/w allows more */
 
 #define        OPENPIC_QUIRK_SINGLE_BIND       1       /* Bind interrupts to 
only 1 CPU */
+#define        OPENPIC_QUIRK_HIDDEN_IRQS       2       /* May have IRQs beyond 
FRR[NIRQ] */
 
 /* Names match the macros in openpicreg.h. */
 struct openpic_timer {

Modified: head/sys/powerpc/mpc85xx/pci_mpc85xx.c
==============================================================================
--- head/sys/powerpc/mpc85xx/pci_mpc85xx.c      Fri Nov  8 03:27:56 2019        
(r354524)
+++ head/sys/powerpc/mpc85xx/pci_mpc85xx.c      Fri Nov  8 03:36:19 2019        
(r354525)
@@ -51,8 +51,10 @@ __FBSDID("$FreeBSD$");
 #include <sys/bus.h>
 #include <sys/lock.h>
 #include <sys/mutex.h>
+#include <sys/queue.h>
 #include <sys/rman.h>
 #include <sys/endian.h>
+#include <sys/vmem.h>
 
 #include <vm/vm.h>
 #include <vm/pmap.h>
@@ -67,6 +69,7 @@ __FBSDID("$FreeBSD$");
 
 #include "ofw_bus_if.h"
 #include "pcib_if.h"
+#include "pic_if.h"
 
 #include <machine/resource.h>
 #include <machine/bus.h>
@@ -80,6 +83,12 @@ __FBSDID("$FreeBSD$");
 #define        REG_CFG_DATA    0x0004
 #define        REG_INT_ACK     0x0008
 
+#define        REG_PEX_IP_BLK_REV1     0x0bf8
+#define          IP_MJ_M                 0x0000ff00
+#define          IP_MJ_S                 8
+#define          IP_MN_M                 0x000000ff
+#define          IP_MN_S                 0
+
 #define        REG_POTAR(n)    (0x0c00 + 0x20 * (n))
 #define        REG_POTEAR(n)   (0x0c04 + 0x20 * (n))
 #define        REG_POWBAR(n)   (0x0c08 + 0x20 * (n))
@@ -89,6 +98,12 @@ __FBSDID("$FreeBSD$");
 #define        REG_PIWBAR(n)   (0x0e08 - 0x20 * (n))
 #define        REG_PIWBEAR(n)  (0x0e0c - 0x20 * (n))
 #define        REG_PIWAR(n)    (0x0e10 - 0x20 * (n))
+#define          PIWAR_EN        0x80000000
+#define          PIWAR_PF        0x40000000
+#define          PIWAR_TRGT_M    0x00f00000
+#define          PIWAR_TRGT_S    20
+#define          PIWAR_TRGT_CCSR         0xe
+#define          PIWAR_TRGT_LOCAL        0xf
 
 #define        REG_PEX_MES_DR  0x0020
 #define        REG_PEX_MES_IER 0x0028
@@ -123,10 +138,14 @@ __FBSDID("$FreeBSD$");
 
 #define        DEVFN(b, s, f)  ((b << 16) | (s << 8) | f)
 
+#define        FSL_NUM_MSIS            256     /* 8 registers of 32 bits (8 
hardware IRQs) */
+
 struct fsl_pcib_softc {
        struct ofw_pci_softc pci_sc;
        device_t        sc_dev;
        struct mtx      sc_cfg_mtx;
+       int             sc_ip_maj;
+       int             sc_ip_min;
 
        int             sc_iomem_target;
        bus_addr_t      sc_iomem_start, sc_iomem_end;
@@ -151,6 +170,14 @@ struct fsl_pcib_err_dr {
        uint32_t        err_dr_mask;
 };
 
+struct fsl_msi_map {
+       SLIST_ENTRY(fsl_msi_map) slist;
+       uint32_t        irq_base;
+       bus_addr_t      target;
+};
+
+SLIST_HEAD(msi_head, fsl_msi_map) fsl_msis = SLIST_HEAD_INITIALIZER(msi_head);
+
 static const struct fsl_pcib_err_dr pci_err[] = {
        {"ME",          REG_PEX_ERR_DR_ME},
        {"PCT",         REG_PEX_ERR_DR_PCT},
@@ -195,7 +222,17 @@ static int fsl_pcib_maxslots(device_t);
 static uint32_t fsl_pcib_read_config(device_t, u_int, u_int, u_int, u_int, 
int);
 static void fsl_pcib_write_config(device_t, u_int, u_int, u_int, u_int,
     uint32_t, int);
+static int fsl_pcib_alloc_msi(device_t dev, device_t child,
+    int count, int maxcount, int *irqs);
+static int fsl_pcib_release_msi(device_t dev, device_t child,
+    int count, int *irqs);
+static int fsl_pcib_alloc_msix(device_t dev, device_t child, int *irq);
+static int fsl_pcib_release_msix(device_t dev, device_t child, int irq);
+static int fsl_pcib_map_msi(device_t dev, device_t child,
+    int irq, uint64_t *addr, uint32_t *data);
 
+static vmem_t *msi_vmem;       /* Global MSI vmem, holds all MSI ranges. */
+
 /*
  * Bus interface definitions.
  */
@@ -209,6 +246,11 @@ static device_method_t fsl_pcib_methods[] = {
        DEVMETHOD(pcib_maxslots,        fsl_pcib_maxslots),
        DEVMETHOD(pcib_read_config,     fsl_pcib_read_config),
        DEVMETHOD(pcib_write_config,    fsl_pcib_write_config),
+       DEVMETHOD(pcib_alloc_msi,       fsl_pcib_alloc_msi),
+       DEVMETHOD(pcib_release_msi,     fsl_pcib_release_msi),
+       DEVMETHOD(pcib_alloc_msix,      fsl_pcib_alloc_msix),
+       DEVMETHOD(pcib_release_msix,    fsl_pcib_release_msix),
+       DEVMETHOD(pcib_map_msi,         fsl_pcib_map_msi),
 
        DEVMETHOD_END
 };
@@ -272,7 +314,7 @@ fsl_pcib_attach(device_t dev)
 {
        struct fsl_pcib_softc *sc;
        phandle_t node;
-       uint32_t cfgreg, brctl;
+       uint32_t cfgreg, brctl, ipreg;
        int error, rid;
        uint8_t ltssm, capptr;
 
@@ -290,6 +332,9 @@ fsl_pcib_attach(device_t dev)
        sc->sc_bsh = rman_get_bushandle(sc->sc_res);
        sc->sc_busnr = 0;
 
+       ipreg = bus_read_4(sc->sc_res, REG_PEX_IP_BLK_REV1);
+       sc->sc_ip_min = (ipreg & IP_MN_M) >> IP_MN_S;
+       sc->sc_ip_maj = (ipreg & IP_MJ_M) >> IP_MJ_S;
        mtx_init(&sc->sc_cfg_mtx, "pcicfg", NULL, MTX_SPIN);
 
        cfgreg = fsl_pcib_cfgread(sc, 0, 0, 0, PCIR_VENDOR, 2);
@@ -533,14 +578,16 @@ fsl_pcib_inbound(struct fsl_pcib_softc *sc, int wnd, i
 
        KASSERT(wnd > 0, ("%s: inbound window 0 is invalid", __func__));
 
+       attr = PIWAR_EN;
+
        switch (tgt) {
-       /* XXX OCP85XX_TGTIF_RAM2, OCP85XX_TGTIF_RAM_INTL should be handled */
-       case OCP85XX_TGTIF_RAM1_85XX:
-       case OCP85XX_TGTIF_RAM1_QORIQ:
-               attr = 0xa0f55000 | (ffsl(size) - 2);
+       case -1:
+               attr &= ~PIWAR_EN;
                break;
+       case PIWAR_TRGT_LOCAL:
+               attr |= (ffsl(size) - 2);
        default:
-               attr = 0;
+               attr |= (tgt << PIWAR_TRGT_S);
                break;
        }
        tar = start >> 12;
@@ -702,8 +749,209 @@ fsl_pcib_decode_win(phandle_t node, struct fsl_pcib_so
 
        fsl_pcib_inbound(sc, 1, -1, 0, 0, 0);
        fsl_pcib_inbound(sc, 2, -1, 0, 0, 0);
-       fsl_pcib_inbound(sc, 3, OCP85XX_TGTIF_RAM1, 0,
-           2U * 1024U * 1024U * 1024U, 0);
+       fsl_pcib_inbound(sc, 3, PIWAR_TRGT_LOCAL, 0,
+           ptoa(Maxmem), 0);
 
+       /* Direct-map the CCSR for MSIs. */
+       /* Freescale PCIe 2.x has a dedicated MSI window. */
+       /* inbound window 8 makes it hit 0xD00 offset, the MSI window. */
+       if (sc->sc_ip_maj >= 2)
+               fsl_pcib_inbound(sc, 8, PIWAR_TRGT_CCSR, ccsrbar_pa,
+                   ccsrbar_size, ccsrbar_pa);
+       else
+               fsl_pcib_inbound(sc, 1, PIWAR_TRGT_CCSR, ccsrbar_pa,
+                   ccsrbar_size, ccsrbar_pa);
+
        return (0);
 }
+
+static int fsl_pcib_alloc_msi(device_t dev, device_t child,
+    int count, int maxcount, int *irqs)
+{
+       struct fsl_pcib_softc *sc;
+       vmem_addr_t start;
+       int err, i;
+
+       sc = device_get_softc(dev);
+       if (msi_vmem == NULL)
+               return (ENODEV);
+
+       err = vmem_xalloc(msi_vmem, count, powerof2(count), 0, 0,
+           VMEM_ADDR_MIN, VMEM_ADDR_MAX, M_BESTFIT | M_WAITOK, &start);
+
+       if (err)
+               return (err);
+
+       for (i = 0; i < count; i++)
+               irqs[i] = start + i;
+
+       return (0);
+}
+
+static int fsl_pcib_release_msi(device_t dev, device_t child,
+    int count, int *irqs)
+{
+       if (msi_vmem == NULL)
+               return (ENODEV);
+
+       vmem_xfree(msi_vmem, irqs[0], count);
+       return (0);
+}
+
+static int fsl_pcib_alloc_msix(device_t dev, device_t child, int *irq)
+{
+       return (fsl_pcib_alloc_msi(dev, child, 1, 1, irq));
+}
+
+static int fsl_pcib_release_msix(device_t dev, device_t child, int irq)
+{
+       return (fsl_pcib_release_msi(dev, child, 1, &irq));
+}
+
+static int fsl_pcib_map_msi(device_t dev, device_t child,
+    int irq, uint64_t *addr, uint32_t *data)
+{
+       struct fsl_msi_map *mp;
+
+       SLIST_FOREACH(mp, &fsl_msis, slist) {
+               if (irq >= mp->irq_base && irq < mp->irq_base + FSL_NUM_MSIS)
+                       break;
+       }
+
+       if (mp == NULL)
+               return (ENODEV);
+
+       *data = (irq & 255);
+       *addr = ccsrbar_pa + mp->target;
+
+       return (0);
+}
+
+
+/*
+ * Linux device trees put the msi@<x> as children of the SoC, with ranges based
+ * on the CCSR.  Since rman doesn't permit overlapping or sub-ranges between
+ * devices (bus_space_subregion(9) could do it, but let's not touch the PIC
+ * driver just to allocate a subregion for a sibling driver).  This driver will
+ * use ccsr_write() and ccsr_read() instead.
+ */
+
+#define        FSL_NUM_IRQS            8
+#define        FSL_NUM_MSI_PER_IRQ     32
+#define        FSL_MSI_TARGET  0x140
+
+struct fsl_msi_softc {
+       vm_offset_t     sc_base;
+       vm_offset_t     sc_target;
+       int             sc_msi_base_irq;
+       struct fsl_msi_map sc_map;
+       struct fsl_msi_irq {
+               /* This struct gets passed as the filter private data. */
+               struct fsl_msi_softc *sc_ptr;   /* Pointer back to softc. */
+               struct resource *res;
+               int irq;
+               void *cookie;
+               int vectors[FSL_NUM_MSI_PER_IRQ];
+               vm_offset_t reg;
+       } sc_msi_irq[FSL_NUM_IRQS];
+};
+
+static int
+fsl_msi_intr_filter(void *priv)
+{
+       struct fsl_msi_irq *data = priv;
+       uint32_t reg;
+       int i;
+
+       reg = ccsr_read4(ccsrbar_va + data->reg);
+       i = 0;
+       while (reg != 0) {
+               if (reg & 1)
+                       powerpc_dispatch_intr(data->vectors[i], NULL);
+               reg >>= 1;
+               i++;
+       }
+
+       return (FILTER_HANDLED);
+}
+
+static int
+fsl_msi_probe(device_t dev)
+{
+       if (!ofw_bus_is_compatible(dev, "fsl,mpic-msi"))
+               return (ENXIO);
+
+       device_set_desc(dev, "Freescale MSI");
+
+       return (BUS_PROBE_DEFAULT);
+}
+
+static int
+fsl_msi_attach(device_t dev)
+{
+       struct fsl_msi_softc *sc;
+       struct fsl_msi_irq *irq;
+       int i;
+
+       sc = device_get_softc(dev);
+
+       if (msi_vmem == NULL)
+               msi_vmem = vmem_create("MPIC MSI", 0, 0, 1, 1, M_BESTFIT | 
M_WAITOK);
+
+       /* Manually play with resource entries. */
+       sc->sc_base = bus_get_resource_start(dev, SYS_RES_MEMORY, 0);
+       sc->sc_map.target = bus_get_resource_start(dev, SYS_RES_MEMORY, 1);
+
+       if (sc->sc_map.target == 0)
+               sc->sc_map.target = sc->sc_base + FSL_MSI_TARGET;
+
+       for (i = 0; i < FSL_NUM_IRQS; i++) {
+               irq = &sc->sc_msi_irq[i];
+               irq->irq = i;
+               irq->reg = sc->sc_base + 16 * i;
+               irq->res = bus_alloc_resource_any(dev, SYS_RES_IRQ,
+                   &irq->irq, RF_ACTIVE);
+               bus_setup_intr(dev, irq->res, INTR_TYPE_MISC | INTR_MPSAFE,
+                   fsl_msi_intr_filter, NULL, irq, &irq->cookie);
+       }
+       sc->sc_map.irq_base = powerpc_register_pic(dev, ofw_bus_get_node(dev),
+           FSL_NUM_MSIS, 0, 0);
+
+       /* Let vmem and the IRQ subsystem work their magic for allocations. */
+       vmem_add(msi_vmem, sc->sc_map.irq_base, FSL_NUM_MSIS, M_WAITOK);
+
+       SLIST_INSERT_HEAD(&fsl_msis, &sc->sc_map, slist);
+
+       return (0);
+}
+
+static void
+fsl_msi_enable(device_t dev, u_int irq, u_int vector, void **priv)
+{
+       struct fsl_msi_softc *sc;
+       struct fsl_msi_irq *irqd;
+
+       sc = device_get_softc(dev);
+
+       irqd = &sc->sc_msi_irq[irq / FSL_NUM_MSI_PER_IRQ];
+       irqd->vectors[irq % FSL_NUM_MSI_PER_IRQ] = vector;
+}
+
+static device_method_t fsl_msi_methods[] = {
+       DEVMETHOD(device_probe,         fsl_msi_probe),
+       DEVMETHOD(device_attach,        fsl_msi_attach),
+
+       DEVMETHOD(pic_enable,           fsl_msi_enable),
+       DEVMETHOD_END
+};
+
+static devclass_t fsl_msi_devclass;
+
+static driver_t fsl_msi_driver = {
+       "fsl_msi",
+       fsl_msi_methods,
+       sizeof(struct fsl_msi_softc)
+};
+
+EARLY_DRIVER_MODULE(fsl_msi, simplebus, fsl_msi_driver, fsl_msi_devclass, 0, 0,
+    BUS_PASS_INTERRUPT + 1);

Modified: head/sys/powerpc/ofw/openpic_ofw.c
==============================================================================
--- head/sys/powerpc/ofw/openpic_ofw.c  Fri Nov  8 03:27:56 2019        
(r354524)
+++ head/sys/powerpc/ofw/openpic_ofw.c  Fri Nov  8 03:36:19 2019        
(r354525)
@@ -139,8 +139,10 @@ openpic_ofw_attach(device_t dev)
            OF_getencprop(node, "linux,phandle", &xref, sizeof(xref)) == -1)
                xref = node;
        
-       if (ofw_bus_is_compatible(dev, "fsl,mpic"))
+       if (ofw_bus_is_compatible(dev, "fsl,mpic")) {
                sc->sc_quirks = OPENPIC_QUIRK_SINGLE_BIND;
+               sc->sc_quirks |= OPENPIC_QUIRK_HIDDEN_IRQS;
+       }
 
        return (openpic_common_attach(dev, xref));
 }

Modified: head/sys/powerpc/powerpc/openpic.c
==============================================================================
--- head/sys/powerpc/powerpc/openpic.c  Fri Nov  8 03:27:56 2019        
(r354524)
+++ head/sys/powerpc/powerpc/openpic.c  Fri Nov  8 03:36:19 2019        
(r354525)
@@ -52,6 +52,8 @@
 
 #include "pic_if.h"
 
+#define        OPENPIC_NIPIS           4
+
 devclass_t openpic_devclass;
 
 /*
@@ -182,6 +184,14 @@ openpic_common_attach(device_t dev, uint32_t node)
                    "Version %s, supports %d CPUs and %d irqs\n",
                    sc->sc_version, sc->sc_ncpu, sc->sc_nirq);
 
+       /*
+        * Allow more IRQs than what the PIC says it handles.  Some Freescale 
PICs
+        * have MSIs that show up above the PIC's self-described 196 IRQs
+        * (P5020 starts MSI IRQs at 224).
+        */
+       if (sc->sc_quirks & OPENPIC_QUIRK_HIDDEN_IRQS)
+               sc->sc_nirq = OPENPIC_IRQMAX - OPENPIC_NIPIS;
+
        for (cpu = 0; cpu < sc->sc_ncpu; cpu++)
                openpic_write(sc, OPENPIC_PCPU_TPR(cpu), 15);
 
@@ -196,7 +206,7 @@ openpic_common_attach(device_t dev, uint32_t node)
        }
 
        /* Reset and disable all IPIs. */
-       for (ipi = 0; ipi < 4; ipi++) {
+       for (ipi = 0; ipi < OPENPIC_NIPIS; ipi++) {
                x = sc->sc_nirq + ipi;
                x |= OPENPIC_IMASK;
                x |= 15 << OPENPIC_PRIORITY_SHIFT;
@@ -221,7 +231,7 @@ openpic_common_attach(device_t dev, uint32_t node)
        for (cpu = 0; cpu < sc->sc_ncpu; cpu++)
                openpic_write(sc, OPENPIC_PCPU_TPR(cpu), 0);
 
-       powerpc_register_pic(dev, node, sc->sc_nirq, 4, FALSE);
+       powerpc_register_pic(dev, node, sc->sc_nirq, OPENPIC_NIPIS, FALSE);
 
        /* If this is not a cascaded PIC, it must be the root PIC */
        if (sc->sc_intr == NULL)
@@ -411,7 +421,7 @@ openpic_suspend(device_t dev)
        sc = device_get_softc(dev);
 
        sc->sc_saved_config = bus_read_4(sc->sc_memr, OPENPIC_CONFIG);
-       for (i = 0; i < 4; i++) {
+       for (i = 0; i < OPENPIC_NIPIS; i++) {
                sc->sc_saved_ipis[i] = bus_read_4(sc->sc_memr, 
OPENPIC_IPI_VECTOR(i));
        }
 
@@ -442,7 +452,7 @@ openpic_resume(device_t dev)
        sc = device_get_softc(dev);
 
        sc->sc_saved_config = bus_read_4(sc->sc_memr, OPENPIC_CONFIG);
-       for (i = 0; i < 4; i++) {
+       for (i = 0; i < OPENPIC_NIPIS; i++) {
                bus_write_4(sc->sc_memr, OPENPIC_IPI_VECTOR(i), 
sc->sc_saved_ipis[i]);
        }
 
_______________________________________________
[email protected] mailing list
https://lists.freebsd.org/mailman/listinfo/svn-src-all
To unsubscribe, send any mail to "[email protected]"

Reply via email to