Page MenuHomeFreeBSD

D7579.id19491.diff
No OneTemporary

D7579.id19491.diff

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(&reg[0]);
+
+ if (par_size == 1)
+ reg_size = (uint64_t)reg[par_addr];
+ else
+ reg_size = OFW_CELL_TO_UINT64(&reg[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

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)

Event Timeline