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_sei.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) 2019 Michal Meloun <mmel@FreeBSD.org>
    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  */
   28 
   29 #include <sys/cdefs.h>
   30 __FBSDID("$FreeBSD$");
   31 
   32 #include <sys/param.h>
   33 #include <sys/systm.h>
   34 #include <sys/bus.h>
   35 
   36 #include <sys/bitset.h>
   37 #include <sys/kernel.h>
   38 #include <sys/proc.h>
   39 #include <sys/rman.h>
   40 #include <sys/lock.h>
   41 #include <sys/module.h>
   42 #include <sys/mutex.h>
   43 
   44 #include <machine/bus.h>
   45 #include <machine/intr.h>
   46 #include <machine/resource.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 "msi_if.h"
   54 #include "pic_if.h"
   55 
   56 #define MV_AP806_SEI_LOCK(_sc)          mtx_lock(&(_sc)->mtx)
   57 #define MV_AP806_SEI_UNLOCK(_sc)        mtx_unlock(&(_sc)->mtx)
   58 #define MV_AP806_SEI_LOCK_INIT(_sc)     mtx_init(&_sc->mtx,                     \
   59             device_get_nameunit(_sc->dev), "mv_ap806_sei", MTX_DEF)
   60 #define MV_AP806_SEI_LOCK_DESTROY(_sc)  mtx_destroy(&_sc->mtx);
   61 #define MV_AP806_SEI_ASSERT_LOCKED(_sc) mtx_assert(&_sc->mtx, MA_OWNED);
   62 #define MV_AP806_SEI_ASSERT_UNLOCKED(_sc) mtx_assert(&_sc->mtx, MA_NOTOWNED);
   63 
   64 #define GICP_SECR0              0x00
   65 #define GICP_SECR1              0x04
   66 #define GICP_SECR(i)            (0x00  + (((i)/32) * 0x4))
   67 #define GICP_SECR_BIT(i)        ((i) % 32)
   68 #define GICP_SEMR0              0x20
   69 #define GICP_SEMR1              0x24
   70 #define GICP_SEMR(i)            (0x20  + (((i)/32) * 0x4))
   71 #define GICP_SEMR_BIT(i)        ((i) % 32)
   72 
   73 #define MV_AP806_SEI_AP_FIRST   0
   74 #define MV_AP806_SEI_AP_SIZE    21
   75 #define MV_AP806_SEI_CP_FIRST   21
   76 #define MV_AP806_SEI_CP_SIZE    43
   77 #define MV_AP806_SEI_MAX_NIRQS  (MV_AP806_SEI_AP_SIZE + MV_AP806_SEI_CP_SIZE)
   78 
   79 #define MV_AP806_SEI_SETSPI_OFFSET      0x30
   80 
   81 BITSET_DEFINE(sei_msi_bitmap, MV_AP806_SEI_CP_SIZE);
   82 
   83 struct mv_ap806_sei_irqsrc {
   84         struct intr_irqsrc      isrc;
   85         u_int                   irq;
   86 };
   87 
   88 struct mv_ap806_sei_softc {
   89         device_t                dev;
   90         struct resource         *mem_res;
   91         struct resource         *irq_res;
   92         void                    *irq_ih;
   93         struct mtx              mtx;
   94 
   95         struct mv_ap806_sei_irqsrc *isrcs;
   96 
   97         struct sei_msi_bitmap   msi_bitmap;
   98 };
   99 
  100 static struct ofw_compat_data compat_data[] = {
  101         {"marvell,ap806-sei", 1},
  102         {NULL,             0}
  103 };
  104 
  105 #define RD4(sc, reg)            bus_read_4((sc)->mem_res, (reg))
  106 #define WR4(sc, reg, val)       bus_write_4((sc)->mem_res, (reg), (val))
  107 
  108 static msi_alloc_msi_t mv_ap806_sei_alloc_msi;
  109 static msi_release_msi_t mv_ap806_sei_release_msi;
  110 static msi_map_msi_t mv_ap806_sei_map_msi;
  111 
  112 static inline void
  113 mv_ap806_sei_isrc_mask(struct mv_ap806_sei_softc *sc,
  114      struct mv_ap806_sei_irqsrc *sisrc, uint32_t val)
  115 {
  116         uint32_t tmp;
  117         int bit;
  118 
  119         bit = GICP_SEMR_BIT(sisrc->irq);
  120         MV_AP806_SEI_LOCK(sc);
  121         tmp = RD4(sc, GICP_SEMR(sisrc->irq));
  122         if (val != 0)
  123                 tmp |= 1 << bit;
  124         else
  125                 tmp &= ~(1 << bit);
  126         WR4(sc, GICP_SEMR(sisrc->irq), tmp);
  127         MV_AP806_SEI_UNLOCK(sc);
  128 }
  129 
  130 static inline void
  131 mv_ap806_sei_isrc_eoi(struct mv_ap806_sei_softc *sc,
  132      struct mv_ap806_sei_irqsrc *sisrc)
  133 {
  134 
  135         WR4(sc, GICP_SECR(sisrc->irq), GICP_SECR_BIT(sisrc->irq));
  136 }
  137 
  138 static void
  139 mv_ap806_sei_enable_intr(device_t dev, struct intr_irqsrc *isrc)
  140 {
  141         struct mv_ap806_sei_softc *sc;
  142         struct mv_ap806_sei_irqsrc *sisrc;
  143 
  144         sc = device_get_softc(dev);
  145         sisrc = (struct mv_ap806_sei_irqsrc *)isrc;
  146         mv_ap806_sei_isrc_mask(sc, sisrc, 0);
  147 }
  148 
  149 static void
  150 mv_ap806_sei_disable_intr(device_t dev, struct intr_irqsrc *isrc)
  151 {
  152         struct mv_ap806_sei_softc *sc;
  153         struct mv_ap806_sei_irqsrc *sisrc;
  154 
  155         sc = device_get_softc(dev);
  156         sisrc = (struct mv_ap806_sei_irqsrc *)isrc;
  157         mv_ap806_sei_isrc_mask(sc, sisrc, 1);
  158 }
  159 
  160 static int
  161 mv_ap806_sei_map(device_t dev, struct intr_map_data *data, u_int *irqp)
  162 {
  163         struct intr_map_data_fdt *daf;
  164         u_int irq;
  165 
  166         if (data->type != INTR_MAP_DATA_FDT)
  167                 return (ENOTSUP);
  168 
  169         daf = (struct intr_map_data_fdt *)data;
  170         if (daf->ncells != 1)
  171                 return (EINVAL);
  172 
  173         if (daf->cells[0] < MV_AP806_SEI_AP_FIRST ||
  174             daf->cells[0] >= MV_AP806_SEI_AP_FIRST + MV_AP806_SEI_AP_SIZE)
  175                 return (EINVAL);
  176 
  177         irq = daf->cells[0];
  178         if (irqp != NULL)
  179                 *irqp = irq;
  180 
  181         return(0);
  182 }
  183 
  184 static int
  185 mv_ap806_sei_map_intr(device_t dev, struct intr_map_data *data,
  186     struct intr_irqsrc **isrcp)
  187 {
  188         struct mv_ap806_sei_softc *sc;
  189         u_int irq;
  190         int rv;
  191 
  192         sc = device_get_softc(dev);
  193         rv = mv_ap806_sei_map(dev, data, &irq);
  194         if (rv == 0)
  195                 *isrcp = &sc->isrcs[irq].isrc;
  196 
  197         return (rv);
  198 }
  199 
  200 static int
  201 mv_ap806_sei_setup_intr(device_t dev, struct intr_irqsrc *isrc,
  202     struct resource *res, struct intr_map_data *data)
  203 {
  204         struct mv_ap806_sei_softc *sc;
  205         struct mv_ap806_sei_irqsrc *sisrc;
  206         u_int irq;
  207         int rv;
  208 
  209         sc = device_get_softc(dev);
  210         sisrc = (struct mv_ap806_sei_irqsrc *)isrc;
  211         if (data == NULL)
  212                 return (ENOTSUP);
  213         rv = mv_ap806_sei_map(dev, data, &irq);
  214         if (rv != 0)
  215                 return (rv);
  216         if (irq != sisrc->irq)
  217                 return (EINVAL);
  218         mv_ap806_sei_isrc_mask(sc, sisrc, 0);
  219         return (0);
  220 }
  221 
  222 static int
  223 mv_ap806_sei_teardown_intr(device_t dev, struct intr_irqsrc *isrc,
  224     struct resource *res, struct intr_map_data *data)
  225 {
  226         struct mv_ap806_sei_softc *sc;
  227         struct mv_ap806_sei_irqsrc *sisrc;
  228 
  229         sc = device_get_softc(dev);
  230         sisrc = (struct mv_ap806_sei_irqsrc *)isrc;
  231 
  232         mv_ap806_sei_isrc_mask(sc, sisrc, 1);
  233         return (0);
  234 }
  235 
  236 static void
  237 mv_ap806_sei_pre_ithread(device_t dev, struct intr_irqsrc *isrc)
  238 {
  239         struct mv_ap806_sei_softc *sc;
  240         struct mv_ap806_sei_irqsrc *sisrc;
  241 
  242         sc = device_get_softc(dev);
  243         sisrc = (struct mv_ap806_sei_irqsrc *)isrc;
  244 
  245         mv_ap806_sei_isrc_mask(sc, sisrc, 1);
  246         mv_ap806_sei_isrc_eoi(sc, sisrc);
  247 }
  248 
  249 static void
  250 mv_ap806_sei_post_ithread(device_t dev, struct intr_irqsrc *isrc)
  251 {
  252         struct mv_ap806_sei_softc *sc;
  253         struct mv_ap806_sei_irqsrc *sisrc;
  254 
  255         sc = device_get_softc(dev);
  256         sisrc = (struct mv_ap806_sei_irqsrc *)isrc;
  257 
  258         mv_ap806_sei_isrc_mask(sc, sisrc, 1);
  259 }
  260 
  261 static void
  262 mv_ap806_sei_post_filter(device_t dev, struct intr_irqsrc *isrc)
  263 {
  264         struct mv_ap806_sei_softc *sc;
  265         struct mv_ap806_sei_irqsrc *sisrc;
  266 
  267         sc = device_get_softc(dev);
  268         sisrc = (struct mv_ap806_sei_irqsrc *)isrc;
  269 
  270         mv_ap806_sei_isrc_mask(sc, sisrc, 1);
  271         mv_ap806_sei_isrc_eoi(sc, sisrc);
  272 }
  273 
  274 /* ----------------------------------------------------------------------------
  275  *
  276  *              B u s    i n t e r f a c e
  277  */
  278 static int
  279 mv_ap806_sei_intr(void *arg)
  280 {
  281         struct mv_ap806_sei_softc *sc;
  282         struct mv_ap806_sei_irqsrc *sirq;
  283         struct trapframe *tf;
  284         uint64_t cause;
  285         u_int irq;
  286 
  287         sc = (struct mv_ap806_sei_softc *)arg;
  288         tf = curthread->td_intr_frame;
  289         while (1) {
  290                 cause = RD4(sc, GICP_SECR1);
  291                 cause <<= 32;
  292                 cause |= RD4(sc, GICP_SECR0);
  293 
  294                 irq = ffsll(cause);
  295                 if (irq == 0) break;
  296                 irq--;
  297                 sirq = &sc->isrcs[irq];
  298                 if (intr_isrc_dispatch(&sirq->isrc, tf) != 0) {
  299                         mv_ap806_sei_isrc_mask(sc, sirq, 0);
  300                         mv_ap806_sei_isrc_eoi(sc, sirq);
  301                         device_printf(sc->dev,
  302                             "Stray irq %u disabled\n", irq);
  303                 }
  304         }
  305 
  306         return (FILTER_HANDLED);
  307 }
  308 
  309 static int
  310 mv_ap806_sei_probe(device_t dev)
  311 {
  312 
  313         if (!ofw_bus_status_okay(dev))
  314                 return (ENXIO);
  315 
  316         if (ofw_bus_search_compatible(dev, compat_data)->ocd_data == 0)
  317                 return (ENXIO);
  318 
  319         device_set_desc(dev, "Marvell SEI");
  320         return (BUS_PROBE_DEFAULT);
  321 }
  322 
  323 static int
  324 mv_ap806_sei_attach(device_t dev)
  325 {
  326         struct mv_ap806_sei_softc *sc;
  327         phandle_t xref, node;
  328         uint32_t irq;
  329         const char *name;
  330         int rv, rid;
  331 
  332         sc = device_get_softc(dev);
  333         sc->dev = dev;
  334         node = ofw_bus_get_node(dev);
  335         MV_AP806_SEI_LOCK_INIT(sc);
  336 
  337         /* Allocate resources. */
  338         rid = 0;
  339         sc->mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid,
  340             RF_ACTIVE);
  341         if (sc->mem_res == NULL) {
  342                 device_printf(dev, "Cannot allocate memory resources\n");
  343                 rv = ENXIO;
  344                 goto fail;
  345         }
  346 
  347         rid = 0;
  348         sc->irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid, RF_ACTIVE);
  349         if (sc->irq_res == NULL) {
  350                 device_printf(dev, "Cannot allocate IRQ resources\n");
  351                 rv = ENXIO;
  352                 goto fail;
  353         }
  354 
  355         /* Mask all interrupts) */
  356         WR4(sc, GICP_SEMR0, 0xFFFFFFFF);
  357         WR4(sc, GICP_SEMR1, 0xFFFFFFFF);
  358 
  359         /* Create all interrupt sources */
  360         sc->isrcs = malloc(sizeof(*sc->isrcs) * MV_AP806_SEI_MAX_NIRQS,
  361             M_DEVBUF, M_WAITOK | M_ZERO);
  362         name = device_get_nameunit(sc->dev);
  363         for (irq = 0; irq < MV_AP806_SEI_MAX_NIRQS; irq++) {
  364                 sc->isrcs[irq].irq = irq;
  365                 rv = intr_isrc_register(&sc->isrcs[irq].isrc,
  366                     sc->dev, 0, "%s,%u", name, irq);
  367                 if (rv != 0)
  368                         goto fail; /* XXX deregister ISRCs */
  369         }
  370         xref = OF_xref_from_node(node);
  371         if (intr_pic_register(dev, xref) == NULL) {
  372                 device_printf(dev, "Cannot register SEI\n");
  373                 rv = ENXIO;
  374                 goto fail;
  375         }
  376         if (bus_setup_intr(dev, sc->irq_res,INTR_TYPE_MISC | INTR_MPSAFE,
  377             mv_ap806_sei_intr, NULL, sc, &sc->irq_ih)) {
  378                 device_printf(dev,
  379                     "Unable to register interrupt handler\n");
  380                 rv = ENXIO;
  381                 goto fail;
  382         }
  383 
  384         /*
  385          * Bitmap of all IRQs.
  386          * 1 - available, 0 - used.
  387          */
  388         BIT_FILL(MV_AP806_SEI_CP_SIZE, &sc->msi_bitmap);
  389 
  390         OF_device_register_xref(xref, dev);
  391         return (0);
  392 
  393 fail:
  394         if (sc->irq_ih != NULL)
  395                 bus_teardown_intr(dev, sc->irq_res, sc->irq_ih);
  396         if (sc->irq_res != NULL)
  397                 bus_release_resource(dev, SYS_RES_IRQ, 0, sc->irq_res);
  398         if (sc->mem_res != NULL)
  399                 bus_release_resource(dev, SYS_RES_MEMORY, 0, sc->mem_res);
  400         MV_AP806_SEI_LOCK_DESTROY(sc);
  401         return (ENXIO);
  402 }
  403 
  404 static int
  405 mv_ap806_sei_detach(device_t dev)
  406 {
  407 
  408         return (EBUSY);
  409 }
  410 
  411 static int
  412 mv_ap806_sei_alloc_msi(device_t dev, device_t child, int count, int maxcount,
  413     device_t *pic, struct intr_irqsrc **srcs)
  414 {
  415         struct mv_ap806_sei_softc *sc;
  416         int i, ret = 0, vector;
  417 
  418         sc = device_get_softc(dev);
  419 
  420         for (i = 0; i < count; i++) {
  421                 /*
  422                  * Find first available MSI vector represented by first set bit
  423                  * in the bitmap. BIT_FFS starts the count from 1,
  424                  * 0 means that nothing was found.
  425                  */
  426                 vector = BIT_FFS_AT(MV_AP806_SEI_CP_SIZE, &sc->msi_bitmap, 0);
  427                 if (vector == 0) {
  428                         ret = ENOMEM;
  429                         i--;
  430                         goto fail;
  431                 }
  432 
  433                 vector--;
  434                 BIT_CLR(MV_AP806_SEI_CP_SIZE, vector, &sc->msi_bitmap);
  435                 vector += MV_AP806_SEI_CP_FIRST;
  436 
  437                 srcs[i] = &sc->isrcs[vector].isrc;
  438         }
  439 
  440         return (ret);
  441 fail:
  442         mv_ap806_sei_release_msi(dev, child, i + 1, srcs);
  443         return (ret);
  444 }
  445 
  446 static int
  447 mv_ap806_sei_release_msi(device_t dev, device_t child, int count, struct intr_irqsrc **srcs)
  448 {
  449         struct mv_ap806_sei_softc *sc;
  450         int i;
  451 
  452         sc = device_get_softc(dev);
  453 
  454         for (i = 0; i < count; i++) {
  455                 BIT_SET(MV_AP806_SEI_CP_SIZE,
  456                     srcs[i]->isrc_irq - MV_AP806_SEI_CP_FIRST,
  457                     &sc->msi_bitmap);
  458         }
  459 
  460         return (0);
  461 }
  462 
  463 static int
  464 mv_ap806_sei_map_msi(device_t dev, device_t child, struct intr_irqsrc *isrc,
  465     uint64_t *addr, uint32_t *data)
  466 {
  467         struct mv_ap806_sei_softc *sc;
  468 
  469         sc = device_get_softc(dev);
  470 
  471         *addr = rman_get_start(sc->mem_res) + MV_AP806_SEI_SETSPI_OFFSET;
  472         *data = isrc->isrc_irq;
  473 
  474         return (0);
  475 }
  476 
  477 static device_method_t mv_ap806_sei_methods[] = {
  478         /* Device interface */
  479         DEVMETHOD(device_probe,         mv_ap806_sei_probe),
  480         DEVMETHOD(device_attach,        mv_ap806_sei_attach),
  481         DEVMETHOD(device_detach,        mv_ap806_sei_detach),
  482 
  483         /* Interrupt controller interface */
  484         DEVMETHOD(pic_disable_intr,     mv_ap806_sei_disable_intr),
  485         DEVMETHOD(pic_enable_intr,      mv_ap806_sei_enable_intr),
  486         DEVMETHOD(pic_map_intr,         mv_ap806_sei_map_intr),
  487         DEVMETHOD(pic_setup_intr,       mv_ap806_sei_setup_intr),
  488         DEVMETHOD(pic_teardown_intr,    mv_ap806_sei_teardown_intr),
  489         DEVMETHOD(pic_post_filter,      mv_ap806_sei_post_filter),
  490         DEVMETHOD(pic_post_ithread,     mv_ap806_sei_post_ithread),
  491         DEVMETHOD(pic_pre_ithread,      mv_ap806_sei_pre_ithread),
  492 
  493         /* MSI interface */
  494         DEVMETHOD(msi_alloc_msi,        mv_ap806_sei_alloc_msi),
  495         DEVMETHOD(msi_release_msi,      mv_ap806_sei_release_msi),
  496         DEVMETHOD(msi_map_msi,          mv_ap806_sei_map_msi),
  497 
  498         DEVMETHOD_END
  499 };
  500 
  501 static driver_t mv_ap806_sei_driver = {
  502         "mv_ap806_sei",
  503         mv_ap806_sei_methods,
  504         sizeof(struct mv_ap806_sei_softc),
  505 };
  506 
  507 EARLY_DRIVER_MODULE(mv_ap806_sei, simplebus, mv_ap806_sei_driver, 0, 0,
  508     BUS_PASS_INTERRUPT + BUS_PASS_ORDER_MIDDLE);

Cache object: d1e4c7ff14a9c01d9aa2b5e503af2973


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