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/pl310.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) 2012 Olivier Houchard <cognet@FreeBSD.org>
    3  * Copyright (c) 2011
    4  *      Ben Gray <ben.r.gray@gmail.com>.
    5  * All rights reserved.
    6  *
    7  * Redistribution and use in source and binary forms, with or without
    8  * modification, are permitted provided that the following conditions
    9  * are met:
   10  * 1. Redistributions of source code must retain the above copyright
   11  *    notice, this list of conditions and the following disclaimer.
   12  * 2. Redistributions in binary form must reproduce the above copyright
   13  *    notice, this list of conditions and the following disclaimer in the
   14  *    documentation and/or other materials provided with the distribution.
   15  * 3. The name of the company nor the name of the author may be used to
   16  *    endorse or promote products derived from this software without specific
   17  *    prior written permission.
   18  *
   19  * THIS SOFTWARE IS PROVIDED BY BEN GRAY ``AS IS'' AND ANY EXPRESS OR
   20  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
   21  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
   22  * IN NO EVENT SHALL BEN GRAY BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
   23  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
   24  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
   25  * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
   26  * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
   27  * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
   28  * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
   29  */
   30 
   31 #include <sys/cdefs.h>
   32 __FBSDID("$FreeBSD: releng/10.0/sys/arm/arm/pl310.c 253788 2013-07-29 21:45:39Z cognet $");
   33 #include <sys/param.h>
   34 #include <sys/systm.h>
   35 #include <sys/bus.h>
   36 #include <sys/kernel.h>
   37 #include <sys/rman.h>
   38 #include <sys/module.h>
   39 #include <sys/lock.h>
   40 #include <sys/mutex.h>
   41 #include <machine/intr.h>
   42 
   43 #include <machine/bus.h>
   44 #include <machine/pl310.h>
   45 
   46 #include <dev/fdt/fdt_common.h>
   47 #include <dev/ofw/openfirm.h>
   48 #include <dev/ofw/ofw_bus.h>
   49 #include <dev/ofw/ofw_bus_subr.h>
   50 
   51 /*
   52  * Define this if you need to disable PL310 for debugging purpose
   53  * Spec: 
   54  * http://infocenter.arm.com/help/topic/com.arm.doc.ddi0246e/DDI0246E_l2c310_r3p1_trm.pdf
   55  */
   56 
   57 /* 
   58  * Hardcode errata for now
   59  * http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.ddi0246b/pr01s02s02.html
   60  */
   61 #define PL310_ERRATA_588369
   62 #define PL310_ERRATA_753970
   63 #define PL310_ERRATA_727915
   64 
   65 #define PL310_LOCK(sc) do {             \
   66         mtx_lock_spin(&(sc)->sc_mtx);   \
   67 } while(0);
   68 
   69 #define PL310_UNLOCK(sc) do {           \
   70         mtx_unlock_spin(&(sc)->sc_mtx); \
   71 } while(0);
   72 
   73 static int pl310_enabled = 1;
   74 TUNABLE_INT("pl310.enabled", &pl310_enabled);
   75 
   76 static uint32_t g_l2cache_way_mask;
   77 
   78 static const uint32_t g_l2cache_line_size = 32;
   79 static const uint32_t g_l2cache_align_mask = (32 - 1);
   80 
   81 static uint32_t g_l2cache_size;
   82 static uint32_t g_way_size;
   83 static uint32_t g_ways_assoc;
   84 
   85 static struct pl310_softc *pl310_softc;
   86 
   87 static int
   88 pl310_filter(void *arg)
   89 {
   90         struct pl310_softc *sc = arg;
   91         uint32_t intr;
   92 
   93         intr = pl310_read4(sc, PL310_INTR_MASK);
   94 
   95         if (!sc->sc_enabled && (intr & INTR_MASK_ECNTR)) {
   96                 /*
   97                  * This is for debug purpose, so be blunt about it
   98                  * We disable PL310 only when something fishy is going
   99                  * on and we need to make sure L2 cache is 100% disabled
  100                  */
  101                 panic("pl310: caches disabled but cache event detected\n");
  102         }
  103 
  104         return (FILTER_HANDLED);
  105 }
  106 
  107 static __inline void
  108 pl310_wait_background_op(uint32_t off, uint32_t mask)
  109 {
  110 
  111         while (pl310_read4(pl310_softc, off) & mask);
  112 }
  113 
  114 
  115 /**
  116  *      pl310_cache_sync - performs a cache sync operation
  117  * 
  118  *      According to the TRM:
  119  *
  120  *  "Before writing to any other register you must perform an explicit
  121  *   Cache Sync operation. This is particularly important when the cache is
  122  *   enabled and changes to how the cache allocates new lines are to be made."
  123  *
  124  *
  125  */
  126 static __inline void
  127 pl310_cache_sync(void)
  128 {
  129         if ((pl310_softc == NULL) || !pl310_softc->sc_enabled)
  130                 return;
  131 
  132 #ifdef PL310_ERRATA_753970
  133         if (pl310_softc->sc_rtl_revision == CACHE_ID_RELEASE_r3p0)
  134                 /* Write uncached PL310 register */
  135                 pl310_write4(pl310_softc, 0x740, 0xffffffff);
  136         else
  137 #endif
  138                 pl310_write4(pl310_softc, PL310_CACHE_SYNC, 0xffffffff);
  139 }
  140 
  141 
  142 static void
  143 pl310_wbinv_all(void)
  144 {
  145 
  146         if ((pl310_softc == NULL) || !pl310_softc->sc_enabled)
  147                 return;
  148 
  149         PL310_LOCK(pl310_softc);
  150 #ifdef PL310_ERRATA_727915
  151         if (pl310_softc->sc_rtl_revision == CACHE_ID_RELEASE_r2p0) {
  152                 int i, j;
  153 
  154                 for (i = 0; i < g_ways_assoc; i++) {
  155                         for (j = 0; j < g_way_size / g_l2cache_line_size; j++) {
  156                                 pl310_write4(pl310_softc, 
  157                                     PL310_CLEAN_INV_LINE_IDX,
  158                                     (i << 28 | j << 5));
  159                         }
  160                 }
  161                 pl310_cache_sync();
  162                 PL310_UNLOCK(pl310_softc);
  163                 return;
  164 
  165         }
  166         if (pl310_softc->sc_rtl_revision == CACHE_ID_RELEASE_r3p0)
  167                 platform_pl310_write_debug(pl310_softc, 3);
  168 #endif
  169         pl310_write4(pl310_softc, PL310_CLEAN_INV_WAY, g_l2cache_way_mask);
  170         pl310_wait_background_op(PL310_CLEAN_INV_WAY, g_l2cache_way_mask);
  171         pl310_cache_sync();
  172 #ifdef PL310_ERRATA_727915
  173         if (pl310_softc->sc_rtl_revision == CACHE_ID_RELEASE_r3p0)
  174                 platform_pl310_write_debug(pl310_softc, 0);
  175 #endif
  176         PL310_UNLOCK(pl310_softc);
  177 }
  178 
  179 static void
  180 pl310_wbinv_range(vm_paddr_t start, vm_size_t size)
  181 {
  182 
  183         if ((pl310_softc == NULL) || !pl310_softc->sc_enabled)
  184                 return;
  185 
  186         PL310_LOCK(pl310_softc);
  187         if (start & g_l2cache_align_mask) {
  188                 size += start & g_l2cache_align_mask;
  189                 start &= ~g_l2cache_align_mask;
  190         }
  191         if (size & g_l2cache_align_mask) {
  192                 size &= ~g_l2cache_align_mask;
  193                 size += g_l2cache_line_size;
  194         }
  195 
  196 
  197 #ifdef PL310_ERRATA_727915
  198         platform_pl310_write_debug(pl310_softc, 3);
  199 #endif
  200         while (size > 0) {
  201 #ifdef PL310_ERRATA_588369
  202                 if (pl310_softc->sc_rtl_revision <= CACHE_ID_RELEASE_r1p0) {
  203                         /* 
  204                          * Errata 588369 says that clean + inv may keep the 
  205                          * cache line if it was clean, the recommanded
  206                          * workaround is to clean then invalidate the cache
  207                          * line, with write-back and cache linefill disabled.
  208                          */
  209                         pl310_write4(pl310_softc, PL310_CLEAN_LINE_PA, start);
  210                         pl310_write4(pl310_softc, PL310_INV_LINE_PA, start);
  211                 } else
  212 #endif
  213                         pl310_write4(pl310_softc, PL310_CLEAN_INV_LINE_PA,
  214                             start);
  215                 start += g_l2cache_line_size;
  216                 size -= g_l2cache_line_size;
  217         }
  218 #ifdef PL310_ERRATA_727915
  219         platform_pl310_write_debug(pl310_softc, 0);
  220 #endif
  221 
  222         pl310_cache_sync();
  223         PL310_UNLOCK(pl310_softc);
  224 }
  225 
  226 static void
  227 pl310_wb_range(vm_paddr_t start, vm_size_t size)
  228 {
  229 
  230         if ((pl310_softc == NULL) || !pl310_softc->sc_enabled)
  231                 return;
  232 
  233         PL310_LOCK(pl310_softc);
  234         if (start & g_l2cache_align_mask) {
  235                 size += start & g_l2cache_align_mask;
  236                 start &= ~g_l2cache_align_mask;
  237         }
  238 
  239         if (size & g_l2cache_align_mask) {
  240                 size &= ~g_l2cache_align_mask;
  241                 size += g_l2cache_line_size;
  242         }
  243 
  244         while (size > 0) {
  245                 pl310_write4(pl310_softc, PL310_CLEAN_LINE_PA, start);
  246                 start += g_l2cache_line_size;
  247                 size -= g_l2cache_line_size;
  248         }
  249 
  250         pl310_cache_sync();
  251         PL310_UNLOCK(pl310_softc);
  252 }
  253 
  254 static void
  255 pl310_inv_range(vm_paddr_t start, vm_size_t size)
  256 {
  257 
  258         if ((pl310_softc == NULL) || !pl310_softc->sc_enabled)
  259                 return;
  260 
  261         PL310_LOCK(pl310_softc);
  262         if (start & g_l2cache_align_mask) {
  263                 size += start & g_l2cache_align_mask;
  264                 start &= ~g_l2cache_align_mask;
  265         }
  266         if (size & g_l2cache_align_mask) {
  267                 size &= ~g_l2cache_align_mask;
  268                 size += g_l2cache_line_size;
  269         }
  270         while (size > 0) {
  271                 pl310_write4(pl310_softc, PL310_INV_LINE_PA, start);
  272                 start += g_l2cache_line_size;
  273                 size -= g_l2cache_line_size;
  274         }
  275 
  276         pl310_cache_sync();
  277         PL310_UNLOCK(pl310_softc);
  278 }
  279 
  280 static int
  281 pl310_probe(device_t dev)
  282 {
  283         
  284         if (!ofw_bus_is_compatible(dev, "arm,pl310"))
  285                 return (ENXIO);
  286         device_set_desc(dev, "PL310 L2 cache controller");
  287         return (0);
  288 }
  289 
  290 static int
  291 pl310_attach(device_t dev)
  292 {
  293         struct pl310_softc *sc = device_get_softc(dev);
  294         int rid = 0;
  295         uint32_t aux_value;
  296         uint32_t ctrl_value;
  297         uint32_t cache_id;
  298 
  299         sc->sc_dev = dev;
  300         sc->sc_mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, 
  301             RF_ACTIVE);
  302         if (sc->sc_mem_res == NULL)
  303                 panic("%s: Cannot map registers", device_get_name(dev));
  304 
  305         /* Allocate an IRQ resource */
  306         rid = 0;
  307         sc->sc_irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid,
  308                                                 RF_ACTIVE | RF_SHAREABLE);
  309         if (sc->sc_irq_res == NULL) {
  310                 panic("Cannot allocate IRQ\n");
  311         }
  312 
  313         pl310_softc = sc;
  314         mtx_init(&sc->sc_mtx, "pl310lock", NULL, MTX_SPIN);
  315         sc->sc_enabled = pl310_enabled;
  316 
  317         /* activate the interrupt */
  318         bus_setup_intr(dev, sc->sc_irq_res, INTR_TYPE_MISC | INTR_MPSAFE,
  319                                 pl310_filter, NULL, sc, &sc->sc_irq_h);
  320 
  321         cache_id = pl310_read4(sc, PL310_CACHE_ID);
  322         sc->sc_rtl_revision = (cache_id >> CACHE_ID_RELEASE_SHIFT) &
  323             CACHE_ID_RELEASE_MASK;
  324         device_printf(dev, "Part number: 0x%x, release: 0x%x\n",
  325             (cache_id >> CACHE_ID_PARTNUM_SHIFT) & CACHE_ID_PARTNUM_MASK,
  326             (cache_id >> CACHE_ID_RELEASE_SHIFT) & CACHE_ID_RELEASE_MASK);
  327         aux_value = pl310_read4(sc, PL310_AUX_CTRL);
  328         g_way_size = (aux_value & AUX_CTRL_WAY_SIZE_MASK) >>
  329             AUX_CTRL_WAY_SIZE_SHIFT;
  330         g_way_size = 1 << (g_way_size + 13);
  331         if (aux_value & (1 << AUX_CTRL_ASSOCIATIVITY_SHIFT))
  332                 g_ways_assoc = 16;
  333         else
  334                 g_ways_assoc = 8;
  335         g_l2cache_way_mask = (1 << g_ways_assoc) - 1;
  336         g_l2cache_size = g_way_size * g_ways_assoc;
  337         /* Print the information */
  338         device_printf(dev, "L2 Cache: %uKB/%dB %d ways\n", (g_l2cache_size / 1024),
  339                g_l2cache_line_size, g_ways_assoc);
  340 
  341         ctrl_value = pl310_read4(sc, PL310_CTRL);
  342 
  343         if (sc->sc_enabled && !(ctrl_value & CTRL_ENABLED)) {
  344                 /* Enable the L2 cache if disabled */
  345                 platform_pl310_write_ctrl(sc, CTRL_ENABLED);
  346         } 
  347 
  348         if (!sc->sc_enabled && (ctrl_value & CTRL_ENABLED)) {
  349                 /*
  350                  * Set counters so when cache event happens
  351                  * we'll get interrupt and be warned that something 
  352                  * is off
  353                  */
  354 
  355                 /* Cache Line Eviction for Counter 0 */
  356                 pl310_write4(sc, PL310_EVENT_COUNTER0_CONF, 
  357                     EVENT_COUNTER_CONF_INCR | EVENT_COUNTER_CONF_CO);
  358                 /* Data Read Request for Counter 1 */
  359                 pl310_write4(sc, PL310_EVENT_COUNTER1_CONF, 
  360                     EVENT_COUNTER_CONF_INCR | EVENT_COUNTER_CONF_DRREQ);
  361 
  362                 /* Temporary switch on for final flush*/
  363                 sc->sc_enabled = 1;
  364                 pl310_wbinv_all();
  365                 sc->sc_enabled = 0;
  366                 platform_pl310_write_ctrl(sc, CTRL_DISABLED);
  367 
  368                 /* Enable and clear pending interrupts */
  369                 pl310_write4(sc, PL310_INTR_CLEAR, INTR_MASK_ECNTR);
  370                 pl310_write4(sc, PL310_INTR_MASK, INTR_MASK_ALL);
  371 
  372                 /* Enable counters and reset C0 and C1 */
  373                 pl310_write4(sc, PL310_EVENT_COUNTER_CTRL, 
  374                     EVENT_COUNTER_CTRL_ENABLED | 
  375                     EVENT_COUNTER_CTRL_C0_RESET | 
  376                     EVENT_COUNTER_CTRL_C1_RESET);
  377 
  378         }
  379 
  380         if (sc->sc_enabled)
  381                 platform_pl310_init(sc);
  382 
  383         pl310_wbinv_all();
  384 
  385         /* Set the l2 functions in the set of cpufuncs */
  386         cpufuncs.cf_l2cache_wbinv_all = pl310_wbinv_all;
  387         cpufuncs.cf_l2cache_wbinv_range = pl310_wbinv_range;
  388         cpufuncs.cf_l2cache_inv_range = pl310_inv_range;
  389         cpufuncs.cf_l2cache_wb_range = pl310_wb_range;
  390 
  391         return (0);
  392 }
  393 
  394 static device_method_t pl310_methods[] = {
  395         DEVMETHOD(device_probe, pl310_probe),
  396         DEVMETHOD(device_attach, pl310_attach),
  397         {0, 0},
  398 };
  399 
  400 static driver_t pl310_driver = {
  401         "l2cache",
  402         pl310_methods,
  403         sizeof(struct pl310_softc),
  404 };
  405 static devclass_t pl310_devclass;
  406 
  407 DRIVER_MODULE(pl310, simplebus, pl310_driver, pl310_devclass, 0, 0);

Cache object: 8b51f4644a4a38dd2fd71ed5c2f1bee0


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