Page Menu
Home
FreeBSD
Search
Configure Global Search
Log In
Files
F135792689
D5985.id15380.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Flag For Later
Award Token
Size
23 KB
Referenced Files
None
Subscribers
None
D5985.id15380.diff
View Options
Index: sys/arm/arm/gic.c
===================================================================
--- sys/arm/arm/gic.c
+++ sys/arm/arm/gic.c
@@ -53,6 +53,10 @@
#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 @@
#ifdef INTRNG
#include "pic_if.h"
+#include "msi_if.h"
#endif
#define GIC_DEBUG_SPURIOUS
@@ -123,6 +128,10 @@
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,28 @@
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_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_flags |= GI_FLAG_MSI;
+ }
+}
#endif
static int
@@ -1006,6 +1037,7 @@
enum intr_polarity pol;
enum intr_trigger trig;
struct arm_gic_softc *sc;
+ struct gic_irqsrc *gi;
sc = device_get_softc(dev);
switch (data->type) {
@@ -1014,8 +1046,19 @@
if (gic_map_fdt(dev, data->fdt.ncells, data->fdt.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
+ case INTR_MAP_DATA_INTERNAL:
+ gi = (struct gic_irqsrc *)data->src;
+
+ irq = gi->gi_irq;
+ pol = gi->gi_pol;
+ trig = gi->gi_trig;
+ break;
default:
return (EINVAL);
}
@@ -1087,11 +1130,17 @@
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;
+ /* For MSI/MSI-X we should have already configured these */
+ if ((gi->gi_flags & GI_FLAG_MSI) != GI_FLAG_MSI) {
+ 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;
+ } else {
+ KASSERT(gi->gi_pol == pol, ("GIC polarity was not set"));
+ KASSERT(gi->gi_trig == trig, ("GIC trigger was not set"));
+ }
/*
* XXX - In case that per CPU interrupt is going to be enabled in time
@@ -1103,7 +1152,7 @@
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);
}
@@ -1498,8 +1547,8 @@
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[] = {
@@ -1525,9 +1574,11 @@
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;
@@ -1541,9 +1592,16 @@
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);
@@ -1551,11 +1609,187 @@
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;
+ /* MSI interrupts need to be edge triggered */
+ psc->gic_irqs[irq + i].gi_trig = INTR_TRIGGER_EDGE;
+ psc->gic_irqs[irq + i].gi_pol = INTR_POLARITY_HIGH;
+
+ }
+ 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) == 0,
+ ("%s: Trying to release an unused MSI-X interrupt",
+ __func__));
+
+ gi->gi_flags &= ~GI_FLAG_MSI_USED;
+ gi->gi_trig = INTR_TRIGGER_CONFORM;
+ gi->gi_pol = INTR_POLARITY_CONFORM;
+ 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;
+
+ /* MSI-X interrupts need to be edge triggered */
+ psc->gic_irqs[irq].gi_trig = INTR_TRIGGER_EDGE;
+ psc->gic_irqs[irq].gi_pol = INTR_POLARITY_HIGH;
+ 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) == 0,
+ ("%s: Trying to release an unused MSI-X interrupt", __func__));
+
+ mtx_lock(&sc->sc_mutex);
+ gi->gi_flags &= ~GI_FLAG_MSI_USED;
+ gi->gi_trig = INTR_TRIGGER_CONFORM;
+ gi->gi_pol = INTR_POLARITY_CONFORM;
+ 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
};
Index: sys/conf/files.arm
===================================================================
--- sys/conf/files.arm
+++ sys/conf/files.arm
@@ -115,6 +115,7 @@
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_sfbuf.c standard
Index: sys/dev/pci/pci_host_generic.h
===================================================================
--- sys/dev/pci/pci_host_generic.h
+++ sys/dev/pci/pci_host_generic.h
@@ -60,6 +60,7 @@
bus_space_handle_t ioh;
#ifdef FDT
struct ofw_bus_iinfo pci_iinfo;
+ phandle_t msi_parent;
#endif
};
Index: sys/dev/pci/pci_host_generic.c
===================================================================
--- sys/dev/pci/pci_host_generic.c
+++ sys/dev/pci/pci_host_generic.c
@@ -46,6 +46,10 @@
#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 @@
uint64_t phys_base;
uint64_t pci_base;
uint64_t size;
+ phandle_t node;
int error;
int tuple;
int rid;
@@ -227,8 +232,12 @@
}
}
- 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));
@@ -660,8 +669,13 @@
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);
@@ -671,8 +685,12 @@
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);
@@ -683,8 +701,12 @@
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);
@@ -694,8 +716,12 @@
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);
@@ -705,8 +731,12 @@
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);
Index: sys/kern/msi_if.m
===================================================================
--- /dev/null
+++ 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;
+};
+
Index: sys/kern/subr_intr.c
===================================================================
--- sys/kern/subr_intr.c
+++ sys/kern/subr_intr.c
@@ -70,6 +70,7 @@
#endif
#include "pic_if.h"
+#include "msi_if.h"
#define INTRNAME_LEN (2*MAXCOMLEN + 1)
@@ -99,10 +100,16 @@
device_t pic_dev;
};
-static struct mtx pic_list_lock;
-static SLIST_HEAD(, intr_pic) pic_list;
+struct intr_pic_list {
+ struct mtx lock;
+ SLIST_HEAD(, intr_pic) list;
+};
+
+struct intr_pic_list pic_list;
+struct intr_pic_list msi_list;
-static struct intr_pic *pic_lookup(device_t dev, intptr_t xref);
+static struct intr_pic *pic_lookup(struct intr_pic_list *list, device_t dev,
+ intptr_t xref);
/* Interrupt source definition. */
static struct mtx isrc_table_lock;
@@ -168,8 +175,12 @@
intr_irq_init(void *dummy __unused)
{
- SLIST_INIT(&pic_list);
- mtx_init(&pic_list_lock, "intr pic list", NULL, MTX_DEF);
+ SLIST_INIT(&pic_list.list);
+ mtx_init(&pic_list.lock, "intr pic list", NULL, MTX_DEF);
+
+ SLIST_INIT(&msi_list.list);
+ mtx_init(&msi_list.lock, "intr msi 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);
@@ -570,6 +581,25 @@
return (ddata->idd_irq);
}
#endif
+/*
+ * Internal mapping of an interrupt source, e.g. with MSI/MSI-X. We have
+ * already allocated the source, but will need to keep it around so we can
+ * find it later.
+ */
+static u_int
+intr_internal_map_irq(device_t dev, struct intr_irqsrc *src)
+{
+ struct intr_dev_data *ddata;
+
+ ddata = intr_ddata_alloc(0);
+ if (ddata == NULL)
+ return (0xFFFFFFFF); /* no space left */
+
+ ddata->idd_dev = dev;
+ ddata->idd_data.type = INTR_MAP_DATA_INTERNAL;
+ ddata->idd_data.src = src;
+ return (ddata->idd_irq);
+}
#ifdef FDT
/*
* Map interrupt source according to FDT data into framework. If such mapping
@@ -786,17 +816,17 @@
* Lookup interrupt controller locked.
*/
static inline struct intr_pic *
-pic_lookup_locked(device_t dev, intptr_t xref)
+pic_lookup_locked(struct intr_pic_list *list, device_t dev, intptr_t xref)
{
struct intr_pic *pic;
- mtx_assert(&pic_list_lock, MA_OWNED);
+ mtx_assert(&list->lock, MA_OWNED);
if (dev == NULL && xref == 0)
return (NULL);
/* Note that pic->pic_dev is never NULL on registered PIC. */
- SLIST_FOREACH(pic, &pic_list, pic_next) {
+ SLIST_FOREACH(pic, &list->list, pic_next) {
if (dev == NULL) {
if (xref == pic->pic_xref)
return (pic);
@@ -813,13 +843,13 @@
* Lookup interrupt controller.
*/
static struct intr_pic *
-pic_lookup(device_t dev, intptr_t xref)
+pic_lookup(struct intr_pic_list *list, device_t dev, intptr_t xref)
{
struct intr_pic *pic;
- mtx_lock(&pic_list_lock);
- pic = pic_lookup_locked(dev, xref);
- mtx_unlock(&pic_list_lock);
+ mtx_lock(&list->lock);
+ pic = pic_lookup_locked(list, dev, xref);
+ mtx_unlock(&list->lock);
return (pic);
}
@@ -827,21 +857,21 @@
* Create interrupt controller.
*/
static struct intr_pic *
-pic_create(device_t dev, intptr_t xref)
+pic_create(struct intr_pic_list *list, device_t dev, intptr_t xref)
{
struct intr_pic *pic;
- mtx_lock(&pic_list_lock);
- pic = pic_lookup_locked(dev, xref);
+ mtx_lock(&list->lock);
+ pic = pic_lookup_locked(list, dev, xref);
if (pic != NULL) {
- mtx_unlock(&pic_list_lock);
+ mtx_unlock(&list->lock);
return (pic);
}
pic = malloc(sizeof(*pic), M_INTRNG, M_NOWAIT | M_ZERO);
pic->pic_xref = xref;
pic->pic_dev = dev;
- SLIST_INSERT_HEAD(&pic_list, pic, pic_next);
- mtx_unlock(&pic_list_lock);
+ SLIST_INSERT_HEAD(&list->list, pic, pic_next);
+ mtx_unlock(&list->lock);
return (pic);
}
@@ -850,18 +880,18 @@
* Destroy interrupt controller.
*/
static void
-pic_destroy(device_t dev, intptr_t xref)
+pic_destroy(struct intr_pic_list *list, device_t dev, intptr_t xref)
{
struct intr_pic *pic;
- mtx_lock(&pic_list_lock);
- pic = pic_lookup_locked(dev, xref);
+ mtx_lock(&list->lock);
+ pic = pic_lookup_locked(list, dev, xref);
if (pic == NULL) {
- mtx_unlock(&pic_list_lock);
+ mtx_unlock(&list->lock);
return;
}
- SLIST_REMOVE(&pic_list, pic, intr_pic, pic_next);
- mtx_unlock(&pic_list_lock);
+ SLIST_REMOVE(&list->list, pic, intr_pic, pic_next);
+ mtx_unlock(&list->lock);
free(pic, M_INTRNG);
}
@@ -876,7 +906,7 @@
if (dev == NULL)
return (EINVAL);
- pic = pic_create(dev, xref);
+ pic = pic_create(&pic_list, dev, xref);
if (pic == NULL)
return (ENOMEM);
@@ -912,7 +942,7 @@
void *arg, u_int ipicount)
{
- if (pic_lookup(dev, xref) == NULL) {
+ if (pic_lookup(&pic_list, dev, xref) == NULL) {
device_printf(dev, "not registered\n");
return (EINVAL);
}
@@ -951,7 +981,7 @@
if (data == NULL)
return (EINVAL);
- pic = pic_lookup(dev, xref);
+ pic = pic_lookup(&pic_list, dev, xref);
if (pic == NULL || pic->pic_dev == NULL)
return (ESRCH);
@@ -1222,6 +1252,138 @@
}
#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(&msi_list, dev, xref);
+ if (pic == NULL)
+ return (ENOMEM);
+
+ printf("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(&msi_list, NULL, xref);
+ if (pic == NULL || pic->pic_dev == NULL)
+ return (ESRCH);
+
+ 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] = intr_internal_map_irq(pdev, isrc[i]);
+ }
+ }
+
+ 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(&msi_list, NULL, xref);
+ if (pic == NULL || pic->pic_dev == NULL)
+ return (ESRCH);
+
+ isrc = malloc(sizeof(*isrc) * count, M_INTRNG, M_WAITOK);
+
+ for (i = 0; i < count; i++) {
+ isrc[i] = intr_ddata_lookup(irqs[i], NULL);
+ 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(&msi_list, NULL, xref);
+ if (pic == NULL || pic->pic_dev == NULL)
+ return (ESRCH);
+
+ err = MSI_ALLOC_MSIX(pic->pic_dev, child, &pdev, &isrc);
+ if (err != 0)
+ return (err);
+
+ *irq = intr_internal_map_irq(pdev, isrc);
+ 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(&msi_list, NULL, xref);
+ if (pic == NULL || pic->pic_dev == NULL)
+ return (ESRCH);
+
+ isrc = intr_ddata_lookup(irq, NULL);
+ 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(&msi_list, NULL, xref);
+ if (pic == NULL || pic->pic_dev == NULL)
+ return (ESRCH);
+
+ isrc = intr_ddata_lookup(irq, NULL);
+ if (isrc == NULL)
+ return (EINVAL);
+
+ err = MSI_MAP_MSI(pic->pic_dev, child, isrc, addr, data);
+ return (err);
+}
+
+
void dosoftints(void);
void
dosoftints(void)
Index: sys/sys/intr.h
===================================================================
--- sys/sys/intr.h
+++ sys/sys/intr.h
@@ -35,6 +35,7 @@
enum intr_map_data_type {
INTR_MAP_DATA_ACPI,
INTR_MAP_DATA_FDT,
+ INTR_MAP_DATA_INTERNAL,
};
#ifdef DEV_ACPI
@@ -60,6 +61,7 @@
#ifdef FDT
struct intr_map_data_fdt fdt;
#endif
+ struct intr_irqsrc *src;
};
};
@@ -123,6 +125,14 @@
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);
File Metadata
Details
Attached
Mime Type
text/plain
Expires
Thu, Nov 13, 10:33 PM (6 h, 9 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
25268140
Default Alt Text
D5985.id15380.diff (23 KB)
Attached To
Mode
D5985: Add MSI/MSI-X support to intrng
Attached
Detach File
Event Timeline
Log In to Comment