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:
Andrew Turner 2016-05-16 09:11:40 +00:00
parent 81ccfbbc2e
commit 3fc155dc64
8 changed files with 541 additions and 28 deletions

View File

@ -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
};

View File

@ -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

View File

@ -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);

View File

@ -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
View 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;
};

View File

@ -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)

View File

@ -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

View File

@ -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);