1239268Sgonzo/*- 2239268Sgonzo * Copyright (c) 2012 Olivier Houchard <cognet@FreeBSD.org> 3239268Sgonzo * Copyright (c) 2011 4239268Sgonzo * Ben Gray <ben.r.gray@gmail.com>. 5239268Sgonzo * All rights reserved. 6239268Sgonzo * 7239268Sgonzo * Redistribution and use in source and binary forms, with or without 8239268Sgonzo * modification, are permitted provided that the following conditions 9239268Sgonzo * are met: 10239268Sgonzo * 1. Redistributions of source code must retain the above copyright 11239268Sgonzo * notice, this list of conditions and the following disclaimer. 12239268Sgonzo * 2. Redistributions in binary form must reproduce the above copyright 13239268Sgonzo * notice, this list of conditions and the following disclaimer in the 14239268Sgonzo * documentation and/or other materials provided with the distribution. 15239268Sgonzo * 3. The name of the company nor the name of the author may be used to 16239268Sgonzo * endorse or promote products derived from this software without specific 17239268Sgonzo * prior written permission. 18239268Sgonzo * 19239268Sgonzo * THIS SOFTWARE IS PROVIDED BY BEN GRAY ``AS IS'' AND ANY EXPRESS OR 20239268Sgonzo * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 21239268Sgonzo * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 22239268Sgonzo * IN NO EVENT SHALL BEN GRAY BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 23239268Sgonzo * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 24239268Sgonzo * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; 25239268Sgonzo * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 26239268Sgonzo * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR 27239268Sgonzo * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF 28239268Sgonzo * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 29239268Sgonzo */ 30239268Sgonzo 31239268Sgonzo#include <sys/cdefs.h> 32239268Sgonzo__FBSDID("$FreeBSD$"); 33239268Sgonzo#include <sys/param.h> 34239268Sgonzo#include <sys/systm.h> 35239268Sgonzo#include <sys/bus.h> 36239268Sgonzo#include <sys/kernel.h> 37239268Sgonzo#include <sys/rman.h> 38239268Sgonzo#include <sys/module.h> 39239268Sgonzo#include <sys/lock.h> 40239268Sgonzo#include <sys/mutex.h> 41239268Sgonzo#include <machine/intr.h> 42244914Sgonzo 43244914Sgonzo#include <machine/bus.h> 44239268Sgonzo#include <machine/pl310.h> 45239268Sgonzo 46239268Sgonzo#include <dev/fdt/fdt_common.h> 47239268Sgonzo#include <dev/ofw/openfirm.h> 48239268Sgonzo#include <dev/ofw/ofw_bus.h> 49239268Sgonzo#include <dev/ofw/ofw_bus_subr.h> 50239268Sgonzo 51244914Sgonzo/* 52244914Sgonzo * Define this if you need to disable PL310 for debugging purpose 53283366Sandrew * Spec: 54244914Sgonzo * http://infocenter.arm.com/help/topic/com.arm.doc.ddi0246e/DDI0246E_l2c310_r3p1_trm.pdf 55239268Sgonzo */ 56239268Sgonzo 57283366Sandrew/* 58244914Sgonzo * Hardcode errata for now 59244914Sgonzo * http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.ddi0246b/pr01s02s02.html 60244914Sgonzo */ 61244914Sgonzo#define PL310_ERRATA_588369 62244914Sgonzo#define PL310_ERRATA_753970 63244914Sgonzo#define PL310_ERRATA_727915 64239268Sgonzo 65244914Sgonzo#define PL310_LOCK(sc) do { \ 66244914Sgonzo mtx_lock_spin(&(sc)->sc_mtx); \ 67244914Sgonzo} while(0); 68239268Sgonzo 69244914Sgonzo#define PL310_UNLOCK(sc) do { \ 70244914Sgonzo mtx_unlock_spin(&(sc)->sc_mtx); \ 71244914Sgonzo} while(0); 72239268Sgonzo 73244914Sgonzostatic int pl310_enabled = 1; 74265440SianTUNABLE_INT("hw.pl310.enabled", &pl310_enabled); 75244914Sgonzo 76239268Sgonzostatic uint32_t g_l2cache_way_mask; 77239268Sgonzo 78239268Sgonzostatic const uint32_t g_l2cache_line_size = 32; 79239268Sgonzostatic const uint32_t g_l2cache_align_mask = (32 - 1); 80239268Sgonzo 81239268Sgonzostatic uint32_t g_l2cache_size; 82253788Scognetstatic uint32_t g_way_size; 83253788Scognetstatic uint32_t g_ways_assoc; 84239268Sgonzo 85239268Sgonzostatic struct pl310_softc *pl310_softc; 86239268Sgonzo 87273590Sianstatic struct ofw_compat_data compat_data[] = { 88273590Sian {"arm,pl310", true}, /* Non-standard, FreeBSD. */ 89273590Sian {"arm,pl310-cache", true}, 90273590Sian {NULL, false} 91273590Sian}; 92273590Sian 93290974Sandrewstatic void 94265035Sianpl310_print_config(struct pl310_softc *sc) 95265035Sian{ 96265035Sian uint32_t aux, prefetch; 97265035Sian const char *dis = "disabled"; 98265035Sian const char *ena = "enabled"; 99265035Sian 100265035Sian aux = pl310_read4(sc, PL310_AUX_CTRL); 101265035Sian prefetch = pl310_read4(sc, PL310_PREFETCH_CTRL); 102265035Sian 103265035Sian device_printf(sc->sc_dev, "Early BRESP response: %s\n", 104265035Sian (aux & AUX_CTRL_EARLY_BRESP) ? ena : dis); 105265035Sian device_printf(sc->sc_dev, "Instruction prefetch: %s\n", 106265035Sian (aux & AUX_CTRL_INSTR_PREFETCH) ? ena : dis); 107265035Sian device_printf(sc->sc_dev, "Data prefetch: %s\n", 108265035Sian (aux & AUX_CTRL_DATA_PREFETCH) ? ena : dis); 109265035Sian device_printf(sc->sc_dev, "Non-secure interrupt control: %s\n", 110265035Sian (aux & AUX_CTRL_NS_INT_CTRL) ? ena : dis); 111265035Sian device_printf(sc->sc_dev, "Non-secure lockdown: %s\n", 112265035Sian (aux & AUX_CTRL_NS_LOCKDOWN) ? ena : dis); 113265035Sian device_printf(sc->sc_dev, "Share override: %s\n", 114265035Sian (aux & AUX_CTRL_SHARE_OVERRIDE) ? ena : dis); 115265035Sian 116265035Sian device_printf(sc->sc_dev, "Double linefill: %s\n", 117265035Sian (prefetch & PREFETCH_CTRL_DL) ? ena : dis); 118265035Sian device_printf(sc->sc_dev, "Instruction prefetch: %s\n", 119265035Sian (prefetch & PREFETCH_CTRL_INSTR_PREFETCH) ? ena : dis); 120265035Sian device_printf(sc->sc_dev, "Data prefetch: %s\n", 121265035Sian (prefetch & PREFETCH_CTRL_DATA_PREFETCH) ? ena : dis); 122265035Sian device_printf(sc->sc_dev, "Double linefill on WRAP request: %s\n", 123265035Sian (prefetch & PREFETCH_CTRL_DL_ON_WRAP) ? ena : dis); 124265035Sian device_printf(sc->sc_dev, "Prefetch drop: %s\n", 125265035Sian (prefetch & PREFETCH_CTRL_PREFETCH_DROP) ? ena : dis); 126265035Sian device_printf(sc->sc_dev, "Incr double Linefill: %s\n", 127265035Sian (prefetch & PREFETCH_CTRL_INCR_DL) ? ena : dis); 128265035Sian device_printf(sc->sc_dev, "Not same ID on exclusive sequence: %s\n", 129265035Sian (prefetch & PREFETCH_CTRL_NOTSAMEID) ? ena : dis); 130265035Sian device_printf(sc->sc_dev, "Prefetch offset: %d\n", 131265035Sian (prefetch & PREFETCH_CTRL_OFFSET_MASK)); 132265035Sian} 133265035Sian 134265446Sianvoid 135265446Sianpl310_set_ram_latency(struct pl310_softc *sc, uint32_t which_reg, 136265446Sian uint32_t read, uint32_t write, uint32_t setup) 137265446Sian{ 138265446Sian uint32_t v; 139265446Sian 140283366Sandrew KASSERT(which_reg == PL310_TAG_RAM_CTRL || 141265446Sian which_reg == PL310_DATA_RAM_CTRL, 142265446Sian ("bad pl310 ram latency register address")); 143265446Sian 144265446Sian v = pl310_read4(sc, which_reg); 145265446Sian if (setup != 0) { 146265446Sian KASSERT(setup <= 8, ("bad pl310 setup latency: %d", setup)); 147265446Sian v &= ~RAM_CTRL_SETUP_MASK; 148265446Sian v |= (setup - 1) << RAM_CTRL_SETUP_SHIFT; 149265446Sian } 150265446Sian if (read != 0) { 151265446Sian KASSERT(read <= 8, ("bad pl310 read latency: %d", read)); 152265446Sian v &= ~RAM_CTRL_READ_MASK; 153265446Sian v |= (read - 1) << RAM_CTRL_READ_SHIFT; 154265446Sian } 155265446Sian if (write != 0) { 156265446Sian KASSERT(write <= 8, ("bad pl310 write latency: %d", write)); 157265446Sian v &= ~RAM_CTRL_WRITE_MASK; 158265446Sian v |= (write - 1) << RAM_CTRL_WRITE_SHIFT; 159265446Sian } 160265446Sian pl310_write4(sc, which_reg, v); 161265446Sian} 162265446Sian 163244914Sgonzostatic int 164244914Sgonzopl310_filter(void *arg) 165239268Sgonzo{ 166244914Sgonzo struct pl310_softc *sc = arg; 167244914Sgonzo uint32_t intr; 168244914Sgonzo 169244914Sgonzo intr = pl310_read4(sc, PL310_INTR_MASK); 170244914Sgonzo 171244914Sgonzo if (!sc->sc_enabled && (intr & INTR_MASK_ECNTR)) { 172244914Sgonzo /* 173244914Sgonzo * This is for debug purpose, so be blunt about it 174244914Sgonzo * We disable PL310 only when something fishy is going 175244914Sgonzo * on and we need to make sure L2 cache is 100% disabled 176244914Sgonzo */ 177244914Sgonzo panic("pl310: caches disabled but cache event detected\n"); 178244914Sgonzo } 179244914Sgonzo 180244914Sgonzo return (FILTER_HANDLED); 181239268Sgonzo} 182239268Sgonzo 183239268Sgonzostatic __inline void 184239268Sgonzopl310_wait_background_op(uint32_t off, uint32_t mask) 185239268Sgonzo{ 186244914Sgonzo 187265440Sian while (pl310_read4(pl310_softc, off) & mask) 188265440Sian continue; 189239268Sgonzo} 190239268Sgonzo 191239268Sgonzo 192239268Sgonzo/** 193239268Sgonzo * pl310_cache_sync - performs a cache sync operation 194283366Sandrew * 195239268Sgonzo * According to the TRM: 196239268Sgonzo * 197239268Sgonzo * "Before writing to any other register you must perform an explicit 198239268Sgonzo * Cache Sync operation. This is particularly important when the cache is 199239268Sgonzo * enabled and changes to how the cache allocates new lines are to be made." 200239268Sgonzo * 201239268Sgonzo * 202239268Sgonzo */ 203239268Sgonzostatic __inline void 204239268Sgonzopl310_cache_sync(void) 205239268Sgonzo{ 206265440Sian 207244914Sgonzo if ((pl310_softc == NULL) || !pl310_softc->sc_enabled) 208244914Sgonzo return; 209244914Sgonzo 210244914Sgonzo#ifdef PL310_ERRATA_753970 211245087Sandrew if (pl310_softc->sc_rtl_revision == CACHE_ID_RELEASE_r3p0) 212245083Sandrew /* Write uncached PL310 register */ 213245083Sandrew pl310_write4(pl310_softc, 0x740, 0xffffffff); 214245083Sandrew else 215244914Sgonzo#endif 216245083Sandrew pl310_write4(pl310_softc, PL310_CACHE_SYNC, 0xffffffff); 217239268Sgonzo} 218239268Sgonzo 219239268Sgonzo 220239268Sgonzostatic void 221239268Sgonzopl310_wbinv_all(void) 222239268Sgonzo{ 223244914Sgonzo 224244914Sgonzo if ((pl310_softc == NULL) || !pl310_softc->sc_enabled) 225244914Sgonzo return; 226244914Sgonzo 227244914Sgonzo PL310_LOCK(pl310_softc); 228244914Sgonzo#ifdef PL310_ERRATA_727915 229253788Scognet if (pl310_softc->sc_rtl_revision == CACHE_ID_RELEASE_r2p0) { 230253788Scognet int i, j; 231253788Scognet 232253788Scognet for (i = 0; i < g_ways_assoc; i++) { 233253788Scognet for (j = 0; j < g_way_size / g_l2cache_line_size; j++) { 234283366Sandrew pl310_write4(pl310_softc, 235253788Scognet PL310_CLEAN_INV_LINE_IDX, 236253788Scognet (i << 28 | j << 5)); 237253788Scognet } 238253788Scognet } 239253788Scognet pl310_cache_sync(); 240253788Scognet PL310_UNLOCK(pl310_softc); 241253788Scognet return; 242253788Scognet 243253788Scognet } 244253788Scognet if (pl310_softc->sc_rtl_revision == CACHE_ID_RELEASE_r3p0) 245245083Sandrew platform_pl310_write_debug(pl310_softc, 3); 246239268Sgonzo#endif 247244914Sgonzo pl310_write4(pl310_softc, PL310_CLEAN_INV_WAY, g_l2cache_way_mask); 248239268Sgonzo pl310_wait_background_op(PL310_CLEAN_INV_WAY, g_l2cache_way_mask); 249239268Sgonzo pl310_cache_sync(); 250244914Sgonzo#ifdef PL310_ERRATA_727915 251253788Scognet if (pl310_softc->sc_rtl_revision == CACHE_ID_RELEASE_r3p0) 252245083Sandrew platform_pl310_write_debug(pl310_softc, 0); 253239268Sgonzo#endif 254244914Sgonzo PL310_UNLOCK(pl310_softc); 255239268Sgonzo} 256239268Sgonzo 257239268Sgonzostatic void 258239268Sgonzopl310_wbinv_range(vm_paddr_t start, vm_size_t size) 259239268Sgonzo{ 260244914Sgonzo 261244914Sgonzo if ((pl310_softc == NULL) || !pl310_softc->sc_enabled) 262244914Sgonzo return; 263244914Sgonzo 264244914Sgonzo PL310_LOCK(pl310_softc); 265243359Scognet if (start & g_l2cache_align_mask) { 266243359Scognet size += start & g_l2cache_align_mask; 267243359Scognet start &= ~g_l2cache_align_mask; 268243359Scognet } 269239268Sgonzo if (size & g_l2cache_align_mask) { 270239268Sgonzo size &= ~g_l2cache_align_mask; 271243359Scognet size += g_l2cache_line_size; 272239268Sgonzo } 273239268Sgonzo 274244914Sgonzo 275244914Sgonzo#ifdef PL310_ERRATA_727915 276245120Sgonzo platform_pl310_write_debug(pl310_softc, 3); 277239268Sgonzo#endif 278239268Sgonzo while (size > 0) { 279244914Sgonzo#ifdef PL310_ERRATA_588369 280245087Sandrew if (pl310_softc->sc_rtl_revision <= CACHE_ID_RELEASE_r1p0) { 281283366Sandrew /* 282283366Sandrew * Errata 588369 says that clean + inv may keep the 283245083Sandrew * cache line if it was clean, the recommanded 284245083Sandrew * workaround is to clean then invalidate the cache 285245083Sandrew * line, with write-back and cache linefill disabled. 286245083Sandrew */ 287245083Sandrew pl310_write4(pl310_softc, PL310_CLEAN_LINE_PA, start); 288245083Sandrew pl310_write4(pl310_softc, PL310_INV_LINE_PA, start); 289245083Sandrew } else 290239268Sgonzo#endif 291245083Sandrew pl310_write4(pl310_softc, PL310_CLEAN_INV_LINE_PA, 292245083Sandrew start); 293239268Sgonzo start += g_l2cache_line_size; 294239268Sgonzo size -= g_l2cache_line_size; 295239268Sgonzo } 296244914Sgonzo#ifdef PL310_ERRATA_727915 297245120Sgonzo platform_pl310_write_debug(pl310_softc, 0); 298239268Sgonzo#endif 299244914Sgonzo 300239268Sgonzo pl310_cache_sync(); 301244914Sgonzo PL310_UNLOCK(pl310_softc); 302239268Sgonzo} 303239268Sgonzo 304239268Sgonzostatic void 305239268Sgonzopl310_wb_range(vm_paddr_t start, vm_size_t size) 306239268Sgonzo{ 307244914Sgonzo 308244914Sgonzo if ((pl310_softc == NULL) || !pl310_softc->sc_enabled) 309244914Sgonzo return; 310244914Sgonzo 311244914Sgonzo PL310_LOCK(pl310_softc); 312243359Scognet if (start & g_l2cache_align_mask) { 313243359Scognet size += start & g_l2cache_align_mask; 314243359Scognet start &= ~g_l2cache_align_mask; 315243359Scognet } 316244914Sgonzo 317239268Sgonzo if (size & g_l2cache_align_mask) { 318239268Sgonzo size &= ~g_l2cache_align_mask; 319239268Sgonzo size += g_l2cache_line_size; 320239268Sgonzo } 321244914Sgonzo 322239268Sgonzo while (size > 0) { 323244914Sgonzo pl310_write4(pl310_softc, PL310_CLEAN_LINE_PA, start); 324239268Sgonzo start += g_l2cache_line_size; 325239268Sgonzo size -= g_l2cache_line_size; 326239268Sgonzo } 327244914Sgonzo 328239268Sgonzo pl310_cache_sync(); 329244914Sgonzo PL310_UNLOCK(pl310_softc); 330239268Sgonzo} 331239268Sgonzo 332239268Sgonzostatic void 333239268Sgonzopl310_inv_range(vm_paddr_t start, vm_size_t size) 334239268Sgonzo{ 335239268Sgonzo 336244914Sgonzo if ((pl310_softc == NULL) || !pl310_softc->sc_enabled) 337244914Sgonzo return; 338244914Sgonzo 339244914Sgonzo PL310_LOCK(pl310_softc); 340243359Scognet if (start & g_l2cache_align_mask) { 341243359Scognet size += start & g_l2cache_align_mask; 342243359Scognet start &= ~g_l2cache_align_mask; 343243359Scognet } 344239268Sgonzo if (size & g_l2cache_align_mask) { 345239268Sgonzo size &= ~g_l2cache_align_mask; 346239268Sgonzo size += g_l2cache_line_size; 347239268Sgonzo } 348239268Sgonzo while (size > 0) { 349244914Sgonzo pl310_write4(pl310_softc, PL310_INV_LINE_PA, start); 350239268Sgonzo start += g_l2cache_line_size; 351239268Sgonzo size -= g_l2cache_line_size; 352239268Sgonzo } 353244914Sgonzo 354239268Sgonzo pl310_cache_sync(); 355244914Sgonzo PL310_UNLOCK(pl310_softc); 356239268Sgonzo} 357239268Sgonzo 358265441Sianstatic void 359265870Sianpl310_drain_writebuf(void) 360265870Sian{ 361265870Sian 362265870Sian if ((pl310_softc == NULL) || !pl310_softc->sc_enabled) 363265870Sian return; 364265870Sian 365265870Sian PL310_LOCK(pl310_softc); 366265870Sian pl310_cache_sync(); 367265870Sian PL310_UNLOCK(pl310_softc); 368265870Sian} 369265870Sian 370265870Sianstatic void 371265441Sianpl310_set_way_sizes(struct pl310_softc *sc) 372265441Sian{ 373265441Sian uint32_t aux_value; 374265441Sian 375265441Sian aux_value = pl310_read4(sc, PL310_AUX_CTRL); 376265441Sian g_way_size = (aux_value & AUX_CTRL_WAY_SIZE_MASK) >> 377265441Sian AUX_CTRL_WAY_SIZE_SHIFT; 378265441Sian g_way_size = 1 << (g_way_size + 13); 379265441Sian if (aux_value & (1 << AUX_CTRL_ASSOCIATIVITY_SHIFT)) 380265441Sian g_ways_assoc = 16; 381265441Sian else 382265441Sian g_ways_assoc = 8; 383265441Sian g_l2cache_way_mask = (1 << g_ways_assoc) - 1; 384265441Sian g_l2cache_size = g_way_size * g_ways_assoc; 385265441Sian} 386265441Sian 387269598Sian/* 388269598Sian * Setup interrupt handling. This is done only if the cache controller is 389269598Sian * disabled, for debugging. We set counters so when a cache event happens we'll 390269598Sian * get interrupted and be warned that something is wrong, because no cache 391269598Sian * events should happen if we're disabled. 392269598Sian */ 393269598Sianstatic void 394269598Sianpl310_config_intr(void *arg) 395269598Sian{ 396269598Sian struct pl310_softc * sc; 397269598Sian 398269598Sian sc = arg; 399269598Sian 400269598Sian /* activate the interrupt */ 401269598Sian bus_setup_intr(sc->sc_dev, sc->sc_irq_res, INTR_TYPE_MISC | INTR_MPSAFE, 402269598Sian pl310_filter, NULL, sc, &sc->sc_irq_h); 403269598Sian 404269598Sian /* Cache Line Eviction for Counter 0 */ 405283366Sandrew pl310_write4(sc, PL310_EVENT_COUNTER0_CONF, 406269598Sian EVENT_COUNTER_CONF_INCR | EVENT_COUNTER_CONF_CO); 407269598Sian /* Data Read Request for Counter 1 */ 408283366Sandrew pl310_write4(sc, PL310_EVENT_COUNTER1_CONF, 409269598Sian EVENT_COUNTER_CONF_INCR | EVENT_COUNTER_CONF_DRREQ); 410269598Sian 411269598Sian /* Enable and clear pending interrupts */ 412269598Sian pl310_write4(sc, PL310_INTR_CLEAR, INTR_MASK_ECNTR); 413269598Sian pl310_write4(sc, PL310_INTR_MASK, INTR_MASK_ALL); 414269598Sian 415269598Sian /* Enable counters and reset C0 and C1 */ 416283366Sandrew pl310_write4(sc, PL310_EVENT_COUNTER_CTRL, 417283366Sandrew EVENT_COUNTER_CTRL_ENABLED | 418283366Sandrew EVENT_COUNTER_CTRL_C0_RESET | 419269598Sian EVENT_COUNTER_CTRL_C1_RESET); 420269598Sian 421269598Sian config_intrhook_disestablish(sc->sc_ich); 422269598Sian free(sc->sc_ich, M_DEVBUF); 423280979Sgonzo sc->sc_ich = NULL; 424269598Sian} 425269598Sian 426239268Sgonzostatic int 427239268Sgonzopl310_probe(device_t dev) 428239268Sgonzo{ 429283366Sandrew 430261410Sian if (!ofw_bus_status_okay(dev)) 431261410Sian return (ENXIO); 432273590Sian if (!ofw_bus_search_compatible(dev, compat_data)->ocd_data) 433239268Sgonzo return (ENXIO); 434239268Sgonzo device_set_desc(dev, "PL310 L2 cache controller"); 435239268Sgonzo return (0); 436239268Sgonzo} 437239268Sgonzo 438239268Sgonzostatic int 439239268Sgonzopl310_attach(device_t dev) 440239268Sgonzo{ 441239268Sgonzo struct pl310_softc *sc = device_get_softc(dev); 442265440Sian int rid; 443265444Sian uint32_t cache_id, debug_ctrl; 444239268Sgonzo 445244914Sgonzo sc->sc_dev = dev; 446265440Sian rid = 0; 447283366Sandrew sc->sc_mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, 448239268Sgonzo RF_ACTIVE); 449239268Sgonzo if (sc->sc_mem_res == NULL) 450239268Sgonzo panic("%s: Cannot map registers", device_get_name(dev)); 451244914Sgonzo 452244914Sgonzo /* Allocate an IRQ resource */ 453244914Sgonzo rid = 0; 454244914Sgonzo sc->sc_irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid, 455244914Sgonzo RF_ACTIVE | RF_SHAREABLE); 456244914Sgonzo if (sc->sc_irq_res == NULL) { 457280979Sgonzo device_printf(dev, "cannot allocate IRQ, not using interrupt\n"); 458244914Sgonzo } 459244914Sgonzo 460239268Sgonzo pl310_softc = sc; 461244914Sgonzo mtx_init(&sc->sc_mtx, "pl310lock", NULL, MTX_SPIN); 462239268Sgonzo 463244914Sgonzo cache_id = pl310_read4(sc, PL310_CACHE_ID); 464245087Sandrew sc->sc_rtl_revision = (cache_id >> CACHE_ID_RELEASE_SHIFT) & 465245083Sandrew CACHE_ID_RELEASE_MASK; 466244914Sgonzo device_printf(dev, "Part number: 0x%x, release: 0x%x\n", 467244914Sgonzo (cache_id >> CACHE_ID_PARTNUM_SHIFT) & CACHE_ID_PARTNUM_MASK, 468244914Sgonzo (cache_id >> CACHE_ID_RELEASE_SHIFT) & CACHE_ID_RELEASE_MASK); 469265441Sian 470265444Sian /* 471265444Sian * If L2 cache is already enabled then something has violated the rules, 472265444Sian * because caches are supposed to be off at kernel entry. The cache 473265444Sian * must be disabled to write the configuration registers without 474265444Sian * triggering an access error (SLVERR), but there's no documented safe 475265444Sian * procedure for disabling the L2 cache in the manual. So we'll try to 476265444Sian * invent one: 477265444Sian * - Use the debug register to force write-through mode and prevent 478265444Sian * linefills (allocation of new lines on read); now anything we do 479265444Sian * will not cause new data to come into the L2 cache. 480265444Sian * - Writeback and invalidate the current contents. 481265444Sian * - Disable the controller. 482265444Sian * - Restore the original debug settings. 483265444Sian */ 484265444Sian if (pl310_read4(sc, PL310_CTRL) & CTRL_ENABLED) { 485265444Sian device_printf(dev, "Warning: L2 Cache should not already be " 486265444Sian "active; trying to de-activate and re-initialize...\n"); 487265444Sian sc->sc_enabled = 1; 488265444Sian debug_ctrl = pl310_read4(sc, PL310_DEBUG_CTRL); 489265444Sian platform_pl310_write_debug(sc, debug_ctrl | 490265444Sian DEBUG_CTRL_DISABLE_WRITEBACK | DEBUG_CTRL_DISABLE_LINEFILL); 491265444Sian pl310_set_way_sizes(sc); 492265444Sian pl310_wbinv_all(); 493265444Sian platform_pl310_write_ctrl(sc, CTRL_DISABLED); 494265444Sian platform_pl310_write_debug(sc, debug_ctrl); 495265444Sian } 496265444Sian sc->sc_enabled = pl310_enabled; 497265441Sian 498265444Sian if (sc->sc_enabled) { 499265444Sian platform_pl310_init(sc); 500265444Sian pl310_set_way_sizes(sc); /* platform init might change these */ 501256647Sian pl310_write4(pl310_softc, PL310_INV_WAY, 0xffff); 502256647Sian pl310_wait_background_op(PL310_INV_WAY, 0xffff); 503244914Sgonzo platform_pl310_write_ctrl(sc, CTRL_ENABLED); 504283366Sandrew device_printf(dev, "L2 Cache enabled: %uKB/%dB %d ways\n", 505265444Sian (g_l2cache_size / 1024), g_l2cache_line_size, g_ways_assoc); 506265035Sian if (bootverbose) 507265035Sian pl310_print_config(sc); 508265444Sian } else { 509280979Sgonzo if (sc->sc_irq_res != NULL) { 510280979Sgonzo sc->sc_ich = malloc(sizeof(*sc->sc_ich), M_DEVBUF, M_WAITOK); 511280979Sgonzo sc->sc_ich->ich_func = pl310_config_intr; 512280979Sgonzo sc->sc_ich->ich_arg = sc; 513280979Sgonzo if (config_intrhook_establish(sc->sc_ich) != 0) { 514280979Sgonzo device_printf(dev, 515280979Sgonzo "config_intrhook_establish failed\n"); 516280979Sgonzo free(sc->sc_ich, M_DEVBUF); 517280979Sgonzo return(ENXIO); 518280979Sgonzo } 519269598Sian } 520280979Sgonzo 521256647Sian device_printf(dev, "L2 Cache disabled\n"); 522239268Sgonzo } 523244914Sgonzo 524239268Sgonzo /* Set the l2 functions in the set of cpufuncs */ 525239268Sgonzo cpufuncs.cf_l2cache_wbinv_all = pl310_wbinv_all; 526239268Sgonzo cpufuncs.cf_l2cache_wbinv_range = pl310_wbinv_range; 527239268Sgonzo cpufuncs.cf_l2cache_inv_range = pl310_inv_range; 528239268Sgonzo cpufuncs.cf_l2cache_wb_range = pl310_wb_range; 529265870Sian cpufuncs.cf_l2cache_drain_writebuf = pl310_drain_writebuf; 530244914Sgonzo 531239268Sgonzo return (0); 532239268Sgonzo} 533239268Sgonzo 534239268Sgonzostatic device_method_t pl310_methods[] = { 535239268Sgonzo DEVMETHOD(device_probe, pl310_probe), 536239268Sgonzo DEVMETHOD(device_attach, pl310_attach), 537265440Sian DEVMETHOD_END 538239268Sgonzo}; 539239268Sgonzo 540239268Sgonzostatic driver_t pl310_driver = { 541239268Sgonzo "l2cache", 542239268Sgonzo pl310_methods, 543239268Sgonzo sizeof(struct pl310_softc), 544239268Sgonzo}; 545239268Sgonzostatic devclass_t pl310_devclass; 546239268Sgonzo 547269598SianEARLY_DRIVER_MODULE(pl310, simplebus, pl310_driver, pl310_devclass, 0, 0, 548269598Sian BUS_PASS_CPU + BUS_PASS_ORDER_MIDDLE); 549269598Sian 550