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$");
   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 static int
  107 bcm_bmips_attach(device_t dev)
  108 {
  109         struct bcm_bmips_softc  *sc;
  110         int                      error;
  111 
  112         sc = device_get_softc(dev);
  113         sc->dev = dev;
  114 
  115         /* Allocate our core's register block */
  116         sc->mem_rid = 0;
  117         sc->mem = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &sc->mem_rid,
  118             RF_ACTIVE);
  119         if (sc->mem == NULL) {
  120                 device_printf(dev, "failed to allocate cpu register block\n");
  121                 error = ENXIO;
  122                 goto failed;
  123         }
  124 
  125         /* Determine the resource ID for our siba CFG0 registers */
  126         sc->cfg_rid = bhnd_get_port_rid(dev, BHND_PORT_AGENT, 0, 0);
  127         if (sc->cfg_rid == -1) {
  128                 device_printf(dev, "missing required cfg0 register block\n");
  129                 error = ENXIO;
  130                 goto failed;
  131         }
  132 
  133         /* Allocate our CFG0 register block */
  134         sc->cfg = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &sc->cfg_rid,
  135             RF_ACTIVE|RF_SHAREABLE);
  136         if (sc->cfg == NULL) {
  137                 device_printf(dev, "failed to allocate cfg0 register block\n");
  138                 error = ENXIO;
  139                 goto failed;
  140         }
  141 
  142         /* Clear interrupt map */
  143         bus_write_4(sc->cfg, SIBA_CFG0_INTVEC, 0x0);    /* MIPS IRQ0 */
  144         bus_write_4(sc->cfg, SIBA_CFG0_IPSFLAG, 0x0);   /* MIPS IRQ1-4 */
  145 
  146         /* Initialize the generic BHND MIPS driver state */
  147         error = bcm_mips_attach(dev, BCM_BMIPS_NCPU_IRQS, BCM_BMIPS_TIMER_IRQ,
  148             bcm_bmips_pic_intr);
  149         if (error)
  150                 goto failed;
  151 
  152         return (0);
  153 
  154 failed:
  155         if (sc->mem != NULL)
  156                 bus_release_resource(dev, SYS_RES_MEMORY, sc->mem_rid, sc->mem);
  157 
  158         if (sc->cfg != NULL)
  159                 bus_release_resource(dev, SYS_RES_MEMORY, sc->cfg_rid, sc->cfg);
  160 
  161         return (error);
  162 }
  163 
  164 static int
  165 bcm_bmips_detach(device_t dev)
  166 {
  167         struct bcm_bmips_softc  *sc;
  168         int                      error;
  169 
  170         sc = device_get_softc(dev);
  171 
  172         if ((error = bcm_mips_detach(dev)))
  173                 return (error);
  174 
  175         bus_release_resource(dev, SYS_RES_MEMORY, sc->mem_rid, sc->mem);
  176         bus_release_resource(dev, SYS_RES_MEMORY, sc->cfg_rid, sc->cfg);
  177 
  178         return (0);
  179 }
  180 
  181 /* PIC_DISABLE_INTR() */
  182 static void
  183 bcm_bmips_pic_disable_intr(device_t dev, struct intr_irqsrc *irqsrc)
  184 {
  185         struct bcm_bmips_softc  *sc;
  186         struct bcm_mips_irqsrc  *isrc;
  187 
  188         sc = device_get_softc(dev);
  189         isrc = (struct bcm_mips_irqsrc *)irqsrc;
  190 
  191         KASSERT(isrc->cpuirq != NULL, ("no assigned MIPS IRQ"));
  192 
  193         bcm_bmips_mask_irq(sc, isrc->cpuirq->mips_irq, isrc->ivec);
  194 }
  195 
  196 /* PIC_ENABLE_INTR() */
  197 static void
  198 bcm_bmips_pic_enable_intr(device_t dev, struct intr_irqsrc *irqsrc)
  199 {
  200         struct bcm_bmips_softc  *sc;
  201         struct bcm_mips_irqsrc  *isrc;
  202 
  203         sc = device_get_softc(dev);
  204         isrc = (struct bcm_mips_irqsrc *)irqsrc;
  205 
  206         KASSERT(isrc->cpuirq != NULL, ("no assigned MIPS IRQ"));
  207 
  208         bcm_bmips_unmask_irq(sc, isrc->cpuirq->mips_irq, isrc->ivec);
  209 }
  210 
  211 /* PIC_PRE_ITHREAD() */
  212 static void
  213 bcm_bmips_pic_pre_ithread(device_t dev, struct intr_irqsrc *isrc)
  214 {
  215         bcm_bmips_pic_disable_intr(dev, isrc);
  216 }
  217 
  218 /* PIC_POST_ITHREAD() */
  219 static void
  220 bcm_bmips_pic_post_ithread(device_t dev, struct intr_irqsrc *isrc)
  221 {
  222         bcm_bmips_pic_enable_intr(dev, isrc);
  223 }
  224 
  225 /* PIC_POST_FILTER() */
  226 static void
  227 bcm_bmips_pic_post_filter(device_t dev, struct intr_irqsrc *isrc)
  228 {
  229 }
  230 
  231 /**
  232  * Disable routing of backplane interrupt vector @p ivec to MIPS IRQ
  233  * @p mips_irq.
  234  */
  235 static void
  236 bcm_bmips_mask_irq(struct bcm_bmips_softc *sc, u_int mips_irq, u_int ivec)
  237 {
  238         KASSERT(ivec < SIBA_MAX_INTR, ("invalid sbflag# ivec"));
  239         KASSERT(mips_irq < sc->bcm_mips.num_cpuirqs, ("invalid MIPS IRQ %u",
  240             mips_irq));
  241 
  242         if (mips_irq == 0) {
  243                 uint32_t sbintvec;
  244 
  245                 sbintvec = bus_read_4(sc->cfg, SIBA_CFG0_INTVEC);
  246                 sbintvec &= ~(1 << ivec);
  247                 bus_write_4(sc->cfg, SIBA_CFG0_INTVEC, sbintvec);
  248         } else {
  249                 uint32_t ipsflag;
  250 
  251                 /* Can we route this via ipsflag? */
  252                 KASSERT(((1 << ivec) & SIBA_IPS_INT1_MASK) != 0,
  253                     ("cannot route high sbflag# ivec %u", ivec));
  254 
  255                 ipsflag = bus_read_4(sc->cfg, SIBA_CFG0_IPSFLAG);
  256                 ipsflag &= ~(
  257                     ((1 << ivec) << SIBA_IPS_INT_SHIFT(mips_irq)) &
  258                     SIBA_IPS_INT_MASK(mips_irq));
  259                 bus_write_4(sc->cfg, SIBA_CFG0_IPSFLAG, ipsflag);
  260         }
  261 
  262 }
  263 
  264 /**
  265  * Enable routing of an interrupt.
  266  */
  267 static void
  268 bcm_bmips_unmask_irq(struct bcm_bmips_softc *sc, u_int mips_irq, u_int ivec)
  269 {
  270         KASSERT(ivec < SIBA_MAX_INTR, ("invalid sbflag# ivec"));
  271         KASSERT(mips_irq < sc->bcm_mips.num_cpuirqs, ("invalid MIPS IRQ %u",
  272             mips_irq));
  273 
  274         if (mips_irq == 0) {
  275                 uint32_t sbintvec;
  276 
  277                 sbintvec = bus_read_4(sc->cfg, SIBA_CFG0_INTVEC);
  278                 sbintvec |= (1 << ivec);
  279                 bus_write_4(sc->cfg, SIBA_CFG0_INTVEC, sbintvec);
  280         } else {
  281                 uint32_t ipsflag;
  282 
  283                 /* Can we route this via ipsflag? */
  284                 KASSERT(((1 << ivec) & SIBA_IPS_INT1_MASK) != 0,
  285                     ("cannot route high sbflag# ivec %u", ivec));
  286 
  287                 ipsflag = bus_read_4(sc->cfg, SIBA_CFG0_IPSFLAG);
  288                 ipsflag |= (ivec << SIBA_IPS_INT_SHIFT(mips_irq)) &
  289                     SIBA_IPS_INT_MASK(mips_irq);
  290                 bus_write_4(sc->cfg, SIBA_CFG0_IPSFLAG, ipsflag);
  291         }
  292 }
  293 
  294 /* our MIPS CPU interrupt filter */
  295 static int
  296 bcm_bmips_pic_intr(void *arg)
  297 {
  298         struct bcm_bmips_softc  *sc;
  299         struct bcm_mips_cpuirq  *cpuirq;
  300         struct bcm_mips_irqsrc  *isrc_solo;
  301         uint32_t                 sbintvec, sbstatus;
  302         u_int                    mips_irq, i;
  303         int                      error;
  304 
  305         cpuirq = arg;
  306         sc = (struct bcm_bmips_softc*)cpuirq->sc;
  307 
  308         /* Fetch current interrupt state */
  309         sbstatus = bus_read_4(sc->cfg, SIBA_CFG0_FLAGST);
  310 
  311         /* Fetch mask of interrupt vectors routed to this MIPS IRQ */
  312         mips_irq = cpuirq->mips_irq;
  313         if (mips_irq == 0) {
  314                 sbintvec = bus_read_4(sc->cfg, SIBA_CFG0_INTVEC);
  315         } else {
  316                 uint32_t ipsflag;
  317 
  318                 ipsflag = bus_read_4(sc->cfg, SIBA_CFG0_IPSFLAG);
  319 
  320                 /* Map to an intvec-compatible representation */
  321                 switch (mips_irq) {
  322                 case 1:
  323                         sbintvec = (ipsflag & SIBA_IPS_INT1_MASK) >>
  324                             SIBA_IPS_INT1_SHIFT;
  325                         break;
  326                 case 2:
  327                         sbintvec = (ipsflag & SIBA_IPS_INT2_MASK) >>
  328                             SIBA_IPS_INT2_SHIFT;
  329                         break;
  330                 case 3:
  331                         sbintvec = (ipsflag & SIBA_IPS_INT3_MASK) >>
  332                             SIBA_IPS_INT3_SHIFT;
  333                         break;
  334                 case 4: 
  335                         sbintvec = (ipsflag & SIBA_IPS_INT4_MASK) >>
  336                             SIBA_IPS_INT4_SHIFT;
  337                         break;
  338                 default:
  339                         panic("invalid irq %u", mips_irq);
  340                 }
  341         }
  342 
  343         /* Ignore interrupts not routed to this MIPS IRQ */
  344         sbstatus &= sbintvec;
  345 
  346         /* Handle isrc_solo direct dispatch path */
  347         isrc_solo = cpuirq->isrc_solo;
  348         if (isrc_solo != NULL) {
  349                 if (sbstatus & BCM_MIPS_IVEC_MASK(isrc_solo)) {
  350                         error = intr_isrc_dispatch(&isrc_solo->isrc,
  351                             curthread->td_intr_frame);
  352                         if (error) {
  353                                 device_printf(sc->dev, "Stray interrupt %u "
  354                                     "detected\n", isrc_solo->ivec);
  355                                 bcm_bmips_pic_disable_intr(sc->dev,
  356                                     &isrc_solo->isrc);
  357                         }
  358                 }
  359 
  360                 sbstatus &= ~(BCM_MIPS_IVEC_MASK(isrc_solo));
  361                 if (sbstatus == 0)
  362                         return (FILTER_HANDLED);
  363 
  364                 /* Report and mask additional stray interrupts */
  365                 while ((i = fls(sbstatus)) != 0) {
  366                         i--; /* Get a 0-offset interrupt. */
  367                         sbstatus &= ~(1 << i);
  368 
  369                         device_printf(sc->dev, "Stray interrupt %u "
  370                                 "detected\n", i);
  371                         bcm_bmips_mask_irq(sc, mips_irq, i);
  372                 }
  373 
  374                 return (FILTER_HANDLED);
  375         }
  376 
  377         /* Standard dispatch path  */
  378         while ((i = fls(sbstatus)) != 0) {
  379                 i--; /* Get a 0-offset interrupt. */
  380                 sbstatus &= ~(1 << i);
  381 
  382                 KASSERT(i < nitems(sc->bcm_mips.isrcs), ("invalid ivec %u", i));
  383 
  384                 error = intr_isrc_dispatch(&sc->bcm_mips.isrcs[i].isrc,
  385                     curthread->td_intr_frame);
  386                 if (error) {
  387                         device_printf(sc->dev, "Stray interrupt %u detected\n",
  388                             i);
  389                         bcm_bmips_mask_irq(sc, mips_irq, i);
  390                         continue;
  391                 }
  392         }
  393 
  394         return (FILTER_HANDLED);
  395 }
  396 
  397 static device_method_t bcm_bmips_methods[] = {
  398         /* Device interface */
  399         DEVMETHOD(device_probe,         bcm_bmips_probe),
  400         DEVMETHOD(device_attach,        bcm_bmips_attach),
  401         DEVMETHOD(device_detach,        bcm_bmips_detach),
  402 
  403         /* Interrupt controller interface */
  404         DEVMETHOD(pic_disable_intr,     bcm_bmips_pic_disable_intr),
  405         DEVMETHOD(pic_enable_intr,      bcm_bmips_pic_enable_intr),
  406         DEVMETHOD(pic_pre_ithread,      bcm_bmips_pic_pre_ithread),
  407         DEVMETHOD(pic_post_ithread,     bcm_bmips_pic_post_ithread),
  408         DEVMETHOD(pic_post_filter,      bcm_bmips_pic_post_filter),
  409 
  410         DEVMETHOD_END
  411 };
  412 
  413 static devclass_t bcm_mips_devclass;
  414 
  415 DEFINE_CLASS_1(bcm_mips, bcm_bmips_driver, bcm_bmips_methods, sizeof(struct bcm_bmips_softc), bcm_mips_driver);
  416 EARLY_DRIVER_MODULE(bcm_bmips, bhnd, bcm_bmips_driver, bcm_mips_devclass, 0, 0, BUS_PASS_INTERRUPT + BUS_PASS_ORDER_MIDDLE);
  417 
  418 MODULE_VERSION(bcm_bmips, 1);
  419 MODULE_DEPEND(bcm_bmips, bhnd, 1, 1, 1);

Cache object: bca82960068739b0ca7927705bb4d353


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