summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAndrew Turner <andrew@FreeBSD.org>2016-05-16 09:11:40 +0000
committerAndrew Turner <andrew@FreeBSD.org>2016-05-16 09:11:40 +0000
commit3fc155dc64bd967aebcde25b51c4210d870718b9 (patch)
tree1a5b256ed0cc34205d0c81d9e55ac1c30ac8af0d
parent81ccfbbc2e968afaabfa7a0de25d7d4972e0e1f8 (diff)
Notes
-rw-r--r--sys/arm/arm/gic.c266
-rw-r--r--sys/conf/files.arm1
-rw-r--r--sys/dev/pci/pci_host_generic.c44
-rw-r--r--sys/dev/pci/pci_host_generic.h1
-rw-r--r--sys/kern/msi_if.m74
-rw-r--r--sys/kern/subr_intr.c174
-rw-r--r--sys/mips/mediatek/files.mediatek1
-rw-r--r--sys/sys/intr.h8
8 files changed, 541 insertions, 28 deletions
diff --git a/sys/arm/arm/gic.c b/sys/arm/arm/gic.c
index 17e152557077..c55337e0a96e 100644
--- a/sys/arm/arm/gic.c
+++ b/sys/arm/arm/gic.c
@@ -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,15 +1107,23 @@ arm_gic_setup_intr(device_t dev, struct intr_irqsrc *isrc,
enum intr_trigger trig;
enum intr_polarity pol;
- if (data == NULL)
- return (ENOTSUP);
-
- /* Get config for resource. */
- if (gic_map_intr(dev, data, &irq, &pol, &trig))
- return (EINVAL);
+ 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);
+ if (gi->gi_irq != irq)
+ return (EINVAL);
+ } else
+ return (ENOTSUP);
/* Compare config if this is not first setup. */
if (isrc->isrc_handlers != 0) {
@@ -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;
- 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;
+ /* 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
};
diff --git a/sys/conf/files.arm b/sys/conf/files.arm
index 8a278887e1e6..06800ae58966 100644
--- a/sys/conf/files.arm
+++ b/sys/conf/files.arm
@@ -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
diff --git a/sys/dev/pci/pci_host_generic.c b/sys/dev/pci/pci_host_generic.c
index 8ecf9c665c5a..f1d13e97d66f 100644
--- a/sys/dev/pci/pci_host_generic.c
+++ b/sys/dev/pci/pci_host_generic.c
@@ -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);
diff --git a/sys/dev/pci/pci_host_generic.h b/sys/dev/pci/pci_host_generic.h
index 3ecc43b39344..4343c5084021 100644
--- a/sys/dev/pci/pci_host_generic.h
+++ b/sys/dev/pci/pci_host_generic.h
@@ -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
};
diff --git a/sys/kern/msi_if.m b/sys/kern/msi_if.m
new file mode 100644
index 000000000000..19b6af4501cb
--- /dev/null
+++ b/sys/kern/msi_if.m
@@ -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;
+};
+
diff --git a/sys/kern/subr_intr.c b/sys/kern/subr_intr.c
index 9b92e7314f60..16964efe971c 100644
--- a/sys/kern/subr_intr.c
+++ b/sys/kern/subr_intr.c
@@ -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)
diff --git a/sys/mips/mediatek/files.mediatek b/sys/mips/mediatek/files.mediatek
index b99f6a58f2c7..81c12170b416 100644
--- a/sys/mips/mediatek/files.mediatek
+++ b/sys/mips/mediatek/files.mediatek
@@ -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
diff --git a/sys/sys/intr.h b/sys/sys/intr.h
index e1e2a22bed6e..14267c49177d 100644
--- a/sys/sys/intr.h
+++ b/sys/sys/intr.h
@@ -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);