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/arm/gic.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) 2011 The FreeBSD Foundation
    3  * All rights reserved.
    4  *
    5  * Developed by Damjan Marion <damjan.marion@gmail.com>
    6  *
    7  * Based on OMAP4 GIC code by Ben Gray
    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  * 2. Redistributions in binary form must reproduce the above copyright
   15  *    notice, this list of conditions and the following disclaimer in the
   16  *    documentation and/or other materials provided with the distribution.
   17  * 3. The name of the company nor the name of the author may be used to
   18  *    endorse or promote products derived from this software without specific
   19  *    prior written permission.
   20  *
   21  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
   22  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
   23  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
   24  * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
   25  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
   26  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
   27  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
   28  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
   29  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
   30  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
   31  * SUCH DAMAGE.
   32  */
   33 
   34 #include <sys/cdefs.h>
   35 __FBSDID("$FreeBSD: releng/10.0/sys/arm/arm/gic.c 253896 2013-08-02 20:32:26Z cognet $");
   36 
   37 #include <sys/param.h>
   38 #include <sys/systm.h>
   39 #include <sys/bus.h>
   40 #include <sys/kernel.h>
   41 #include <sys/ktr.h>
   42 #include <sys/module.h>
   43 #include <sys/rman.h>
   44 #include <sys/pcpu.h>
   45 #include <sys/proc.h>
   46 #include <sys/cpuset.h>
   47 #include <sys/lock.h>
   48 #include <sys/mutex.h>
   49 #include <machine/bus.h>
   50 #include <machine/intr.h>
   51 #include <machine/smp.h>
   52 
   53 #include <dev/fdt/fdt_common.h>
   54 #include <dev/ofw/openfirm.h>
   55 #include <dev/ofw/ofw_bus.h>
   56 #include <dev/ofw/ofw_bus_subr.h>
   57 
   58 /* We are using GICv2 register naming */
   59 
   60 /* Distributor Registers */
   61 #define GICD_CTLR               0x000                   /* v1 ICDDCR */
   62 #define GICD_TYPER              0x004                   /* v1 ICDICTR */
   63 #define GICD_IIDR               0x008                   /* v1 ICDIIDR */
   64 #define GICD_IGROUPR(n)         (0x0080 + ((n) * 4))    /* v1 ICDISER */
   65 #define GICD_ISENABLER(n)       (0x0100 + ((n) * 4))    /* v1 ICDISER */
   66 #define GICD_ICENABLER(n)       (0x0180 + ((n) * 4))    /* v1 ICDICER */
   67 #define GICD_ISPENDR(n)         (0x0200 + ((n) * 4))    /* v1 ICDISPR */
   68 #define GICD_ICPENDR(n)         (0x0280 + ((n) * 4))    /* v1 ICDICPR */
   69 #define GICD_ICACTIVER(n)       (0x0380 + ((n) * 4))    /* v1 ICDABR */
   70 #define GICD_IPRIORITYR(n)      (0x0400 + ((n) * 4))    /* v1 ICDIPR */
   71 #define GICD_ITARGETSR(n)       (0x0800 + ((n) * 4))    /* v1 ICDIPTR */
   72 #define GICD_ICFGR(n)           (0x0C00 + ((n) * 4))    /* v1 ICDICFR */
   73 #define GICD_SGIR(n)            (0x0F00 + ((n) * 4))    /* v1 ICDSGIR */
   74 
   75 /* CPU Registers */
   76 #define GICC_CTLR               0x0000                  /* v1 ICCICR */
   77 #define GICC_PMR                0x0004                  /* v1 ICCPMR */
   78 #define GICC_BPR                0x0008                  /* v1 ICCBPR */
   79 #define GICC_IAR                0x000C                  /* v1 ICCIAR */
   80 #define GICC_EOIR               0x0010                  /* v1 ICCEOIR */
   81 #define GICC_RPR                0x0014                  /* v1 ICCRPR */
   82 #define GICC_HPPIR              0x0018                  /* v1 ICCHPIR */
   83 #define GICC_ABPR               0x001C                  /* v1 ICCABPR */
   84 #define GICC_IIDR               0x00FC                  /* v1 ICCIIDR*/
   85 
   86 struct arm_gic_softc {
   87         struct resource *       gic_res[3];
   88         bus_space_tag_t         gic_c_bst;
   89         bus_space_tag_t         gic_d_bst;
   90         bus_space_handle_t      gic_c_bsh;
   91         bus_space_handle_t      gic_d_bsh;
   92         uint8_t                 ver;
   93 };
   94 
   95 static struct resource_spec arm_gic_spec[] = {
   96         { SYS_RES_MEMORY,       0,      RF_ACTIVE },    /* Distributor registers */
   97         { SYS_RES_MEMORY,       1,      RF_ACTIVE },    /* CPU Interrupt Intf. registers */
   98         { -1, 0 }
   99 };
  100 
  101 static struct arm_gic_softc *arm_gic_sc = NULL;
  102 
  103 #define gic_c_read_4(reg)               \
  104     bus_space_read_4(arm_gic_sc->gic_c_bst, arm_gic_sc->gic_c_bsh, reg)
  105 #define gic_c_write_4(reg, val)         \
  106     bus_space_write_4(arm_gic_sc->gic_c_bst, arm_gic_sc->gic_c_bsh, reg, val)
  107 #define gic_d_read_4(reg)               \
  108     bus_space_read_4(arm_gic_sc->gic_d_bst, arm_gic_sc->gic_d_bsh, reg)
  109 #define gic_d_write_4(reg, val)         \
  110     bus_space_write_4(arm_gic_sc->gic_d_bst, arm_gic_sc->gic_d_bsh, reg, val)
  111 
  112 static void gic_post_filter(void *);
  113 
  114 static int
  115 arm_gic_probe(device_t dev)
  116 {
  117 
  118         if (!ofw_bus_is_compatible(dev, "arm,gic"))
  119                 return (ENXIO);
  120         device_set_desc(dev, "ARM Generic Interrupt Controller");
  121         return (BUS_PROBE_DEFAULT);
  122 }
  123 
  124 void
  125 gic_init_secondary(void)
  126 {
  127         int i, nirqs;
  128 
  129         /* Get the number of interrupts */
  130         nirqs = gic_d_read_4(GICD_TYPER);
  131         nirqs = 32 * ((nirqs & 0x1f) + 1);
  132 
  133         for (i = 0; i < nirqs; i += 4)
  134                 gic_d_write_4(GICD_IPRIORITYR(i >> 2), 0);
  135 
  136         /* Set all the interrupts to be in Group 0 (secure) */
  137         for (i = 0; i < nirqs; i += 32) {
  138                 gic_d_write_4(GICD_IGROUPR(i >> 5), 0);
  139         }
  140 
  141         /* Enable CPU interface */
  142         gic_c_write_4(GICC_CTLR, 1);
  143 
  144         /* Set priority mask register. */
  145         gic_c_write_4(GICC_PMR, 0xff);
  146 
  147         /* Enable interrupt distribution */
  148         gic_d_write_4(GICD_CTLR, 0x01);
  149 
  150         /* Activate IRQ 29, ie private timer IRQ*/
  151         gic_d_write_4(GICD_ISENABLER(29 >> 5), (1UL << (29 & 0x1F)));
  152 }
  153 
  154 static int
  155 arm_gic_attach(device_t dev)
  156 {
  157         struct          arm_gic_softc *sc;
  158         int             i;
  159         uint32_t        icciidr;
  160         uint32_t        nirqs;
  161 
  162         if (arm_gic_sc)
  163                 return (ENXIO);
  164 
  165         sc = device_get_softc(dev);
  166 
  167         if (bus_alloc_resources(dev, arm_gic_spec, sc->gic_res)) {
  168                 device_printf(dev, "could not allocate resources\n");
  169                 return (ENXIO);
  170         }
  171 
  172         arm_post_filter = gic_post_filter;
  173 
  174         /* Distributor Interface */
  175         sc->gic_d_bst = rman_get_bustag(sc->gic_res[0]);
  176         sc->gic_d_bsh = rman_get_bushandle(sc->gic_res[0]);
  177 
  178         /* CPU Interface */
  179         sc->gic_c_bst = rman_get_bustag(sc->gic_res[1]);
  180         sc->gic_c_bsh = rman_get_bushandle(sc->gic_res[1]);
  181 
  182         arm_gic_sc = sc;
  183 
  184         /* Disable interrupt forwarding to the CPU interface */
  185         gic_d_write_4(GICD_CTLR, 0x00);
  186 
  187         /* Get the number of interrupts */
  188         nirqs = gic_d_read_4(GICD_TYPER);
  189         nirqs = 32 * ((nirqs & 0x1f) + 1);
  190 
  191         icciidr = gic_c_read_4(GICC_IIDR);
  192         device_printf(dev,"pn 0x%x, arch 0x%x, rev 0x%x, implementer 0x%x nirqs %u\n",
  193                         icciidr>>20, (icciidr>>16) & 0xF, (icciidr>>12) & 0xf,
  194                         (icciidr & 0xfff), nirqs);
  195 
  196         /* Set all global interrupts to be level triggered, active low. */
  197         for (i = 32; i < nirqs; i += 32) {
  198                 gic_d_write_4(GICD_ICFGR(i >> 5), 0x00000000);
  199         }
  200 
  201         /* Disable all interrupts. */
  202         for (i = 32; i < nirqs; i += 32) {
  203                 gic_d_write_4(GICD_ICENABLER(i >> 5), 0xFFFFFFFF);
  204         }
  205 
  206         for (i = 0; i < nirqs; i += 4) {
  207                 gic_d_write_4(GICD_IPRIORITYR(i >> 2), 0);
  208                 gic_d_write_4(GICD_ITARGETSR(i >> 2), 1 << 0 | 1 << 8 | 1 << 16 | 1 << 24);
  209         }
  210 
  211         /* Set all the interrupts to be in Group 0 (secure) */
  212         for (i = 0; i < nirqs; i += 32) {
  213                 gic_d_write_4(GICD_IGROUPR(i >> 5), 0);
  214         }
  215 
  216         /* Enable CPU interface */
  217         gic_c_write_4(GICC_CTLR, 1);
  218 
  219         /* Set priority mask register. */
  220         gic_c_write_4(GICC_PMR, 0xff);
  221 
  222         /* Enable interrupt distribution */
  223         gic_d_write_4(GICD_CTLR, 0x01);
  224 
  225         return (0);
  226 }
  227 
  228 static device_method_t arm_gic_methods[] = {
  229         DEVMETHOD(device_probe,         arm_gic_probe),
  230         DEVMETHOD(device_attach,        arm_gic_attach),
  231         { 0, 0 }
  232 };
  233 
  234 static driver_t arm_gic_driver = {
  235         "gic",
  236         arm_gic_methods,
  237         sizeof(struct arm_gic_softc),
  238 };
  239 
  240 static devclass_t arm_gic_devclass;
  241 
  242 DRIVER_MODULE(gic, simplebus, arm_gic_driver, arm_gic_devclass, 0, 0);
  243 
  244 static void
  245 gic_post_filter(void *arg)
  246 {
  247         uintptr_t irq = (uintptr_t) arg;
  248 
  249         gic_c_write_4(GICC_EOIR, irq);
  250 }
  251 
  252 int
  253 arm_get_next_irq(int last_irq)
  254 {
  255         uint32_t active_irq;
  256 
  257         active_irq = gic_c_read_4(GICC_IAR);
  258 
  259         /*
  260          * Immediatly EOIR the SGIs, because doing so requires the other
  261          * bits (ie CPU number), not just the IRQ number, and we do not
  262          * have this information later.
  263          */
  264 
  265         if ((active_irq & 0x3ff) < 16)
  266                 gic_c_write_4(GICC_EOIR, active_irq);
  267         active_irq &= 0x3FF;
  268 
  269         if (active_irq == 0x3FF) {
  270                 if (last_irq == -1)
  271                         printf("Spurious interrupt detected [0x%08x]\n", active_irq);
  272                 return -1;
  273         }
  274         gic_c_write_4(GICC_EOIR, active_irq);
  275 
  276         return active_irq;
  277 }
  278 
  279 void
  280 arm_mask_irq(uintptr_t nb)
  281 {
  282         gic_d_write_4(GICD_ICENABLER(nb >> 5), (1UL << (nb & 0x1F)));
  283 }
  284 
  285 void
  286 arm_unmask_irq(uintptr_t nb)
  287 {
  288 
  289         gic_c_write_4(GICC_EOIR, nb);
  290         gic_d_write_4(GICD_ISENABLER(nb >> 5), (1UL << (nb & 0x1F)));
  291 }
  292 
  293 #ifdef SMP
  294 void
  295 pic_ipi_send(cpuset_t cpus, u_int ipi)
  296 {
  297         uint32_t val = 0, i;
  298 
  299         for (i = 0; i < MAXCPU; i++)
  300                 if (CPU_ISSET(i, &cpus))
  301                         val |= 1 << (16 + i);
  302         gic_d_write_4(GICD_SGIR(0), val | ipi);
  303 
  304 }
  305 
  306 int
  307 pic_ipi_get(int i)
  308 {
  309 
  310         if (i != -1) {
  311                 /*
  312                  * The intr code will automagically give the frame pointer
  313                  * if the interrupt argument is 0.
  314                  */
  315                 if ((unsigned int)i > 16)
  316                         return (0);
  317                 return (i);
  318         }
  319         return (0x3ff);
  320 }
  321 
  322 void
  323 pic_ipi_clear(int ipi)
  324 {
  325 }
  326 #endif
  327 

Cache object: 45c72d8aee4892d192cb87fc6c71665a


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