Index: sys/arm/arm/gic.c =================================================================== --- sys/arm/arm/gic.c +++ sys/arm/arm/gic.c @@ -53,6 +53,10 @@ #ifdef INTRNG #include #endif + +#include +#include + #include #include #include @@ -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 #include +#if defined(INTRNG) +#include +#endif + #include #include #include @@ -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 \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);