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_bmips.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) 2016 Landon Fuller <landonf@FreeBSD.org>
    3  * Copyright (c) 2017 The FreeBSD Foundation
    4  * All rights reserved.
    5  *
    6  * Portions of this software were developed by Landon Fuller
    7  * under sponsorship from the FreeBSD Foundation.
    8  *
    9  * Redistribution and use in source and binary forms, with or without
   10  * modification, are permitted provided that the following conditions
   11  * are met:
   12  * 1. Redistributions of source code must retain the above copyright
   13  *    notice, this list of conditions and the following disclaimer,
   14  *    without modification.
   15  * 2. Redistributions in binary form must reproduce at minimum a disclaimer
   16  *    similar to the "NO WARRANTY" disclaimer below ("Disclaimer") and any
   17  *    redistribution must be conditioned upon including a substantially
   18  *    similar Disclaimer requirement for further binary redistribution.
   19  *
   20  * NO WARRANTY
   21  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
   22  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
   23  * LIMITED TO, THE IMPLIED WARRANTIES OF NONINFRINGEMENT, MERCHANTIBILITY
   24  * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
   25  * THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY,
   26  * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
   27  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
   28  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
   29  * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
   30  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
   31  * THE POSSIBILITY OF SUCH DAMAGES.
   32  */
   33 
   34 #include <sys/cdefs.h>
   35 __FBSDID("$FreeBSD: releng/12.0/sys/mips/broadcom/bcm_bmips.c 326079 2017-11-21 23:15:20Z landonf $");
   36 
   37 #include <sys/param.h>
   38 #include <sys/kernel.h>
   39 #include <sys/bus.h>
   40 #include <sys/module.h>
   41 #include <sys/proc.h>
   42 
   43 #include <machine/bus.h>
   44 #include <sys/rman.h>
   45 
   46 #include <machine/intr.h>
   47 #include <machine/resource.h>
   48 
   49 #include <dev/bhnd/bhnd.h>
   50 #include <dev/bhnd/siba/sibareg.h>
   51 
   52 #include "pic_if.h"
   53 
   54 #include "bcm_mipsvar.h"
   55 #include "bcm_bmipsreg.h"
   56 
   57 /*
   58  * BMIPS32 and BMIPS3300 core driver.
   59  *
   60  * These cores are only found on siba(4) chipsets, allowing
   61  * us to assume the availability of siba interrupt registers.
   62  */
   63 
   64 struct bcm_bmips_softc;
   65 
   66 static int      bcm_bmips_pic_intr(void *arg);
   67 static void     bcm_bmips_mask_irq(struct bcm_bmips_softc *sc, u_int mips_irq,
   68                     u_int ivec);
   69 static void     bcm_bmips_unmask_irq(struct bcm_bmips_softc *sc, u_int mips_irq,
   70                     u_int ivec);
   71 
   72 static const struct bhnd_device bcm_bmips_devs[] = {
   73         BHND_DEVICE(BCM, MIPS33, NULL, NULL, BHND_DF_SOC),
   74         BHND_DEVICE_END
   75 };
   76 
   77 struct bcm_bmips_softc {
   78         struct bcm_mips_softc    bcm_mips;      /**< parent softc */
   79         device_t                 dev;
   80         struct resource         *mem;           /**< cpu core registers */
   81         int                      mem_rid;
   82         struct resource         *cfg;           /**< cpu core's cfg0 register block */
   83         int                      cfg_rid;
   84 };
   85 
   86 #define BCM_BMIPS_NCPU_IRQS     5       /**< MIPS HW IRQs 0-4 are assignable */
   87 #define BCM_BMIPS_TIMER_IRQ     5       /**< MIPS HW IRQ5 is always assigned to the timer */
   88 
   89 static int
   90 bcm_bmips_probe(device_t dev)
   91 {
   92         const struct bhnd_device *id;
   93 
   94         id = bhnd_device_lookup(dev, bcm_bmips_devs, sizeof(bcm_bmips_devs[0]));
   95         if (id == NULL)
   96                 return (ENXIO);
   97 
   98         /* Check the chip type; should only be found on siba(4) chipsets */
   99         if (bhnd_get_chipid(dev)->chip_type != BHND_CHIPTYPE_SIBA)
  100                 return (ENXIO);
  101 
  102         bhnd_set_default_core_desc(dev);
  103         return (BUS_PROBE_DEFAULT);
  104 }
  105 
  106 
  107 static int
  108 bcm_bmips_attach(device_t dev)
  109 {
  110         struct bcm_bmips_softc  *sc;
  111         int                      error;
  112 
  113         sc = device_get_softc(dev);
  114         sc->dev = dev;
  115 
  116         /* Allocate our core's register block */
  117         sc->mem_rid = 0;
  118         sc->mem = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &sc->mem_rid,
  119             RF_ACTIVE);
  120         if (sc->mem == NULL) {
  121                 device_printf(dev, "failed to allocate cpu register block\n");
  122                 error = ENXIO;
  123                 goto failed;
  124         }
  125 
  126         /* Determine the resource ID for our siba CFG0 registers */
  127         sc->cfg_rid = bhnd_get_port_rid(dev, BHND_PORT_AGENT, 0, 0);
  128         if (sc->cfg_rid == -1) {
  129                 device_printf(dev, "missing required cfg0 register block\n");
  130                 error = ENXIO;
  131                 goto failed;
  132         }
  133 
  134         /* Allocate our CFG0 register block */
  135         sc->cfg = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &sc->cfg_rid,
  136             RF_ACTIVE|RF_SHAREABLE);
  137         if (sc->cfg == NULL) {
  138                 device_printf(dev, "failed to allocate cfg0 register block\n");
  139                 error = ENXIO;
  140                 goto failed;
  141         }
  142 
  143         /* Clear interrupt map */
  144         bus_write_4(sc->cfg, SIBA_CFG0_INTVEC, 0x0);    /* MIPS IRQ0 */
  145         bus_write_4(sc->cfg, SIBA_CFG0_IPSFLAG, 0x0);   /* MIPS IRQ1-4 */
  146 
  147         /* Initialize the generic BHND MIPS driver state */
  148         error = bcm_mips_attach(dev, BCM_BMIPS_NCPU_IRQS, BCM_BMIPS_TIMER_IRQ,
  149             bcm_bmips_pic_intr);
  150         if (error)
  151                 goto failed;
  152 
  153         return (0);
  154 
  155 failed:
  156         if (sc->mem != NULL)
  157                 bus_release_resource(dev, SYS_RES_MEMORY, sc->mem_rid, sc->mem);
  158 
  159         if (sc->cfg != NULL)
  160                 bus_release_resource(dev, SYS_RES_MEMORY, sc->cfg_rid, sc->cfg);
  161 
  162         return (error);
  163 }
  164 
  165 static int
  166 bcm_bmips_detach(device_t dev)
  167 {
  168         struct bcm_bmips_softc  *sc;
  169         int                      error;
  170 
  171         sc = device_get_softc(dev);
  172 
  173         if ((error = bcm_mips_detach(dev)))
  174                 return (error);
  175 
  176         bus_release_resource(dev, SYS_RES_MEMORY, sc->mem_rid, sc->mem);
  177         bus_release_resource(dev, SYS_RES_MEMORY, sc->cfg_rid, sc->cfg);
  178 
  179         return (0);
  180 }
  181 
  182 /* PIC_DISABLE_INTR() */
  183 static void
  184 bcm_bmips_pic_disable_intr(device_t dev, struct intr_irqsrc *irqsrc)
  185 {
  186         struct bcm_bmips_softc  *sc;
  187         struct bcm_mips_irqsrc  *isrc;
  188 
  189         sc = device_get_softc(dev);
  190         isrc = (struct bcm_mips_irqsrc *)irqsrc;
  191 
  192         KASSERT(isrc->cpuirq != NULL, ("no assigned MIPS IRQ"));
  193 
  194         bcm_bmips_mask_irq(sc, isrc->cpuirq->mips_irq, isrc->ivec);
  195 }
  196 
  197 /* PIC_ENABLE_INTR() */
  198 static void
  199 bcm_bmips_pic_enable_intr(device_t dev, struct intr_irqsrc *irqsrc)
  200 {
  201         struct bcm_bmips_softc  *sc;
  202         struct bcm_mips_irqsrc  *isrc;
  203 
  204         sc = device_get_softc(dev);
  205         isrc = (struct bcm_mips_irqsrc *)irqsrc;
  206 
  207         KASSERT(isrc->cpuirq != NULL, ("no assigned MIPS IRQ"));
  208 
  209         bcm_bmips_unmask_irq(sc, isrc->cpuirq->mips_irq, isrc->ivec);
  210 }
  211 
  212 /* PIC_PRE_ITHREAD() */
  213 static void
  214 bcm_bmips_pic_pre_ithread(device_t dev, struct intr_irqsrc *isrc)
  215 {
  216         bcm_bmips_pic_disable_intr(dev, isrc);
  217 }
  218 
  219 /* PIC_POST_ITHREAD() */
  220 static void
  221 bcm_bmips_pic_post_ithread(device_t dev, struct intr_irqsrc *isrc)
  222 {
  223         bcm_bmips_pic_enable_intr(dev, isrc);
  224 }
  225 
  226 /* PIC_POST_FILTER() */
  227 static void
  228 bcm_bmips_pic_post_filter(device_t dev, struct intr_irqsrc *isrc)
  229 {
  230 }
  231 
  232 /**
  233  * Disable routing of backplane interrupt vector @p ivec to MIPS IRQ
  234  * @p mips_irq.
  235  */
  236 static void
  237 bcm_bmips_mask_irq(struct bcm_bmips_softc *sc, u_int mips_irq, u_int ivec)
  238 {
  239         KASSERT(ivec < SIBA_MAX_INTR, ("invalid sbflag# ivec"));
  240         KASSERT(mips_irq < sc->bcm_mips.num_cpuirqs, ("invalid MIPS IRQ %u",
  241             mips_irq));
  242 
  243         if (mips_irq == 0) {
  244                 uint32_t sbintvec;
  245 
  246                 sbintvec = bus_read_4(sc->cfg, SIBA_CFG0_INTVEC);
  247                 sbintvec &= ~(1 << ivec);
  248                 bus_write_4(sc->cfg, SIBA_CFG0_INTVEC, sbintvec);
  249         } else {
  250                 uint32_t ipsflag;
  251 
  252                 /* Can we route this via ipsflag? */
  253                 KASSERT(((1 << ivec) & SIBA_IPS_INT1_MASK) != 0,
  254                     ("cannot route high sbflag# ivec %u", ivec));
  255 
  256                 ipsflag = bus_read_4(sc->cfg, SIBA_CFG0_IPSFLAG);
  257                 ipsflag &= ~(
  258                     ((1 << ivec) << SIBA_IPS_INT_SHIFT(mips_irq)) &
  259                     SIBA_IPS_INT_MASK(mips_irq));
  260                 bus_write_4(sc->cfg, SIBA_CFG0_IPSFLAG, ipsflag);
  261         }
  262 
  263 }
  264 
  265 /**
  266  * Enable routing of an interrupt.
  267  */
  268 static void
  269 bcm_bmips_unmask_irq(struct bcm_bmips_softc *sc, u_int mips_irq, u_int ivec)
  270 {
  271         KASSERT(ivec < SIBA_MAX_INTR, ("invalid sbflag# ivec"));
  272         KASSERT(mips_irq < sc->bcm_mips.num_cpuirqs, ("invalid MIPS IRQ %u",
  273             mips_irq));
  274 
  275         if (mips_irq == 0) {
  276                 uint32_t sbintvec;
  277 
  278                 sbintvec = bus_read_4(sc->cfg, SIBA_CFG0_INTVEC);
  279                 sbintvec |= (1 << ivec);
  280                 bus_write_4(sc->cfg, SIBA_CFG0_INTVEC, sbintvec);
  281         } else {
  282                 uint32_t ipsflag;
  283 
  284                 /* Can we route this via ipsflag? */
  285                 KASSERT(((1 << ivec) & SIBA_IPS_INT1_MASK) != 0,
  286                     ("cannot route high sbflag# ivec %u", ivec));
  287 
  288                 ipsflag = bus_read_4(sc->cfg, SIBA_CFG0_IPSFLAG);
  289                 ipsflag |= (ivec << SIBA_IPS_INT_SHIFT(mips_irq)) &
  290                     SIBA_IPS_INT_MASK(mips_irq);
  291                 bus_write_4(sc->cfg, SIBA_CFG0_IPSFLAG, ipsflag);
  292         }
  293 }
  294 
  295 /* our MIPS CPU interrupt filter */
  296 static int
  297 bcm_bmips_pic_intr(void *arg)
  298 {
  299         struct bcm_bmips_softc  *sc;
  300         struct bcm_mips_cpuirq  *cpuirq;
  301         struct bcm_mips_irqsrc  *isrc_solo;
  302         uint32_t                 sbintvec, sbstatus;
  303         u_int                    mips_irq, i;
  304         int                      error;
  305 
  306         cpuirq = arg;
  307         sc = (struct bcm_bmips_softc*)cpuirq->sc;
  308 
  309         /* Fetch current interrupt state */
  310         sbstatus = bus_read_4(sc->cfg, SIBA_CFG0_FLAGST);
  311 
  312         /* Fetch mask of interrupt vectors routed to this MIPS IRQ */
  313         mips_irq = cpuirq->mips_irq;
  314         if (mips_irq == 0) {
  315                 sbintvec = bus_read_4(sc->cfg, SIBA_CFG0_INTVEC);
  316         } else {
  317                 uint32_t ipsflag;
  318 
  319                 ipsflag = bus_read_4(sc->cfg, SIBA_CFG0_IPSFLAG);
  320 
  321                 /* Map to an intvec-compatible representation */
  322                 switch (mips_irq) {
  323                 case 1:
  324                         sbintvec = (ipsflag & SIBA_IPS_INT1_MASK) >>
  325                             SIBA_IPS_INT1_SHIFT;
  326                         break;
  327                 case 2:
  328                         sbintvec = (ipsflag & SIBA_IPS_INT2_MASK) >>
  329                             SIBA_IPS_INT2_SHIFT;
  330                         break;
  331                 case 3:
  332                         sbintvec = (ipsflag & SIBA_IPS_INT3_MASK) >>
  333                             SIBA_IPS_INT3_SHIFT;
  334                         break;
  335                 case 4: 
  336                         sbintvec = (ipsflag & SIBA_IPS_INT4_MASK) >>
  337                             SIBA_IPS_INT4_SHIFT;
  338                         break;
  339                 default:
  340                         panic("invalid irq %u", mips_irq);
  341                 }
  342         }
  343 
  344         /* Ignore interrupts not routed to this MIPS IRQ */
  345         sbstatus &= sbintvec;
  346 
  347         /* Handle isrc_solo direct dispatch path */
  348         isrc_solo = cpuirq->isrc_solo;
  349         if (isrc_solo != NULL) {
  350                 if (sbstatus & BCM_MIPS_IVEC_MASK(isrc_solo)) {
  351                         error = intr_isrc_dispatch(&isrc_solo->isrc,
  352                             curthread->td_intr_frame);
  353                         if (error) {
  354                                 device_printf(sc->dev, "Stray interrupt %u "
  355                                     "detected\n", isrc_solo->ivec);
  356                                 bcm_bmips_pic_disable_intr(sc->dev,
  357                                     &isrc_solo->isrc);
  358                         }
  359                 }
  360 
  361                 sbstatus &= ~(BCM_MIPS_IVEC_MASK(isrc_solo));
  362                 if (sbstatus == 0)
  363                         return (FILTER_HANDLED);
  364 
  365                 /* Report and mask additional stray interrupts */
  366                 while ((i = fls(sbstatus)) != 0) {
  367                         i--; /* Get a 0-offset interrupt. */
  368                         sbstatus &= ~(1 << i);
  369 
  370                         device_printf(sc->dev, "Stray interrupt %u "
  371                                 "detected\n", i);
  372                         bcm_bmips_mask_irq(sc, mips_irq, i);
  373                 }
  374 
  375                 return (FILTER_HANDLED);
  376         }
  377 
  378         /* Standard dispatch path  */
  379         while ((i = fls(sbstatus)) != 0) {
  380                 i--; /* Get a 0-offset interrupt. */
  381                 sbstatus &= ~(1 << i);
  382 
  383                 KASSERT(i < nitems(sc->bcm_mips.isrcs), ("invalid ivec %u", i));
  384 
  385                 error = intr_isrc_dispatch(&sc->bcm_mips.isrcs[i].isrc,
  386                     curthread->td_intr_frame);
  387                 if (error) {
  388                         device_printf(sc->dev, "Stray interrupt %u detected\n",
  389                             i);
  390                         bcm_bmips_mask_irq(sc, mips_irq, i);
  391                         continue;
  392                 }
  393         }
  394 
  395         return (FILTER_HANDLED);
  396 }
  397 
  398 static device_method_t bcm_bmips_methods[] = {
  399         /* Device interface */
  400         DEVMETHOD(device_probe,         bcm_bmips_probe),
  401         DEVMETHOD(device_attach,        bcm_bmips_attach),
  402         DEVMETHOD(device_detach,        bcm_bmips_detach),
  403 
  404         /* Interrupt controller interface */
  405         DEVMETHOD(pic_disable_intr,     bcm_bmips_pic_disable_intr),
  406         DEVMETHOD(pic_enable_intr,      bcm_bmips_pic_enable_intr),
  407         DEVMETHOD(pic_pre_ithread,      bcm_bmips_pic_pre_ithread),
  408         DEVMETHOD(pic_post_ithread,     bcm_bmips_pic_post_ithread),
  409         DEVMETHOD(pic_post_filter,      bcm_bmips_pic_post_filter),
  410 
  411         DEVMETHOD_END
  412 };
  413 
  414 static devclass_t bcm_mips_devclass;
  415 
  416 DEFINE_CLASS_1(bcm_mips, bcm_bmips_driver, bcm_bmips_methods, sizeof(struct bcm_bmips_softc), bcm_mips_driver);
  417 EARLY_DRIVER_MODULE(bcm_bmips, bhnd, bcm_bmips_driver, bcm_mips_devclass, 0, 0, BUS_PASS_INTERRUPT + BUS_PASS_ORDER_MIDDLE);
  418 
  419 MODULE_VERSION(bcm_bmips, 1);
  420 MODULE_DEPEND(bcm_bmips, bhnd, 1, 1, 1);

Cache object: 107aabbbf878b99752d991a88b3ed8f4


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