Introduce MSI and MSI-X support to intrng. This adds a new msi device
interface with 5 methods to mirror the 5 MSI/MSI-X methods in the pcib interface. The pcib driver will need to perform a device specific lookup to find the MSI controller and pass this to intrng as the xref. Intrng will finally find the controller and have it handle the requested operation. Obtained from: ABT Systems Ltd MFH: yes Sponsored by: The FreeBSD Foundation Differential Revision: https://reviews.freebsd.org/D5985
This commit is contained in:
parent
81ccfbbc2e
commit
3fc155dc64
@ -53,6 +53,10 @@ __FBSDID("$FreeBSD$");
|
||||
#ifdef INTRNG
|
||||
#include <sys/sched.h>
|
||||
#endif
|
||||
|
||||
#include <vm/vm.h>
|
||||
#include <vm/pmap.h>
|
||||
|
||||
#include <machine/bus.h>
|
||||
#include <machine/intr.h>
|
||||
#include <machine/smp.h>
|
||||
@ -64,6 +68,7 @@ __FBSDID("$FreeBSD$");
|
||||
|
||||
#ifdef INTRNG
|
||||
#include "pic_if.h"
|
||||
#include "msi_if.h"
|
||||
#endif
|
||||
|
||||
#define GIC_DEBUG_SPURIOUS
|
||||
@ -123,6 +128,10 @@ struct gic_irqsrc {
|
||||
enum intr_polarity gi_pol;
|
||||
enum intr_trigger gi_trig;
|
||||
#define GI_FLAG_EARLY_EOI (1 << 0)
|
||||
#define GI_FLAG_MSI (1 << 1) /* This interrupt source should only */
|
||||
/* be used for MSI/MSI-X interrupts */
|
||||
#define GI_FLAG_MSI_USED (1 << 2) /* This irq is already allocated */
|
||||
/* for a MSI/MSI-X interrupt */
|
||||
u_int gi_flags;
|
||||
};
|
||||
|
||||
@ -562,6 +571,33 @@ arm_gic_add_children(device_t dev)
|
||||
|
||||
return (true);
|
||||
}
|
||||
|
||||
static void
|
||||
arm_gic_reserve_msi_range(device_t dev, u_int start, u_int count)
|
||||
{
|
||||
struct arm_gic_softc *sc;
|
||||
int i;
|
||||
|
||||
sc = device_get_softc(dev);
|
||||
|
||||
KASSERT((start + count) < sc->nirqs,
|
||||
("%s: Trying to allocate too many MSI IRQs: %d + %d > %d", __func__,
|
||||
start, count, sc->nirqs));
|
||||
for (i = 0; i < count; i++) {
|
||||
KASSERT(sc->gic_irqs[start + i].gi_isrc.isrc_handlers == 0,
|
||||
("%s: MSI interrupt %d already has a handler", __func__,
|
||||
count + i));
|
||||
KASSERT(sc->gic_irqs[start + i].gi_pol == INTR_POLARITY_CONFORM,
|
||||
("%s: MSI interrupt %d already has a polarity", __func__,
|
||||
count + i));
|
||||
KASSERT(sc->gic_irqs[start + i].gi_trig == INTR_TRIGGER_CONFORM,
|
||||
("%s: MSI interrupt %d already has a trigger", __func__,
|
||||
count + i));
|
||||
sc->gic_irqs[start + i].gi_pol = INTR_POLARITY_HIGH;
|
||||
sc->gic_irqs[start + i].gi_trig = INTR_TRIGGER_EDGE;
|
||||
sc->gic_irqs[start + i].gi_flags |= GI_FLAG_MSI;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
static int
|
||||
@ -1018,6 +1054,10 @@ gic_map_intr(device_t dev, struct intr_map_data *data, u_int *irqp,
|
||||
if (gic_map_fdt(dev, daf->ncells, daf->cells, &irq, &pol,
|
||||
&trig) != 0)
|
||||
return (EINVAL);
|
||||
KASSERT(irq >= sc->nirqs ||
|
||||
(sc->gic_irqs[irq].gi_flags & GI_FLAG_MSI) == 0,
|
||||
("%s: Attempting to map a MSI interrupt from FDT",
|
||||
__func__));
|
||||
break;
|
||||
#endif
|
||||
default:
|
||||
@ -1067,16 +1107,24 @@ arm_gic_setup_intr(device_t dev, struct intr_irqsrc *isrc,
|
||||
enum intr_trigger trig;
|
||||
enum intr_polarity pol;
|
||||
|
||||
if (data == NULL)
|
||||
if ((gi->gi_flags & GI_FLAG_MSI) == GI_FLAG_MSI) {
|
||||
irq = gi->gi_irq;
|
||||
pol = gi->gi_pol;
|
||||
trig = gi->gi_trig;
|
||||
KASSERT(pol == INTR_POLARITY_HIGH,
|
||||
("%s: MSI interrupts must be active-high", __func__));
|
||||
KASSERT(trig == INTR_TRIGGER_EDGE,
|
||||
("%s: MSI interrupts must be edge triggered", __func__));
|
||||
} else if (data != NULL) {
|
||||
/* Get config for resource. */
|
||||
if (gic_map_intr(dev, data, &irq, &pol, &trig))
|
||||
return (EINVAL);
|
||||
|
||||
if (gi->gi_irq != irq)
|
||||
return (EINVAL);
|
||||
} else
|
||||
return (ENOTSUP);
|
||||
|
||||
/* Get config for resource. */
|
||||
if (gic_map_intr(dev, data, &irq, &pol, &trig))
|
||||
return (EINVAL);
|
||||
|
||||
if (gi->gi_irq != irq)
|
||||
return (EINVAL);
|
||||
|
||||
/* Compare config if this is not first setup. */
|
||||
if (isrc->isrc_handlers != 0) {
|
||||
if ((pol != INTR_POLARITY_CONFORM && pol != gi->gi_pol) ||
|
||||
@ -1086,16 +1134,20 @@ arm_gic_setup_intr(device_t dev, struct intr_irqsrc *isrc,
|
||||
return (0);
|
||||
}
|
||||
|
||||
if (pol == INTR_POLARITY_CONFORM)
|
||||
pol = INTR_POLARITY_LOW; /* just pick some */
|
||||
if (trig == INTR_TRIGGER_CONFORM)
|
||||
trig = INTR_TRIGGER_EDGE; /* just pick some */
|
||||
/* For MSI/MSI-X we should have already configured these */
|
||||
if ((gi->gi_flags & GI_FLAG_MSI) == 0) {
|
||||
if (pol == INTR_POLARITY_CONFORM)
|
||||
pol = INTR_POLARITY_LOW; /* just pick some */
|
||||
if (trig == INTR_TRIGGER_CONFORM)
|
||||
trig = INTR_TRIGGER_EDGE; /* just pick some */
|
||||
|
||||
gi->gi_pol = pol;
|
||||
gi->gi_trig = trig;
|
||||
/* Edge triggered interrupts need an early EOI sent */
|
||||
if (gi->gi_pol == INTR_TRIGGER_EDGE)
|
||||
gi->gi_flags |= GI_FLAG_EARLY_EOI;
|
||||
gi->gi_pol = pol;
|
||||
gi->gi_trig = trig;
|
||||
|
||||
/* Edge triggered interrupts need an early EOI sent */
|
||||
if (gi->gi_pol == INTR_TRIGGER_EDGE)
|
||||
gi->gi_flags |= GI_FLAG_EARLY_EOI;
|
||||
}
|
||||
|
||||
/*
|
||||
* XXX - In case that per CPU interrupt is going to be enabled in time
|
||||
@ -1107,7 +1159,7 @@ arm_gic_setup_intr(device_t dev, struct intr_irqsrc *isrc,
|
||||
if (isrc->isrc_flags & INTR_ISRCF_PPI)
|
||||
CPU_SET(PCPU_GET(cpuid), &isrc->isrc_cpu);
|
||||
|
||||
gic_config(sc, gi->gi_irq, trig, pol);
|
||||
gic_config(sc, gi->gi_irq, gi->gi_trig, gi->gi_pol);
|
||||
arm_gic_bind_intr(dev, isrc);
|
||||
return (0);
|
||||
}
|
||||
@ -1118,7 +1170,7 @@ arm_gic_teardown_intr(device_t dev, struct intr_irqsrc *isrc,
|
||||
{
|
||||
struct gic_irqsrc *gi = (struct gic_irqsrc *)isrc;
|
||||
|
||||
if (isrc->isrc_handlers == 0) {
|
||||
if (isrc->isrc_handlers == 0 && (gi->gi_flags & GI_FLAG_MSI) == 0) {
|
||||
gi->gi_pol = INTR_POLARITY_CONFORM;
|
||||
gi->gi_trig = INTR_TRIGGER_CONFORM;
|
||||
}
|
||||
@ -1502,8 +1554,8 @@ struct arm_gicv2m_softc {
|
||||
struct resource *sc_mem;
|
||||
struct mtx sc_mutex;
|
||||
u_int sc_spi_start;
|
||||
u_int sc_spi_end;
|
||||
u_int sc_spi_count;
|
||||
u_int sc_spi_offset;
|
||||
};
|
||||
|
||||
static struct ofw_compat_data gicv2m_compat_data[] = {
|
||||
@ -1529,9 +1581,11 @@ static int
|
||||
arm_gicv2m_attach(device_t dev)
|
||||
{
|
||||
struct arm_gicv2m_softc *sc;
|
||||
struct arm_gic_softc *psc;
|
||||
uint32_t typer;
|
||||
int rid;
|
||||
|
||||
psc = device_get_softc(device_get_parent(dev));
|
||||
sc = device_get_softc(dev);
|
||||
|
||||
rid = 0;
|
||||
@ -1545,9 +1599,16 @@ arm_gicv2m_attach(device_t dev)
|
||||
typer = bus_read_4(sc->sc_mem, GICV2M_MSI_TYPER);
|
||||
sc->sc_spi_start = MSI_TYPER_SPI_BASE(typer);
|
||||
sc->sc_spi_count = MSI_TYPER_SPI_COUNT(typer);
|
||||
sc->sc_spi_end = sc->sc_spi_start + sc->sc_spi_count;
|
||||
|
||||
/* Reserve these interrupts for MSI/MSI-X use */
|
||||
arm_gic_reserve_msi_range(device_get_parent(dev), sc->sc_spi_start,
|
||||
sc->sc_spi_count);
|
||||
|
||||
mtx_init(&sc->sc_mutex, "GICv2m lock", "", MTX_DEF);
|
||||
|
||||
intr_msi_register(dev, gic_xref(dev));
|
||||
|
||||
if (bootverbose)
|
||||
device_printf(dev, "using spi %u to %u\n", sc->sc_spi_start,
|
||||
sc->sc_spi_start + sc->sc_spi_count - 1);
|
||||
@ -1555,11 +1616,176 @@ arm_gicv2m_attach(device_t dev)
|
||||
return (0);
|
||||
}
|
||||
|
||||
static int
|
||||
arm_gicv2m_alloc_msi(device_t dev, device_t child, int count, int maxcount,
|
||||
device_t *pic, struct intr_irqsrc **srcs)
|
||||
{
|
||||
struct arm_gic_softc *psc;
|
||||
struct arm_gicv2m_softc *sc;
|
||||
int i, irq, end_irq;
|
||||
bool found;
|
||||
|
||||
KASSERT(powerof2(count), ("%s: bad count", __func__));
|
||||
KASSERT(powerof2(maxcount), ("%s: bad maxcount", __func__));
|
||||
|
||||
psc = device_get_softc(device_get_parent(dev));
|
||||
sc = device_get_softc(dev);
|
||||
|
||||
mtx_lock(&sc->sc_mutex);
|
||||
|
||||
found = false;
|
||||
for (irq = sc->sc_spi_start; irq < sc->sc_spi_end && !found; irq++) {
|
||||
/* Start on an aligned interrupt */
|
||||
if ((irq & (maxcount - 1)) != 0)
|
||||
continue;
|
||||
|
||||
/* Assume we found a valid range until shown otherwise */
|
||||
found = true;
|
||||
|
||||
/* Check this range is valid */
|
||||
for (end_irq = irq; end_irq != irq + count - 1; end_irq++) {
|
||||
/* No free interrupts */
|
||||
if (end_irq == sc->sc_spi_end) {
|
||||
found = false;
|
||||
break;
|
||||
}
|
||||
|
||||
KASSERT((psc->gic_irqs[irq].gi_flags & GI_FLAG_MSI)!= 0,
|
||||
("%s: Non-MSI interrupt found", __func__));
|
||||
|
||||
/* This is already used */
|
||||
if ((psc->gic_irqs[irq].gi_flags & GI_FLAG_MSI_USED) ==
|
||||
GI_FLAG_MSI_USED) {
|
||||
found = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Not enough interrupts were found */
|
||||
if (!found || irq == sc->sc_spi_end) {
|
||||
mtx_unlock(&sc->sc_mutex);
|
||||
return (ENXIO);
|
||||
}
|
||||
|
||||
for (i = 0; i < count; i++) {
|
||||
/* Mark the interrupt as used */
|
||||
psc->gic_irqs[irq + i].gi_flags |= GI_FLAG_MSI_USED;
|
||||
|
||||
}
|
||||
mtx_unlock(&sc->sc_mutex);
|
||||
|
||||
for (i = 0; i < count; i++)
|
||||
srcs[i] = (struct intr_irqsrc *)&psc->gic_irqs[irq + i];
|
||||
*pic = device_get_parent(dev);
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
static int
|
||||
arm_gicv2m_release_msi(device_t dev, device_t child, int count,
|
||||
struct intr_irqsrc **isrc)
|
||||
{
|
||||
struct arm_gicv2m_softc *sc;
|
||||
struct gic_irqsrc *gi;
|
||||
int i;
|
||||
|
||||
sc = device_get_softc(dev);
|
||||
|
||||
mtx_lock(&sc->sc_mutex);
|
||||
for (i = 0; i < count; i++) {
|
||||
gi = (struct gic_irqsrc *)isrc;
|
||||
|
||||
KASSERT((gi->gi_flags & GI_FLAG_MSI_USED) == GI_FLAG_MSI_USED,
|
||||
("%s: Trying to release an unused MSI-X interrupt",
|
||||
__func__));
|
||||
|
||||
gi->gi_flags &= ~GI_FLAG_MSI_USED;
|
||||
mtx_unlock(&sc->sc_mutex);
|
||||
}
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
static int
|
||||
arm_gicv2m_alloc_msix(device_t dev, device_t child, device_t *pic,
|
||||
struct intr_irqsrc **isrcp)
|
||||
{
|
||||
struct arm_gicv2m_softc *sc;
|
||||
struct arm_gic_softc *psc;
|
||||
int irq;
|
||||
|
||||
psc = device_get_softc(device_get_parent(dev));
|
||||
sc = device_get_softc(dev);
|
||||
|
||||
mtx_lock(&sc->sc_mutex);
|
||||
/* Find an unused interrupt */
|
||||
for (irq = sc->sc_spi_start; irq < sc->sc_spi_end; irq++) {
|
||||
KASSERT((psc->gic_irqs[irq].gi_flags & GI_FLAG_MSI) != 0,
|
||||
("%s: Non-MSI interrupt found", __func__));
|
||||
if ((psc->gic_irqs[irq].gi_flags & GI_FLAG_MSI_USED) == 0)
|
||||
break;
|
||||
}
|
||||
/* No free interrupt was found */
|
||||
if (irq == sc->sc_spi_end) {
|
||||
mtx_unlock(&sc->sc_mutex);
|
||||
return (ENXIO);
|
||||
}
|
||||
|
||||
/* Mark the interrupt as used */
|
||||
psc->gic_irqs[irq].gi_flags |= GI_FLAG_MSI_USED;
|
||||
mtx_unlock(&sc->sc_mutex);
|
||||
|
||||
*isrcp = (struct intr_irqsrc *)&psc->gic_irqs[irq];
|
||||
*pic = device_get_parent(dev);
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
static int
|
||||
arm_gicv2m_release_msix(device_t dev, device_t child, struct intr_irqsrc *isrc)
|
||||
{
|
||||
struct arm_gicv2m_softc *sc;
|
||||
struct gic_irqsrc *gi;
|
||||
|
||||
sc = device_get_softc(dev);
|
||||
gi = (struct gic_irqsrc *)isrc;
|
||||
|
||||
KASSERT((gi->gi_flags & GI_FLAG_MSI_USED) == GI_FLAG_MSI_USED,
|
||||
("%s: Trying to release an unused MSI-X interrupt", __func__));
|
||||
|
||||
mtx_lock(&sc->sc_mutex);
|
||||
gi->gi_flags &= ~GI_FLAG_MSI_USED;
|
||||
mtx_unlock(&sc->sc_mutex);
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
static int
|
||||
arm_gicv2m_map_msi(device_t dev, device_t child, struct intr_irqsrc *isrc,
|
||||
uint64_t *addr, uint32_t *data)
|
||||
{
|
||||
struct arm_gicv2m_softc *sc = device_get_softc(dev);
|
||||
struct gic_irqsrc *gi = (struct gic_irqsrc *)isrc;
|
||||
|
||||
*addr = vtophys(rman_get_virtual(sc->sc_mem)) + GICv2M_MSI_SETSPI_NS;
|
||||
*data = gi->gi_irq;
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
static device_method_t arm_gicv2m_methods[] = {
|
||||
/* Device interface */
|
||||
DEVMETHOD(device_probe, arm_gicv2m_probe),
|
||||
DEVMETHOD(device_attach, arm_gicv2m_attach),
|
||||
|
||||
/* MSI/MSI-X */
|
||||
DEVMETHOD(msi_alloc_msi, arm_gicv2m_alloc_msi),
|
||||
DEVMETHOD(msi_release_msi, arm_gicv2m_release_msi),
|
||||
DEVMETHOD(msi_alloc_msix, arm_gicv2m_alloc_msix),
|
||||
DEVMETHOD(msi_release_msix, arm_gicv2m_release_msix),
|
||||
DEVMETHOD(msi_map_msi, arm_gicv2m_map_msi),
|
||||
|
||||
/* End */
|
||||
DEVMETHOD_END
|
||||
};
|
||||
|
@ -114,6 +114,7 @@ font.h optional sc \
|
||||
compile-with "uudecode < /usr/share/syscons/fonts/${SC_DFLT_FONT}-8x16.fnt && file2c 'u_char dflt_font_16[16*256] = {' '};' < ${SC_DFLT_FONT}-8x16 > font.h && uudecode < /usr/share/syscons/fonts/${SC_DFLT_FONT}-8x14.fnt && file2c 'u_char dflt_font_14[14*256] = {' '};' < ${SC_DFLT_FONT}-8x14 >> font.h && uudecode < /usr/share/syscons/fonts/${SC_DFLT_FONT}-8x8.fnt && file2c 'u_char dflt_font_8[8*256] = {' '};' < ${SC_DFLT_FONT}-8x8 >> font.h" \
|
||||
no-obj no-implicit-rule before-depend \
|
||||
clean "font.h ${SC_DFLT_FONT}-8x14 ${SC_DFLT_FONT}-8x16 ${SC_DFLT_FONT}-8x8"
|
||||
kern/msi_if.m optional intrng
|
||||
kern/pic_if.m optional intrng
|
||||
kern/subr_busdma_bufalloc.c standard
|
||||
kern/subr_devmap.c standard
|
||||
|
@ -46,6 +46,10 @@ __FBSDID("$FreeBSD$");
|
||||
#include <sys/cpuset.h>
|
||||
#include <sys/rwlock.h>
|
||||
|
||||
#if defined(INTRNG)
|
||||
#include <machine/intr.h>
|
||||
#endif
|
||||
|
||||
#include <dev/ofw/openfirm.h>
|
||||
#include <dev/ofw/ofw_bus.h>
|
||||
#include <dev/ofw/ofw_bus_subr.h>
|
||||
@ -158,6 +162,7 @@ pci_host_generic_attach(device_t dev)
|
||||
uint64_t phys_base;
|
||||
uint64_t pci_base;
|
||||
uint64_t size;
|
||||
phandle_t node;
|
||||
int error;
|
||||
int tuple;
|
||||
int rid;
|
||||
@ -227,8 +232,12 @@ pci_host_generic_attach(device_t dev)
|
||||
}
|
||||
}
|
||||
|
||||
ofw_bus_setup_iinfo(ofw_bus_get_node(dev), &sc->pci_iinfo,
|
||||
sizeof(cell_t));
|
||||
node = ofw_bus_get_node(dev);
|
||||
ofw_bus_setup_iinfo(node, &sc->pci_iinfo, sizeof(cell_t));
|
||||
|
||||
/* Find the MSI interrupt handler */
|
||||
OF_searchencprop(node, "msi-parent", &sc->msi_parent,
|
||||
sizeof(sc->msi_parent));
|
||||
|
||||
device_add_child(dev, "pci", -1);
|
||||
return (bus_generic_attach(dev));
|
||||
@ -661,8 +670,13 @@ static int
|
||||
generic_pcie_alloc_msi(device_t pci, device_t child, int count, int maxcount,
|
||||
int *irqs)
|
||||
{
|
||||
#if defined(INTRNG)
|
||||
struct generic_pcie_softc *sc;
|
||||
|
||||
#if defined(__aarch64__)
|
||||
sc = device_get_softc(pci);
|
||||
return (intr_alloc_msi(pci, child, sc->msi_parent, count, maxcount,
|
||||
irqs));
|
||||
#elif defined(__aarch64__)
|
||||
return (arm_alloc_msi(pci, child, count, maxcount, irqs));
|
||||
#else
|
||||
return (ENXIO);
|
||||
@ -672,8 +686,12 @@ generic_pcie_alloc_msi(device_t pci, device_t child, int count, int maxcount,
|
||||
static int
|
||||
generic_pcie_release_msi(device_t pci, device_t child, int count, int *irqs)
|
||||
{
|
||||
#if defined(INTRNG)
|
||||
struct generic_pcie_softc *sc;
|
||||
|
||||
#if defined(__aarch64__)
|
||||
sc = device_get_softc(pci);
|
||||
return (intr_release_msi(pci, child, sc->msi_parent, count, irqs));
|
||||
#elif defined(__aarch64__)
|
||||
return (arm_release_msi(pci, child, count, irqs));
|
||||
#else
|
||||
return (ENXIO);
|
||||
@ -684,8 +702,12 @@ static int
|
||||
generic_pcie_map_msi(device_t pci, device_t child, int irq, uint64_t *addr,
|
||||
uint32_t *data)
|
||||
{
|
||||
#if defined(INTRNG)
|
||||
struct generic_pcie_softc *sc;
|
||||
|
||||
#if defined(__aarch64__)
|
||||
sc = device_get_softc(pci);
|
||||
return (intr_map_msi(pci, child, sc->msi_parent, irq, addr, data));
|
||||
#elif defined(__aarch64__)
|
||||
return (arm_map_msi(pci, child, irq, addr, data));
|
||||
#else
|
||||
return (ENXIO);
|
||||
@ -695,8 +717,12 @@ generic_pcie_map_msi(device_t pci, device_t child, int irq, uint64_t *addr,
|
||||
static int
|
||||
generic_pcie_alloc_msix(device_t pci, device_t child, int *irq)
|
||||
{
|
||||
#if defined(INTRNG)
|
||||
struct generic_pcie_softc *sc;
|
||||
|
||||
#if defined(__aarch64__)
|
||||
sc = device_get_softc(pci);
|
||||
return (intr_alloc_msix(pci, child, sc->msi_parent, irq));
|
||||
#elif defined(__aarch64__)
|
||||
return (arm_alloc_msix(pci, child, irq));
|
||||
#else
|
||||
return (ENXIO);
|
||||
@ -706,8 +732,12 @@ generic_pcie_alloc_msix(device_t pci, device_t child, int *irq)
|
||||
static int
|
||||
generic_pcie_release_msix(device_t pci, device_t child, int irq)
|
||||
{
|
||||
#if defined(INTRNG)
|
||||
struct generic_pcie_softc *sc;
|
||||
|
||||
#if defined(__aarch64__)
|
||||
sc = device_get_softc(pci);
|
||||
return (intr_release_msix(pci, child, sc->msi_parent, irq));
|
||||
#elif defined(__aarch64__)
|
||||
return (arm_release_msix(pci, child, irq));
|
||||
#else
|
||||
return (ENXIO);
|
||||
|
@ -60,6 +60,7 @@ struct generic_pcie_softc {
|
||||
bus_space_handle_t ioh;
|
||||
#ifdef FDT
|
||||
struct ofw_bus_iinfo pci_iinfo;
|
||||
phandle_t msi_parent;
|
||||
#endif
|
||||
};
|
||||
|
||||
|
74
sys/kern/msi_if.m
Normal file
74
sys/kern/msi_if.m
Normal file
@ -0,0 +1,74 @@
|
||||
#-
|
||||
# Copyright (c) 2016 The FreeBSD Foundation
|
||||
# All rights reserved.
|
||||
#
|
||||
# This software was developed by Andrew Turner under
|
||||
# sponsorship from the FreeBSD Foundation.
|
||||
#
|
||||
# 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$
|
||||
#
|
||||
|
||||
INTERFACE msi;
|
||||
|
||||
HEADER {
|
||||
struct intr_irqsrc;
|
||||
};
|
||||
|
||||
METHOD int alloc_msi {
|
||||
device_t dev;
|
||||
device_t child;
|
||||
int count;
|
||||
int maxcount;
|
||||
device_t *pic;
|
||||
struct intr_irqsrc **srcs;
|
||||
};
|
||||
|
||||
METHOD int release_msi {
|
||||
device_t dev;
|
||||
device_t child;
|
||||
int count;
|
||||
struct intr_irqsrc **srcs;
|
||||
};
|
||||
|
||||
METHOD int alloc_msix {
|
||||
device_t dev;
|
||||
device_t child;
|
||||
device_t *pic;
|
||||
struct intr_irqsrc **src;
|
||||
};
|
||||
|
||||
METHOD int release_msix {
|
||||
device_t dev;
|
||||
device_t child;
|
||||
struct intr_irqsrc *src;
|
||||
};
|
||||
|
||||
METHOD int map_msi {
|
||||
device_t dev;
|
||||
device_t child;
|
||||
struct intr_irqsrc *src;
|
||||
uint64_t *addr;
|
||||
uint32_t *data;
|
||||
};
|
||||
|
@ -70,6 +70,7 @@ __FBSDID("$FreeBSD$");
|
||||
#endif
|
||||
|
||||
#include "pic_if.h"
|
||||
#include "msi_if.h"
|
||||
|
||||
#define INTRNAME_LEN (2*MAXCOMLEN + 1)
|
||||
|
||||
@ -97,6 +98,9 @@ struct intr_pic {
|
||||
SLIST_ENTRY(intr_pic) pic_next;
|
||||
intptr_t pic_xref; /* hardware identification */
|
||||
device_t pic_dev;
|
||||
#define FLAG_PIC (1 << 0)
|
||||
#define FLAG_MSI (1 << 1)
|
||||
u_int pic_flags;
|
||||
};
|
||||
|
||||
static struct mtx pic_list_lock;
|
||||
@ -168,6 +172,7 @@ intr_irq_init(void *dummy __unused)
|
||||
|
||||
SLIST_INIT(&pic_list);
|
||||
mtx_init(&pic_list_lock, "intr pic list", NULL, MTX_DEF);
|
||||
|
||||
mtx_init(&isrc_table_lock, "intr isrc table", NULL, MTX_DEF);
|
||||
}
|
||||
SYSINIT(intr_irq_init, SI_SUB_INTR, SI_ORDER_FIRST, intr_irq_init, NULL);
|
||||
@ -917,6 +922,8 @@ intr_pic_register(device_t dev, intptr_t xref)
|
||||
if (pic == NULL)
|
||||
return (ENOMEM);
|
||||
|
||||
pic->pic_flags |= FLAG_PIC;
|
||||
|
||||
debugf("PIC %p registered for %s <dev %p, xref %x>\n", pic,
|
||||
device_get_nameunit(dev), dev, xref);
|
||||
return (0);
|
||||
@ -948,11 +955,18 @@ int
|
||||
intr_pic_claim_root(device_t dev, intptr_t xref, intr_irq_filter_t *filter,
|
||||
void *arg, u_int ipicount)
|
||||
{
|
||||
struct intr_pic *pic;
|
||||
|
||||
if (pic_lookup(dev, xref) == NULL) {
|
||||
pic = pic_lookup(dev, xref);
|
||||
if (pic == NULL) {
|
||||
device_printf(dev, "not registered\n");
|
||||
return (EINVAL);
|
||||
}
|
||||
|
||||
KASSERT((pic->pic_flags & FLAG_PIC) != 0,
|
||||
("%s: Found a non-PIC controller: %s", __func__,
|
||||
device_get_name(pic->pic_dev)));
|
||||
|
||||
if (filter == NULL) {
|
||||
device_printf(dev, "filter missing\n");
|
||||
return (EINVAL);
|
||||
@ -992,6 +1006,10 @@ intr_map_irq(device_t dev, intptr_t xref, struct intr_map_data *data,
|
||||
if (pic == NULL)
|
||||
return (ESRCH);
|
||||
|
||||
KASSERT((pic->pic_flags & FLAG_PIC) != 0,
|
||||
("%s: Found a non-PIC controller: %s", __func__,
|
||||
device_get_name(pic->pic_dev)));
|
||||
|
||||
error = PIC_MAP_INTR(pic->pic_dev, data, &isrc);
|
||||
if (error == 0)
|
||||
*irqp = isrc->isrc_irq;
|
||||
@ -1259,6 +1277,160 @@ intr_irq_next_cpu(u_int current_cpu, cpuset_t *cpumask)
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Register a MSI/MSI-X interrupt controller
|
||||
*/
|
||||
int
|
||||
intr_msi_register(device_t dev, intptr_t xref)
|
||||
{
|
||||
struct intr_pic *pic;
|
||||
|
||||
if (dev == NULL)
|
||||
return (EINVAL);
|
||||
pic = pic_create(dev, xref);
|
||||
if (pic == NULL)
|
||||
return (ENOMEM);
|
||||
|
||||
pic->pic_flags |= FLAG_MSI;
|
||||
|
||||
debugf("PIC %p registered for %s <dev %p, xref %jx>\n", pic,
|
||||
device_get_nameunit(dev), dev, (uintmax_t)xref);
|
||||
return (0);
|
||||
}
|
||||
|
||||
int
|
||||
intr_alloc_msi(device_t pci, device_t child, intptr_t xref, int count,
|
||||
int maxcount, int *irqs)
|
||||
{
|
||||
struct intr_irqsrc **isrc;
|
||||
struct intr_pic *pic;
|
||||
device_t pdev;
|
||||
int err, i;
|
||||
|
||||
pic = pic_lookup(NULL, xref);
|
||||
if (pic == NULL)
|
||||
return (ESRCH);
|
||||
|
||||
KASSERT((pic->pic_flags & FLAG_MSI) != 0,
|
||||
("%s: Found a non-MSI controller: %s", __func__,
|
||||
device_get_name(pic->pic_dev)));
|
||||
|
||||
isrc = malloc(sizeof(*isrc) * count, M_INTRNG, M_WAITOK);
|
||||
err = MSI_ALLOC_MSI(pic->pic_dev, child, count, maxcount, &pdev, isrc);
|
||||
if (err == 0) {
|
||||
for (i = 0; i < count; i++) {
|
||||
irqs[i] = isrc[i]->isrc_irq;
|
||||
}
|
||||
}
|
||||
|
||||
free(isrc, M_INTRNG);
|
||||
|
||||
return (err);
|
||||
}
|
||||
|
||||
int
|
||||
intr_release_msi(device_t pci, device_t child, intptr_t xref, int count,
|
||||
int *irqs)
|
||||
{
|
||||
struct intr_irqsrc **isrc;
|
||||
struct intr_pic *pic;
|
||||
int i, err;
|
||||
|
||||
pic = pic_lookup(NULL, xref);
|
||||
if (pic == NULL)
|
||||
return (ESRCH);
|
||||
|
||||
KASSERT((pic->pic_flags & FLAG_MSI) != 0,
|
||||
("%s: Found a non-MSI controller: %s", __func__,
|
||||
device_get_name(pic->pic_dev)));
|
||||
|
||||
isrc = malloc(sizeof(*isrc) * count, M_INTRNG, M_WAITOK);
|
||||
|
||||
for (i = 0; i < count; i++) {
|
||||
isrc[i] = isrc_lookup(irqs[i]);
|
||||
if (isrc == NULL) {
|
||||
free(isrc, M_INTRNG);
|
||||
return (EINVAL);
|
||||
}
|
||||
}
|
||||
|
||||
err = MSI_RELEASE_MSI(pic->pic_dev, child, count, isrc);
|
||||
free(isrc, M_INTRNG);
|
||||
return (err);
|
||||
}
|
||||
|
||||
int
|
||||
intr_alloc_msix(device_t pci, device_t child, intptr_t xref, int *irq)
|
||||
{
|
||||
struct intr_irqsrc *isrc;
|
||||
struct intr_pic *pic;
|
||||
device_t pdev;
|
||||
int err;
|
||||
|
||||
pic = pic_lookup(NULL, xref);
|
||||
if (pic == NULL)
|
||||
return (ESRCH);
|
||||
|
||||
KASSERT((pic->pic_flags & FLAG_MSI) != 0,
|
||||
("%s: Found a non-MSI controller: %s", __func__,
|
||||
device_get_name(pic->pic_dev)));
|
||||
|
||||
err = MSI_ALLOC_MSIX(pic->pic_dev, child, &pdev, &isrc);
|
||||
if (err != 0)
|
||||
return (err);
|
||||
|
||||
*irq = isrc->isrc_irq;
|
||||
return (0);
|
||||
}
|
||||
|
||||
int
|
||||
intr_release_msix(device_t pci, device_t child, intptr_t xref, int irq)
|
||||
{
|
||||
struct intr_irqsrc *isrc;
|
||||
struct intr_pic *pic;
|
||||
int err;
|
||||
|
||||
pic = pic_lookup(NULL, xref);
|
||||
if (pic == NULL)
|
||||
return (ESRCH);
|
||||
|
||||
KASSERT((pic->pic_flags & FLAG_MSI) != 0,
|
||||
("%s: Found a non-MSI controller: %s", __func__,
|
||||
device_get_name(pic->pic_dev)));
|
||||
|
||||
isrc = isrc_lookup(irq);
|
||||
if (isrc == NULL)
|
||||
return (EINVAL);
|
||||
|
||||
err = MSI_RELEASE_MSIX(pic->pic_dev, child, isrc);
|
||||
return (err);
|
||||
}
|
||||
|
||||
int
|
||||
intr_map_msi(device_t pci, device_t child, intptr_t xref, int irq,
|
||||
uint64_t *addr, uint32_t *data)
|
||||
{
|
||||
struct intr_irqsrc *isrc;
|
||||
struct intr_pic *pic;
|
||||
int err;
|
||||
|
||||
pic = pic_lookup(NULL, xref);
|
||||
if (pic == NULL)
|
||||
return (ESRCH);
|
||||
|
||||
KASSERT((pic->pic_flags & FLAG_MSI) != 0,
|
||||
("%s: Found a non-MSI controller: %s", __func__,
|
||||
device_get_name(pic->pic_dev)));
|
||||
|
||||
isrc = isrc_lookup(irq);
|
||||
if (isrc == NULL)
|
||||
return (EINVAL);
|
||||
|
||||
err = MSI_MAP_MSI(pic->pic_dev, child, isrc, addr, data);
|
||||
return (err);
|
||||
}
|
||||
|
||||
|
||||
void dosoftints(void);
|
||||
void
|
||||
dosoftints(void)
|
||||
|
@ -28,6 +28,7 @@ dev/rt/if_rt.c optional rt
|
||||
|
||||
# Hack to reuse ARM intrng code
|
||||
kern/subr_intr.c standard
|
||||
kern/msi_if.m standard
|
||||
kern/pic_if.m standard
|
||||
|
||||
# Intrng compatible MIPS32 interrupt controller
|
||||
|
@ -128,6 +128,14 @@ int intr_teardown_irq(device_t, struct resource *, void *);
|
||||
|
||||
int intr_describe_irq(device_t, struct resource *, void *, const char *);
|
||||
|
||||
/* MSI/MSI-X handling */
|
||||
int intr_msi_register(device_t, intptr_t);
|
||||
int intr_alloc_msi(device_t, device_t, intptr_t, int, int, int *);
|
||||
int intr_release_msi(device_t, device_t, intptr_t, int, int *);
|
||||
int intr_map_msi(device_t, device_t, intptr_t, int, uint64_t *, uint32_t *);
|
||||
int intr_alloc_msix(device_t, device_t, intptr_t, int *);
|
||||
int intr_release_msix(device_t, device_t, intptr_t, int);
|
||||
|
||||
#ifdef DEV_ACPI
|
||||
u_int intr_acpi_map_irq(device_t, u_int, enum intr_polarity,
|
||||
enum intr_trigger);
|
||||
|
Loading…
x
Reference in New Issue
Block a user