Page Menu
Home
FreeBSD
Search
Configure Global Search
Log In
Files
F132650311
D7579.id19491.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Flag For Later
Award Token
Size
13 KB
Referenced Files
None
Subscribers
None
D7579.id19491.diff
View Options
Index: sys/arm/annapurna/alpine/alpine_pci_msix.c
===================================================================
--- /dev/null
+++ sys/arm/annapurna/alpine/alpine_pci_msix.c
@@ -0,0 +1,434 @@
+/*-
+ * Copyright (c) 2015,2016 Annapurna Labs Ltd. and affiliates
+ * All rights reserved.
+ *
+ * Developed by Semihalf.
+ *
+ * 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.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/lock.h>
+#include <sys/malloc.h>
+#include <sys/module.h>
+#include <sys/mutex.h>
+#include <sys/queue.h>
+#include <sys/bus.h>
+#include <sys/rman.h>
+#include <sys/endian.h>
+#include <sys/kdb.h>
+
+#include <machine/intr.h>
+
+#include <vm/vm.h>
+#include <vm/pmap.h>
+
+#include <dev/fdt/fdt_common.h>
+#include <dev/ofw/ofw_bus.h>
+#include <dev/ofw/ofw_bus_subr.h>
+
+#include "alpine_pci.h"
+
+#include "msi_if.h"
+#include "pic_if.h"
+
+#define AL_SPI_INTR 0
+#define AL_LOW_TO_HIGH_TRIG 1
+#define ERR_NOT_IN_MAP -1
+#define IRQ_OFFSET 1
+#define SPI_OFFSET 32
+#define INTR_RANGE_COUNT 2
+#define MAX_MSIX_COUNT 160
+
+#define OFW_CELL_TO_UINT64(cell) \
+ (((uint64_t)(*(cell)) << 32) | (uint64_t)(*((cell) + 1)))
+
+static int al_msix_attach(device_t);
+static int al_msix_probe(device_t);
+
+static msi_alloc_msi_t al_msix_alloc_msi;
+static msi_release_msi_t al_msix_release_msi;
+static msi_alloc_msix_t al_msix_alloc_msix;
+static msi_release_msix_t al_msix_release_msix;
+static msi_map_msi_t al_msix_map_msi;
+
+static int al_find_intr_pos_in_map(struct intr_irqsrc *);
+
+static struct ofw_compat_data compat_data[] = {
+ {"annapurna-labs,al-msix", true},
+ {"annapurna-labs,alpine-msix", true},
+ {NULL, false}
+};
+
+/*
+ * Bus interface definitions.
+ */
+static device_method_t al_msix_methods[] = {
+ DEVMETHOD(device_probe, al_msix_probe),
+ DEVMETHOD(device_attach, al_msix_attach),
+
+ /* Interrupt controller interface */
+ DEVMETHOD(msi_alloc_msi, al_msix_alloc_msi),
+ DEVMETHOD(msi_release_msi, al_msix_release_msi),
+ DEVMETHOD(msi_alloc_msix, al_msix_alloc_msix),
+ DEVMETHOD(msi_release_msix, al_msix_release_msix),
+ DEVMETHOD(msi_map_msi, al_msix_map_msi),
+
+ DEVMETHOD_END
+};
+
+struct al_msix_softc {
+ struct intr_pic *sc_pic;
+ device_t sc_dev;
+ bus_addr_t base_addr;
+ struct resource *reg;
+ uint32_t irq_min;
+ uint32_t irq_max;
+ uint32_t irq_count;
+ struct mtx msi_mtx;
+ char msi_bitmap[(MAX_MSIX_COUNT-1)/sizeof(char) + 1];
+ device_t gic_dev;
+ /* Table of isrcs maps isrc pointer to msi bitmap index */
+ struct intr_irqsrc *isrcs[MAX_MSIX_COUNT];
+};
+
+static driver_t al_msix_driver = {
+ "al_msix",
+ al_msix_methods,
+ sizeof(struct al_msix_softc),
+};
+
+devclass_t al_msix_devclass;
+
+DRIVER_MODULE(al_msix, ofwbus, al_msix_driver, al_msix_devclass, 0, 0);
+DRIVER_MODULE(al_msix, simplebus, al_msix_driver, al_msix_devclass, 0, 0);
+
+struct al_msix_softc *g_softc;
+
+static int
+al_msix_probe(device_t dev)
+{
+
+ if (!ofw_bus_status_okay(dev))
+ return (ENXIO);
+
+ if (!ofw_bus_search_compatible(dev, compat_data)->ocd_data)
+ return (ENXIO);
+
+ device_set_desc(dev, "Annapurna-Labs MSI-X Controller");
+ return (BUS_PROBE_DEFAULT);
+}
+
+static int
+al_msix_attach(device_t dev)
+{
+ struct al_msix_softc *sc;
+ device_t gic_dev;
+ uint64_t reg_base;
+ uint64_t reg_size;
+ uint32_t icells, *intr;
+ pcell_t reg[4];
+ pcell_t par_addr, par_size;
+ intptr_t xref;
+ phandle_t iparent;
+ phandle_t node;
+ int interrupts[INTR_RANGE_COUNT];
+ int nintr, i, rid;
+
+ if (g_softc) {
+ device_printf(dev,
+ "Error, only one MSI-X controller is supported\n");
+ return (ENXIO);
+ }
+
+ sc = device_get_softc(dev);
+ node = ofw_bus_get_node(dev);
+ sc->sc_dev = dev;
+
+ rid = 0;
+ sc->reg = bus_alloc_resource_any(dev, SYS_RES_MEMORY,
+ &rid, RF_ACTIVE);
+ if (sc->reg == NULL) {
+ device_printf(dev, "Failed to allocate resource\n");
+ return (ENXIO);
+ }
+
+ OF_getencprop(OF_parent(node), "#address-cells",
+ &par_addr, sizeof(par_addr));
+
+ OF_getencprop(OF_parent(node), "#size-cells",
+ &par_size, sizeof(par_size));
+
+ OF_getencprop(node, "reg", (pcell_t*)reg, sizeof(reg));
+
+ if (par_addr == 1)
+ reg_base = (uint64_t)reg[0];
+ else
+ reg_base = OFW_CELL_TO_UINT64(®[0]);
+
+ if (par_size == 1)
+ reg_size = (uint64_t)reg[par_addr];
+ else
+ reg_size = OFW_CELL_TO_UINT64(®[par_addr]);
+
+ sc->base_addr = (bus_addr_t)reg_base;
+
+ memset(interrupts, 0, sizeof(interrupts));
+
+ xref = OF_xref_from_node(node);
+
+ /* Register this device to handle MSI interrupts */
+ if (intr_msi_register(dev, xref) != 0)
+ device_printf(dev, "could not register MSI-X controller\n");
+ else
+ device_printf(dev, "MSI-X controller registered\n");
+
+ OF_device_register_xref(xref, dev);
+
+ /* Find root interrupt controller */
+ iparent = ofw_bus_find_iparent(node);
+ if (iparent == 0) {
+ device_printf(dev, "No interrupt-parrent found. "
+ "Error in DTB\n");
+ return (ENXIO);
+ } else {
+ /* While at parent - store interrupt cells prop */
+ if (OF_searchencprop(OF_node_from_xref(iparent),
+ "#interrupt-cells", &icells, sizeof(icells)) == -1) {
+ device_printf(dev, "DTB: Missing #interrupt-cells "
+ "property in GIC node\n");
+ return (ENXIO);
+ }
+ }
+
+ gic_dev = OF_device_from_xref(iparent);
+ if (gic_dev == NULL) {
+ device_printf(dev, "Cannot find GIC device\n");
+ return (ENXIO);
+ }
+ sc->gic_dev = gic_dev;
+
+ /* Manually read range of interrupts from DTB */
+ nintr = OF_getencprop_alloc(node, "interrupts", sizeof(*intr),
+ (void **)&intr);
+ if (nintr == 0) {
+ device_printf(dev, "Cannot read interrupts prop from DTB\n");
+ return (ENXIO);
+ } else if ((nintr / icells) != INTR_RANGE_COUNT) {
+ /* Supposed to have min and max value only */
+ device_printf(dev, "Unexpected count of interrupts "
+ "in DTB node\n");
+ return (EINVAL);
+ }
+
+ /* Read interrupt range values */
+ for (i = 0; i < INTR_RANGE_COUNT; i++)
+ interrupts[i] = intr[(i * icells) + IRQ_OFFSET];
+
+ sc->irq_min = interrupts[0];
+ sc->irq_max = interrupts[1];
+ sc->irq_count = (sc->irq_max - sc->irq_min + 1);
+
+ if (sc->irq_count > MAX_MSIX_COUNT) {
+ device_printf(dev, "Available MSI-X count exceeds buffer size."
+ " Capping to %d\n", MAX_MSIX_COUNT);
+ sc->irq_count = MAX_MSIX_COUNT;
+ }
+
+ mtx_init(&sc->msi_mtx, "msi_mtx", NULL, MTX_DEF);
+
+ device_printf(dev,
+ "MSI-X base address 0x%jx, size 0x%jx, IRQ %d-%d\n",
+ (uintmax_t)reg_base, (uintmax_t)reg_size, sc->irq_min, sc->irq_max);
+
+ g_softc = sc;
+
+ return (bus_generic_attach(dev));
+}
+
+static int
+al_find_intr_pos_in_map(struct intr_irqsrc *isrc)
+{
+ int i;
+
+ for (i = 0; i < MAX_MSIX_COUNT; i++)
+ if (g_softc->isrcs[i] == isrc)
+ return (i);
+ return (ERR_NOT_IN_MAP);
+}
+
+static int
+al_msix_map_msi(device_t dev, device_t child, struct intr_irqsrc *isrc,
+ uint64_t *addr, uint32_t *data)
+{
+ int i, irq;
+
+ i = al_find_intr_pos_in_map(isrc);
+ if (i == ERR_NOT_IN_MAP)
+ return (EINVAL);
+
+ irq = g_softc->irq_min + i;
+ /* Validate parameters */
+ if (isclr(&g_softc->msi_bitmap, i)) {
+ device_printf(g_softc->sc_dev,
+ "SPI %d mapped but not allocated\n", irq);
+ return (EINVAL);
+ }
+ /*
+ * MSIX message address format:
+ * [63:20] - MSIx TBAR
+ * Same value as the MSIx Translation Base Address Register
+ * [19] - WFE_EXIT
+ * Once set by MSIx message, an EVENTI is signal to the CPUs
+ * cluster specified by ‘Local GIC Target List’
+ * [18:17] - Target GIC ID
+ * Specifies which IO-GIC (external shared GIC) is targeted
+ * 0: Local GIC, as specified by the Local GIC Target List
+ * 1: IO-GIC 0
+ * 2: Reserved
+ * 3: Reserved
+ * [16:13] - Local GIC Target List
+ * Specifies the Local GICs list targeted by this MSIx
+ * message.
+ * [16] If set, SPIn is set in Cluster 0 local GIC
+ * [15:13] Reserved
+ * [15] If set, SPIn is set in Cluster 1 local GIC
+ * [14] If set, SPIn is set in Cluster 2 local GIC
+ * [13] If set, SPIn is set in Cluster 3 local GIC
+ * [12:3] - SPIn
+ * Specifies the SPI (Shared Peripheral Interrupt) index to
+ * be set in target GICs
+ * Notes:
+ * If targeting any local GIC than only SPI[249:0] are valid
+ * [2] - Function vector
+ * MSI Data vector extension hint
+ * [1:0] - Reserved
+ * Must be set to zero
+ */
+ *addr = (uint64_t)g_softc->base_addr + (uint64_t)((1 << 16) +
+ (irq << 3));
+ *data = 0;
+
+ if (bootverbose)
+ device_printf(g_softc->sc_dev,
+ "MSI mapping: irq: s%d addr: %jx data: %x\n",
+ irq, *addr, *data);
+ return (0);
+}
+
+static int
+al_msix_alloc_msi(device_t dev, device_t child, int count, int maxcount,
+ device_t *pic, struct intr_irqsrc **srcs)
+{
+ struct intr_map_data_fdt fdt_data;
+ struct al_msix_softc *sc;
+ u_int i, start;
+ int error;
+
+ sc = g_softc;
+
+ if ((powerof2(count) == 0) || (count > 8))
+ return (EINVAL);
+
+ mtx_lock(&sc->msi_mtx);
+
+ for (start = 0; (start + count) < (sc->irq_count); start++) {
+ for (i = start; i < start + count; i++) {
+ if (isset(&sc->msi_bitmap, i))
+ break;
+ }
+ if (i == (start + count))
+ break;
+ }
+
+ /* Fabricate OFW data to get ISRC from GIC and return it */
+ pcell_t cells[3] = {0, 0, 1}; /* SPI, undefined, trig=edge, pol=high */
+
+ fdt_data.hdr.type = INTR_MAP_DATA_FDT;
+ fdt_data.hdr.destruct = NULL;
+ fdt_data.iparent = 0;
+ fdt_data.ncells = 3;
+ fdt_data.cells = cells;
+
+ for (i = start; i < start + count; i++) {
+ cells[1] = sc->irq_min + i;
+ error = PIC_MAP_INTR(sc->gic_dev,
+ (struct intr_map_data *)&fdt_data, srcs);
+ if (error)
+ return (error);
+
+ g_softc->isrcs[i] = *srcs;
+ srcs++;
+ setbit(&sc->msi_bitmap, i);
+ }
+
+ if (bootverbose)
+ device_printf(sc->sc_dev,
+ "MSI-X allocation: start IRQ %d, count %d\n",
+ start + sc->irq_min, count);
+
+ mtx_unlock(&sc->msi_mtx);
+
+ *pic = sc->gic_dev;
+
+ return (0);
+}
+
+static int
+al_msix_release_msi(device_t dev, device_t child, int count,
+ struct intr_irqsrc **srcs)
+{
+ int i, pos;
+
+ mtx_lock(&g_softc->msi_mtx);
+
+ for (i = 0; i < count; i++) {
+ pos = al_find_intr_pos_in_map(*srcs);
+ if (pos != ERR_NOT_IN_MAP)
+ clrbit(&g_softc->msi_bitmap, pos);
+ srcs++;
+ }
+
+ mtx_unlock(&g_softc->msi_mtx);
+
+ return (0);
+}
+
+static int
+al_msix_alloc_msix(device_t dev, device_t child, device_t *pic,
+ struct intr_irqsrc **isrcp)
+{
+
+ return (al_msix_alloc_msi(dev, child, 1, 1, pic, isrcp));
+}
+
+static int
+al_msix_release_msix(device_t dev, device_t child, struct intr_irqsrc *isrc)
+{
+
+ return (al_msix_release_msi(dev, child, 1, &isrc));
+}
Index: sys/arm/annapurna/alpine/files.alpine
===================================================================
--- sys/arm/annapurna/alpine/files.alpine
+++ sys/arm/annapurna/alpine/files.alpine
@@ -7,6 +7,7 @@
dev/uart/uart_dev_ns8250.c optional uart
arm/annapurna/alpine/alpine_pci.c optional pci
+arm/annapurna/alpine/alpine_pci_msix.c optional pci
arm/annapurna/alpine/common.c standard
arm/annapurna/alpine/alpine_ccu.c standard
arm/annapurna/alpine/alpine_nb_service.c standard
Index: sys/boot/fdt/dts/arm/annapurna-alpine.dts
===================================================================
--- sys/boot/fdt/dts/arm/annapurna-alpine.dts
+++ sys/boot/fdt/dts/arm/annapurna-alpine.dts
@@ -170,6 +170,16 @@
};
};
+ /* MSIX Configuration */
+ msix: msix {
+ compatible = "annapurna-labs,al-msix";
+ #address-cells = <2>;
+ #size-cells = <1>;
+ reg = <0xfbe00000 0x100000>;
+ interrupts = <0 96 1 0 159 1>;
+ interrupt-parent = <&MPIC>;
+ };
+
pcie-internal {
compatible = "annapurna-labs,al-internal-pcie";
device_type = "pci";
@@ -182,6 +192,7 @@
<0x3800 0 0 1 &MPIC 0 36 4>,
<0x4000 0 0 1 &MPIC 0 43 4>, // SATA 0 (PCIe expander)
<0x4800 0 0 1 &MPIC 0 44 1>; // SATA 1 (onboard)
+ msi-parent = <&msix>;
// ranges:
// - ECAM - non prefetchable config space
Index: sys/conf/files.arm64
===================================================================
--- sys/conf/files.arm64
+++ sys/conf/files.arm64
@@ -2,6 +2,7 @@
arm/annapurna/alpine/alpine_ccu.c optional soc_anpa_alpinev2
arm/annapurna/alpine/alpine_nb_service.c optional soc_anpa_alpinev2
arm/annapurna/alpine/alpine_pci.c optional soc_anpa_alpinev2 pci fdt
+arm/annapurna/alpine/alpine_pci_msix.c optional soc_anpa_alpinev2 pci fdt
arm/arm/generic_timer.c standard
arm/arm/gic.c optional intrng
arm/arm/pmu.c standard
File Metadata
Details
Attached
Mime Type
text/plain
Expires
Sun, Oct 19, 6:28 PM (1 h, 29 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
23936408
Default Alt Text
D7579.id19491.diff (13 KB)
Attached To
Mode
D7579: Support for MSI-X on Annapurna Alpine
Attached
Detach File
Event Timeline
Log In to Comment