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.
This commit is contained in:
parent
8dc188276e
commit
bdcbfe133b
@ -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 {
|
||||
|
@ -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,6 +222,16 @@ 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, int tgt, uint64_t start,
|
||||
|
||||
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_softc *sc)
|
||||
|
||||
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);
|
||||
|
@ -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));
|
||||
}
|
||||
|
@ -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]);
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user