Skip site navigation (1)Skip section navigation (2)
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>