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/mv/mv_ap806_gicp.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  * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
    3  *
    4  * Copyright (c) 2018 Rubicon Communications, LLC (Netgate)
    5  *
    6  * Redistribution and use in source and binary forms, with or without
    7  * modification, are permitted provided that the following conditions
    8  * are met:
    9  * 1. Redistributions of source code must retain the above copyright
   10  *    notice, this list of conditions and the following disclaimer.
   11  * 2. Redistributions in binary form must reproduce the above copyright
   12  *    notice, this list of conditions and the following disclaimer in the
   13  *    documentation and/or other materials provided with the distribution.
   14  *
   15  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
   16  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
   17  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
   18  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
   19  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
   20  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
   21  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
   22  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
   23  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
   24  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
   25  * SUCH DAMAGE.
   26  *
   27  * $FreeBSD$
   28  */
   29 
   30 #include <sys/cdefs.h>
   31 __FBSDID("$FreeBSD$");
   32 
   33 #include <sys/param.h>
   34 #include <sys/systm.h>
   35 #include <sys/bus.h>
   36 
   37 #include <sys/bitset.h>
   38 #include <sys/kernel.h>
   39 #include <sys/module.h>
   40 #include <sys/rman.h>
   41 #include <sys/lock.h>
   42 #include <sys/mutex.h>
   43 
   44 #include <machine/bus.h>
   45 #include <machine/resource.h>
   46 #include <machine/intr.h>
   47 
   48 #include <dev/fdt/simplebus.h>
   49 
   50 #include <dev/ofw/ofw_bus.h>
   51 #include <dev/ofw/ofw_bus_subr.h>
   52 
   53 #include <arm/arm/gic_common.h>
   54 
   55 #include <dt-bindings/interrupt-controller/irq.h>
   56 
   57 #include "msi_if.h"
   58 #include "pic_if.h"
   59 
   60 #define MV_AP806_GICP_MAX_NIRQS 207
   61 
   62 MALLOC_DECLARE(M_GICP);
   63 MALLOC_DEFINE(M_GICP, "gicp", "Marvell gicp driver");
   64 
   65 struct mv_ap806_gicp_softc {
   66         device_t                dev;
   67         device_t                parent;
   68         struct resource         *res;
   69 
   70         ssize_t                 spi_ranges_cnt;
   71         uint32_t                *spi_ranges;
   72         struct intr_map_data_fdt *parent_map_data;
   73 
   74         ssize_t                 msi_bitmap_size; /* Nr of bits in the bitmap. */
   75         BITSET_DEFINE_VAR()     *msi_bitmap;
   76 };
   77 
   78 static struct ofw_compat_data compat_data[] = {
   79         {"marvell,ap806-gicp", 1},
   80         {NULL,             0}
   81 };
   82 
   83 #define RD4(sc, reg)            bus_read_4((sc)->res, (reg))
   84 #define WR4(sc, reg, val)       bus_write_4((sc)->res, (reg), (val))
   85 
   86 static msi_alloc_msi_t mv_ap806_gicp_alloc_msi;
   87 static msi_release_msi_t mv_ap806_gicp_release_msi;
   88 static msi_map_msi_t mv_ap806_gicp_map_msi;
   89 
   90 static int
   91 mv_ap806_gicp_probe(device_t dev)
   92 {
   93 
   94         if (!ofw_bus_status_okay(dev))
   95                 return (ENXIO);
   96 
   97         if (ofw_bus_search_compatible(dev, compat_data)->ocd_data == 0)
   98                 return (ENXIO);
   99 
  100         device_set_desc(dev, "Marvell GICP");
  101         return (BUS_PROBE_DEFAULT);
  102 }
  103 
  104 static int
  105 mv_ap806_gicp_attach(device_t dev)
  106 {
  107         struct mv_ap806_gicp_softc *sc;
  108         phandle_t node, xref, intr_parent;
  109         int i, rid;
  110 
  111         sc = device_get_softc(dev);
  112         sc->dev = dev;
  113         node = ofw_bus_get_node(dev);
  114 
  115         /* Look for our parent */
  116         if ((intr_parent = ofw_bus_find_iparent(node)) == 0) {
  117                 device_printf(dev,
  118                      "Cannot find our parent interrupt controller\n");
  119                 return (ENXIO);
  120         }
  121         if ((sc->parent = OF_device_from_xref(intr_parent)) == NULL) {
  122                 device_printf(dev,
  123                      "cannot find parent interrupt controller device\n");
  124                 return (ENXIO);
  125         }
  126 
  127         rid = 0;
  128         sc->res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, RF_ACTIVE);
  129         if (sc->res == NULL) {
  130                 device_printf(dev, "cannot allocate resources for device\n");
  131                 return (ENXIO);
  132         }
  133 
  134         sc->spi_ranges_cnt = OF_getencprop_alloc_multi(node, "marvell,spi-ranges",
  135             sizeof(*sc->spi_ranges), (void **)&sc->spi_ranges);
  136 
  137         sc->msi_bitmap_size = 0;
  138         for (i = 0; i < sc->spi_ranges_cnt; i += 2)
  139                 sc->msi_bitmap_size += sc->spi_ranges[i + 1];
  140 
  141         /*
  142          * Create a bitmap of all MSIs that we have.
  143          * Each has a correspoding SPI in the GIC.
  144          * It will be used to dynamically allocate IRQs when requested.
  145          */
  146         sc->msi_bitmap = BITSET_ALLOC(sc->msi_bitmap_size, M_GICP, M_WAITOK);
  147         BIT_FILL(sc->msi_bitmap_size, sc->msi_bitmap);  /* 1 - available, 0 - used. */
  148 
  149         xref = OF_xref_from_node(node);
  150         if (intr_pic_register(dev, xref) == NULL) {
  151                 device_printf(dev, "Cannot register GICP\n");
  152                 return (ENXIO);
  153         }
  154         /* Allocate GIC compatible mapping entry (3 cells) */
  155         sc->parent_map_data = (struct intr_map_data_fdt *)intr_alloc_map_data(
  156             INTR_MAP_DATA_FDT, sizeof(struct intr_map_data_fdt) +
  157             + 3 * sizeof(phandle_t), M_WAITOK | M_ZERO);
  158         OF_device_register_xref(xref, dev);
  159 
  160         return (0);
  161 }
  162 
  163 static int
  164 mv_ap806_gicp_detach(device_t dev)
  165 {
  166 
  167         return (EBUSY);
  168 }
  169 
  170 static uint32_t
  171 mv_ap806_gicp_msi_to_spi(struct mv_ap806_gicp_softc *sc, int irq)
  172 {
  173         int i;
  174 
  175         for (i = 0; i < sc->spi_ranges_cnt; i += 2) {
  176                 if (irq < sc->spi_ranges[i + 1]) {
  177                         irq += sc->spi_ranges[i];
  178                         break;
  179                 }
  180                 irq -= sc->spi_ranges[i + 1];
  181         }
  182 
  183         return (irq - GIC_FIRST_SPI);
  184 }
  185 
  186 static uint32_t
  187 mv_ap806_gicp_irq_to_msi(struct mv_ap806_gicp_softc *sc, int irq)
  188 {
  189         int i;
  190 
  191         for (i = 0; i < sc->spi_ranges_cnt; i += 2) {
  192                 if (irq >= sc->spi_ranges[i] &&
  193                     irq - sc->spi_ranges[i] < sc->spi_ranges[i + 1]) {
  194                         irq -= sc->spi_ranges[i];
  195                         break;
  196                 }
  197         }
  198 
  199         return (irq);
  200 }
  201 
  202 static struct intr_map_data *
  203 mv_ap806_gicp_convert_map_data(struct mv_ap806_gicp_softc *sc,
  204     struct intr_map_data *data)
  205 {
  206         struct intr_map_data_fdt *daf;
  207         uint32_t irq_num;
  208 
  209         daf = (struct intr_map_data_fdt *)data;
  210         if (daf->ncells != 2)
  211                 return (NULL);
  212 
  213         irq_num = daf->cells[0];
  214         if (irq_num >= MV_AP806_GICP_MAX_NIRQS)
  215                 return (NULL);
  216 
  217         /* Construct GIC compatible mapping. */
  218         sc->parent_map_data->ncells = 3;
  219         sc->parent_map_data->cells[0] = 0; /* SPI */
  220         sc->parent_map_data->cells[1] = mv_ap806_gicp_msi_to_spi(sc, irq_num);
  221         sc->parent_map_data->cells[2] = IRQ_TYPE_LEVEL_HIGH;
  222 
  223         return ((struct intr_map_data *)sc->parent_map_data);
  224 }
  225 
  226 static int
  227 mv_ap806_gicp_activate_intr(device_t dev, struct intr_irqsrc *isrc,
  228     struct resource *res, struct intr_map_data *data)
  229 {
  230         struct mv_ap806_gicp_softc *sc;
  231 
  232         sc = device_get_softc(dev);
  233         data = mv_ap806_gicp_convert_map_data(sc, data);
  234         if (data == NULL)
  235                 return (EINVAL);
  236 
  237         return (PIC_ACTIVATE_INTR(sc->parent, isrc, res, data));
  238 }
  239 
  240 static void
  241 mv_ap806_gicp_enable_intr(device_t dev, struct intr_irqsrc *isrc)
  242 {
  243         struct mv_ap806_gicp_softc *sc;
  244 
  245         sc = device_get_softc(dev);
  246 
  247         PIC_ENABLE_INTR(sc->parent, isrc);
  248 }
  249 
  250 static void
  251 mv_ap806_gicp_disable_intr(device_t dev, struct intr_irqsrc *isrc)
  252 {
  253         struct mv_ap806_gicp_softc *sc;
  254 
  255         sc = device_get_softc(dev);
  256 
  257         PIC_DISABLE_INTR(sc->parent, isrc);
  258 }
  259 
  260 static int
  261 mv_ap806_gicp_map_intr(device_t dev, struct intr_map_data *data,
  262     struct intr_irqsrc **isrcp)
  263 {
  264 
  265         panic("%s: MSI interface has to be used to map an interrupt.\n",
  266             __func__);
  267 }
  268 
  269 static int
  270 mv_ap806_gicp_deactivate_intr(device_t dev, struct intr_irqsrc *isrc,
  271     struct resource *res, struct intr_map_data *data)
  272 {
  273         struct mv_ap806_gicp_softc *sc;
  274 
  275         sc = device_get_softc(dev);
  276 
  277         data = mv_ap806_gicp_convert_map_data(sc, data);
  278         if (data == NULL)
  279                 return (EINVAL);
  280 
  281         return (PIC_DEACTIVATE_INTR(sc->parent, isrc, res, data));
  282 }
  283 
  284 static int
  285 mv_ap806_gicp_setup_intr(device_t dev, struct intr_irqsrc *isrc,
  286     struct resource *res, struct intr_map_data *data)
  287 {
  288         struct mv_ap806_gicp_softc *sc;
  289 
  290         sc = device_get_softc(dev);
  291         data = mv_ap806_gicp_convert_map_data(sc, data);
  292         if (data == NULL)
  293                 return (EINVAL);
  294 
  295         return (PIC_SETUP_INTR(sc->parent, isrc, res, data));
  296 }
  297 
  298 static int
  299 mv_ap806_gicp_teardown_intr(device_t dev, struct intr_irqsrc *isrc,
  300     struct resource *res, struct intr_map_data *data)
  301 {
  302         struct mv_ap806_gicp_softc *sc;
  303 
  304         sc = device_get_softc(dev);
  305         data = mv_ap806_gicp_convert_map_data(sc, data);
  306         if (data == NULL)
  307                 return (EINVAL);
  308 
  309         return (PIC_TEARDOWN_INTR(sc->parent, isrc, res, data));
  310 }
  311 
  312 static void
  313 mv_ap806_gicp_pre_ithread(device_t dev, struct intr_irqsrc *isrc)
  314 {
  315         struct mv_ap806_gicp_softc *sc;
  316 
  317         sc = device_get_softc(dev);
  318 
  319         PIC_PRE_ITHREAD(sc->parent, isrc);
  320 }
  321 
  322 static void
  323 mv_ap806_gicp_post_ithread(device_t dev, struct intr_irqsrc *isrc)
  324 {
  325         struct mv_ap806_gicp_softc *sc;
  326 
  327         sc = device_get_softc(dev);
  328 
  329         PIC_POST_ITHREAD(sc->parent, isrc);
  330 }
  331 
  332 static void
  333 mv_ap806_gicp_post_filter(device_t dev, struct intr_irqsrc *isrc)
  334 {
  335         struct mv_ap806_gicp_softc *sc;
  336 
  337         sc = device_get_softc(dev);
  338 
  339         PIC_POST_FILTER(sc->parent, isrc);
  340 }
  341 
  342 static int
  343 mv_ap806_gicp_alloc_msi(device_t dev, device_t child, int count, int maxcount,
  344     device_t *pic, struct intr_irqsrc **srcs)
  345 {
  346         struct mv_ap806_gicp_softc *sc;
  347         int i, ret, vector;
  348 
  349         sc = device_get_softc(dev);
  350 
  351         for (i = 0; i < count; i++) {
  352                 /*
  353                  * Find first available vector represented by first set bit
  354                  * in the bitmap. BIT_FFS starts the count from 1, 0 means
  355                  * that nothing was found.
  356                  */
  357                 vector = BIT_FFS(sc->msi_bitmap_size, sc->msi_bitmap);
  358                 if (vector == 0) {
  359                         ret = ENOMEM;
  360                         i--;
  361                         goto fail;
  362                 }
  363                 vector--;
  364                 BIT_CLR(sc->msi_bitmap_size, vector, sc->msi_bitmap);
  365 
  366                 /* Create GIC compatible SPI interrupt description. */
  367                 sc->parent_map_data->ncells = 3;
  368                 sc->parent_map_data->cells[0] = 0;      /* SPI */
  369                 sc->parent_map_data->cells[1] = mv_ap806_gicp_msi_to_spi(sc, vector);
  370                 sc->parent_map_data->cells[2] = IRQ_TYPE_LEVEL_HIGH;
  371 
  372                 ret = PIC_MAP_INTR(sc->parent,
  373                     (struct intr_map_data *)sc->parent_map_data,
  374                     &srcs[i]);
  375                 if (ret != 0)
  376                         goto fail;
  377 
  378                 srcs[i]->isrc_dev = dev;
  379         }
  380 
  381         return (0);
  382 fail:
  383         mv_ap806_gicp_release_msi(dev, child, i + 1, srcs);
  384         return (ret);
  385 }
  386 
  387 static int
  388 mv_ap806_gicp_release_msi(device_t dev, device_t child, int count,
  389     struct intr_irqsrc **srcs)
  390 {
  391         struct mv_ap806_gicp_softc *sc;
  392         int i;
  393 
  394         sc = device_get_softc(dev);
  395 
  396         for (i = 0; i < count; i++) {
  397                 BIT_SET(sc->msi_bitmap_size,
  398                     mv_ap806_gicp_irq_to_msi(sc, srcs[i]->isrc_irq),
  399                     sc->msi_bitmap);
  400         }
  401 
  402         return (0);
  403 }
  404 
  405 static int
  406 mv_ap806_gicp_map_msi(device_t dev, device_t child, struct intr_irqsrc *isrc,
  407     uint64_t *addr, uint32_t *data)
  408 {
  409         struct mv_ap806_gicp_softc *sc;
  410 
  411         sc = device_get_softc(dev);
  412 
  413         *addr = rman_get_start(sc->res);
  414         *data = mv_ap806_gicp_irq_to_msi(sc, isrc->isrc_irq);
  415 
  416         return (0);
  417 }
  418 
  419 static device_method_t mv_ap806_gicp_methods[] = {
  420         /* Device interface */
  421         DEVMETHOD(device_probe,         mv_ap806_gicp_probe),
  422         DEVMETHOD(device_attach,        mv_ap806_gicp_attach),
  423         DEVMETHOD(device_detach,        mv_ap806_gicp_detach),
  424 
  425         /* Interrupt controller interface */
  426         DEVMETHOD(pic_activate_intr,    mv_ap806_gicp_activate_intr),
  427         DEVMETHOD(pic_disable_intr,     mv_ap806_gicp_disable_intr),
  428         DEVMETHOD(pic_enable_intr,      mv_ap806_gicp_enable_intr),
  429         DEVMETHOD(pic_map_intr,         mv_ap806_gicp_map_intr),
  430         DEVMETHOD(pic_deactivate_intr,  mv_ap806_gicp_deactivate_intr),
  431         DEVMETHOD(pic_setup_intr,       mv_ap806_gicp_setup_intr),
  432         DEVMETHOD(pic_teardown_intr,    mv_ap806_gicp_teardown_intr),
  433         DEVMETHOD(pic_post_filter,      mv_ap806_gicp_post_filter),
  434         DEVMETHOD(pic_post_ithread,     mv_ap806_gicp_post_ithread),
  435         DEVMETHOD(pic_pre_ithread,      mv_ap806_gicp_pre_ithread),
  436 
  437         /* MSI interface */
  438         DEVMETHOD(msi_alloc_msi,        mv_ap806_gicp_alloc_msi),
  439         DEVMETHOD(msi_release_msi,      mv_ap806_gicp_release_msi),
  440         DEVMETHOD(msi_map_msi,          mv_ap806_gicp_map_msi),
  441 
  442         DEVMETHOD_END
  443 };
  444 
  445 static driver_t mv_ap806_gicp_driver = {
  446         "mv_ap806_gicp",
  447         mv_ap806_gicp_methods,
  448         sizeof(struct mv_ap806_gicp_softc),
  449 };
  450 
  451 EARLY_DRIVER_MODULE(mv_ap806_gicp, simplebus, mv_ap806_gicp_driver, 0, 0,
  452     BUS_PASS_INTERRUPT + BUS_PASS_ORDER_MIDDLE);

Cache object: 1b8f8dabfabf0c647a9d5aa13e0fae7c


[ 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.