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/mips/broadcom/bcm_mips.c

Version: -  FREEBSD  -  FREEBSD-12-STABLE  -  FREEBSD-12-0  -  FREEBSD-11-STABLE  -  FREEBSD-11-2  -  FREEBSD-11-1  -  FREEBSD-11-0  -  FREEBSD-10-STABLE  -  FREEBSD-10-4  -  FREEBSD-10-3  -  FREEBSD-10-2  -  FREEBSD-10-1  -  FREEBSD-10-0  -  FREEBSD-9-STABLE  -  FREEBSD-9-3  -  FREEBSD-9-2  -  FREEBSD-9-1  -  FREEBSD-9-0  -  FREEBSD-8-STABLE  -  FREEBSD-8-4  -  FREEBSD-8-3  -  FREEBSD-8-2  -  FREEBSD-8-1  -  FREEBSD-8-0  -  FREEBSD-7-STABLE  -  FREEBSD-7-4  -  FREEBSD-7-3  -  FREEBSD-7-2  -  FREEBSD-7-1  -  FREEBSD-7-0  -  FREEBSD-6-STABLE  -  FREEBSD-6-4  -  FREEBSD-6-3  -  FREEBSD-6-2  -  FREEBSD-6-1  -  FREEBSD-6-0  -  FREEBSD-5-STABLE  -  FREEBSD-5-5  -  FREEBSD-5-4  -  FREEBSD-5-3  -  FREEBSD-5-2  -  FREEBSD-5-1  -  FREEBSD-5-0  -  FREEBSD-4-STABLE  -  FREEBSD-3-STABLE  -  FREEBSD22  -  linux-2.6  -  linux-2.4.22  -  MK83  -  MK84  -  PLAN9  -  DFBSD  -  NETBSD  -  NETBSD5  -  NETBSD4  -  NETBSD3  -  NETBSD20  -  OPENBSD  -  xnu-517  -  xnu-792  -  xnu-792.6.70  -  xnu-1228  -  xnu-1456.1.26  -  xnu-1699.24.8  -  xnu-2050.18.24  -  OPENSOLARIS  -  minix-3-1-1 
SearchContext: -  none  -  3  -  10 

    1 /*-
    2  * Copyright (c) 2017 The FreeBSD Foundation
    3  * All rights reserved.
    4  *
    5  * This software was developed by Landon Fuller under sponsorship from
    6  * the FreeBSD Foundation.
    7  *
    8  * Redistribution and use in source and binary forms, with or without
    9  * modification, are permitted provided that the following conditions
   10  * are met:
   11  * 1. Redistributions of source code must retain the above copyright
   12  *     notice, this list of conditions and the following disclaimer.
   13  * 2. Redistributions in binary form must reproduce the above copyright
   14  *    notice, this list of conditions and the following disclaimer in the
   15  *    documentation and/or other materials provided with the distribution.
   16  *
   17  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
   18  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
   19  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
   20  * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
   21  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
   22  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
   23  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
   24  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
   25  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
   26  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
   27  * SUCH DAMAGE.
   28  */
   29 
   30 #include <sys/cdefs.h>
   31 __FBSDID("$FreeBSD: stable/12/sys/mips/broadcom/bcm_mips.c 326079 2017-11-21 23:15:20Z landonf $");
   32 
   33 #include <sys/param.h>
   34 #include <sys/kernel.h>
   35 #include <sys/bus.h>
   36 #include <sys/module.h>
   37 #include <sys/limits.h>
   38 #include <sys/systm.h>
   39 
   40 #include <machine/bus.h>
   41 #include <machine/intr.h>
   42 #include <machine/resource.h>
   43 #include <sys/rman.h>
   44 
   45 #include <dev/bhnd/bhnd.h>
   46 #include <dev/bhnd/siba/sibareg.h>
   47 
   48 #include "pic_if.h"
   49 
   50 #include "bcm_mipsvar.h"
   51 
   52 /*
   53  * Broadcom MIPS core driver.
   54  *
   55  * Abstract driver for Broadcom MIPS CPU/PIC cores.
   56  */
   57 
   58 static uintptr_t        bcm_mips_pic_xref(struct bcm_mips_softc *sc);
   59 static device_t         bcm_mips_find_bhnd_parent(device_t dev);
   60 static int              bcm_mips_retain_cpu_intr(struct bcm_mips_softc *sc,
   61                             struct bcm_mips_irqsrc *isrc, struct resource *res);
   62 static int              bcm_mips_release_cpu_intr(struct bcm_mips_softc *sc,
   63                             struct bcm_mips_irqsrc *isrc, struct resource *res);
   64 
   65 static const int bcm_mips_debug = 0;
   66 
   67 #define DPRINTF(fmt, ...) do {                                          \
   68         if (bcm_mips_debug)                                             \
   69                 printf("%s: " fmt, __FUNCTION__, ##__VA_ARGS__);        \
   70 } while (0)
   71 
   72 #define DENTRY(dev, fmt, ...) do {                                      \
   73         if (bcm_mips_debug)                                             \
   74                 printf("%s(%s, " fmt ")\n", __FUNCTION__,               \
   75                     device_get_nameunit(dev), ##__VA_ARGS__);           \
   76 } while (0)
   77 
   78 /**
   79  * Register all interrupt source definitions.
   80  */
   81 static int
   82 bcm_mips_register_isrcs(struct bcm_mips_softc *sc)
   83 {
   84         const char      *name;
   85         uintptr_t        xref;
   86         int              error;
   87 
   88         xref = bcm_mips_pic_xref(sc);
   89 
   90         name = device_get_nameunit(sc->dev);
   91         for (size_t ivec = 0; ivec < nitems(sc->isrcs); ivec++) {
   92                 sc->isrcs[ivec].ivec = ivec;
   93                 sc->isrcs[ivec].cpuirq = NULL;
   94                 sc->isrcs[ivec].refs = 0;
   95 
   96                 error = intr_isrc_register(&sc->isrcs[ivec].isrc, sc->dev,
   97                     xref, "%s,%u", name, ivec);
   98                 if (error) {
   99                         for (size_t i = 0; i < ivec; i++)
  100                                 intr_isrc_deregister(&sc->isrcs[i].isrc);
  101 
  102                         device_printf(sc->dev, "error registering IRQ %zu: "
  103                             "%d\n", ivec, error);
  104                         return (error);
  105                 }
  106         }
  107 
  108         return (0);
  109 }
  110 
  111 /**
  112  * Initialize the given @p cpuirq state as unavailable.
  113  * 
  114  * @param sc            BHND MIPS driver instance state.
  115  * @param cpuirq        The CPU IRQ state to be initialized.
  116  *
  117  * @retval 0            success
  118  * @retval non-zero     if initializing @p cpuirq otherwise fails, a regular
  119  *                      unix error code will be returned.
  120  */
  121 static int
  122 bcm_mips_init_cpuirq_unavail(struct bcm_mips_softc *sc,
  123     struct bcm_mips_cpuirq *cpuirq)
  124 {
  125         BCM_MIPS_LOCK(sc);
  126 
  127         KASSERT(cpuirq->sc == NULL, ("cpuirq already initialized"));
  128         cpuirq->sc = sc;
  129         cpuirq->mips_irq = 0;
  130         cpuirq->irq_rid = -1;
  131         cpuirq->irq_res = NULL;
  132         cpuirq->irq_cookie = NULL;
  133         cpuirq->isrc_solo = NULL;
  134         cpuirq->refs = 0;
  135 
  136         BCM_MIPS_UNLOCK(sc);
  137 
  138         return (0);
  139 }
  140 
  141 /**
  142  * Allocate required resources and initialize the given @p cpuirq state.
  143  * 
  144  * @param sc            BHND MIPS driver instance state.
  145  * @param cpuirq        The CPU IRQ state to be initialized.
  146  * @param rid           The resource ID to be assigned for the CPU IRQ resource,
  147  *                      or -1 if no resource should be assigned.
  148  * @param irq           The MIPS HW IRQ# to be allocated.
  149  * @param filter        The interrupt filter to be setup.
  150  *
  151  * @retval 0            success
  152  * @retval non-zero     if initializing @p cpuirq otherwise fails, a regular
  153  *                      unix error code will be returned.
  154  */
  155 static int
  156 bcm_mips_init_cpuirq(struct bcm_mips_softc *sc, struct bcm_mips_cpuirq *cpuirq,
  157     int rid, u_int irq, driver_filter_t filter)
  158 {
  159         struct resource *res;
  160         void            *cookie;
  161         u_int            irq_real;
  162         int              error;
  163 
  164         /* Must fall within MIPS HW IRQ range */
  165         if (irq >= NHARD_IRQS)
  166                 return (EINVAL);
  167 
  168         /* HW IRQs are numbered relative to SW IRQs */
  169         irq_real = irq + NSOFT_IRQS;
  170 
  171         /* Try to assign and allocate the resource */
  172         BCM_MIPS_LOCK(sc);
  173 
  174         KASSERT(cpuirq->sc == NULL, ("cpuirq already initialized"));
  175 
  176         error = bus_set_resource(sc->dev, SYS_RES_IRQ, rid, irq_real, 1);
  177         if (error) {
  178                 BCM_MIPS_UNLOCK(sc);
  179                 device_printf(sc->dev, "failed to assign interrupt %u: "
  180                     "%d\n", irq, error);
  181                 return (error);
  182         }
  183 
  184         res = bus_alloc_resource_any(sc->dev, SYS_RES_IRQ, &rid, RF_ACTIVE);
  185         if (res == NULL) {
  186                 BCM_MIPS_UNLOCK(sc);
  187                 device_printf(sc->dev, "failed to allocate interrupt "
  188                         "%u resource\n", irq);
  189                 bus_delete_resource(sc->dev, SYS_RES_IRQ, rid);
  190                 return (ENXIO);
  191         }
  192 
  193         error = bus_setup_intr(sc->dev, res,
  194             INTR_TYPE_MISC | INTR_MPSAFE | INTR_EXCL, filter, NULL, cpuirq,
  195             &cookie);
  196         if (error) {
  197                 BCM_MIPS_UNLOCK(sc);
  198 
  199                 printf("failed to setup internal interrupt handler: %d\n",
  200                     error);
  201 
  202                 bus_release_resource(sc->dev, SYS_RES_IRQ, rid, res);
  203                 bus_delete_resource(sc->dev, SYS_RES_IRQ, rid);
  204 
  205                 return (error);
  206         }
  207 
  208         /* Initialize CPU IRQ state */
  209         cpuirq->sc = sc;
  210         cpuirq->mips_irq = irq;
  211         cpuirq->irq_rid = rid;
  212         cpuirq->irq_res = res;
  213         cpuirq->irq_cookie = cookie;
  214         cpuirq->isrc_solo = NULL;
  215         cpuirq->refs = 0;
  216 
  217         BCM_MIPS_UNLOCK(sc);
  218         return (0);
  219 }
  220 
  221 /**
  222  * Free any resources associated with the given @p cpuirq state.
  223  * 
  224  * @param sc            BHND MIPS driver instance state.
  225  * @param cpuirq        A CPU IRQ instance previously successfully initialized
  226  *                      via bcm_mips_init_cpuirq().
  227  *
  228  * @retval 0            success
  229  * @retval non-zero     if finalizing @p cpuirq otherwise fails, a regular
  230  *                      unix error code will be returned.
  231  */
  232 static int
  233 bcm_mips_fini_cpuirq(struct bcm_mips_softc *sc, struct bcm_mips_cpuirq *cpuirq)
  234 {
  235         int error;
  236 
  237         BCM_MIPS_LOCK(sc);
  238 
  239         if (cpuirq->sc == NULL) {
  240                 KASSERT(cpuirq->irq_res == NULL, ("leaking cpuirq resource"));
  241 
  242                 BCM_MIPS_UNLOCK(sc);
  243                 return (0);     /* not initialized */
  244         }
  245 
  246         if (cpuirq->refs != 0) {
  247                 BCM_MIPS_UNLOCK(sc);
  248                 return (EBUSY);
  249         }
  250 
  251         if (cpuirq->irq_cookie != NULL) {
  252                 KASSERT(cpuirq->irq_res != NULL, ("resource missing"));
  253 
  254                 error = bus_teardown_intr(sc->dev, cpuirq->irq_res,
  255                         cpuirq->irq_cookie);
  256                 if (error) {
  257                         BCM_MIPS_UNLOCK(sc);
  258                         return (error);
  259                 }
  260 
  261                 cpuirq->irq_cookie = NULL;
  262         }
  263 
  264         if (cpuirq->irq_res != NULL) {
  265                 bus_release_resource(sc->dev, SYS_RES_IRQ, cpuirq->irq_rid,
  266                     cpuirq->irq_res);
  267                 cpuirq->irq_res = NULL;
  268         }
  269 
  270         if (cpuirq->irq_rid != -1) {
  271                 bus_delete_resource(sc->dev, SYS_RES_IRQ, cpuirq->irq_rid);
  272                 cpuirq->irq_rid = -1;
  273         }
  274 
  275         BCM_MIPS_UNLOCK(sc);
  276 
  277         return (0);
  278 }
  279 
  280 static int
  281 bcm_mips_attach_default(device_t dev)
  282 {
  283         /* subclassing drivers must provide an implementation of
  284          * DEVICE_ATTACH() */
  285         panic("device_attach() unimplemented");
  286 }
  287 
  288 /**
  289  * BHND MIPS device attach.
  290  * 
  291  * This must be called from subclass drivers' DEVICE_ATTACH().
  292  * 
  293  * @param dev BHND MIPS device.
  294  * @param num_cpuirqs The number of usable MIPS HW IRQs.
  295  * @param timer_irq The MIPS HW IRQ assigned to the MIPS CPU timer.
  296  * @param filter The subclass's core-specific IRQ dispatch filter. Will be
  297  * passed the associated bcm_mips_cpuirq instance as its argument.
  298  */
  299 int
  300 bcm_mips_attach(device_t dev, u_int num_cpuirqs, u_int timer_irq,
  301     driver_filter_t filter)
  302 {
  303         struct bcm_mips_softc   *sc;
  304         struct intr_pic         *pic;
  305         uintptr_t                xref;
  306         u_int                    irq_rid;
  307         rman_res_t               irq;
  308         int                      error;
  309 
  310         sc = device_get_softc(dev);
  311         sc->dev = dev;
  312         sc->num_cpuirqs = num_cpuirqs;
  313         sc->timer_irq = timer_irq;
  314 
  315         /* Must not exceed the actual size of our fixed IRQ array */
  316         if (sc->num_cpuirqs > nitems(sc->cpuirqs)) {
  317                 device_printf(dev, "%u nirqs exceeds maximum supported %zu",
  318                     sc->num_cpuirqs, nitems(sc->cpuirqs));
  319                 return (ENXIO);
  320         }
  321 
  322         pic = NULL;
  323         xref = bcm_mips_pic_xref(sc);
  324 
  325         BCM_MIPS_LOCK_INIT(sc);
  326 
  327         /* Register our interrupt sources */
  328         if ((error = bcm_mips_register_isrcs(sc))) {
  329                 BCM_MIPS_LOCK_DESTROY(sc);
  330                 return (error);
  331         }
  332 
  333         /* Initialize our CPU interrupt state */
  334         irq_rid = bhnd_get_intr_count(dev); /* last bhnd-assigned RID + 1 */
  335         irq = 0;
  336         for (u_int i = 0; i < sc->num_cpuirqs; i++) {
  337                 /* Must not overflow signed resource ID representation */
  338                 if (irq_rid >= INT_MAX) {
  339                         device_printf(dev, "exhausted IRQ resource IDs\n");
  340                         error = ENOMEM;
  341                         goto failed;
  342                 }
  343 
  344                 if (irq == sc->timer_irq) {
  345                         /* Mark the CPU timer's IRQ as unavailable */
  346                         error = bcm_mips_init_cpuirq_unavail(sc,
  347                             &sc->cpuirqs[i]);
  348                 } else {
  349                         /* Initialize state */
  350                         error = bcm_mips_init_cpuirq(sc, &sc->cpuirqs[i],
  351                             irq_rid, irq, filter);
  352                 }
  353 
  354                 if (error)
  355                         goto failed;
  356 
  357                 /* Increment IRQ and resource ID for next allocation */
  358                 irq_rid++;
  359                 irq++;
  360         }
  361 
  362         /* Sanity check; our shared IRQ must be available */
  363         if (sc->num_cpuirqs <= BCM_MIPS_IRQ_SHARED)
  364                 panic("missing shared interrupt %d\n", BCM_MIPS_IRQ_SHARED);
  365 
  366         if (sc->cpuirqs[BCM_MIPS_IRQ_SHARED].irq_rid == -1)
  367                 panic("shared interrupt %d unavailable", BCM_MIPS_IRQ_SHARED);
  368 
  369         /* Register PIC */
  370         if ((pic = intr_pic_register(dev, xref)) == NULL) {
  371                 device_printf(dev, "error registering PIC\n");
  372                 error = ENXIO;
  373                 goto failed;
  374         }
  375 
  376         return (0);
  377 
  378 failed:
  379         /* Deregister PIC before performing any other cleanup */
  380         if (pic != NULL)
  381                 intr_pic_deregister(dev, 0);
  382 
  383         /* Deregister all interrupt sources */
  384         for (size_t i = 0; i < nitems(sc->isrcs); i++)
  385                 intr_isrc_deregister(&sc->isrcs[i].isrc);
  386 
  387         /* Free our MIPS CPU interrupt handler state */
  388         for (u_int i = 0; i < sc->num_cpuirqs; i++)
  389                 bcm_mips_fini_cpuirq(sc, &sc->cpuirqs[i]);
  390 
  391         BCM_MIPS_LOCK_DESTROY(sc);
  392         return (error);
  393 }
  394 
  395 int
  396 bcm_mips_detach(device_t dev)
  397 {
  398         struct bcm_mips_softc *sc;
  399 
  400         sc = device_get_softc(dev);
  401 
  402         /* Deregister PIC before performing any other cleanup */
  403         intr_pic_deregister(dev, 0);
  404 
  405         /* Deregister all interrupt sources */
  406         for (size_t i = 0; i < nitems(sc->isrcs); i++)
  407                 intr_isrc_deregister(&sc->isrcs[i].isrc);
  408 
  409         /* Free our MIPS CPU interrupt handler state */
  410         for (u_int i = 0; i < sc->num_cpuirqs; i++)
  411                 bcm_mips_fini_cpuirq(sc, &sc->cpuirqs[i]);
  412 
  413         return (0);
  414 }
  415 
  416 /* PIC_MAP_INTR() */
  417 static int
  418 bcm_mips_pic_map_intr(device_t dev, struct intr_map_data *d,
  419     struct intr_irqsrc **isrcp)
  420 {
  421         struct bcm_mips_softc           *sc;
  422         struct bcm_mips_intr_map_data   *data;
  423 
  424         sc = device_get_softc(dev);
  425 
  426         if (d->type != INTR_MAP_DATA_BCM_MIPS) {
  427                 DENTRY(dev, "type=%d", d->type);
  428                 return (ENOTSUP);
  429         }
  430 
  431         data = (struct bcm_mips_intr_map_data *)d;
  432         DENTRY(dev, "type=%d, ivec=%u", d->type, data->ivec);
  433         if (data->ivec < 0 || data->ivec >= nitems(sc->isrcs))
  434                 return (EINVAL);
  435 
  436         *isrcp = &sc->isrcs[data->ivec].isrc;
  437         return (0);
  438 }
  439 
  440 /* PIC_SETUP_INTR() */
  441 static int
  442 bcm_mips_pic_setup_intr(device_t dev, struct intr_irqsrc *irqsrc,
  443     struct resource *res, struct intr_map_data *data)
  444 {
  445         struct bcm_mips_softc   *sc;
  446         struct bcm_mips_irqsrc  *isrc;
  447         int                      error;
  448 
  449         sc = device_get_softc(dev);
  450         isrc = (struct bcm_mips_irqsrc *)irqsrc;
  451 
  452         /* Assign a CPU interrupt */
  453         BCM_MIPS_LOCK(sc);
  454         error = bcm_mips_retain_cpu_intr(sc, isrc, res);
  455         BCM_MIPS_UNLOCK(sc);
  456 
  457         return (error);
  458 }
  459 
  460 /* PIC_TEARDOWN_INTR() */
  461 static int
  462 bcm_mips_pic_teardown_intr(device_t dev, struct intr_irqsrc *irqsrc,
  463     struct resource *res, struct intr_map_data *data)
  464 {
  465         struct bcm_mips_softc   *sc;
  466         struct bcm_mips_irqsrc  *isrc;
  467         int                      error;
  468 
  469         sc = device_get_softc(dev);
  470         isrc = (struct bcm_mips_irqsrc *)irqsrc;
  471 
  472         /* Release the CPU interrupt */
  473         BCM_MIPS_LOCK(sc);
  474         error = bcm_mips_release_cpu_intr(sc, isrc, res);
  475         BCM_MIPS_UNLOCK(sc);
  476 
  477         return (error);
  478 }
  479 
  480 
  481 /** return our PIC's xref */
  482 static uintptr_t
  483 bcm_mips_pic_xref(struct bcm_mips_softc *sc)
  484 {
  485         uintptr_t xref;
  486 
  487         /* Determine our interrupt domain */
  488         xref = BHND_BUS_GET_INTR_DOMAIN(device_get_parent(sc->dev), sc->dev,
  489             true);
  490         KASSERT(xref != 0, ("missing interrupt domain"));
  491 
  492         return (xref);
  493 }
  494 
  495 /**
  496  * Walk up the device tree from @p dev until we find a bhnd-attached core,
  497  * returning either the core, or NULL if @p dev is not attached under a bhnd
  498  * bus.
  499  */    
  500 static device_t
  501 bcm_mips_find_bhnd_parent(device_t dev)
  502 {
  503         device_t        core, bus;
  504         devclass_t      bhnd_class;
  505 
  506         bhnd_class = devclass_find("bhnd");
  507         core = dev;
  508         while ((bus = device_get_parent(core)) != NULL) {
  509                 if (device_get_devclass(bus) == bhnd_class)
  510                         return (core);
  511 
  512                 core = bus;
  513         }
  514 
  515         /* Not found */
  516         return (NULL);
  517 }
  518 
  519 /**
  520  * Retain @p isrc and assign a MIPS CPU interrupt on behalf of @p res; if
  521  * the @p isrc already has a MIPS CPU interrupt assigned, the existing
  522  * reference will be left unmodified.
  523  * 
  524  * @param sc            BHND MIPS driver state.
  525  * @param isrc          The interrupt source corresponding to @p res.
  526  * @param res           The interrupt resource for which a MIPS CPU IRQ will be
  527  *                      assigned.
  528  */
  529 static int
  530 bcm_mips_retain_cpu_intr(struct bcm_mips_softc *sc,
  531     struct bcm_mips_irqsrc *isrc, struct resource *res)
  532 {
  533         struct bcm_mips_cpuirq  *cpuirq;
  534         bhnd_devclass_t          devclass;
  535         device_t                 core;
  536 
  537         BCM_MIPS_LOCK_ASSERT(sc, MA_OWNED);
  538 
  539         /* Prefer existing assignment */
  540         if (isrc->cpuirq != NULL) {
  541                 KASSERT(isrc->cpuirq->refs > 0, ("assigned IRQ has no "
  542                     "references"));
  543 
  544                 /* Increment our reference count */
  545                 if (isrc->refs == UINT_MAX)
  546                         return (ENOMEM);        /* would overflow */
  547 
  548                 isrc->refs++;
  549                 return (0);
  550         }
  551 
  552         /* Use the device class of the bhnd core to which the interrupt
  553          * vector is routed to determine whether a shared interrupt should
  554          * be preferred. */
  555         devclass = BHND_DEVCLASS_OTHER;
  556         core = bcm_mips_find_bhnd_parent(rman_get_device(res));
  557         if (core != NULL)
  558                 devclass = bhnd_get_class(core);
  559 
  560         switch (devclass) {
  561         case BHND_DEVCLASS_CC:
  562         case BHND_DEVCLASS_CC_B:
  563         case BHND_DEVCLASS_PMU:
  564         case BHND_DEVCLASS_RAM:
  565         case BHND_DEVCLASS_MEMC:
  566         case BHND_DEVCLASS_CPU:
  567         case BHND_DEVCLASS_SOC_ROUTER:
  568         case BHND_DEVCLASS_SOC_BRIDGE:
  569         case BHND_DEVCLASS_EROM:
  570         case BHND_DEVCLASS_NVRAM:
  571                 /* Always use a shared interrupt for these devices */
  572                 cpuirq = &sc->cpuirqs[BCM_MIPS_IRQ_SHARED];
  573                 break;
  574 
  575         case BHND_DEVCLASS_PCI:
  576         case BHND_DEVCLASS_PCIE:
  577         case BHND_DEVCLASS_PCCARD:      
  578         case BHND_DEVCLASS_ENET:
  579         case BHND_DEVCLASS_ENET_MAC:
  580         case BHND_DEVCLASS_ENET_PHY:
  581         case BHND_DEVCLASS_WLAN:
  582         case BHND_DEVCLASS_WLAN_MAC:
  583         case BHND_DEVCLASS_WLAN_PHY:
  584         case BHND_DEVCLASS_USB_HOST:
  585         case BHND_DEVCLASS_USB_DEV:
  586         case BHND_DEVCLASS_USB_DUAL:
  587         case BHND_DEVCLASS_OTHER:
  588         case BHND_DEVCLASS_INVALID:
  589         default:
  590                 /* Fall back on a shared interrupt */
  591                 cpuirq = &sc->cpuirqs[BCM_MIPS_IRQ_SHARED];
  592 
  593                 /* Try to assign a dedicated MIPS HW interrupt */
  594                 for (u_int i = 0; i < sc->num_cpuirqs; i++) {
  595                         if (i == BCM_MIPS_IRQ_SHARED)
  596                                 continue;
  597 
  598                         if (sc->cpuirqs[i].irq_rid == -1)
  599                                 continue; /* unavailable */
  600 
  601                         if (sc->cpuirqs[i].refs != 0)
  602                                 continue; /* already assigned */
  603 
  604                         /* Found an unused CPU IRQ */
  605                         cpuirq = &sc->cpuirqs[i];
  606                         break;
  607                 }
  608 
  609                 break;
  610         }
  611 
  612         DPRINTF("routing backplane interrupt vector %u to MIPS IRQ %u\n",
  613             isrc->ivec, cpuirq->mips_irq);
  614 
  615         KASSERT(isrc->cpuirq == NULL, ("CPU IRQ already assigned"));
  616         KASSERT(isrc->refs == 0, ("isrc has active references with no "
  617             "assigned CPU IRQ"));
  618         KASSERT(cpuirq->refs == 1 || cpuirq->isrc_solo == NULL,
  619             ("single isrc dispatch enabled on cpuirq with multiple refs"));
  620 
  621         /* Verify that bumping the cpuirq refcount below will not overflow */
  622         if (cpuirq->refs == UINT_MAX)
  623                 return (ENOMEM);
  624 
  625         /* Increment cpuirq refcount on behalf of the isrc */
  626         cpuirq->refs++;
  627 
  628         /* Increment isrc refcount on behalf of the caller */
  629         isrc->refs++;
  630 
  631         /* Assign the IRQ to the isrc */
  632         isrc->cpuirq = cpuirq;
  633 
  634         /* Can we enable the single isrc dispatch path? */
  635         if (cpuirq->refs == 1 && cpuirq->mips_irq != BCM_MIPS_IRQ_SHARED)
  636                 cpuirq->isrc_solo = isrc;
  637 
  638         return (0);
  639 }
  640 
  641 /**
  642  * Release the MIPS CPU interrupt assigned to @p isrc on behalf of @p res.
  643  *
  644  * @param sc    BHND MIPS driver state.
  645  * @param isrc  The interrupt source corresponding to @p res.
  646  * @param res   The interrupt resource being activated.
  647  */
  648 static int
  649 bcm_mips_release_cpu_intr(struct bcm_mips_softc *sc,
  650     struct bcm_mips_irqsrc *isrc, struct resource *res)
  651 {
  652         struct bcm_mips_cpuirq *cpuirq;
  653 
  654         BCM_MIPS_LOCK_ASSERT(sc, MA_OWNED);
  655 
  656         /* Decrement the refcount */
  657         KASSERT(isrc->refs > 0, ("isrc over-release"));
  658         isrc->refs--;
  659 
  660         /* Nothing else to do if the isrc is still actively referenced */
  661         if (isrc->refs > 0)
  662                 return (0);
  663 
  664         /* Otherwise, we need to release our CPU IRQ reference */
  665         cpuirq = isrc->cpuirq;
  666         isrc->cpuirq = NULL;
  667 
  668         KASSERT(cpuirq != NULL, ("no assigned IRQ"));
  669         KASSERT(cpuirq->refs > 0, ("cpuirq over-release"));
  670 
  671         /* Disable single isrc dispatch path */
  672         if (cpuirq->refs == 1 && cpuirq->isrc_solo != NULL) {
  673                 KASSERT(cpuirq->isrc_solo == isrc, ("invalid solo isrc"));
  674                 cpuirq->isrc_solo = NULL;
  675         }
  676 
  677         cpuirq->refs--;
  678 
  679         return (0);
  680 }
  681 
  682 static device_method_t bcm_mips_methods[] = {
  683         /* Device interface */
  684         DEVMETHOD(device_attach,        bcm_mips_attach_default),
  685         DEVMETHOD(device_detach,        bcm_mips_detach),
  686 
  687         /* Interrupt controller interface */
  688         DEVMETHOD(pic_map_intr,         bcm_mips_pic_map_intr),
  689         DEVMETHOD(pic_setup_intr,       bcm_mips_pic_setup_intr),
  690         DEVMETHOD(pic_teardown_intr,    bcm_mips_pic_teardown_intr),
  691 
  692         DEVMETHOD_END
  693 };
  694 
  695 DEFINE_CLASS_0(bcm_mips, bcm_mips_driver, bcm_mips_methods, sizeof(struct bcm_mips_softc));
  696 
  697 MODULE_VERSION(bcm_mips, 1);
  698 MODULE_DEPEND(bcm_mips, bhnd, 1, 1, 1);

Cache object: f00555f2c82c288f13bb0e85fb3b1d29


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