The Design and Implementation of the FreeBSD Operating System, Second Edition
Now available: The Design and Implementation of the FreeBSD Operating System (Second Edition)


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]

FreeBSD/Linux Kernel Cross Reference
sys/arm/annapurna/alpine/alpine_pci_msix.c

Version: -  FREEBSD  -  FREEBSD-13-STABLE  -  FREEBSD-13-0  -  FREEBSD-12-STABLE  -  FREEBSD-12-0  -  FREEBSD-11-STABLE  -  FREEBSD-11-0  -  FREEBSD-10-STABLE  -  FREEBSD-10-0  -  FREEBSD-9-STABLE  -  FREEBSD-9-0  -  FREEBSD-8-STABLE  -  FREEBSD-8-0  -  FREEBSD-7-STABLE  -  FREEBSD-7-0  -  FREEBSD-6-STABLE  -  FREEBSD-6-0  -  FREEBSD-5-STABLE  -  FREEBSD-5-0  -  FREEBSD-4-STABLE  -  FREEBSD-3-STABLE  -  FREEBSD22  -  l41  -  OPENBSD  -  linux-2.6  -  MK84  -  PLAN9  -  xnu-8792 
SearchContext: -  none  -  3  -  10 

    1 /*-
    2  * Copyright (c) 2015,2016 Annapurna Labs Ltd. and affiliates
    3  * All rights reserved.
    4  *
    5  * Developed by Semihalf.
    6  *
    7  * Redistribution and use in source and binary forms, with or without
    8  * modification, are permitted provided that the following conditions
    9  * are met:
   10  * 1. Redistributions of source code must retain the above copyright
   11  *    notice, this list of conditions and the following disclaimer.
   12  * 2. Redistributions in binary form must reproduce the above copyright
   13  *    notice, this list of conditions and the following disclaimer in the
   14  *    documentation and/or other materials provided with the distribution.
   15  *
   16  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
   17  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
   18  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
   19  * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
   20  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
   21  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
   22  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
   23  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
   24  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
   25  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
   26  * SUCH DAMAGE.
   27  */
   28 
   29 #include <sys/cdefs.h>
   30 __FBSDID("$FreeBSD$");
   31 
   32 #include <sys/param.h>
   33 #include <sys/systm.h>
   34 #include <sys/kernel.h>
   35 #include <sys/lock.h>
   36 #include <sys/malloc.h>
   37 #include <sys/module.h>
   38 #include <sys/mutex.h>
   39 #include <sys/bus.h>
   40 #include <sys/rman.h>
   41 #include <sys/vmem.h>
   42 
   43 #include <dev/ofw/ofw_bus.h>
   44 #include <dev/ofw/ofw_bus_subr.h>
   45 
   46 #include "msi_if.h"
   47 #include "pic_if.h"
   48 
   49 #define AL_SPI_INTR             0
   50 #define AL_EDGE_HIGH            1
   51 #define ERR_NOT_IN_MAP          -1
   52 #define IRQ_OFFSET              1
   53 #define GIC_INTR_CELL_CNT       3
   54 #define INTR_RANGE_COUNT        2
   55 #define MAX_MSIX_COUNT          160
   56 
   57 static int al_msix_attach(device_t);
   58 static int al_msix_probe(device_t);
   59 
   60 static msi_alloc_msi_t al_msix_alloc_msi;
   61 static msi_release_msi_t al_msix_release_msi;
   62 static msi_alloc_msix_t al_msix_alloc_msix;
   63 static msi_release_msix_t al_msix_release_msix;
   64 static msi_map_msi_t al_msix_map_msi;
   65 
   66 static int al_find_intr_pos_in_map(device_t, struct intr_irqsrc *);
   67 
   68 static struct ofw_compat_data compat_data[] = {
   69         {"annapurna-labs,al-msix",      true},
   70         {"annapurna-labs,alpine-msix",  true},
   71         {NULL,                          false}
   72 };
   73 
   74 /*
   75  * Bus interface definitions.
   76  */
   77 static device_method_t al_msix_methods[] = {
   78         DEVMETHOD(device_probe,         al_msix_probe),
   79         DEVMETHOD(device_attach,        al_msix_attach),
   80 
   81         /* Interrupt controller interface */
   82         DEVMETHOD(msi_alloc_msi,        al_msix_alloc_msi),
   83         DEVMETHOD(msi_release_msi,      al_msix_release_msi),
   84         DEVMETHOD(msi_alloc_msix,       al_msix_alloc_msix),
   85         DEVMETHOD(msi_release_msix,     al_msix_release_msix),
   86         DEVMETHOD(msi_map_msi,          al_msix_map_msi),
   87 
   88         DEVMETHOD_END
   89 };
   90 
   91 struct al_msix_softc {
   92         bus_addr_t      base_addr;
   93         struct resource *res;
   94         uint32_t        irq_min;
   95         uint32_t        irq_max;
   96         uint32_t        irq_count;
   97         struct mtx      msi_mtx;
   98         vmem_t          *irq_alloc;
   99         device_t        gic_dev;
  100         /* Table of isrcs maps isrc pointer to vmem_alloc'd irq number */
  101         struct intr_irqsrc      *isrcs[MAX_MSIX_COUNT];
  102 };
  103 
  104 static driver_t al_msix_driver = {
  105         "al_msix",
  106         al_msix_methods,
  107         sizeof(struct al_msix_softc),
  108 };
  109 
  110 DRIVER_MODULE(al_msix, ofwbus, al_msix_driver, 0, 0);
  111 DRIVER_MODULE(al_msix, simplebus, al_msix_driver, 0, 0);
  112 
  113 MALLOC_DECLARE(M_AL_MSIX);
  114 MALLOC_DEFINE(M_AL_MSIX, "al_msix", "Alpine MSIX");
  115 
  116 static int
  117 al_msix_probe(device_t dev)
  118 {
  119 
  120         if (!ofw_bus_status_okay(dev))
  121                 return (ENXIO);
  122 
  123         if (!ofw_bus_search_compatible(dev, compat_data)->ocd_data)
  124                 return (ENXIO);
  125 
  126         device_set_desc(dev, "Annapurna-Labs MSI-X Controller");
  127         return (BUS_PROBE_DEFAULT);
  128 }
  129 
  130 static int
  131 al_msix_attach(device_t dev)
  132 {
  133         struct al_msix_softc    *sc;
  134         device_t                gic_dev;
  135         phandle_t               iparent;
  136         phandle_t               node;
  137         intptr_t                xref;
  138         int                     interrupts[INTR_RANGE_COUNT];
  139         int                     nintr, i, rid;
  140         uint32_t                icells, *intr;
  141 
  142         sc = device_get_softc(dev);
  143 
  144         node = ofw_bus_get_node(dev);
  145         xref = OF_xref_from_node(node);
  146         OF_device_register_xref(xref, dev);
  147 
  148         rid = 0;
  149         sc->res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, RF_ACTIVE);
  150         if (sc->res == NULL) {
  151                 device_printf(dev, "Failed to allocate resource\n");
  152                 return (ENXIO);
  153         }
  154 
  155         sc->base_addr = (bus_addr_t)rman_get_start(sc->res);
  156 
  157         /* Register this device to handle MSI interrupts */
  158         if (intr_msi_register(dev, xref) != 0) {
  159                 device_printf(dev, "could not register MSI-X controller\n");
  160                 return (ENXIO);
  161         }
  162         else
  163                 device_printf(dev, "MSI-X controller registered\n");
  164 
  165         /* Find root interrupt controller */
  166         iparent = ofw_bus_find_iparent(node);
  167         if (iparent == 0) {
  168                 device_printf(dev, "No interrupt-parrent found. "
  169                                 "Error in DTB\n");
  170                 return (ENXIO);
  171         } else {
  172                 /* While at parent - store interrupt cells prop */
  173                 if (OF_searchencprop(OF_node_from_xref(iparent),
  174                     "#interrupt-cells", &icells, sizeof(icells)) == -1) {
  175                         device_printf(dev, "DTB: Missing #interrupt-cells "
  176                             "property in GIC node\n");
  177                         return (ENXIO);
  178                 }
  179         }
  180 
  181         gic_dev = OF_device_from_xref(iparent);
  182         if (gic_dev == NULL) {
  183                 device_printf(dev, "Cannot find GIC device\n");
  184                 return (ENXIO);
  185         }
  186         sc->gic_dev = gic_dev;
  187 
  188         /* Manually read range of interrupts from DTB */
  189         nintr = OF_getencprop_alloc_multi(node, "interrupts", sizeof(*intr),
  190             (void **)&intr);
  191         if (nintr == 0) {
  192                 device_printf(dev, "Cannot read interrupts prop from DTB\n");
  193                 return (ENXIO);
  194         } else if ((nintr / icells) != INTR_RANGE_COUNT) {
  195                 /* Supposed to have min and max value only */
  196                 device_printf(dev, "Unexpected count of interrupts "
  197                                 "in DTB node\n");
  198                 return (EINVAL);
  199         }
  200 
  201         /* Read interrupt range values */
  202         for (i = 0; i < INTR_RANGE_COUNT; i++)
  203                 interrupts[i] = intr[(i * icells) + IRQ_OFFSET];
  204 
  205         sc->irq_min = interrupts[0];
  206         sc->irq_max = interrupts[1];
  207         sc->irq_count = (sc->irq_max - sc->irq_min + 1);
  208 
  209         if (sc->irq_count > MAX_MSIX_COUNT) {
  210                 device_printf(dev, "Available MSI-X count exceeds buffer size."
  211                                 " Capping to %d\n", MAX_MSIX_COUNT);
  212                 sc->irq_count = MAX_MSIX_COUNT;
  213         }
  214 
  215         mtx_init(&sc->msi_mtx, "msi_mtx", NULL, MTX_DEF);
  216 
  217         sc->irq_alloc = vmem_create("Alpine MSI-X IRQs", 0, sc->irq_count,
  218             1, 0, M_FIRSTFIT | M_WAITOK);
  219 
  220         device_printf(dev, "MSI-X SPI IRQ %d-%d\n", sc->irq_min, sc->irq_max);
  221 
  222         return (bus_generic_attach(dev));
  223 }
  224 
  225 static int
  226 al_find_intr_pos_in_map(device_t dev, struct intr_irqsrc *isrc)
  227 {
  228         struct al_msix_softc *sc;
  229         int i;
  230 
  231         sc = device_get_softc(dev);
  232         for (i = 0; i < MAX_MSIX_COUNT; i++)
  233                 if (sc->isrcs[i] == isrc)
  234                         return (i);
  235         return (ERR_NOT_IN_MAP);
  236 }
  237 
  238 static int
  239 al_msix_map_msi(device_t dev, device_t child, struct intr_irqsrc *isrc,
  240     uint64_t *addr, uint32_t *data)
  241 {
  242         struct al_msix_softc *sc;
  243         int i, spi;
  244 
  245         sc = device_get_softc(dev);
  246 
  247         i = al_find_intr_pos_in_map(dev, isrc);
  248         if (i == ERR_NOT_IN_MAP)
  249                 return (EINVAL);
  250 
  251         spi = sc->irq_min + i;
  252 
  253         /*
  254          * MSIX message address format:
  255          * [63:20] - MSIx TBAR
  256          *           Same value as the MSIx Translation Base  Address Register
  257          * [19]    - WFE_EXIT
  258          *           Once set by MSIx message, an EVENTI is signal to the CPUs
  259          *           cluster specified by ‘Local GIC Target List’
  260          * [18:17] - Target GIC ID
  261          *           Specifies which IO-GIC (external shared GIC) is targeted
  262          *           0: Local GIC, as specified by the Local GIC Target List
  263          *           1: IO-GIC 0
  264          *           2: Reserved
  265          *           3: Reserved
  266          * [16:13] - Local GIC Target List
  267          *           Specifies the Local GICs list targeted by this MSIx
  268          *           message.
  269          *           [16]  If set, SPIn is set in Cluster 0 local GIC
  270          *           [15:13] Reserved
  271          *           [15]  If set, SPIn is set in Cluster 1 local GIC
  272          *           [14]  If set, SPIn is set in Cluster 2 local GIC
  273          *           [13]  If set, SPIn is set in Cluster 3 local GIC
  274          * [12:3]  - SPIn
  275          *           Specifies the SPI (Shared Peripheral Interrupt) index to
  276          *           be set in target GICs
  277          *           Notes:
  278          *           If targeting any local GIC than only SPI[249:0] are valid
  279          * [2]     - Function vector
  280          *           MSI Data vector extension hint
  281          * [1:0]   - Reserved
  282          *           Must be set to zero
  283          */
  284         *addr = (uint64_t)sc->base_addr + (uint64_t)((1 << 16) + (spi << 3));
  285         *data = 0;
  286 
  287         if (bootverbose)
  288                 device_printf(dev, "MSI mapping: SPI: %d addr: %jx data: %x\n",
  289                     spi, (uintmax_t)*addr, *data);
  290         return (0);
  291 }
  292 
  293 static int
  294 al_msix_alloc_msi(device_t dev, device_t child, int count, int maxcount,
  295     device_t *pic, struct intr_irqsrc **srcs)
  296 {
  297         struct intr_map_data_fdt *fdt_data;
  298         struct al_msix_softc *sc;
  299         vmem_addr_t irq_base;
  300         int error;
  301         u_int i, j;
  302 
  303         sc = device_get_softc(dev);
  304 
  305         if ((powerof2(count) == 0) || (count > 8))
  306                 return (EINVAL);
  307 
  308         if (vmem_alloc(sc->irq_alloc, count, M_FIRSTFIT | M_NOWAIT,
  309             &irq_base) != 0)
  310                 return (ENOMEM);
  311 
  312         /* Fabricate OFW data to get ISRC from GIC and return it */
  313         fdt_data = malloc(sizeof(*fdt_data) +
  314             GIC_INTR_CELL_CNT * sizeof(pcell_t), M_AL_MSIX, M_WAITOK);
  315         fdt_data->hdr.type = INTR_MAP_DATA_FDT;
  316         fdt_data->iparent = 0;
  317         fdt_data->ncells = GIC_INTR_CELL_CNT;
  318         fdt_data->cells[0] = AL_SPI_INTR;       /* code for SPI interrupt */
  319         fdt_data->cells[1] = 0;                 /* SPI number (uninitialized) */
  320         fdt_data->cells[2] = AL_EDGE_HIGH;      /* trig = edge, pol = high */
  321 
  322         mtx_lock(&sc->msi_mtx);
  323 
  324         for (i = irq_base; i < irq_base + count; i++) {
  325                 fdt_data->cells[1] = sc->irq_min + i;
  326                 error = PIC_MAP_INTR(sc->gic_dev,
  327                     (struct intr_map_data *)fdt_data, srcs);
  328                 if (error) {
  329                         for (j = irq_base; j < i; j++)
  330                                 sc->isrcs[j] = NULL;
  331                         mtx_unlock(&sc->msi_mtx);
  332                         vmem_free(sc->irq_alloc, irq_base, count);
  333                         free(fdt_data, M_AL_MSIX);
  334                         return (error);
  335                 }
  336 
  337                 sc->isrcs[i] = *srcs;
  338                 srcs++;
  339         }
  340 
  341         mtx_unlock(&sc->msi_mtx);
  342         free(fdt_data, M_AL_MSIX);
  343 
  344         if (bootverbose)
  345                 device_printf(dev,
  346                     "MSI-X allocation: start SPI %d, count %d\n",
  347                     (int)irq_base + sc->irq_min, count);
  348 
  349         *pic = sc->gic_dev;
  350 
  351         return (0);
  352 }
  353 
  354 static int
  355 al_msix_release_msi(device_t dev, device_t child, int count,
  356     struct intr_irqsrc **srcs)
  357 {
  358         struct al_msix_softc *sc;
  359         int i, pos;
  360 
  361         sc = device_get_softc(dev);
  362 
  363         mtx_lock(&sc->msi_mtx);
  364 
  365         pos = al_find_intr_pos_in_map(dev, *srcs);
  366         vmem_free(sc->irq_alloc, pos, count);
  367         for (i = 0; i < count; i++) {
  368                 pos = al_find_intr_pos_in_map(dev, *srcs);
  369                 if (pos != ERR_NOT_IN_MAP)
  370                         sc->isrcs[pos] = NULL;
  371                 srcs++;
  372         }
  373 
  374         mtx_unlock(&sc->msi_mtx);
  375 
  376         return (0);
  377 }
  378 
  379 static int
  380 al_msix_alloc_msix(device_t dev, device_t child, device_t *pic,
  381     struct intr_irqsrc **isrcp)
  382 {
  383 
  384         return (al_msix_alloc_msi(dev, child, 1, 1, pic, isrcp));
  385 }
  386 
  387 static int
  388 al_msix_release_msix(device_t dev, device_t child, struct intr_irqsrc *isrc)
  389 {
  390 
  391         return (al_msix_release_msi(dev, child, 1, &isrc));
  392 }

Cache object: 5c2e1f750e6a895ace91d31b5d681ea7


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]


This page is part of the FreeBSD/Linux Linux Kernel Cross-Reference, and was automatically generated using a modified version of the LXR engine.