Path: blob/main/sys/arm/annapurna/alpine/alpine_pci_msix.c
39536 views
/*-1* Copyright (c) 2015,2016 Annapurna Labs Ltd. and affiliates2* All rights reserved.3*4* Developed by Semihalf.5*6* Redistribution and use in source and binary forms, with or without7* modification, are permitted provided that the following conditions8* are met:9* 1. Redistributions of source code must retain the above copyright10* notice, this list of conditions and the following disclaimer.11* 2. Redistributions in binary form must reproduce the above copyright12* notice, this list of conditions and the following disclaimer in the13* documentation and/or other materials provided with the distribution.14*15* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND16* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE17* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE18* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE19* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL20* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS21* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)22* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT23* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY24* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF25* SUCH DAMAGE.26*/2728#include <sys/param.h>29#include <sys/systm.h>30#include <sys/kernel.h>31#include <sys/lock.h>32#include <sys/malloc.h>33#include <sys/module.h>34#include <sys/mutex.h>35#include <sys/bus.h>36#include <sys/rman.h>37#include <sys/vmem.h>3839#include <dev/ofw/ofw_bus.h>40#include <dev/ofw/ofw_bus_subr.h>4142#include "msi_if.h"43#include "pic_if.h"4445#define AL_SPI_INTR 046#define AL_EDGE_HIGH 147#define ERR_NOT_IN_MAP -148#define IRQ_OFFSET 149#define GIC_INTR_CELL_CNT 350#define INTR_RANGE_COUNT 251#define MAX_MSIX_COUNT 1605253static int al_msix_attach(device_t);54static int al_msix_probe(device_t);5556static msi_alloc_msi_t al_msix_alloc_msi;57static msi_release_msi_t al_msix_release_msi;58static msi_alloc_msix_t al_msix_alloc_msix;59static msi_release_msix_t al_msix_release_msix;60static msi_map_msi_t al_msix_map_msi;6162static int al_find_intr_pos_in_map(device_t, struct intr_irqsrc *);6364static struct ofw_compat_data compat_data[] = {65{"annapurna-labs,al-msix", true},66{"annapurna-labs,alpine-msix", true},67{NULL, false}68};6970/*71* Bus interface definitions.72*/73static device_method_t al_msix_methods[] = {74DEVMETHOD(device_probe, al_msix_probe),75DEVMETHOD(device_attach, al_msix_attach),7677/* Interrupt controller interface */78DEVMETHOD(msi_alloc_msi, al_msix_alloc_msi),79DEVMETHOD(msi_release_msi, al_msix_release_msi),80DEVMETHOD(msi_alloc_msix, al_msix_alloc_msix),81DEVMETHOD(msi_release_msix, al_msix_release_msix),82DEVMETHOD(msi_map_msi, al_msix_map_msi),8384DEVMETHOD_END85};8687struct al_msix_softc {88bus_addr_t base_addr;89struct resource *res;90uint32_t irq_min;91uint32_t irq_max;92uint32_t irq_count;93struct mtx msi_mtx;94vmem_t *irq_alloc;95device_t gic_dev;96/* Table of isrcs maps isrc pointer to vmem_alloc'd irq number */97struct intr_irqsrc *isrcs[MAX_MSIX_COUNT];98};99100static driver_t al_msix_driver = {101"al_msix",102al_msix_methods,103sizeof(struct al_msix_softc),104};105106DRIVER_MODULE(al_msix, ofwbus, al_msix_driver, 0, 0);107DRIVER_MODULE(al_msix, simplebus, al_msix_driver, 0, 0);108109MALLOC_DECLARE(M_AL_MSIX);110MALLOC_DEFINE(M_AL_MSIX, "al_msix", "Alpine MSIX");111112static int113al_msix_probe(device_t dev)114{115116if (!ofw_bus_status_okay(dev))117return (ENXIO);118119if (!ofw_bus_search_compatible(dev, compat_data)->ocd_data)120return (ENXIO);121122device_set_desc(dev, "Annapurna-Labs MSI-X Controller");123return (BUS_PROBE_DEFAULT);124}125126static int127al_msix_attach(device_t dev)128{129struct al_msix_softc *sc;130device_t gic_dev;131phandle_t iparent;132phandle_t node;133intptr_t xref;134int interrupts[INTR_RANGE_COUNT];135int nintr, i, rid;136uint32_t icells, *intr;137138sc = device_get_softc(dev);139140node = ofw_bus_get_node(dev);141xref = OF_xref_from_node(node);142OF_device_register_xref(xref, dev);143144rid = 0;145sc->res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, RF_ACTIVE);146if (sc->res == NULL) {147device_printf(dev, "Failed to allocate resource\n");148return (ENXIO);149}150151sc->base_addr = (bus_addr_t)rman_get_start(sc->res);152153/* Register this device to handle MSI interrupts */154if (intr_msi_register(dev, xref) != 0) {155device_printf(dev, "could not register MSI-X controller\n");156return (ENXIO);157}158else159device_printf(dev, "MSI-X controller registered\n");160161/* Find root interrupt controller */162iparent = ofw_bus_find_iparent(node);163if (iparent == 0) {164device_printf(dev, "No interrupt-parrent found. "165"Error in DTB\n");166return (ENXIO);167} else {168/* While at parent - store interrupt cells prop */169if (OF_searchencprop(OF_node_from_xref(iparent),170"#interrupt-cells", &icells, sizeof(icells)) == -1) {171device_printf(dev, "DTB: Missing #interrupt-cells "172"property in GIC node\n");173return (ENXIO);174}175}176177gic_dev = OF_device_from_xref(iparent);178if (gic_dev == NULL) {179device_printf(dev, "Cannot find GIC device\n");180return (ENXIO);181}182sc->gic_dev = gic_dev;183184/* Manually read range of interrupts from DTB */185nintr = OF_getencprop_alloc_multi(node, "interrupts", sizeof(*intr),186(void **)&intr);187if (nintr == 0) {188device_printf(dev, "Cannot read interrupts prop from DTB\n");189return (ENXIO);190} else if ((nintr / icells) != INTR_RANGE_COUNT) {191/* Supposed to have min and max value only */192device_printf(dev, "Unexpected count of interrupts "193"in DTB node\n");194return (EINVAL);195}196197/* Read interrupt range values */198for (i = 0; i < INTR_RANGE_COUNT; i++)199interrupts[i] = intr[(i * icells) + IRQ_OFFSET];200201sc->irq_min = interrupts[0];202sc->irq_max = interrupts[1];203sc->irq_count = (sc->irq_max - sc->irq_min + 1);204205if (sc->irq_count > MAX_MSIX_COUNT) {206device_printf(dev, "Available MSI-X count exceeds buffer size."207" Capping to %d\n", MAX_MSIX_COUNT);208sc->irq_count = MAX_MSIX_COUNT;209}210211mtx_init(&sc->msi_mtx, "msi_mtx", NULL, MTX_DEF);212213sc->irq_alloc = vmem_create("Alpine MSI-X IRQs", 0, sc->irq_count,2141, 0, M_FIRSTFIT | M_WAITOK);215216device_printf(dev, "MSI-X SPI IRQ %d-%d\n", sc->irq_min, sc->irq_max);217218bus_attach_children(dev);219return (0);220}221222static int223al_find_intr_pos_in_map(device_t dev, struct intr_irqsrc *isrc)224{225struct al_msix_softc *sc;226int i;227228sc = device_get_softc(dev);229for (i = 0; i < MAX_MSIX_COUNT; i++)230if (sc->isrcs[i] == isrc)231return (i);232return (ERR_NOT_IN_MAP);233}234235static int236al_msix_map_msi(device_t dev, device_t child, struct intr_irqsrc *isrc,237uint64_t *addr, uint32_t *data)238{239struct al_msix_softc *sc;240int i, spi;241242sc = device_get_softc(dev);243244i = al_find_intr_pos_in_map(dev, isrc);245if (i == ERR_NOT_IN_MAP)246return (EINVAL);247248spi = sc->irq_min + i;249250/*251* MSIX message address format:252* [63:20] - MSIx TBAR253* Same value as the MSIx Translation Base Address Register254* [19] - WFE_EXIT255* Once set by MSIx message, an EVENTI is signal to the CPUs256* cluster specified by ‘Local GIC Target List’257* [18:17] - Target GIC ID258* Specifies which IO-GIC (external shared GIC) is targeted259* 0: Local GIC, as specified by the Local GIC Target List260* 1: IO-GIC 0261* 2: Reserved262* 3: Reserved263* [16:13] - Local GIC Target List264* Specifies the Local GICs list targeted by this MSIx265* message.266* [16] If set, SPIn is set in Cluster 0 local GIC267* [15:13] Reserved268* [15] If set, SPIn is set in Cluster 1 local GIC269* [14] If set, SPIn is set in Cluster 2 local GIC270* [13] If set, SPIn is set in Cluster 3 local GIC271* [12:3] - SPIn272* Specifies the SPI (Shared Peripheral Interrupt) index to273* be set in target GICs274* Notes:275* If targeting any local GIC than only SPI[249:0] are valid276* [2] - Function vector277* MSI Data vector extension hint278* [1:0] - Reserved279* Must be set to zero280*/281*addr = (uint64_t)sc->base_addr + (uint64_t)((1 << 16) + (spi << 3));282*data = 0;283284if (bootverbose)285device_printf(dev, "MSI mapping: SPI: %d addr: %jx data: %x\n",286spi, (uintmax_t)*addr, *data);287return (0);288}289290static int291al_msix_alloc_msi(device_t dev, device_t child, int count, int maxcount,292device_t *pic, struct intr_irqsrc **srcs)293{294struct intr_map_data_fdt *fdt_data;295struct al_msix_softc *sc;296vmem_addr_t irq_base;297int error;298u_int i, j;299300sc = device_get_softc(dev);301302if ((powerof2(count) == 0) || (count > 8))303return (EINVAL);304305if (vmem_alloc(sc->irq_alloc, count, M_FIRSTFIT | M_NOWAIT,306&irq_base) != 0)307return (ENOMEM);308309/* Fabricate OFW data to get ISRC from GIC and return it */310fdt_data = malloc(sizeof(*fdt_data) +311GIC_INTR_CELL_CNT * sizeof(pcell_t), M_AL_MSIX, M_WAITOK);312fdt_data->hdr.type = INTR_MAP_DATA_FDT;313fdt_data->iparent = 0;314fdt_data->ncells = GIC_INTR_CELL_CNT;315fdt_data->cells[0] = AL_SPI_INTR; /* code for SPI interrupt */316fdt_data->cells[1] = 0; /* SPI number (uninitialized) */317fdt_data->cells[2] = AL_EDGE_HIGH; /* trig = edge, pol = high */318319mtx_lock(&sc->msi_mtx);320321for (i = irq_base; i < irq_base + count; i++) {322fdt_data->cells[1] = sc->irq_min + i;323error = PIC_MAP_INTR(sc->gic_dev,324(struct intr_map_data *)fdt_data, srcs);325if (error) {326for (j = irq_base; j < i; j++)327sc->isrcs[j] = NULL;328mtx_unlock(&sc->msi_mtx);329vmem_free(sc->irq_alloc, irq_base, count);330free(fdt_data, M_AL_MSIX);331return (error);332}333334sc->isrcs[i] = *srcs;335srcs++;336}337338mtx_unlock(&sc->msi_mtx);339free(fdt_data, M_AL_MSIX);340341if (bootverbose)342device_printf(dev,343"MSI-X allocation: start SPI %d, count %d\n",344(int)irq_base + sc->irq_min, count);345346*pic = sc->gic_dev;347348return (0);349}350351static int352al_msix_release_msi(device_t dev, device_t child, int count,353struct intr_irqsrc **srcs)354{355struct al_msix_softc *sc;356int i, pos;357358sc = device_get_softc(dev);359360mtx_lock(&sc->msi_mtx);361362pos = al_find_intr_pos_in_map(dev, *srcs);363vmem_free(sc->irq_alloc, pos, count);364for (i = 0; i < count; i++) {365pos = al_find_intr_pos_in_map(dev, *srcs);366if (pos != ERR_NOT_IN_MAP)367sc->isrcs[pos] = NULL;368srcs++;369}370371mtx_unlock(&sc->msi_mtx);372373return (0);374}375376static int377al_msix_alloc_msix(device_t dev, device_t child, device_t *pic,378struct intr_irqsrc **isrcp)379{380381return (al_msix_alloc_msi(dev, child, 1, 1, pic, isrcp));382}383384static int385al_msix_release_msix(device_t dev, device_t child, struct intr_irqsrc *isrc)386{387388return (al_msix_release_msi(dev, child, 1, &isrc));389}390391392