Date: Mon, 16 May 2016 09:11:40 +0000 (UTC) From: Andrew Turner <andrew@FreeBSD.org> To: src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-head@freebsd.org Subject: svn commit: r299928 - in head/sys: arm/arm conf dev/pci kern mips/mediatek sys Message-ID: <201605160911.u4G9Be5W013033@repo.freebsd.org>
next in thread | raw e-mail | index | archive | help
Author: andrew Date: Mon May 16 09:11:40 2016 New Revision: 299928 URL: https://svnweb.freebsd.org/changeset/base/299928 Log: 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 Added: head/sys/kern/msi_if.m (contents, props changed) Modified: head/sys/arm/arm/gic.c head/sys/conf/files.arm head/sys/dev/pci/pci_host_generic.c head/sys/dev/pci/pci_host_generic.h head/sys/kern/subr_intr.c head/sys/mips/mediatek/files.mediatek head/sys/sys/intr.h Modified: head/sys/arm/arm/gic.c ============================================================================== --- head/sys/arm/arm/gic.c Mon May 16 08:50:32 2016 (r299927) +++ head/sys/arm/arm/gic.c Mon May 16 09:11:40 2016 (r299928) @@ -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_m 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 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 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 */ - - 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) == 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; + } /* * 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 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, stru { 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 }; Modified: head/sys/conf/files.arm ============================================================================== --- head/sys/conf/files.arm Mon May 16 08:50:32 2016 (r299927) +++ head/sys/conf/files.arm Mon May 16 09:11:40 2016 (r299928) @@ -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 Modified: head/sys/dev/pci/pci_host_generic.c ============================================================================== --- head/sys/dev/pci/pci_host_generic.c Mon May 16 08:50:32 2016 (r299927) +++ head/sys/dev/pci/pci_host_generic.c Mon May 16 09:11:40 2016 (r299928) @@ -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, dev 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, devic 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, de 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); Modified: head/sys/dev/pci/pci_host_generic.h ============================================================================== --- head/sys/dev/pci/pci_host_generic.h Mon May 16 08:50:32 2016 (r299927) +++ head/sys/dev/pci/pci_host_generic.h Mon May 16 09:11:40 2016 (r299928) @@ -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 }; Added: head/sys/kern/msi_if.m ============================================================================== --- /dev/null 00:00:00 1970 (empty, because file is newly added) +++ head/sys/kern/msi_if.m Mon May 16 09:11:40 2016 (r299928) @@ -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; +}; + Modified: head/sys/kern/subr_intr.c ============================================================================== --- head/sys/kern/subr_intr.c Mon May 16 08:50:32 2016 (r299927) +++ head/sys/kern/subr_intr.c Mon May 16 09:11:40 2016 (r299928) @@ -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 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 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, cpu } #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) Modified: head/sys/mips/mediatek/files.mediatek ============================================================================== --- head/sys/mips/mediatek/files.mediatek Mon May 16 08:50:32 2016 (r299927) +++ head/sys/mips/mediatek/files.mediatek Mon May 16 09:11:40 2016 (r299928) @@ -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 Modified: head/sys/sys/intr.h ============================================================================== --- head/sys/sys/intr.h Mon May 16 08:50:32 2016 (r299927) +++ head/sys/sys/intr.h Mon May 16 09:11:40 2016 (r299928) @@ -128,6 +128,14 @@ int intr_teardown_irq(device_t, struct r 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);
Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?201605160911.u4G9Be5W013033>