18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * arch/arm/mm/cache-l2x0.c - L210/L220/L310 cache controller support 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (C) 2007 ARM Limited 68c2ecf20Sopenharmony_ci */ 78c2ecf20Sopenharmony_ci#include <linux/cpu.h> 88c2ecf20Sopenharmony_ci#include <linux/err.h> 98c2ecf20Sopenharmony_ci#include <linux/init.h> 108c2ecf20Sopenharmony_ci#include <linux/smp.h> 118c2ecf20Sopenharmony_ci#include <linux/spinlock.h> 128c2ecf20Sopenharmony_ci#include <linux/log2.h> 138c2ecf20Sopenharmony_ci#include <linux/io.h> 148c2ecf20Sopenharmony_ci#include <linux/of.h> 158c2ecf20Sopenharmony_ci#include <linux/of_address.h> 168c2ecf20Sopenharmony_ci 178c2ecf20Sopenharmony_ci#include <asm/cacheflush.h> 188c2ecf20Sopenharmony_ci#include <asm/cp15.h> 198c2ecf20Sopenharmony_ci#include <asm/cputype.h> 208c2ecf20Sopenharmony_ci#include <asm/hardware/cache-l2x0.h> 218c2ecf20Sopenharmony_ci#include <asm/hardware/cache-aurora-l2.h> 228c2ecf20Sopenharmony_ci#include "cache-tauros3.h" 238c2ecf20Sopenharmony_ci 248c2ecf20Sopenharmony_cistruct l2c_init_data { 258c2ecf20Sopenharmony_ci const char *type; 268c2ecf20Sopenharmony_ci unsigned way_size_0; 278c2ecf20Sopenharmony_ci unsigned num_lock; 288c2ecf20Sopenharmony_ci void (*of_parse)(const struct device_node *, u32 *, u32 *); 298c2ecf20Sopenharmony_ci void (*enable)(void __iomem *, unsigned); 308c2ecf20Sopenharmony_ci void (*fixup)(void __iomem *, u32, struct outer_cache_fns *); 318c2ecf20Sopenharmony_ci void (*save)(void __iomem *); 328c2ecf20Sopenharmony_ci void (*configure)(void __iomem *); 338c2ecf20Sopenharmony_ci void (*unlock)(void __iomem *, unsigned); 348c2ecf20Sopenharmony_ci struct outer_cache_fns outer_cache; 358c2ecf20Sopenharmony_ci}; 368c2ecf20Sopenharmony_ci 378c2ecf20Sopenharmony_ci#define CACHE_LINE_SIZE 32 388c2ecf20Sopenharmony_ci 398c2ecf20Sopenharmony_cistatic void __iomem *l2x0_base; 408c2ecf20Sopenharmony_cistatic const struct l2c_init_data *l2x0_data; 418c2ecf20Sopenharmony_cistatic DEFINE_RAW_SPINLOCK(l2x0_lock); 428c2ecf20Sopenharmony_cistatic u32 l2x0_way_mask; /* Bitmask of active ways */ 438c2ecf20Sopenharmony_cistatic u32 l2x0_size; 448c2ecf20Sopenharmony_cistatic unsigned long sync_reg_offset = L2X0_CACHE_SYNC; 458c2ecf20Sopenharmony_ci 468c2ecf20Sopenharmony_cistruct l2x0_regs l2x0_saved_regs; 478c2ecf20Sopenharmony_ci 488c2ecf20Sopenharmony_cistatic bool l2x0_bresp_disable; 498c2ecf20Sopenharmony_cistatic bool l2x0_flz_disable; 508c2ecf20Sopenharmony_ci 518c2ecf20Sopenharmony_ci/* 528c2ecf20Sopenharmony_ci * Common code for all cache controllers. 538c2ecf20Sopenharmony_ci */ 548c2ecf20Sopenharmony_cistatic inline void l2c_wait_mask(void __iomem *reg, unsigned long mask) 558c2ecf20Sopenharmony_ci{ 568c2ecf20Sopenharmony_ci /* wait for cache operation by line or way to complete */ 578c2ecf20Sopenharmony_ci while (readl_relaxed(reg) & mask) 588c2ecf20Sopenharmony_ci cpu_relax(); 598c2ecf20Sopenharmony_ci} 608c2ecf20Sopenharmony_ci 618c2ecf20Sopenharmony_ci/* 628c2ecf20Sopenharmony_ci * By default, we write directly to secure registers. Platforms must 638c2ecf20Sopenharmony_ci * override this if they are running non-secure. 648c2ecf20Sopenharmony_ci */ 658c2ecf20Sopenharmony_cistatic void l2c_write_sec(unsigned long val, void __iomem *base, unsigned reg) 668c2ecf20Sopenharmony_ci{ 678c2ecf20Sopenharmony_ci if (val == readl_relaxed(base + reg)) 688c2ecf20Sopenharmony_ci return; 698c2ecf20Sopenharmony_ci if (outer_cache.write_sec) 708c2ecf20Sopenharmony_ci outer_cache.write_sec(val, reg); 718c2ecf20Sopenharmony_ci else 728c2ecf20Sopenharmony_ci writel_relaxed(val, base + reg); 738c2ecf20Sopenharmony_ci} 748c2ecf20Sopenharmony_ci 758c2ecf20Sopenharmony_ci/* 768c2ecf20Sopenharmony_ci * This should only be called when we have a requirement that the 778c2ecf20Sopenharmony_ci * register be written due to a work-around, as platforms running 788c2ecf20Sopenharmony_ci * in non-secure mode may not be able to access this register. 798c2ecf20Sopenharmony_ci */ 808c2ecf20Sopenharmony_cistatic inline void l2c_set_debug(void __iomem *base, unsigned long val) 818c2ecf20Sopenharmony_ci{ 828c2ecf20Sopenharmony_ci l2c_write_sec(val, base, L2X0_DEBUG_CTRL); 838c2ecf20Sopenharmony_ci} 848c2ecf20Sopenharmony_ci 858c2ecf20Sopenharmony_cistatic void __l2c_op_way(void __iomem *reg) 868c2ecf20Sopenharmony_ci{ 878c2ecf20Sopenharmony_ci writel_relaxed(l2x0_way_mask, reg); 888c2ecf20Sopenharmony_ci l2c_wait_mask(reg, l2x0_way_mask); 898c2ecf20Sopenharmony_ci} 908c2ecf20Sopenharmony_ci 918c2ecf20Sopenharmony_cistatic inline void l2c_unlock(void __iomem *base, unsigned num) 928c2ecf20Sopenharmony_ci{ 938c2ecf20Sopenharmony_ci unsigned i; 948c2ecf20Sopenharmony_ci 958c2ecf20Sopenharmony_ci for (i = 0; i < num; i++) { 968c2ecf20Sopenharmony_ci writel_relaxed(0, base + L2X0_LOCKDOWN_WAY_D_BASE + 978c2ecf20Sopenharmony_ci i * L2X0_LOCKDOWN_STRIDE); 988c2ecf20Sopenharmony_ci writel_relaxed(0, base + L2X0_LOCKDOWN_WAY_I_BASE + 998c2ecf20Sopenharmony_ci i * L2X0_LOCKDOWN_STRIDE); 1008c2ecf20Sopenharmony_ci } 1018c2ecf20Sopenharmony_ci} 1028c2ecf20Sopenharmony_ci 1038c2ecf20Sopenharmony_cistatic void l2c_configure(void __iomem *base) 1048c2ecf20Sopenharmony_ci{ 1058c2ecf20Sopenharmony_ci l2c_write_sec(l2x0_saved_regs.aux_ctrl, base, L2X0_AUX_CTRL); 1068c2ecf20Sopenharmony_ci} 1078c2ecf20Sopenharmony_ci 1088c2ecf20Sopenharmony_ci/* 1098c2ecf20Sopenharmony_ci * Enable the L2 cache controller. This function must only be 1108c2ecf20Sopenharmony_ci * called when the cache controller is known to be disabled. 1118c2ecf20Sopenharmony_ci */ 1128c2ecf20Sopenharmony_cistatic void l2c_enable(void __iomem *base, unsigned num_lock) 1138c2ecf20Sopenharmony_ci{ 1148c2ecf20Sopenharmony_ci unsigned long flags; 1158c2ecf20Sopenharmony_ci 1168c2ecf20Sopenharmony_ci if (outer_cache.configure) 1178c2ecf20Sopenharmony_ci outer_cache.configure(&l2x0_saved_regs); 1188c2ecf20Sopenharmony_ci else 1198c2ecf20Sopenharmony_ci l2x0_data->configure(base); 1208c2ecf20Sopenharmony_ci 1218c2ecf20Sopenharmony_ci l2x0_data->unlock(base, num_lock); 1228c2ecf20Sopenharmony_ci 1238c2ecf20Sopenharmony_ci local_irq_save(flags); 1248c2ecf20Sopenharmony_ci __l2c_op_way(base + L2X0_INV_WAY); 1258c2ecf20Sopenharmony_ci writel_relaxed(0, base + sync_reg_offset); 1268c2ecf20Sopenharmony_ci l2c_wait_mask(base + sync_reg_offset, 1); 1278c2ecf20Sopenharmony_ci local_irq_restore(flags); 1288c2ecf20Sopenharmony_ci 1298c2ecf20Sopenharmony_ci l2c_write_sec(L2X0_CTRL_EN, base, L2X0_CTRL); 1308c2ecf20Sopenharmony_ci} 1318c2ecf20Sopenharmony_ci 1328c2ecf20Sopenharmony_cistatic void l2c_disable(void) 1338c2ecf20Sopenharmony_ci{ 1348c2ecf20Sopenharmony_ci void __iomem *base = l2x0_base; 1358c2ecf20Sopenharmony_ci 1368c2ecf20Sopenharmony_ci l2x0_pmu_suspend(); 1378c2ecf20Sopenharmony_ci 1388c2ecf20Sopenharmony_ci outer_cache.flush_all(); 1398c2ecf20Sopenharmony_ci l2c_write_sec(0, base, L2X0_CTRL); 1408c2ecf20Sopenharmony_ci dsb(st); 1418c2ecf20Sopenharmony_ci} 1428c2ecf20Sopenharmony_ci 1438c2ecf20Sopenharmony_cistatic void l2c_save(void __iomem *base) 1448c2ecf20Sopenharmony_ci{ 1458c2ecf20Sopenharmony_ci l2x0_saved_regs.aux_ctrl = readl_relaxed(l2x0_base + L2X0_AUX_CTRL); 1468c2ecf20Sopenharmony_ci} 1478c2ecf20Sopenharmony_ci 1488c2ecf20Sopenharmony_cistatic void l2c_resume(void) 1498c2ecf20Sopenharmony_ci{ 1508c2ecf20Sopenharmony_ci void __iomem *base = l2x0_base; 1518c2ecf20Sopenharmony_ci 1528c2ecf20Sopenharmony_ci /* Do not touch the controller if already enabled. */ 1538c2ecf20Sopenharmony_ci if (!(readl_relaxed(base + L2X0_CTRL) & L2X0_CTRL_EN)) 1548c2ecf20Sopenharmony_ci l2c_enable(base, l2x0_data->num_lock); 1558c2ecf20Sopenharmony_ci 1568c2ecf20Sopenharmony_ci l2x0_pmu_resume(); 1578c2ecf20Sopenharmony_ci} 1588c2ecf20Sopenharmony_ci 1598c2ecf20Sopenharmony_ci/* 1608c2ecf20Sopenharmony_ci * L2C-210 specific code. 1618c2ecf20Sopenharmony_ci * 1628c2ecf20Sopenharmony_ci * The L2C-2x0 PA, set/way and sync operations are atomic, but we must 1638c2ecf20Sopenharmony_ci * ensure that no background operation is running. The way operations 1648c2ecf20Sopenharmony_ci * are all background tasks. 1658c2ecf20Sopenharmony_ci * 1668c2ecf20Sopenharmony_ci * While a background operation is in progress, any new operation is 1678c2ecf20Sopenharmony_ci * ignored (unspecified whether this causes an error.) Thankfully, not 1688c2ecf20Sopenharmony_ci * used on SMP. 1698c2ecf20Sopenharmony_ci * 1708c2ecf20Sopenharmony_ci * Never has a different sync register other than L2X0_CACHE_SYNC, but 1718c2ecf20Sopenharmony_ci * we use sync_reg_offset here so we can share some of this with L2C-310. 1728c2ecf20Sopenharmony_ci */ 1738c2ecf20Sopenharmony_cistatic void __l2c210_cache_sync(void __iomem *base) 1748c2ecf20Sopenharmony_ci{ 1758c2ecf20Sopenharmony_ci writel_relaxed(0, base + sync_reg_offset); 1768c2ecf20Sopenharmony_ci} 1778c2ecf20Sopenharmony_ci 1788c2ecf20Sopenharmony_cistatic void __l2c210_op_pa_range(void __iomem *reg, unsigned long start, 1798c2ecf20Sopenharmony_ci unsigned long end) 1808c2ecf20Sopenharmony_ci{ 1818c2ecf20Sopenharmony_ci while (start < end) { 1828c2ecf20Sopenharmony_ci writel_relaxed(start, reg); 1838c2ecf20Sopenharmony_ci start += CACHE_LINE_SIZE; 1848c2ecf20Sopenharmony_ci } 1858c2ecf20Sopenharmony_ci} 1868c2ecf20Sopenharmony_ci 1878c2ecf20Sopenharmony_cistatic void l2c210_inv_range(unsigned long start, unsigned long end) 1888c2ecf20Sopenharmony_ci{ 1898c2ecf20Sopenharmony_ci void __iomem *base = l2x0_base; 1908c2ecf20Sopenharmony_ci 1918c2ecf20Sopenharmony_ci if (start & (CACHE_LINE_SIZE - 1)) { 1928c2ecf20Sopenharmony_ci start &= ~(CACHE_LINE_SIZE - 1); 1938c2ecf20Sopenharmony_ci writel_relaxed(start, base + L2X0_CLEAN_INV_LINE_PA); 1948c2ecf20Sopenharmony_ci start += CACHE_LINE_SIZE; 1958c2ecf20Sopenharmony_ci } 1968c2ecf20Sopenharmony_ci 1978c2ecf20Sopenharmony_ci if (end & (CACHE_LINE_SIZE - 1)) { 1988c2ecf20Sopenharmony_ci end &= ~(CACHE_LINE_SIZE - 1); 1998c2ecf20Sopenharmony_ci writel_relaxed(end, base + L2X0_CLEAN_INV_LINE_PA); 2008c2ecf20Sopenharmony_ci } 2018c2ecf20Sopenharmony_ci 2028c2ecf20Sopenharmony_ci __l2c210_op_pa_range(base + L2X0_INV_LINE_PA, start, end); 2038c2ecf20Sopenharmony_ci __l2c210_cache_sync(base); 2048c2ecf20Sopenharmony_ci} 2058c2ecf20Sopenharmony_ci 2068c2ecf20Sopenharmony_cistatic void l2c210_clean_range(unsigned long start, unsigned long end) 2078c2ecf20Sopenharmony_ci{ 2088c2ecf20Sopenharmony_ci void __iomem *base = l2x0_base; 2098c2ecf20Sopenharmony_ci 2108c2ecf20Sopenharmony_ci start &= ~(CACHE_LINE_SIZE - 1); 2118c2ecf20Sopenharmony_ci __l2c210_op_pa_range(base + L2X0_CLEAN_LINE_PA, start, end); 2128c2ecf20Sopenharmony_ci __l2c210_cache_sync(base); 2138c2ecf20Sopenharmony_ci} 2148c2ecf20Sopenharmony_ci 2158c2ecf20Sopenharmony_cistatic void l2c210_flush_range(unsigned long start, unsigned long end) 2168c2ecf20Sopenharmony_ci{ 2178c2ecf20Sopenharmony_ci void __iomem *base = l2x0_base; 2188c2ecf20Sopenharmony_ci 2198c2ecf20Sopenharmony_ci start &= ~(CACHE_LINE_SIZE - 1); 2208c2ecf20Sopenharmony_ci __l2c210_op_pa_range(base + L2X0_CLEAN_INV_LINE_PA, start, end); 2218c2ecf20Sopenharmony_ci __l2c210_cache_sync(base); 2228c2ecf20Sopenharmony_ci} 2238c2ecf20Sopenharmony_ci 2248c2ecf20Sopenharmony_cistatic void l2c210_flush_all(void) 2258c2ecf20Sopenharmony_ci{ 2268c2ecf20Sopenharmony_ci void __iomem *base = l2x0_base; 2278c2ecf20Sopenharmony_ci 2288c2ecf20Sopenharmony_ci BUG_ON(!irqs_disabled()); 2298c2ecf20Sopenharmony_ci 2308c2ecf20Sopenharmony_ci __l2c_op_way(base + L2X0_CLEAN_INV_WAY); 2318c2ecf20Sopenharmony_ci __l2c210_cache_sync(base); 2328c2ecf20Sopenharmony_ci} 2338c2ecf20Sopenharmony_ci 2348c2ecf20Sopenharmony_cistatic void l2c210_sync(void) 2358c2ecf20Sopenharmony_ci{ 2368c2ecf20Sopenharmony_ci __l2c210_cache_sync(l2x0_base); 2378c2ecf20Sopenharmony_ci} 2388c2ecf20Sopenharmony_ci 2398c2ecf20Sopenharmony_cistatic const struct l2c_init_data l2c210_data __initconst = { 2408c2ecf20Sopenharmony_ci .type = "L2C-210", 2418c2ecf20Sopenharmony_ci .way_size_0 = SZ_8K, 2428c2ecf20Sopenharmony_ci .num_lock = 1, 2438c2ecf20Sopenharmony_ci .enable = l2c_enable, 2448c2ecf20Sopenharmony_ci .save = l2c_save, 2458c2ecf20Sopenharmony_ci .configure = l2c_configure, 2468c2ecf20Sopenharmony_ci .unlock = l2c_unlock, 2478c2ecf20Sopenharmony_ci .outer_cache = { 2488c2ecf20Sopenharmony_ci .inv_range = l2c210_inv_range, 2498c2ecf20Sopenharmony_ci .clean_range = l2c210_clean_range, 2508c2ecf20Sopenharmony_ci .flush_range = l2c210_flush_range, 2518c2ecf20Sopenharmony_ci .flush_all = l2c210_flush_all, 2528c2ecf20Sopenharmony_ci .disable = l2c_disable, 2538c2ecf20Sopenharmony_ci .sync = l2c210_sync, 2548c2ecf20Sopenharmony_ci .resume = l2c_resume, 2558c2ecf20Sopenharmony_ci }, 2568c2ecf20Sopenharmony_ci}; 2578c2ecf20Sopenharmony_ci 2588c2ecf20Sopenharmony_ci/* 2598c2ecf20Sopenharmony_ci * L2C-220 specific code. 2608c2ecf20Sopenharmony_ci * 2618c2ecf20Sopenharmony_ci * All operations are background operations: they have to be waited for. 2628c2ecf20Sopenharmony_ci * Conflicting requests generate a slave error (which will cause an 2638c2ecf20Sopenharmony_ci * imprecise abort.) Never uses sync_reg_offset, so we hard-code the 2648c2ecf20Sopenharmony_ci * sync register here. 2658c2ecf20Sopenharmony_ci * 2668c2ecf20Sopenharmony_ci * However, we can re-use the l2c210_resume call. 2678c2ecf20Sopenharmony_ci */ 2688c2ecf20Sopenharmony_cistatic inline void __l2c220_cache_sync(void __iomem *base) 2698c2ecf20Sopenharmony_ci{ 2708c2ecf20Sopenharmony_ci writel_relaxed(0, base + L2X0_CACHE_SYNC); 2718c2ecf20Sopenharmony_ci l2c_wait_mask(base + L2X0_CACHE_SYNC, 1); 2728c2ecf20Sopenharmony_ci} 2738c2ecf20Sopenharmony_ci 2748c2ecf20Sopenharmony_cistatic void l2c220_op_way(void __iomem *base, unsigned reg) 2758c2ecf20Sopenharmony_ci{ 2768c2ecf20Sopenharmony_ci unsigned long flags; 2778c2ecf20Sopenharmony_ci 2788c2ecf20Sopenharmony_ci raw_spin_lock_irqsave(&l2x0_lock, flags); 2798c2ecf20Sopenharmony_ci __l2c_op_way(base + reg); 2808c2ecf20Sopenharmony_ci __l2c220_cache_sync(base); 2818c2ecf20Sopenharmony_ci raw_spin_unlock_irqrestore(&l2x0_lock, flags); 2828c2ecf20Sopenharmony_ci} 2838c2ecf20Sopenharmony_ci 2848c2ecf20Sopenharmony_cistatic unsigned long l2c220_op_pa_range(void __iomem *reg, unsigned long start, 2858c2ecf20Sopenharmony_ci unsigned long end, unsigned long flags) 2868c2ecf20Sopenharmony_ci{ 2878c2ecf20Sopenharmony_ci raw_spinlock_t *lock = &l2x0_lock; 2888c2ecf20Sopenharmony_ci 2898c2ecf20Sopenharmony_ci while (start < end) { 2908c2ecf20Sopenharmony_ci unsigned long blk_end = start + min(end - start, 4096UL); 2918c2ecf20Sopenharmony_ci 2928c2ecf20Sopenharmony_ci while (start < blk_end) { 2938c2ecf20Sopenharmony_ci l2c_wait_mask(reg, 1); 2948c2ecf20Sopenharmony_ci writel_relaxed(start, reg); 2958c2ecf20Sopenharmony_ci start += CACHE_LINE_SIZE; 2968c2ecf20Sopenharmony_ci } 2978c2ecf20Sopenharmony_ci 2988c2ecf20Sopenharmony_ci if (blk_end < end) { 2998c2ecf20Sopenharmony_ci raw_spin_unlock_irqrestore(lock, flags); 3008c2ecf20Sopenharmony_ci raw_spin_lock_irqsave(lock, flags); 3018c2ecf20Sopenharmony_ci } 3028c2ecf20Sopenharmony_ci } 3038c2ecf20Sopenharmony_ci 3048c2ecf20Sopenharmony_ci return flags; 3058c2ecf20Sopenharmony_ci} 3068c2ecf20Sopenharmony_ci 3078c2ecf20Sopenharmony_cistatic void l2c220_inv_range(unsigned long start, unsigned long end) 3088c2ecf20Sopenharmony_ci{ 3098c2ecf20Sopenharmony_ci void __iomem *base = l2x0_base; 3108c2ecf20Sopenharmony_ci unsigned long flags; 3118c2ecf20Sopenharmony_ci 3128c2ecf20Sopenharmony_ci raw_spin_lock_irqsave(&l2x0_lock, flags); 3138c2ecf20Sopenharmony_ci if ((start | end) & (CACHE_LINE_SIZE - 1)) { 3148c2ecf20Sopenharmony_ci if (start & (CACHE_LINE_SIZE - 1)) { 3158c2ecf20Sopenharmony_ci start &= ~(CACHE_LINE_SIZE - 1); 3168c2ecf20Sopenharmony_ci writel_relaxed(start, base + L2X0_CLEAN_INV_LINE_PA); 3178c2ecf20Sopenharmony_ci start += CACHE_LINE_SIZE; 3188c2ecf20Sopenharmony_ci } 3198c2ecf20Sopenharmony_ci 3208c2ecf20Sopenharmony_ci if (end & (CACHE_LINE_SIZE - 1)) { 3218c2ecf20Sopenharmony_ci end &= ~(CACHE_LINE_SIZE - 1); 3228c2ecf20Sopenharmony_ci l2c_wait_mask(base + L2X0_CLEAN_INV_LINE_PA, 1); 3238c2ecf20Sopenharmony_ci writel_relaxed(end, base + L2X0_CLEAN_INV_LINE_PA); 3248c2ecf20Sopenharmony_ci } 3258c2ecf20Sopenharmony_ci } 3268c2ecf20Sopenharmony_ci 3278c2ecf20Sopenharmony_ci flags = l2c220_op_pa_range(base + L2X0_INV_LINE_PA, 3288c2ecf20Sopenharmony_ci start, end, flags); 3298c2ecf20Sopenharmony_ci l2c_wait_mask(base + L2X0_INV_LINE_PA, 1); 3308c2ecf20Sopenharmony_ci __l2c220_cache_sync(base); 3318c2ecf20Sopenharmony_ci raw_spin_unlock_irqrestore(&l2x0_lock, flags); 3328c2ecf20Sopenharmony_ci} 3338c2ecf20Sopenharmony_ci 3348c2ecf20Sopenharmony_cistatic void l2c220_clean_range(unsigned long start, unsigned long end) 3358c2ecf20Sopenharmony_ci{ 3368c2ecf20Sopenharmony_ci void __iomem *base = l2x0_base; 3378c2ecf20Sopenharmony_ci unsigned long flags; 3388c2ecf20Sopenharmony_ci 3398c2ecf20Sopenharmony_ci start &= ~(CACHE_LINE_SIZE - 1); 3408c2ecf20Sopenharmony_ci if ((end - start) >= l2x0_size) { 3418c2ecf20Sopenharmony_ci l2c220_op_way(base, L2X0_CLEAN_WAY); 3428c2ecf20Sopenharmony_ci return; 3438c2ecf20Sopenharmony_ci } 3448c2ecf20Sopenharmony_ci 3458c2ecf20Sopenharmony_ci raw_spin_lock_irqsave(&l2x0_lock, flags); 3468c2ecf20Sopenharmony_ci flags = l2c220_op_pa_range(base + L2X0_CLEAN_LINE_PA, 3478c2ecf20Sopenharmony_ci start, end, flags); 3488c2ecf20Sopenharmony_ci l2c_wait_mask(base + L2X0_CLEAN_INV_LINE_PA, 1); 3498c2ecf20Sopenharmony_ci __l2c220_cache_sync(base); 3508c2ecf20Sopenharmony_ci raw_spin_unlock_irqrestore(&l2x0_lock, flags); 3518c2ecf20Sopenharmony_ci} 3528c2ecf20Sopenharmony_ci 3538c2ecf20Sopenharmony_cistatic void l2c220_flush_range(unsigned long start, unsigned long end) 3548c2ecf20Sopenharmony_ci{ 3558c2ecf20Sopenharmony_ci void __iomem *base = l2x0_base; 3568c2ecf20Sopenharmony_ci unsigned long flags; 3578c2ecf20Sopenharmony_ci 3588c2ecf20Sopenharmony_ci start &= ~(CACHE_LINE_SIZE - 1); 3598c2ecf20Sopenharmony_ci if ((end - start) >= l2x0_size) { 3608c2ecf20Sopenharmony_ci l2c220_op_way(base, L2X0_CLEAN_INV_WAY); 3618c2ecf20Sopenharmony_ci return; 3628c2ecf20Sopenharmony_ci } 3638c2ecf20Sopenharmony_ci 3648c2ecf20Sopenharmony_ci raw_spin_lock_irqsave(&l2x0_lock, flags); 3658c2ecf20Sopenharmony_ci flags = l2c220_op_pa_range(base + L2X0_CLEAN_INV_LINE_PA, 3668c2ecf20Sopenharmony_ci start, end, flags); 3678c2ecf20Sopenharmony_ci l2c_wait_mask(base + L2X0_CLEAN_INV_LINE_PA, 1); 3688c2ecf20Sopenharmony_ci __l2c220_cache_sync(base); 3698c2ecf20Sopenharmony_ci raw_spin_unlock_irqrestore(&l2x0_lock, flags); 3708c2ecf20Sopenharmony_ci} 3718c2ecf20Sopenharmony_ci 3728c2ecf20Sopenharmony_cistatic void l2c220_flush_all(void) 3738c2ecf20Sopenharmony_ci{ 3748c2ecf20Sopenharmony_ci l2c220_op_way(l2x0_base, L2X0_CLEAN_INV_WAY); 3758c2ecf20Sopenharmony_ci} 3768c2ecf20Sopenharmony_ci 3778c2ecf20Sopenharmony_cistatic void l2c220_sync(void) 3788c2ecf20Sopenharmony_ci{ 3798c2ecf20Sopenharmony_ci unsigned long flags; 3808c2ecf20Sopenharmony_ci 3818c2ecf20Sopenharmony_ci raw_spin_lock_irqsave(&l2x0_lock, flags); 3828c2ecf20Sopenharmony_ci __l2c220_cache_sync(l2x0_base); 3838c2ecf20Sopenharmony_ci raw_spin_unlock_irqrestore(&l2x0_lock, flags); 3848c2ecf20Sopenharmony_ci} 3858c2ecf20Sopenharmony_ci 3868c2ecf20Sopenharmony_cistatic void l2c220_enable(void __iomem *base, unsigned num_lock) 3878c2ecf20Sopenharmony_ci{ 3888c2ecf20Sopenharmony_ci /* 3898c2ecf20Sopenharmony_ci * Always enable non-secure access to the lockdown registers - 3908c2ecf20Sopenharmony_ci * we write to them as part of the L2C enable sequence so they 3918c2ecf20Sopenharmony_ci * need to be accessible. 3928c2ecf20Sopenharmony_ci */ 3938c2ecf20Sopenharmony_ci l2x0_saved_regs.aux_ctrl |= L220_AUX_CTRL_NS_LOCKDOWN; 3948c2ecf20Sopenharmony_ci 3958c2ecf20Sopenharmony_ci l2c_enable(base, num_lock); 3968c2ecf20Sopenharmony_ci} 3978c2ecf20Sopenharmony_ci 3988c2ecf20Sopenharmony_cistatic void l2c220_unlock(void __iomem *base, unsigned num_lock) 3998c2ecf20Sopenharmony_ci{ 4008c2ecf20Sopenharmony_ci if (readl_relaxed(base + L2X0_AUX_CTRL) & L220_AUX_CTRL_NS_LOCKDOWN) 4018c2ecf20Sopenharmony_ci l2c_unlock(base, num_lock); 4028c2ecf20Sopenharmony_ci} 4038c2ecf20Sopenharmony_ci 4048c2ecf20Sopenharmony_cistatic const struct l2c_init_data l2c220_data = { 4058c2ecf20Sopenharmony_ci .type = "L2C-220", 4068c2ecf20Sopenharmony_ci .way_size_0 = SZ_8K, 4078c2ecf20Sopenharmony_ci .num_lock = 1, 4088c2ecf20Sopenharmony_ci .enable = l2c220_enable, 4098c2ecf20Sopenharmony_ci .save = l2c_save, 4108c2ecf20Sopenharmony_ci .configure = l2c_configure, 4118c2ecf20Sopenharmony_ci .unlock = l2c220_unlock, 4128c2ecf20Sopenharmony_ci .outer_cache = { 4138c2ecf20Sopenharmony_ci .inv_range = l2c220_inv_range, 4148c2ecf20Sopenharmony_ci .clean_range = l2c220_clean_range, 4158c2ecf20Sopenharmony_ci .flush_range = l2c220_flush_range, 4168c2ecf20Sopenharmony_ci .flush_all = l2c220_flush_all, 4178c2ecf20Sopenharmony_ci .disable = l2c_disable, 4188c2ecf20Sopenharmony_ci .sync = l2c220_sync, 4198c2ecf20Sopenharmony_ci .resume = l2c_resume, 4208c2ecf20Sopenharmony_ci }, 4218c2ecf20Sopenharmony_ci}; 4228c2ecf20Sopenharmony_ci 4238c2ecf20Sopenharmony_ci/* 4248c2ecf20Sopenharmony_ci * L2C-310 specific code. 4258c2ecf20Sopenharmony_ci * 4268c2ecf20Sopenharmony_ci * Very similar to L2C-210, the PA, set/way and sync operations are atomic, 4278c2ecf20Sopenharmony_ci * and the way operations are all background tasks. However, issuing an 4288c2ecf20Sopenharmony_ci * operation while a background operation is in progress results in a 4298c2ecf20Sopenharmony_ci * SLVERR response. We can reuse: 4308c2ecf20Sopenharmony_ci * 4318c2ecf20Sopenharmony_ci * __l2c210_cache_sync (using sync_reg_offset) 4328c2ecf20Sopenharmony_ci * l2c210_sync 4338c2ecf20Sopenharmony_ci * l2c210_inv_range (if 588369 is not applicable) 4348c2ecf20Sopenharmony_ci * l2c210_clean_range 4358c2ecf20Sopenharmony_ci * l2c210_flush_range (if 588369 is not applicable) 4368c2ecf20Sopenharmony_ci * l2c210_flush_all (if 727915 is not applicable) 4378c2ecf20Sopenharmony_ci * 4388c2ecf20Sopenharmony_ci * Errata: 4398c2ecf20Sopenharmony_ci * 588369: PL310 R0P0->R1P0, fixed R2P0. 4408c2ecf20Sopenharmony_ci * Affects: all clean+invalidate operations 4418c2ecf20Sopenharmony_ci * clean and invalidate skips the invalidate step, so we need to issue 4428c2ecf20Sopenharmony_ci * separate operations. We also require the above debug workaround 4438c2ecf20Sopenharmony_ci * enclosing this code fragment on affected parts. On unaffected parts, 4448c2ecf20Sopenharmony_ci * we must not use this workaround without the debug register writes 4458c2ecf20Sopenharmony_ci * to avoid exposing a problem similar to 727915. 4468c2ecf20Sopenharmony_ci * 4478c2ecf20Sopenharmony_ci * 727915: PL310 R2P0->R3P0, fixed R3P1. 4488c2ecf20Sopenharmony_ci * Affects: clean+invalidate by way 4498c2ecf20Sopenharmony_ci * clean and invalidate by way runs in the background, and a store can 4508c2ecf20Sopenharmony_ci * hit the line between the clean operation and invalidate operation, 4518c2ecf20Sopenharmony_ci * resulting in the store being lost. 4528c2ecf20Sopenharmony_ci * 4538c2ecf20Sopenharmony_ci * 752271: PL310 R3P0->R3P1-50REL0, fixed R3P2. 4548c2ecf20Sopenharmony_ci * Affects: 8x64-bit (double fill) line fetches 4558c2ecf20Sopenharmony_ci * double fill line fetches can fail to cause dirty data to be evicted 4568c2ecf20Sopenharmony_ci * from the cache before the new data overwrites the second line. 4578c2ecf20Sopenharmony_ci * 4588c2ecf20Sopenharmony_ci * 753970: PL310 R3P0, fixed R3P1. 4598c2ecf20Sopenharmony_ci * Affects: sync 4608c2ecf20Sopenharmony_ci * prevents merging writes after the sync operation, until another L2C 4618c2ecf20Sopenharmony_ci * operation is performed (or a number of other conditions.) 4628c2ecf20Sopenharmony_ci * 4638c2ecf20Sopenharmony_ci * 769419: PL310 R0P0->R3P1, fixed R3P2. 4648c2ecf20Sopenharmony_ci * Affects: store buffer 4658c2ecf20Sopenharmony_ci * store buffer is not automatically drained. 4668c2ecf20Sopenharmony_ci */ 4678c2ecf20Sopenharmony_cistatic void l2c310_inv_range_erratum(unsigned long start, unsigned long end) 4688c2ecf20Sopenharmony_ci{ 4698c2ecf20Sopenharmony_ci void __iomem *base = l2x0_base; 4708c2ecf20Sopenharmony_ci 4718c2ecf20Sopenharmony_ci if ((start | end) & (CACHE_LINE_SIZE - 1)) { 4728c2ecf20Sopenharmony_ci unsigned long flags; 4738c2ecf20Sopenharmony_ci 4748c2ecf20Sopenharmony_ci /* Erratum 588369 for both clean+invalidate operations */ 4758c2ecf20Sopenharmony_ci raw_spin_lock_irqsave(&l2x0_lock, flags); 4768c2ecf20Sopenharmony_ci l2c_set_debug(base, 0x03); 4778c2ecf20Sopenharmony_ci 4788c2ecf20Sopenharmony_ci if (start & (CACHE_LINE_SIZE - 1)) { 4798c2ecf20Sopenharmony_ci start &= ~(CACHE_LINE_SIZE - 1); 4808c2ecf20Sopenharmony_ci writel_relaxed(start, base + L2X0_CLEAN_LINE_PA); 4818c2ecf20Sopenharmony_ci writel_relaxed(start, base + L2X0_INV_LINE_PA); 4828c2ecf20Sopenharmony_ci start += CACHE_LINE_SIZE; 4838c2ecf20Sopenharmony_ci } 4848c2ecf20Sopenharmony_ci 4858c2ecf20Sopenharmony_ci if (end & (CACHE_LINE_SIZE - 1)) { 4868c2ecf20Sopenharmony_ci end &= ~(CACHE_LINE_SIZE - 1); 4878c2ecf20Sopenharmony_ci writel_relaxed(end, base + L2X0_CLEAN_LINE_PA); 4888c2ecf20Sopenharmony_ci writel_relaxed(end, base + L2X0_INV_LINE_PA); 4898c2ecf20Sopenharmony_ci } 4908c2ecf20Sopenharmony_ci 4918c2ecf20Sopenharmony_ci l2c_set_debug(base, 0x00); 4928c2ecf20Sopenharmony_ci raw_spin_unlock_irqrestore(&l2x0_lock, flags); 4938c2ecf20Sopenharmony_ci } 4948c2ecf20Sopenharmony_ci 4958c2ecf20Sopenharmony_ci __l2c210_op_pa_range(base + L2X0_INV_LINE_PA, start, end); 4968c2ecf20Sopenharmony_ci __l2c210_cache_sync(base); 4978c2ecf20Sopenharmony_ci} 4988c2ecf20Sopenharmony_ci 4998c2ecf20Sopenharmony_cistatic void l2c310_flush_range_erratum(unsigned long start, unsigned long end) 5008c2ecf20Sopenharmony_ci{ 5018c2ecf20Sopenharmony_ci raw_spinlock_t *lock = &l2x0_lock; 5028c2ecf20Sopenharmony_ci unsigned long flags; 5038c2ecf20Sopenharmony_ci void __iomem *base = l2x0_base; 5048c2ecf20Sopenharmony_ci 5058c2ecf20Sopenharmony_ci raw_spin_lock_irqsave(lock, flags); 5068c2ecf20Sopenharmony_ci while (start < end) { 5078c2ecf20Sopenharmony_ci unsigned long blk_end = start + min(end - start, 4096UL); 5088c2ecf20Sopenharmony_ci 5098c2ecf20Sopenharmony_ci l2c_set_debug(base, 0x03); 5108c2ecf20Sopenharmony_ci while (start < blk_end) { 5118c2ecf20Sopenharmony_ci writel_relaxed(start, base + L2X0_CLEAN_LINE_PA); 5128c2ecf20Sopenharmony_ci writel_relaxed(start, base + L2X0_INV_LINE_PA); 5138c2ecf20Sopenharmony_ci start += CACHE_LINE_SIZE; 5148c2ecf20Sopenharmony_ci } 5158c2ecf20Sopenharmony_ci l2c_set_debug(base, 0x00); 5168c2ecf20Sopenharmony_ci 5178c2ecf20Sopenharmony_ci if (blk_end < end) { 5188c2ecf20Sopenharmony_ci raw_spin_unlock_irqrestore(lock, flags); 5198c2ecf20Sopenharmony_ci raw_spin_lock_irqsave(lock, flags); 5208c2ecf20Sopenharmony_ci } 5218c2ecf20Sopenharmony_ci } 5228c2ecf20Sopenharmony_ci raw_spin_unlock_irqrestore(lock, flags); 5238c2ecf20Sopenharmony_ci __l2c210_cache_sync(base); 5248c2ecf20Sopenharmony_ci} 5258c2ecf20Sopenharmony_ci 5268c2ecf20Sopenharmony_cistatic void l2c310_flush_all_erratum(void) 5278c2ecf20Sopenharmony_ci{ 5288c2ecf20Sopenharmony_ci void __iomem *base = l2x0_base; 5298c2ecf20Sopenharmony_ci unsigned long flags; 5308c2ecf20Sopenharmony_ci 5318c2ecf20Sopenharmony_ci raw_spin_lock_irqsave(&l2x0_lock, flags); 5328c2ecf20Sopenharmony_ci l2c_set_debug(base, 0x03); 5338c2ecf20Sopenharmony_ci __l2c_op_way(base + L2X0_CLEAN_INV_WAY); 5348c2ecf20Sopenharmony_ci l2c_set_debug(base, 0x00); 5358c2ecf20Sopenharmony_ci __l2c210_cache_sync(base); 5368c2ecf20Sopenharmony_ci raw_spin_unlock_irqrestore(&l2x0_lock, flags); 5378c2ecf20Sopenharmony_ci} 5388c2ecf20Sopenharmony_ci 5398c2ecf20Sopenharmony_cistatic void __init l2c310_save(void __iomem *base) 5408c2ecf20Sopenharmony_ci{ 5418c2ecf20Sopenharmony_ci unsigned revision; 5428c2ecf20Sopenharmony_ci 5438c2ecf20Sopenharmony_ci l2c_save(base); 5448c2ecf20Sopenharmony_ci 5458c2ecf20Sopenharmony_ci l2x0_saved_regs.tag_latency = readl_relaxed(base + 5468c2ecf20Sopenharmony_ci L310_TAG_LATENCY_CTRL); 5478c2ecf20Sopenharmony_ci l2x0_saved_regs.data_latency = readl_relaxed(base + 5488c2ecf20Sopenharmony_ci L310_DATA_LATENCY_CTRL); 5498c2ecf20Sopenharmony_ci l2x0_saved_regs.filter_end = readl_relaxed(base + 5508c2ecf20Sopenharmony_ci L310_ADDR_FILTER_END); 5518c2ecf20Sopenharmony_ci l2x0_saved_regs.filter_start = readl_relaxed(base + 5528c2ecf20Sopenharmony_ci L310_ADDR_FILTER_START); 5538c2ecf20Sopenharmony_ci 5548c2ecf20Sopenharmony_ci revision = readl_relaxed(base + L2X0_CACHE_ID) & 5558c2ecf20Sopenharmony_ci L2X0_CACHE_ID_RTL_MASK; 5568c2ecf20Sopenharmony_ci 5578c2ecf20Sopenharmony_ci /* From r2p0, there is Prefetch offset/control register */ 5588c2ecf20Sopenharmony_ci if (revision >= L310_CACHE_ID_RTL_R2P0) 5598c2ecf20Sopenharmony_ci l2x0_saved_regs.prefetch_ctrl = readl_relaxed(base + 5608c2ecf20Sopenharmony_ci L310_PREFETCH_CTRL); 5618c2ecf20Sopenharmony_ci 5628c2ecf20Sopenharmony_ci /* From r3p0, there is Power control register */ 5638c2ecf20Sopenharmony_ci if (revision >= L310_CACHE_ID_RTL_R3P0) 5648c2ecf20Sopenharmony_ci l2x0_saved_regs.pwr_ctrl = readl_relaxed(base + 5658c2ecf20Sopenharmony_ci L310_POWER_CTRL); 5668c2ecf20Sopenharmony_ci} 5678c2ecf20Sopenharmony_ci 5688c2ecf20Sopenharmony_cistatic void l2c310_configure(void __iomem *base) 5698c2ecf20Sopenharmony_ci{ 5708c2ecf20Sopenharmony_ci unsigned revision; 5718c2ecf20Sopenharmony_ci 5728c2ecf20Sopenharmony_ci l2c_configure(base); 5738c2ecf20Sopenharmony_ci 5748c2ecf20Sopenharmony_ci /* restore pl310 setup */ 5758c2ecf20Sopenharmony_ci l2c_write_sec(l2x0_saved_regs.tag_latency, base, 5768c2ecf20Sopenharmony_ci L310_TAG_LATENCY_CTRL); 5778c2ecf20Sopenharmony_ci l2c_write_sec(l2x0_saved_regs.data_latency, base, 5788c2ecf20Sopenharmony_ci L310_DATA_LATENCY_CTRL); 5798c2ecf20Sopenharmony_ci l2c_write_sec(l2x0_saved_regs.filter_end, base, 5808c2ecf20Sopenharmony_ci L310_ADDR_FILTER_END); 5818c2ecf20Sopenharmony_ci l2c_write_sec(l2x0_saved_regs.filter_start, base, 5828c2ecf20Sopenharmony_ci L310_ADDR_FILTER_START); 5838c2ecf20Sopenharmony_ci 5848c2ecf20Sopenharmony_ci revision = readl_relaxed(base + L2X0_CACHE_ID) & 5858c2ecf20Sopenharmony_ci L2X0_CACHE_ID_RTL_MASK; 5868c2ecf20Sopenharmony_ci 5878c2ecf20Sopenharmony_ci if (revision >= L310_CACHE_ID_RTL_R2P0) 5888c2ecf20Sopenharmony_ci l2c_write_sec(l2x0_saved_regs.prefetch_ctrl, base, 5898c2ecf20Sopenharmony_ci L310_PREFETCH_CTRL); 5908c2ecf20Sopenharmony_ci if (revision >= L310_CACHE_ID_RTL_R3P0) 5918c2ecf20Sopenharmony_ci l2c_write_sec(l2x0_saved_regs.pwr_ctrl, base, 5928c2ecf20Sopenharmony_ci L310_POWER_CTRL); 5938c2ecf20Sopenharmony_ci} 5948c2ecf20Sopenharmony_ci 5958c2ecf20Sopenharmony_cistatic int l2c310_starting_cpu(unsigned int cpu) 5968c2ecf20Sopenharmony_ci{ 5978c2ecf20Sopenharmony_ci set_auxcr(get_auxcr() | BIT(3) | BIT(2) | BIT(1)); 5988c2ecf20Sopenharmony_ci return 0; 5998c2ecf20Sopenharmony_ci} 6008c2ecf20Sopenharmony_ci 6018c2ecf20Sopenharmony_cistatic int l2c310_dying_cpu(unsigned int cpu) 6028c2ecf20Sopenharmony_ci{ 6038c2ecf20Sopenharmony_ci set_auxcr(get_auxcr() & ~(BIT(3) | BIT(2) | BIT(1))); 6048c2ecf20Sopenharmony_ci return 0; 6058c2ecf20Sopenharmony_ci} 6068c2ecf20Sopenharmony_ci 6078c2ecf20Sopenharmony_cistatic void __init l2c310_enable(void __iomem *base, unsigned num_lock) 6088c2ecf20Sopenharmony_ci{ 6098c2ecf20Sopenharmony_ci unsigned rev = readl_relaxed(base + L2X0_CACHE_ID) & L2X0_CACHE_ID_RTL_MASK; 6108c2ecf20Sopenharmony_ci bool cortex_a9 = read_cpuid_part() == ARM_CPU_PART_CORTEX_A9; 6118c2ecf20Sopenharmony_ci u32 aux = l2x0_saved_regs.aux_ctrl; 6128c2ecf20Sopenharmony_ci 6138c2ecf20Sopenharmony_ci if (rev >= L310_CACHE_ID_RTL_R2P0) { 6148c2ecf20Sopenharmony_ci if (cortex_a9 && !l2x0_bresp_disable) { 6158c2ecf20Sopenharmony_ci aux |= L310_AUX_CTRL_EARLY_BRESP; 6168c2ecf20Sopenharmony_ci pr_info("L2C-310 enabling early BRESP for Cortex-A9\n"); 6178c2ecf20Sopenharmony_ci } else if (aux & L310_AUX_CTRL_EARLY_BRESP) { 6188c2ecf20Sopenharmony_ci pr_warn("L2C-310 early BRESP only supported with Cortex-A9\n"); 6198c2ecf20Sopenharmony_ci aux &= ~L310_AUX_CTRL_EARLY_BRESP; 6208c2ecf20Sopenharmony_ci } 6218c2ecf20Sopenharmony_ci } 6228c2ecf20Sopenharmony_ci 6238c2ecf20Sopenharmony_ci if (cortex_a9 && !l2x0_flz_disable) { 6248c2ecf20Sopenharmony_ci u32 aux_cur = readl_relaxed(base + L2X0_AUX_CTRL); 6258c2ecf20Sopenharmony_ci u32 acr = get_auxcr(); 6268c2ecf20Sopenharmony_ci 6278c2ecf20Sopenharmony_ci pr_debug("Cortex-A9 ACR=0x%08x\n", acr); 6288c2ecf20Sopenharmony_ci 6298c2ecf20Sopenharmony_ci if (acr & BIT(3) && !(aux_cur & L310_AUX_CTRL_FULL_LINE_ZERO)) 6308c2ecf20Sopenharmony_ci pr_err("L2C-310: full line of zeros enabled in Cortex-A9 but not L2C-310 - invalid\n"); 6318c2ecf20Sopenharmony_ci 6328c2ecf20Sopenharmony_ci if (aux & L310_AUX_CTRL_FULL_LINE_ZERO && !(acr & BIT(3))) 6338c2ecf20Sopenharmony_ci pr_err("L2C-310: enabling full line of zeros but not enabled in Cortex-A9\n"); 6348c2ecf20Sopenharmony_ci 6358c2ecf20Sopenharmony_ci if (!(aux & L310_AUX_CTRL_FULL_LINE_ZERO) && !outer_cache.write_sec) { 6368c2ecf20Sopenharmony_ci aux |= L310_AUX_CTRL_FULL_LINE_ZERO; 6378c2ecf20Sopenharmony_ci pr_info("L2C-310 full line of zeros enabled for Cortex-A9\n"); 6388c2ecf20Sopenharmony_ci } 6398c2ecf20Sopenharmony_ci } else if (aux & (L310_AUX_CTRL_FULL_LINE_ZERO | L310_AUX_CTRL_EARLY_BRESP)) { 6408c2ecf20Sopenharmony_ci pr_err("L2C-310: disabling Cortex-A9 specific feature bits\n"); 6418c2ecf20Sopenharmony_ci aux &= ~(L310_AUX_CTRL_FULL_LINE_ZERO | L310_AUX_CTRL_EARLY_BRESP); 6428c2ecf20Sopenharmony_ci } 6438c2ecf20Sopenharmony_ci 6448c2ecf20Sopenharmony_ci /* 6458c2ecf20Sopenharmony_ci * Always enable non-secure access to the lockdown registers - 6468c2ecf20Sopenharmony_ci * we write to them as part of the L2C enable sequence so they 6478c2ecf20Sopenharmony_ci * need to be accessible. 6488c2ecf20Sopenharmony_ci */ 6498c2ecf20Sopenharmony_ci l2x0_saved_regs.aux_ctrl = aux | L310_AUX_CTRL_NS_LOCKDOWN; 6508c2ecf20Sopenharmony_ci 6518c2ecf20Sopenharmony_ci l2c_enable(base, num_lock); 6528c2ecf20Sopenharmony_ci 6538c2ecf20Sopenharmony_ci /* Read back resulting AUX_CTRL value as it could have been altered. */ 6548c2ecf20Sopenharmony_ci aux = readl_relaxed(base + L2X0_AUX_CTRL); 6558c2ecf20Sopenharmony_ci 6568c2ecf20Sopenharmony_ci if (aux & (L310_AUX_CTRL_DATA_PREFETCH | L310_AUX_CTRL_INSTR_PREFETCH)) { 6578c2ecf20Sopenharmony_ci u32 prefetch = readl_relaxed(base + L310_PREFETCH_CTRL); 6588c2ecf20Sopenharmony_ci 6598c2ecf20Sopenharmony_ci pr_info("L2C-310 %s%s prefetch enabled, offset %u lines\n", 6608c2ecf20Sopenharmony_ci aux & L310_AUX_CTRL_INSTR_PREFETCH ? "I" : "", 6618c2ecf20Sopenharmony_ci aux & L310_AUX_CTRL_DATA_PREFETCH ? "D" : "", 6628c2ecf20Sopenharmony_ci 1 + (prefetch & L310_PREFETCH_CTRL_OFFSET_MASK)); 6638c2ecf20Sopenharmony_ci } 6648c2ecf20Sopenharmony_ci 6658c2ecf20Sopenharmony_ci /* r3p0 or later has power control register */ 6668c2ecf20Sopenharmony_ci if (rev >= L310_CACHE_ID_RTL_R3P0) { 6678c2ecf20Sopenharmony_ci u32 power_ctrl; 6688c2ecf20Sopenharmony_ci 6698c2ecf20Sopenharmony_ci power_ctrl = readl_relaxed(base + L310_POWER_CTRL); 6708c2ecf20Sopenharmony_ci pr_info("L2C-310 dynamic clock gating %sabled, standby mode %sabled\n", 6718c2ecf20Sopenharmony_ci power_ctrl & L310_DYNAMIC_CLK_GATING_EN ? "en" : "dis", 6728c2ecf20Sopenharmony_ci power_ctrl & L310_STNDBY_MODE_EN ? "en" : "dis"); 6738c2ecf20Sopenharmony_ci } 6748c2ecf20Sopenharmony_ci 6758c2ecf20Sopenharmony_ci if (aux & L310_AUX_CTRL_FULL_LINE_ZERO) 6768c2ecf20Sopenharmony_ci cpuhp_setup_state(CPUHP_AP_ARM_L2X0_STARTING, 6778c2ecf20Sopenharmony_ci "arm/l2x0:starting", l2c310_starting_cpu, 6788c2ecf20Sopenharmony_ci l2c310_dying_cpu); 6798c2ecf20Sopenharmony_ci} 6808c2ecf20Sopenharmony_ci 6818c2ecf20Sopenharmony_cistatic void __init l2c310_fixup(void __iomem *base, u32 cache_id, 6828c2ecf20Sopenharmony_ci struct outer_cache_fns *fns) 6838c2ecf20Sopenharmony_ci{ 6848c2ecf20Sopenharmony_ci unsigned revision = cache_id & L2X0_CACHE_ID_RTL_MASK; 6858c2ecf20Sopenharmony_ci const char *errata[8]; 6868c2ecf20Sopenharmony_ci unsigned n = 0; 6878c2ecf20Sopenharmony_ci 6888c2ecf20Sopenharmony_ci if (IS_ENABLED(CONFIG_PL310_ERRATA_588369) && 6898c2ecf20Sopenharmony_ci revision < L310_CACHE_ID_RTL_R2P0 && 6908c2ecf20Sopenharmony_ci /* For bcm compatibility */ 6918c2ecf20Sopenharmony_ci fns->inv_range == l2c210_inv_range) { 6928c2ecf20Sopenharmony_ci fns->inv_range = l2c310_inv_range_erratum; 6938c2ecf20Sopenharmony_ci fns->flush_range = l2c310_flush_range_erratum; 6948c2ecf20Sopenharmony_ci errata[n++] = "588369"; 6958c2ecf20Sopenharmony_ci } 6968c2ecf20Sopenharmony_ci 6978c2ecf20Sopenharmony_ci if (IS_ENABLED(CONFIG_PL310_ERRATA_727915) && 6988c2ecf20Sopenharmony_ci revision >= L310_CACHE_ID_RTL_R2P0 && 6998c2ecf20Sopenharmony_ci revision < L310_CACHE_ID_RTL_R3P1) { 7008c2ecf20Sopenharmony_ci fns->flush_all = l2c310_flush_all_erratum; 7018c2ecf20Sopenharmony_ci errata[n++] = "727915"; 7028c2ecf20Sopenharmony_ci } 7038c2ecf20Sopenharmony_ci 7048c2ecf20Sopenharmony_ci if (revision >= L310_CACHE_ID_RTL_R3P0 && 7058c2ecf20Sopenharmony_ci revision < L310_CACHE_ID_RTL_R3P2) { 7068c2ecf20Sopenharmony_ci u32 val = l2x0_saved_regs.prefetch_ctrl; 7078c2ecf20Sopenharmony_ci if (val & L310_PREFETCH_CTRL_DBL_LINEFILL) { 7088c2ecf20Sopenharmony_ci val &= ~L310_PREFETCH_CTRL_DBL_LINEFILL; 7098c2ecf20Sopenharmony_ci l2x0_saved_regs.prefetch_ctrl = val; 7108c2ecf20Sopenharmony_ci errata[n++] = "752271"; 7118c2ecf20Sopenharmony_ci } 7128c2ecf20Sopenharmony_ci } 7138c2ecf20Sopenharmony_ci 7148c2ecf20Sopenharmony_ci if (IS_ENABLED(CONFIG_PL310_ERRATA_753970) && 7158c2ecf20Sopenharmony_ci revision == L310_CACHE_ID_RTL_R3P0) { 7168c2ecf20Sopenharmony_ci sync_reg_offset = L2X0_DUMMY_REG; 7178c2ecf20Sopenharmony_ci errata[n++] = "753970"; 7188c2ecf20Sopenharmony_ci } 7198c2ecf20Sopenharmony_ci 7208c2ecf20Sopenharmony_ci if (IS_ENABLED(CONFIG_PL310_ERRATA_769419)) 7218c2ecf20Sopenharmony_ci errata[n++] = "769419"; 7228c2ecf20Sopenharmony_ci 7238c2ecf20Sopenharmony_ci if (n) { 7248c2ecf20Sopenharmony_ci unsigned i; 7258c2ecf20Sopenharmony_ci 7268c2ecf20Sopenharmony_ci pr_info("L2C-310 errat%s", n > 1 ? "a" : "um"); 7278c2ecf20Sopenharmony_ci for (i = 0; i < n; i++) 7288c2ecf20Sopenharmony_ci pr_cont(" %s", errata[i]); 7298c2ecf20Sopenharmony_ci pr_cont(" enabled\n"); 7308c2ecf20Sopenharmony_ci } 7318c2ecf20Sopenharmony_ci} 7328c2ecf20Sopenharmony_ci 7338c2ecf20Sopenharmony_cistatic void l2c310_disable(void) 7348c2ecf20Sopenharmony_ci{ 7358c2ecf20Sopenharmony_ci /* 7368c2ecf20Sopenharmony_ci * If full-line-of-zeros is enabled, we must first disable it in the 7378c2ecf20Sopenharmony_ci * Cortex-A9 auxiliary control register before disabling the L2 cache. 7388c2ecf20Sopenharmony_ci */ 7398c2ecf20Sopenharmony_ci if (l2x0_saved_regs.aux_ctrl & L310_AUX_CTRL_FULL_LINE_ZERO) 7408c2ecf20Sopenharmony_ci set_auxcr(get_auxcr() & ~(BIT(3) | BIT(2) | BIT(1))); 7418c2ecf20Sopenharmony_ci 7428c2ecf20Sopenharmony_ci l2c_disable(); 7438c2ecf20Sopenharmony_ci} 7448c2ecf20Sopenharmony_ci 7458c2ecf20Sopenharmony_cistatic void l2c310_resume(void) 7468c2ecf20Sopenharmony_ci{ 7478c2ecf20Sopenharmony_ci l2c_resume(); 7488c2ecf20Sopenharmony_ci 7498c2ecf20Sopenharmony_ci /* Re-enable full-line-of-zeros for Cortex-A9 */ 7508c2ecf20Sopenharmony_ci if (l2x0_saved_regs.aux_ctrl & L310_AUX_CTRL_FULL_LINE_ZERO) 7518c2ecf20Sopenharmony_ci set_auxcr(get_auxcr() | BIT(3) | BIT(2) | BIT(1)); 7528c2ecf20Sopenharmony_ci} 7538c2ecf20Sopenharmony_ci 7548c2ecf20Sopenharmony_cistatic void l2c310_unlock(void __iomem *base, unsigned num_lock) 7558c2ecf20Sopenharmony_ci{ 7568c2ecf20Sopenharmony_ci if (readl_relaxed(base + L2X0_AUX_CTRL) & L310_AUX_CTRL_NS_LOCKDOWN) 7578c2ecf20Sopenharmony_ci l2c_unlock(base, num_lock); 7588c2ecf20Sopenharmony_ci} 7598c2ecf20Sopenharmony_ci 7608c2ecf20Sopenharmony_cistatic const struct l2c_init_data l2c310_init_fns __initconst = { 7618c2ecf20Sopenharmony_ci .type = "L2C-310", 7628c2ecf20Sopenharmony_ci .way_size_0 = SZ_8K, 7638c2ecf20Sopenharmony_ci .num_lock = 8, 7648c2ecf20Sopenharmony_ci .enable = l2c310_enable, 7658c2ecf20Sopenharmony_ci .fixup = l2c310_fixup, 7668c2ecf20Sopenharmony_ci .save = l2c310_save, 7678c2ecf20Sopenharmony_ci .configure = l2c310_configure, 7688c2ecf20Sopenharmony_ci .unlock = l2c310_unlock, 7698c2ecf20Sopenharmony_ci .outer_cache = { 7708c2ecf20Sopenharmony_ci .inv_range = l2c210_inv_range, 7718c2ecf20Sopenharmony_ci .clean_range = l2c210_clean_range, 7728c2ecf20Sopenharmony_ci .flush_range = l2c210_flush_range, 7738c2ecf20Sopenharmony_ci .flush_all = l2c210_flush_all, 7748c2ecf20Sopenharmony_ci .disable = l2c310_disable, 7758c2ecf20Sopenharmony_ci .sync = l2c210_sync, 7768c2ecf20Sopenharmony_ci .resume = l2c310_resume, 7778c2ecf20Sopenharmony_ci }, 7788c2ecf20Sopenharmony_ci}; 7798c2ecf20Sopenharmony_ci 7808c2ecf20Sopenharmony_cistatic int __init __l2c_init(const struct l2c_init_data *data, 7818c2ecf20Sopenharmony_ci u32 aux_val, u32 aux_mask, u32 cache_id, bool nosync) 7828c2ecf20Sopenharmony_ci{ 7838c2ecf20Sopenharmony_ci struct outer_cache_fns fns; 7848c2ecf20Sopenharmony_ci unsigned way_size_bits, ways; 7858c2ecf20Sopenharmony_ci u32 aux, old_aux; 7868c2ecf20Sopenharmony_ci 7878c2ecf20Sopenharmony_ci /* 7888c2ecf20Sopenharmony_ci * Save the pointer globally so that callbacks which do not receive 7898c2ecf20Sopenharmony_ci * context from callers can access the structure. 7908c2ecf20Sopenharmony_ci */ 7918c2ecf20Sopenharmony_ci l2x0_data = kmemdup(data, sizeof(*data), GFP_KERNEL); 7928c2ecf20Sopenharmony_ci if (!l2x0_data) 7938c2ecf20Sopenharmony_ci return -ENOMEM; 7948c2ecf20Sopenharmony_ci 7958c2ecf20Sopenharmony_ci /* 7968c2ecf20Sopenharmony_ci * Sanity check the aux values. aux_mask is the bits we preserve 7978c2ecf20Sopenharmony_ci * from reading the hardware register, and aux_val is the bits we 7988c2ecf20Sopenharmony_ci * set. 7998c2ecf20Sopenharmony_ci */ 8008c2ecf20Sopenharmony_ci if (aux_val & aux_mask) 8018c2ecf20Sopenharmony_ci pr_alert("L2C: platform provided aux values permit register corruption.\n"); 8028c2ecf20Sopenharmony_ci 8038c2ecf20Sopenharmony_ci old_aux = aux = readl_relaxed(l2x0_base + L2X0_AUX_CTRL); 8048c2ecf20Sopenharmony_ci aux &= aux_mask; 8058c2ecf20Sopenharmony_ci aux |= aux_val; 8068c2ecf20Sopenharmony_ci 8078c2ecf20Sopenharmony_ci if (old_aux != aux) 8088c2ecf20Sopenharmony_ci pr_warn("L2C: DT/platform modifies aux control register: 0x%08x -> 0x%08x\n", 8098c2ecf20Sopenharmony_ci old_aux, aux); 8108c2ecf20Sopenharmony_ci 8118c2ecf20Sopenharmony_ci /* Determine the number of ways */ 8128c2ecf20Sopenharmony_ci switch (cache_id & L2X0_CACHE_ID_PART_MASK) { 8138c2ecf20Sopenharmony_ci case L2X0_CACHE_ID_PART_L310: 8148c2ecf20Sopenharmony_ci if ((aux_val | ~aux_mask) & (L2C_AUX_CTRL_WAY_SIZE_MASK | L310_AUX_CTRL_ASSOCIATIVITY_16)) 8158c2ecf20Sopenharmony_ci pr_warn("L2C: DT/platform tries to modify or specify cache size\n"); 8168c2ecf20Sopenharmony_ci if (aux & (1 << 16)) 8178c2ecf20Sopenharmony_ci ways = 16; 8188c2ecf20Sopenharmony_ci else 8198c2ecf20Sopenharmony_ci ways = 8; 8208c2ecf20Sopenharmony_ci break; 8218c2ecf20Sopenharmony_ci 8228c2ecf20Sopenharmony_ci case L2X0_CACHE_ID_PART_L210: 8238c2ecf20Sopenharmony_ci case L2X0_CACHE_ID_PART_L220: 8248c2ecf20Sopenharmony_ci ways = (aux >> 13) & 0xf; 8258c2ecf20Sopenharmony_ci break; 8268c2ecf20Sopenharmony_ci 8278c2ecf20Sopenharmony_ci case AURORA_CACHE_ID: 8288c2ecf20Sopenharmony_ci ways = (aux >> 13) & 0xf; 8298c2ecf20Sopenharmony_ci ways = 2 << ((ways + 1) >> 2); 8308c2ecf20Sopenharmony_ci break; 8318c2ecf20Sopenharmony_ci 8328c2ecf20Sopenharmony_ci default: 8338c2ecf20Sopenharmony_ci /* Assume unknown chips have 8 ways */ 8348c2ecf20Sopenharmony_ci ways = 8; 8358c2ecf20Sopenharmony_ci break; 8368c2ecf20Sopenharmony_ci } 8378c2ecf20Sopenharmony_ci 8388c2ecf20Sopenharmony_ci l2x0_way_mask = (1 << ways) - 1; 8398c2ecf20Sopenharmony_ci 8408c2ecf20Sopenharmony_ci /* 8418c2ecf20Sopenharmony_ci * way_size_0 is the size that a way_size value of zero would be 8428c2ecf20Sopenharmony_ci * given the calculation: way_size = way_size_0 << way_size_bits. 8438c2ecf20Sopenharmony_ci * So, if way_size_bits=0 is reserved, but way_size_bits=1 is 16k, 8448c2ecf20Sopenharmony_ci * then way_size_0 would be 8k. 8458c2ecf20Sopenharmony_ci * 8468c2ecf20Sopenharmony_ci * L2 cache size = number of ways * way size. 8478c2ecf20Sopenharmony_ci */ 8488c2ecf20Sopenharmony_ci way_size_bits = (aux & L2C_AUX_CTRL_WAY_SIZE_MASK) >> 8498c2ecf20Sopenharmony_ci L2C_AUX_CTRL_WAY_SIZE_SHIFT; 8508c2ecf20Sopenharmony_ci l2x0_size = ways * (data->way_size_0 << way_size_bits); 8518c2ecf20Sopenharmony_ci 8528c2ecf20Sopenharmony_ci fns = data->outer_cache; 8538c2ecf20Sopenharmony_ci fns.write_sec = outer_cache.write_sec; 8548c2ecf20Sopenharmony_ci fns.configure = outer_cache.configure; 8558c2ecf20Sopenharmony_ci if (data->fixup) 8568c2ecf20Sopenharmony_ci data->fixup(l2x0_base, cache_id, &fns); 8578c2ecf20Sopenharmony_ci if (nosync) { 8588c2ecf20Sopenharmony_ci pr_info("L2C: disabling outer sync\n"); 8598c2ecf20Sopenharmony_ci fns.sync = NULL; 8608c2ecf20Sopenharmony_ci } 8618c2ecf20Sopenharmony_ci 8628c2ecf20Sopenharmony_ci /* 8638c2ecf20Sopenharmony_ci * Check if l2x0 controller is already enabled. If we are booting 8648c2ecf20Sopenharmony_ci * in non-secure mode accessing the below registers will fault. 8658c2ecf20Sopenharmony_ci */ 8668c2ecf20Sopenharmony_ci if (!(readl_relaxed(l2x0_base + L2X0_CTRL) & L2X0_CTRL_EN)) { 8678c2ecf20Sopenharmony_ci l2x0_saved_regs.aux_ctrl = aux; 8688c2ecf20Sopenharmony_ci 8698c2ecf20Sopenharmony_ci data->enable(l2x0_base, data->num_lock); 8708c2ecf20Sopenharmony_ci } 8718c2ecf20Sopenharmony_ci 8728c2ecf20Sopenharmony_ci outer_cache = fns; 8738c2ecf20Sopenharmony_ci 8748c2ecf20Sopenharmony_ci /* 8758c2ecf20Sopenharmony_ci * It is strange to save the register state before initialisation, 8768c2ecf20Sopenharmony_ci * but hey, this is what the DT implementations decided to do. 8778c2ecf20Sopenharmony_ci */ 8788c2ecf20Sopenharmony_ci if (data->save) 8798c2ecf20Sopenharmony_ci data->save(l2x0_base); 8808c2ecf20Sopenharmony_ci 8818c2ecf20Sopenharmony_ci /* Re-read it in case some bits are reserved. */ 8828c2ecf20Sopenharmony_ci aux = readl_relaxed(l2x0_base + L2X0_AUX_CTRL); 8838c2ecf20Sopenharmony_ci 8848c2ecf20Sopenharmony_ci pr_info("%s cache controller enabled, %d ways, %d kB\n", 8858c2ecf20Sopenharmony_ci data->type, ways, l2x0_size >> 10); 8868c2ecf20Sopenharmony_ci pr_info("%s: CACHE_ID 0x%08x, AUX_CTRL 0x%08x\n", 8878c2ecf20Sopenharmony_ci data->type, cache_id, aux); 8888c2ecf20Sopenharmony_ci 8898c2ecf20Sopenharmony_ci l2x0_pmu_register(l2x0_base, cache_id); 8908c2ecf20Sopenharmony_ci 8918c2ecf20Sopenharmony_ci return 0; 8928c2ecf20Sopenharmony_ci} 8938c2ecf20Sopenharmony_ci 8948c2ecf20Sopenharmony_civoid __init l2x0_init(void __iomem *base, u32 aux_val, u32 aux_mask) 8958c2ecf20Sopenharmony_ci{ 8968c2ecf20Sopenharmony_ci const struct l2c_init_data *data; 8978c2ecf20Sopenharmony_ci u32 cache_id; 8988c2ecf20Sopenharmony_ci 8998c2ecf20Sopenharmony_ci l2x0_base = base; 9008c2ecf20Sopenharmony_ci 9018c2ecf20Sopenharmony_ci cache_id = readl_relaxed(base + L2X0_CACHE_ID); 9028c2ecf20Sopenharmony_ci 9038c2ecf20Sopenharmony_ci switch (cache_id & L2X0_CACHE_ID_PART_MASK) { 9048c2ecf20Sopenharmony_ci default: 9058c2ecf20Sopenharmony_ci case L2X0_CACHE_ID_PART_L210: 9068c2ecf20Sopenharmony_ci data = &l2c210_data; 9078c2ecf20Sopenharmony_ci break; 9088c2ecf20Sopenharmony_ci 9098c2ecf20Sopenharmony_ci case L2X0_CACHE_ID_PART_L220: 9108c2ecf20Sopenharmony_ci data = &l2c220_data; 9118c2ecf20Sopenharmony_ci break; 9128c2ecf20Sopenharmony_ci 9138c2ecf20Sopenharmony_ci case L2X0_CACHE_ID_PART_L310: 9148c2ecf20Sopenharmony_ci data = &l2c310_init_fns; 9158c2ecf20Sopenharmony_ci break; 9168c2ecf20Sopenharmony_ci } 9178c2ecf20Sopenharmony_ci 9188c2ecf20Sopenharmony_ci /* Read back current (default) hardware configuration */ 9198c2ecf20Sopenharmony_ci if (data->save) 9208c2ecf20Sopenharmony_ci data->save(l2x0_base); 9218c2ecf20Sopenharmony_ci 9228c2ecf20Sopenharmony_ci __l2c_init(data, aux_val, aux_mask, cache_id, false); 9238c2ecf20Sopenharmony_ci} 9248c2ecf20Sopenharmony_ci 9258c2ecf20Sopenharmony_ci#ifdef CONFIG_OF 9268c2ecf20Sopenharmony_cistatic int l2_wt_override; 9278c2ecf20Sopenharmony_ci 9288c2ecf20Sopenharmony_ci/* Aurora don't have the cache ID register available, so we have to 9298c2ecf20Sopenharmony_ci * pass it though the device tree */ 9308c2ecf20Sopenharmony_cistatic u32 cache_id_part_number_from_dt; 9318c2ecf20Sopenharmony_ci 9328c2ecf20Sopenharmony_ci/** 9338c2ecf20Sopenharmony_ci * l2x0_cache_size_of_parse() - read cache size parameters from DT 9348c2ecf20Sopenharmony_ci * @np: the device tree node for the l2 cache 9358c2ecf20Sopenharmony_ci * @aux_val: pointer to machine-supplied auxilary register value, to 9368c2ecf20Sopenharmony_ci * be augmented by the call (bits to be set to 1) 9378c2ecf20Sopenharmony_ci * @aux_mask: pointer to machine-supplied auxilary register mask, to 9388c2ecf20Sopenharmony_ci * be augmented by the call (bits to be set to 0) 9398c2ecf20Sopenharmony_ci * @associativity: variable to return the calculated associativity in 9408c2ecf20Sopenharmony_ci * @max_way_size: the maximum size in bytes for the cache ways 9418c2ecf20Sopenharmony_ci */ 9428c2ecf20Sopenharmony_cistatic int __init l2x0_cache_size_of_parse(const struct device_node *np, 9438c2ecf20Sopenharmony_ci u32 *aux_val, u32 *aux_mask, 9448c2ecf20Sopenharmony_ci u32 *associativity, 9458c2ecf20Sopenharmony_ci u32 max_way_size) 9468c2ecf20Sopenharmony_ci{ 9478c2ecf20Sopenharmony_ci u32 mask = 0, val = 0; 9488c2ecf20Sopenharmony_ci u32 cache_size = 0, sets = 0; 9498c2ecf20Sopenharmony_ci u32 way_size_bits = 1; 9508c2ecf20Sopenharmony_ci u32 way_size = 0; 9518c2ecf20Sopenharmony_ci u32 block_size = 0; 9528c2ecf20Sopenharmony_ci u32 line_size = 0; 9538c2ecf20Sopenharmony_ci 9548c2ecf20Sopenharmony_ci of_property_read_u32(np, "cache-size", &cache_size); 9558c2ecf20Sopenharmony_ci of_property_read_u32(np, "cache-sets", &sets); 9568c2ecf20Sopenharmony_ci of_property_read_u32(np, "cache-block-size", &block_size); 9578c2ecf20Sopenharmony_ci of_property_read_u32(np, "cache-line-size", &line_size); 9588c2ecf20Sopenharmony_ci 9598c2ecf20Sopenharmony_ci if (!cache_size || !sets) 9608c2ecf20Sopenharmony_ci return -ENODEV; 9618c2ecf20Sopenharmony_ci 9628c2ecf20Sopenharmony_ci /* All these l2 caches have the same line = block size actually */ 9638c2ecf20Sopenharmony_ci if (!line_size) { 9648c2ecf20Sopenharmony_ci if (block_size) { 9658c2ecf20Sopenharmony_ci /* If linesize is not given, it is equal to blocksize */ 9668c2ecf20Sopenharmony_ci line_size = block_size; 9678c2ecf20Sopenharmony_ci } else { 9688c2ecf20Sopenharmony_ci /* Fall back to known size */ 9698c2ecf20Sopenharmony_ci pr_warn("L2C OF: no cache block/line size given: " 9708c2ecf20Sopenharmony_ci "falling back to default size %d bytes\n", 9718c2ecf20Sopenharmony_ci CACHE_LINE_SIZE); 9728c2ecf20Sopenharmony_ci line_size = CACHE_LINE_SIZE; 9738c2ecf20Sopenharmony_ci } 9748c2ecf20Sopenharmony_ci } 9758c2ecf20Sopenharmony_ci 9768c2ecf20Sopenharmony_ci if (line_size != CACHE_LINE_SIZE) 9778c2ecf20Sopenharmony_ci pr_warn("L2C OF: DT supplied line size %d bytes does " 9788c2ecf20Sopenharmony_ci "not match hardware line size of %d bytes\n", 9798c2ecf20Sopenharmony_ci line_size, 9808c2ecf20Sopenharmony_ci CACHE_LINE_SIZE); 9818c2ecf20Sopenharmony_ci 9828c2ecf20Sopenharmony_ci /* 9838c2ecf20Sopenharmony_ci * Since: 9848c2ecf20Sopenharmony_ci * set size = cache size / sets 9858c2ecf20Sopenharmony_ci * ways = cache size / (sets * line size) 9868c2ecf20Sopenharmony_ci * way size = cache size / (cache size / (sets * line size)) 9878c2ecf20Sopenharmony_ci * way size = sets * line size 9888c2ecf20Sopenharmony_ci * associativity = ways = cache size / way size 9898c2ecf20Sopenharmony_ci */ 9908c2ecf20Sopenharmony_ci way_size = sets * line_size; 9918c2ecf20Sopenharmony_ci *associativity = cache_size / way_size; 9928c2ecf20Sopenharmony_ci 9938c2ecf20Sopenharmony_ci if (way_size > max_way_size) { 9948c2ecf20Sopenharmony_ci pr_err("L2C OF: set size %dKB is too large\n", way_size); 9958c2ecf20Sopenharmony_ci return -EINVAL; 9968c2ecf20Sopenharmony_ci } 9978c2ecf20Sopenharmony_ci 9988c2ecf20Sopenharmony_ci pr_info("L2C OF: override cache size: %d bytes (%dKB)\n", 9998c2ecf20Sopenharmony_ci cache_size, cache_size >> 10); 10008c2ecf20Sopenharmony_ci pr_info("L2C OF: override line size: %d bytes\n", line_size); 10018c2ecf20Sopenharmony_ci pr_info("L2C OF: override way size: %d bytes (%dKB)\n", 10028c2ecf20Sopenharmony_ci way_size, way_size >> 10); 10038c2ecf20Sopenharmony_ci pr_info("L2C OF: override associativity: %d\n", *associativity); 10048c2ecf20Sopenharmony_ci 10058c2ecf20Sopenharmony_ci /* 10068c2ecf20Sopenharmony_ci * Calculates the bits 17:19 to set for way size: 10078c2ecf20Sopenharmony_ci * 512KB -> 6, 256KB -> 5, ... 16KB -> 1 10088c2ecf20Sopenharmony_ci */ 10098c2ecf20Sopenharmony_ci way_size_bits = ilog2(way_size >> 10) - 3; 10108c2ecf20Sopenharmony_ci if (way_size_bits < 1 || way_size_bits > 6) { 10118c2ecf20Sopenharmony_ci pr_err("L2C OF: cache way size illegal: %dKB is not mapped\n", 10128c2ecf20Sopenharmony_ci way_size); 10138c2ecf20Sopenharmony_ci return -EINVAL; 10148c2ecf20Sopenharmony_ci } 10158c2ecf20Sopenharmony_ci 10168c2ecf20Sopenharmony_ci mask |= L2C_AUX_CTRL_WAY_SIZE_MASK; 10178c2ecf20Sopenharmony_ci val |= (way_size_bits << L2C_AUX_CTRL_WAY_SIZE_SHIFT); 10188c2ecf20Sopenharmony_ci 10198c2ecf20Sopenharmony_ci *aux_val &= ~mask; 10208c2ecf20Sopenharmony_ci *aux_val |= val; 10218c2ecf20Sopenharmony_ci *aux_mask &= ~mask; 10228c2ecf20Sopenharmony_ci 10238c2ecf20Sopenharmony_ci return 0; 10248c2ecf20Sopenharmony_ci} 10258c2ecf20Sopenharmony_ci 10268c2ecf20Sopenharmony_cistatic void __init l2x0_of_parse(const struct device_node *np, 10278c2ecf20Sopenharmony_ci u32 *aux_val, u32 *aux_mask) 10288c2ecf20Sopenharmony_ci{ 10298c2ecf20Sopenharmony_ci u32 data[2] = { 0, 0 }; 10308c2ecf20Sopenharmony_ci u32 tag = 0; 10318c2ecf20Sopenharmony_ci u32 dirty = 0; 10328c2ecf20Sopenharmony_ci u32 val = 0, mask = 0; 10338c2ecf20Sopenharmony_ci u32 assoc; 10348c2ecf20Sopenharmony_ci int ret; 10358c2ecf20Sopenharmony_ci 10368c2ecf20Sopenharmony_ci of_property_read_u32(np, "arm,tag-latency", &tag); 10378c2ecf20Sopenharmony_ci if (tag) { 10388c2ecf20Sopenharmony_ci mask |= L2X0_AUX_CTRL_TAG_LATENCY_MASK; 10398c2ecf20Sopenharmony_ci val |= (tag - 1) << L2X0_AUX_CTRL_TAG_LATENCY_SHIFT; 10408c2ecf20Sopenharmony_ci } 10418c2ecf20Sopenharmony_ci 10428c2ecf20Sopenharmony_ci of_property_read_u32_array(np, "arm,data-latency", 10438c2ecf20Sopenharmony_ci data, ARRAY_SIZE(data)); 10448c2ecf20Sopenharmony_ci if (data[0] && data[1]) { 10458c2ecf20Sopenharmony_ci mask |= L2X0_AUX_CTRL_DATA_RD_LATENCY_MASK | 10468c2ecf20Sopenharmony_ci L2X0_AUX_CTRL_DATA_WR_LATENCY_MASK; 10478c2ecf20Sopenharmony_ci val |= ((data[0] - 1) << L2X0_AUX_CTRL_DATA_RD_LATENCY_SHIFT) | 10488c2ecf20Sopenharmony_ci ((data[1] - 1) << L2X0_AUX_CTRL_DATA_WR_LATENCY_SHIFT); 10498c2ecf20Sopenharmony_ci } 10508c2ecf20Sopenharmony_ci 10518c2ecf20Sopenharmony_ci of_property_read_u32(np, "arm,dirty-latency", &dirty); 10528c2ecf20Sopenharmony_ci if (dirty) { 10538c2ecf20Sopenharmony_ci mask |= L2X0_AUX_CTRL_DIRTY_LATENCY_MASK; 10548c2ecf20Sopenharmony_ci val |= (dirty - 1) << L2X0_AUX_CTRL_DIRTY_LATENCY_SHIFT; 10558c2ecf20Sopenharmony_ci } 10568c2ecf20Sopenharmony_ci 10578c2ecf20Sopenharmony_ci if (of_property_read_bool(np, "arm,parity-enable")) { 10588c2ecf20Sopenharmony_ci mask &= ~L2C_AUX_CTRL_PARITY_ENABLE; 10598c2ecf20Sopenharmony_ci val |= L2C_AUX_CTRL_PARITY_ENABLE; 10608c2ecf20Sopenharmony_ci } else if (of_property_read_bool(np, "arm,parity-disable")) { 10618c2ecf20Sopenharmony_ci mask &= ~L2C_AUX_CTRL_PARITY_ENABLE; 10628c2ecf20Sopenharmony_ci } 10638c2ecf20Sopenharmony_ci 10648c2ecf20Sopenharmony_ci if (of_property_read_bool(np, "arm,shared-override")) { 10658c2ecf20Sopenharmony_ci mask &= ~L2C_AUX_CTRL_SHARED_OVERRIDE; 10668c2ecf20Sopenharmony_ci val |= L2C_AUX_CTRL_SHARED_OVERRIDE; 10678c2ecf20Sopenharmony_ci } 10688c2ecf20Sopenharmony_ci 10698c2ecf20Sopenharmony_ci ret = l2x0_cache_size_of_parse(np, aux_val, aux_mask, &assoc, SZ_256K); 10708c2ecf20Sopenharmony_ci if (ret) 10718c2ecf20Sopenharmony_ci return; 10728c2ecf20Sopenharmony_ci 10738c2ecf20Sopenharmony_ci if (assoc > 8) { 10748c2ecf20Sopenharmony_ci pr_err("l2x0 of: cache setting yield too high associativity\n"); 10758c2ecf20Sopenharmony_ci pr_err("l2x0 of: %d calculated, max 8\n", assoc); 10768c2ecf20Sopenharmony_ci } else { 10778c2ecf20Sopenharmony_ci mask |= L2X0_AUX_CTRL_ASSOC_MASK; 10788c2ecf20Sopenharmony_ci val |= (assoc << L2X0_AUX_CTRL_ASSOC_SHIFT); 10798c2ecf20Sopenharmony_ci } 10808c2ecf20Sopenharmony_ci 10818c2ecf20Sopenharmony_ci *aux_val &= ~mask; 10828c2ecf20Sopenharmony_ci *aux_val |= val; 10838c2ecf20Sopenharmony_ci *aux_mask &= ~mask; 10848c2ecf20Sopenharmony_ci} 10858c2ecf20Sopenharmony_ci 10868c2ecf20Sopenharmony_cistatic const struct l2c_init_data of_l2c210_data __initconst = { 10878c2ecf20Sopenharmony_ci .type = "L2C-210", 10888c2ecf20Sopenharmony_ci .way_size_0 = SZ_8K, 10898c2ecf20Sopenharmony_ci .num_lock = 1, 10908c2ecf20Sopenharmony_ci .of_parse = l2x0_of_parse, 10918c2ecf20Sopenharmony_ci .enable = l2c_enable, 10928c2ecf20Sopenharmony_ci .save = l2c_save, 10938c2ecf20Sopenharmony_ci .configure = l2c_configure, 10948c2ecf20Sopenharmony_ci .unlock = l2c_unlock, 10958c2ecf20Sopenharmony_ci .outer_cache = { 10968c2ecf20Sopenharmony_ci .inv_range = l2c210_inv_range, 10978c2ecf20Sopenharmony_ci .clean_range = l2c210_clean_range, 10988c2ecf20Sopenharmony_ci .flush_range = l2c210_flush_range, 10998c2ecf20Sopenharmony_ci .flush_all = l2c210_flush_all, 11008c2ecf20Sopenharmony_ci .disable = l2c_disable, 11018c2ecf20Sopenharmony_ci .sync = l2c210_sync, 11028c2ecf20Sopenharmony_ci .resume = l2c_resume, 11038c2ecf20Sopenharmony_ci }, 11048c2ecf20Sopenharmony_ci}; 11058c2ecf20Sopenharmony_ci 11068c2ecf20Sopenharmony_cistatic const struct l2c_init_data of_l2c220_data __initconst = { 11078c2ecf20Sopenharmony_ci .type = "L2C-220", 11088c2ecf20Sopenharmony_ci .way_size_0 = SZ_8K, 11098c2ecf20Sopenharmony_ci .num_lock = 1, 11108c2ecf20Sopenharmony_ci .of_parse = l2x0_of_parse, 11118c2ecf20Sopenharmony_ci .enable = l2c220_enable, 11128c2ecf20Sopenharmony_ci .save = l2c_save, 11138c2ecf20Sopenharmony_ci .configure = l2c_configure, 11148c2ecf20Sopenharmony_ci .unlock = l2c220_unlock, 11158c2ecf20Sopenharmony_ci .outer_cache = { 11168c2ecf20Sopenharmony_ci .inv_range = l2c220_inv_range, 11178c2ecf20Sopenharmony_ci .clean_range = l2c220_clean_range, 11188c2ecf20Sopenharmony_ci .flush_range = l2c220_flush_range, 11198c2ecf20Sopenharmony_ci .flush_all = l2c220_flush_all, 11208c2ecf20Sopenharmony_ci .disable = l2c_disable, 11218c2ecf20Sopenharmony_ci .sync = l2c220_sync, 11228c2ecf20Sopenharmony_ci .resume = l2c_resume, 11238c2ecf20Sopenharmony_ci }, 11248c2ecf20Sopenharmony_ci}; 11258c2ecf20Sopenharmony_ci 11268c2ecf20Sopenharmony_cistatic void __init l2c310_of_parse(const struct device_node *np, 11278c2ecf20Sopenharmony_ci u32 *aux_val, u32 *aux_mask) 11288c2ecf20Sopenharmony_ci{ 11298c2ecf20Sopenharmony_ci u32 data[3] = { 0, 0, 0 }; 11308c2ecf20Sopenharmony_ci u32 tag[3] = { 0, 0, 0 }; 11318c2ecf20Sopenharmony_ci u32 filter[2] = { 0, 0 }; 11328c2ecf20Sopenharmony_ci u32 assoc; 11338c2ecf20Sopenharmony_ci u32 prefetch; 11348c2ecf20Sopenharmony_ci u32 power; 11358c2ecf20Sopenharmony_ci u32 val; 11368c2ecf20Sopenharmony_ci int ret; 11378c2ecf20Sopenharmony_ci 11388c2ecf20Sopenharmony_ci of_property_read_u32_array(np, "arm,tag-latency", tag, ARRAY_SIZE(tag)); 11398c2ecf20Sopenharmony_ci if (tag[0] && tag[1] && tag[2]) 11408c2ecf20Sopenharmony_ci l2x0_saved_regs.tag_latency = 11418c2ecf20Sopenharmony_ci L310_LATENCY_CTRL_RD(tag[0] - 1) | 11428c2ecf20Sopenharmony_ci L310_LATENCY_CTRL_WR(tag[1] - 1) | 11438c2ecf20Sopenharmony_ci L310_LATENCY_CTRL_SETUP(tag[2] - 1); 11448c2ecf20Sopenharmony_ci 11458c2ecf20Sopenharmony_ci of_property_read_u32_array(np, "arm,data-latency", 11468c2ecf20Sopenharmony_ci data, ARRAY_SIZE(data)); 11478c2ecf20Sopenharmony_ci if (data[0] && data[1] && data[2]) 11488c2ecf20Sopenharmony_ci l2x0_saved_regs.data_latency = 11498c2ecf20Sopenharmony_ci L310_LATENCY_CTRL_RD(data[0] - 1) | 11508c2ecf20Sopenharmony_ci L310_LATENCY_CTRL_WR(data[1] - 1) | 11518c2ecf20Sopenharmony_ci L310_LATENCY_CTRL_SETUP(data[2] - 1); 11528c2ecf20Sopenharmony_ci 11538c2ecf20Sopenharmony_ci of_property_read_u32_array(np, "arm,filter-ranges", 11548c2ecf20Sopenharmony_ci filter, ARRAY_SIZE(filter)); 11558c2ecf20Sopenharmony_ci if (filter[1]) { 11568c2ecf20Sopenharmony_ci l2x0_saved_regs.filter_end = 11578c2ecf20Sopenharmony_ci ALIGN(filter[0] + filter[1], SZ_1M); 11588c2ecf20Sopenharmony_ci l2x0_saved_regs.filter_start = (filter[0] & ~(SZ_1M - 1)) 11598c2ecf20Sopenharmony_ci | L310_ADDR_FILTER_EN; 11608c2ecf20Sopenharmony_ci } 11618c2ecf20Sopenharmony_ci 11628c2ecf20Sopenharmony_ci ret = l2x0_cache_size_of_parse(np, aux_val, aux_mask, &assoc, SZ_512K); 11638c2ecf20Sopenharmony_ci if (!ret) { 11648c2ecf20Sopenharmony_ci switch (assoc) { 11658c2ecf20Sopenharmony_ci case 16: 11668c2ecf20Sopenharmony_ci *aux_val &= ~L2X0_AUX_CTRL_ASSOC_MASK; 11678c2ecf20Sopenharmony_ci *aux_val |= L310_AUX_CTRL_ASSOCIATIVITY_16; 11688c2ecf20Sopenharmony_ci *aux_mask &= ~L2X0_AUX_CTRL_ASSOC_MASK; 11698c2ecf20Sopenharmony_ci break; 11708c2ecf20Sopenharmony_ci case 8: 11718c2ecf20Sopenharmony_ci *aux_val &= ~L2X0_AUX_CTRL_ASSOC_MASK; 11728c2ecf20Sopenharmony_ci *aux_mask &= ~L2X0_AUX_CTRL_ASSOC_MASK; 11738c2ecf20Sopenharmony_ci break; 11748c2ecf20Sopenharmony_ci default: 11758c2ecf20Sopenharmony_ci pr_err("L2C-310 OF cache associativity %d invalid, only 8 or 16 permitted\n", 11768c2ecf20Sopenharmony_ci assoc); 11778c2ecf20Sopenharmony_ci break; 11788c2ecf20Sopenharmony_ci } 11798c2ecf20Sopenharmony_ci } 11808c2ecf20Sopenharmony_ci 11818c2ecf20Sopenharmony_ci if (of_property_read_bool(np, "arm,shared-override")) { 11828c2ecf20Sopenharmony_ci *aux_val |= L2C_AUX_CTRL_SHARED_OVERRIDE; 11838c2ecf20Sopenharmony_ci *aux_mask &= ~L2C_AUX_CTRL_SHARED_OVERRIDE; 11848c2ecf20Sopenharmony_ci } 11858c2ecf20Sopenharmony_ci 11868c2ecf20Sopenharmony_ci if (of_property_read_bool(np, "arm,parity-enable")) { 11878c2ecf20Sopenharmony_ci *aux_val |= L2C_AUX_CTRL_PARITY_ENABLE; 11888c2ecf20Sopenharmony_ci *aux_mask &= ~L2C_AUX_CTRL_PARITY_ENABLE; 11898c2ecf20Sopenharmony_ci } else if (of_property_read_bool(np, "arm,parity-disable")) { 11908c2ecf20Sopenharmony_ci *aux_val &= ~L2C_AUX_CTRL_PARITY_ENABLE; 11918c2ecf20Sopenharmony_ci *aux_mask &= ~L2C_AUX_CTRL_PARITY_ENABLE; 11928c2ecf20Sopenharmony_ci } 11938c2ecf20Sopenharmony_ci 11948c2ecf20Sopenharmony_ci if (of_property_read_bool(np, "arm,early-bresp-disable")) 11958c2ecf20Sopenharmony_ci l2x0_bresp_disable = true; 11968c2ecf20Sopenharmony_ci 11978c2ecf20Sopenharmony_ci if (of_property_read_bool(np, "arm,full-line-zero-disable")) 11988c2ecf20Sopenharmony_ci l2x0_flz_disable = true; 11998c2ecf20Sopenharmony_ci 12008c2ecf20Sopenharmony_ci prefetch = l2x0_saved_regs.prefetch_ctrl; 12018c2ecf20Sopenharmony_ci 12028c2ecf20Sopenharmony_ci ret = of_property_read_u32(np, "arm,double-linefill", &val); 12038c2ecf20Sopenharmony_ci if (ret == 0) { 12048c2ecf20Sopenharmony_ci if (val) 12058c2ecf20Sopenharmony_ci prefetch |= L310_PREFETCH_CTRL_DBL_LINEFILL; 12068c2ecf20Sopenharmony_ci else 12078c2ecf20Sopenharmony_ci prefetch &= ~L310_PREFETCH_CTRL_DBL_LINEFILL; 12088c2ecf20Sopenharmony_ci } else if (ret != -EINVAL) { 12098c2ecf20Sopenharmony_ci pr_err("L2C-310 OF arm,double-linefill property value is missing\n"); 12108c2ecf20Sopenharmony_ci } 12118c2ecf20Sopenharmony_ci 12128c2ecf20Sopenharmony_ci ret = of_property_read_u32(np, "arm,double-linefill-incr", &val); 12138c2ecf20Sopenharmony_ci if (ret == 0) { 12148c2ecf20Sopenharmony_ci if (val) 12158c2ecf20Sopenharmony_ci prefetch |= L310_PREFETCH_CTRL_DBL_LINEFILL_INCR; 12168c2ecf20Sopenharmony_ci else 12178c2ecf20Sopenharmony_ci prefetch &= ~L310_PREFETCH_CTRL_DBL_LINEFILL_INCR; 12188c2ecf20Sopenharmony_ci } else if (ret != -EINVAL) { 12198c2ecf20Sopenharmony_ci pr_err("L2C-310 OF arm,double-linefill-incr property value is missing\n"); 12208c2ecf20Sopenharmony_ci } 12218c2ecf20Sopenharmony_ci 12228c2ecf20Sopenharmony_ci ret = of_property_read_u32(np, "arm,double-linefill-wrap", &val); 12238c2ecf20Sopenharmony_ci if (ret == 0) { 12248c2ecf20Sopenharmony_ci if (!val) 12258c2ecf20Sopenharmony_ci prefetch |= L310_PREFETCH_CTRL_DBL_LINEFILL_WRAP; 12268c2ecf20Sopenharmony_ci else 12278c2ecf20Sopenharmony_ci prefetch &= ~L310_PREFETCH_CTRL_DBL_LINEFILL_WRAP; 12288c2ecf20Sopenharmony_ci } else if (ret != -EINVAL) { 12298c2ecf20Sopenharmony_ci pr_err("L2C-310 OF arm,double-linefill-wrap property value is missing\n"); 12308c2ecf20Sopenharmony_ci } 12318c2ecf20Sopenharmony_ci 12328c2ecf20Sopenharmony_ci ret = of_property_read_u32(np, "arm,prefetch-drop", &val); 12338c2ecf20Sopenharmony_ci if (ret == 0) { 12348c2ecf20Sopenharmony_ci if (val) 12358c2ecf20Sopenharmony_ci prefetch |= L310_PREFETCH_CTRL_PREFETCH_DROP; 12368c2ecf20Sopenharmony_ci else 12378c2ecf20Sopenharmony_ci prefetch &= ~L310_PREFETCH_CTRL_PREFETCH_DROP; 12388c2ecf20Sopenharmony_ci } else if (ret != -EINVAL) { 12398c2ecf20Sopenharmony_ci pr_err("L2C-310 OF arm,prefetch-drop property value is missing\n"); 12408c2ecf20Sopenharmony_ci } 12418c2ecf20Sopenharmony_ci 12428c2ecf20Sopenharmony_ci ret = of_property_read_u32(np, "arm,prefetch-offset", &val); 12438c2ecf20Sopenharmony_ci if (ret == 0) { 12448c2ecf20Sopenharmony_ci prefetch &= ~L310_PREFETCH_CTRL_OFFSET_MASK; 12458c2ecf20Sopenharmony_ci prefetch |= val & L310_PREFETCH_CTRL_OFFSET_MASK; 12468c2ecf20Sopenharmony_ci } else if (ret != -EINVAL) { 12478c2ecf20Sopenharmony_ci pr_err("L2C-310 OF arm,prefetch-offset property value is missing\n"); 12488c2ecf20Sopenharmony_ci } 12498c2ecf20Sopenharmony_ci 12508c2ecf20Sopenharmony_ci ret = of_property_read_u32(np, "prefetch-data", &val); 12518c2ecf20Sopenharmony_ci if (ret == 0) { 12528c2ecf20Sopenharmony_ci if (val) { 12538c2ecf20Sopenharmony_ci prefetch |= L310_PREFETCH_CTRL_DATA_PREFETCH; 12548c2ecf20Sopenharmony_ci *aux_val |= L310_PREFETCH_CTRL_DATA_PREFETCH; 12558c2ecf20Sopenharmony_ci } else { 12568c2ecf20Sopenharmony_ci prefetch &= ~L310_PREFETCH_CTRL_DATA_PREFETCH; 12578c2ecf20Sopenharmony_ci *aux_val &= ~L310_PREFETCH_CTRL_DATA_PREFETCH; 12588c2ecf20Sopenharmony_ci } 12598c2ecf20Sopenharmony_ci *aux_mask &= ~L310_PREFETCH_CTRL_DATA_PREFETCH; 12608c2ecf20Sopenharmony_ci } else if (ret != -EINVAL) { 12618c2ecf20Sopenharmony_ci pr_err("L2C-310 OF prefetch-data property value is missing\n"); 12628c2ecf20Sopenharmony_ci } 12638c2ecf20Sopenharmony_ci 12648c2ecf20Sopenharmony_ci ret = of_property_read_u32(np, "prefetch-instr", &val); 12658c2ecf20Sopenharmony_ci if (ret == 0) { 12668c2ecf20Sopenharmony_ci if (val) { 12678c2ecf20Sopenharmony_ci prefetch |= L310_PREFETCH_CTRL_INSTR_PREFETCH; 12688c2ecf20Sopenharmony_ci *aux_val |= L310_PREFETCH_CTRL_INSTR_PREFETCH; 12698c2ecf20Sopenharmony_ci } else { 12708c2ecf20Sopenharmony_ci prefetch &= ~L310_PREFETCH_CTRL_INSTR_PREFETCH; 12718c2ecf20Sopenharmony_ci *aux_val &= ~L310_PREFETCH_CTRL_INSTR_PREFETCH; 12728c2ecf20Sopenharmony_ci } 12738c2ecf20Sopenharmony_ci *aux_mask &= ~L310_PREFETCH_CTRL_INSTR_PREFETCH; 12748c2ecf20Sopenharmony_ci } else if (ret != -EINVAL) { 12758c2ecf20Sopenharmony_ci pr_err("L2C-310 OF prefetch-instr property value is missing\n"); 12768c2ecf20Sopenharmony_ci } 12778c2ecf20Sopenharmony_ci 12788c2ecf20Sopenharmony_ci l2x0_saved_regs.prefetch_ctrl = prefetch; 12798c2ecf20Sopenharmony_ci 12808c2ecf20Sopenharmony_ci power = l2x0_saved_regs.pwr_ctrl | 12818c2ecf20Sopenharmony_ci L310_DYNAMIC_CLK_GATING_EN | L310_STNDBY_MODE_EN; 12828c2ecf20Sopenharmony_ci 12838c2ecf20Sopenharmony_ci ret = of_property_read_u32(np, "arm,dynamic-clock-gating", &val); 12848c2ecf20Sopenharmony_ci if (!ret) { 12858c2ecf20Sopenharmony_ci if (!val) 12868c2ecf20Sopenharmony_ci power &= ~L310_DYNAMIC_CLK_GATING_EN; 12878c2ecf20Sopenharmony_ci } else if (ret != -EINVAL) { 12888c2ecf20Sopenharmony_ci pr_err("L2C-310 OF dynamic-clock-gating property value is missing or invalid\n"); 12898c2ecf20Sopenharmony_ci } 12908c2ecf20Sopenharmony_ci ret = of_property_read_u32(np, "arm,standby-mode", &val); 12918c2ecf20Sopenharmony_ci if (!ret) { 12928c2ecf20Sopenharmony_ci if (!val) 12938c2ecf20Sopenharmony_ci power &= ~L310_STNDBY_MODE_EN; 12948c2ecf20Sopenharmony_ci } else if (ret != -EINVAL) { 12958c2ecf20Sopenharmony_ci pr_err("L2C-310 OF standby-mode property value is missing or invalid\n"); 12968c2ecf20Sopenharmony_ci } 12978c2ecf20Sopenharmony_ci 12988c2ecf20Sopenharmony_ci l2x0_saved_regs.pwr_ctrl = power; 12998c2ecf20Sopenharmony_ci} 13008c2ecf20Sopenharmony_ci 13018c2ecf20Sopenharmony_cistatic const struct l2c_init_data of_l2c310_data __initconst = { 13028c2ecf20Sopenharmony_ci .type = "L2C-310", 13038c2ecf20Sopenharmony_ci .way_size_0 = SZ_8K, 13048c2ecf20Sopenharmony_ci .num_lock = 8, 13058c2ecf20Sopenharmony_ci .of_parse = l2c310_of_parse, 13068c2ecf20Sopenharmony_ci .enable = l2c310_enable, 13078c2ecf20Sopenharmony_ci .fixup = l2c310_fixup, 13088c2ecf20Sopenharmony_ci .save = l2c310_save, 13098c2ecf20Sopenharmony_ci .configure = l2c310_configure, 13108c2ecf20Sopenharmony_ci .unlock = l2c310_unlock, 13118c2ecf20Sopenharmony_ci .outer_cache = { 13128c2ecf20Sopenharmony_ci .inv_range = l2c210_inv_range, 13138c2ecf20Sopenharmony_ci .clean_range = l2c210_clean_range, 13148c2ecf20Sopenharmony_ci .flush_range = l2c210_flush_range, 13158c2ecf20Sopenharmony_ci .flush_all = l2c210_flush_all, 13168c2ecf20Sopenharmony_ci .disable = l2c310_disable, 13178c2ecf20Sopenharmony_ci .sync = l2c210_sync, 13188c2ecf20Sopenharmony_ci .resume = l2c310_resume, 13198c2ecf20Sopenharmony_ci }, 13208c2ecf20Sopenharmony_ci}; 13218c2ecf20Sopenharmony_ci 13228c2ecf20Sopenharmony_ci/* 13238c2ecf20Sopenharmony_ci * This is a variant of the of_l2c310_data with .sync set to 13248c2ecf20Sopenharmony_ci * NULL. Outer sync operations are not needed when the system is I/O 13258c2ecf20Sopenharmony_ci * coherent, and potentially harmful in certain situations (PCIe/PL310 13268c2ecf20Sopenharmony_ci * deadlock on Armada 375/38x due to hardware I/O coherency). The 13278c2ecf20Sopenharmony_ci * other operations are kept because they are infrequent (therefore do 13288c2ecf20Sopenharmony_ci * not cause the deadlock in practice) and needed for secondary CPU 13298c2ecf20Sopenharmony_ci * boot and other power management activities. 13308c2ecf20Sopenharmony_ci */ 13318c2ecf20Sopenharmony_cistatic const struct l2c_init_data of_l2c310_coherent_data __initconst = { 13328c2ecf20Sopenharmony_ci .type = "L2C-310 Coherent", 13338c2ecf20Sopenharmony_ci .way_size_0 = SZ_8K, 13348c2ecf20Sopenharmony_ci .num_lock = 8, 13358c2ecf20Sopenharmony_ci .of_parse = l2c310_of_parse, 13368c2ecf20Sopenharmony_ci .enable = l2c310_enable, 13378c2ecf20Sopenharmony_ci .fixup = l2c310_fixup, 13388c2ecf20Sopenharmony_ci .save = l2c310_save, 13398c2ecf20Sopenharmony_ci .configure = l2c310_configure, 13408c2ecf20Sopenharmony_ci .unlock = l2c310_unlock, 13418c2ecf20Sopenharmony_ci .outer_cache = { 13428c2ecf20Sopenharmony_ci .inv_range = l2c210_inv_range, 13438c2ecf20Sopenharmony_ci .clean_range = l2c210_clean_range, 13448c2ecf20Sopenharmony_ci .flush_range = l2c210_flush_range, 13458c2ecf20Sopenharmony_ci .flush_all = l2c210_flush_all, 13468c2ecf20Sopenharmony_ci .disable = l2c310_disable, 13478c2ecf20Sopenharmony_ci .resume = l2c310_resume, 13488c2ecf20Sopenharmony_ci }, 13498c2ecf20Sopenharmony_ci}; 13508c2ecf20Sopenharmony_ci 13518c2ecf20Sopenharmony_ci/* 13528c2ecf20Sopenharmony_ci * Note that the end addresses passed to Linux primitives are 13538c2ecf20Sopenharmony_ci * noninclusive, while the hardware cache range operations use 13548c2ecf20Sopenharmony_ci * inclusive start and end addresses. 13558c2ecf20Sopenharmony_ci */ 13568c2ecf20Sopenharmony_cistatic unsigned long aurora_range_end(unsigned long start, unsigned long end) 13578c2ecf20Sopenharmony_ci{ 13588c2ecf20Sopenharmony_ci /* 13598c2ecf20Sopenharmony_ci * Limit the number of cache lines processed at once, 13608c2ecf20Sopenharmony_ci * since cache range operations stall the CPU pipeline 13618c2ecf20Sopenharmony_ci * until completion. 13628c2ecf20Sopenharmony_ci */ 13638c2ecf20Sopenharmony_ci if (end > start + AURORA_MAX_RANGE_SIZE) 13648c2ecf20Sopenharmony_ci end = start + AURORA_MAX_RANGE_SIZE; 13658c2ecf20Sopenharmony_ci 13668c2ecf20Sopenharmony_ci /* 13678c2ecf20Sopenharmony_ci * Cache range operations can't straddle a page boundary. 13688c2ecf20Sopenharmony_ci */ 13698c2ecf20Sopenharmony_ci if (end > PAGE_ALIGN(start+1)) 13708c2ecf20Sopenharmony_ci end = PAGE_ALIGN(start+1); 13718c2ecf20Sopenharmony_ci 13728c2ecf20Sopenharmony_ci return end; 13738c2ecf20Sopenharmony_ci} 13748c2ecf20Sopenharmony_ci 13758c2ecf20Sopenharmony_cistatic void aurora_pa_range(unsigned long start, unsigned long end, 13768c2ecf20Sopenharmony_ci unsigned long offset) 13778c2ecf20Sopenharmony_ci{ 13788c2ecf20Sopenharmony_ci void __iomem *base = l2x0_base; 13798c2ecf20Sopenharmony_ci unsigned long range_end; 13808c2ecf20Sopenharmony_ci unsigned long flags; 13818c2ecf20Sopenharmony_ci 13828c2ecf20Sopenharmony_ci /* 13838c2ecf20Sopenharmony_ci * round start and end adresses up to cache line size 13848c2ecf20Sopenharmony_ci */ 13858c2ecf20Sopenharmony_ci start &= ~(CACHE_LINE_SIZE - 1); 13868c2ecf20Sopenharmony_ci end = ALIGN(end, CACHE_LINE_SIZE); 13878c2ecf20Sopenharmony_ci 13888c2ecf20Sopenharmony_ci /* 13898c2ecf20Sopenharmony_ci * perform operation on all full cache lines between 'start' and 'end' 13908c2ecf20Sopenharmony_ci */ 13918c2ecf20Sopenharmony_ci while (start < end) { 13928c2ecf20Sopenharmony_ci range_end = aurora_range_end(start, end); 13938c2ecf20Sopenharmony_ci 13948c2ecf20Sopenharmony_ci raw_spin_lock_irqsave(&l2x0_lock, flags); 13958c2ecf20Sopenharmony_ci writel_relaxed(start, base + AURORA_RANGE_BASE_ADDR_REG); 13968c2ecf20Sopenharmony_ci writel_relaxed(range_end - CACHE_LINE_SIZE, base + offset); 13978c2ecf20Sopenharmony_ci raw_spin_unlock_irqrestore(&l2x0_lock, flags); 13988c2ecf20Sopenharmony_ci 13998c2ecf20Sopenharmony_ci writel_relaxed(0, base + AURORA_SYNC_REG); 14008c2ecf20Sopenharmony_ci start = range_end; 14018c2ecf20Sopenharmony_ci } 14028c2ecf20Sopenharmony_ci} 14038c2ecf20Sopenharmony_cistatic void aurora_inv_range(unsigned long start, unsigned long end) 14048c2ecf20Sopenharmony_ci{ 14058c2ecf20Sopenharmony_ci aurora_pa_range(start, end, AURORA_INVAL_RANGE_REG); 14068c2ecf20Sopenharmony_ci} 14078c2ecf20Sopenharmony_ci 14088c2ecf20Sopenharmony_cistatic void aurora_clean_range(unsigned long start, unsigned long end) 14098c2ecf20Sopenharmony_ci{ 14108c2ecf20Sopenharmony_ci /* 14118c2ecf20Sopenharmony_ci * If L2 is forced to WT, the L2 will always be clean and we 14128c2ecf20Sopenharmony_ci * don't need to do anything here. 14138c2ecf20Sopenharmony_ci */ 14148c2ecf20Sopenharmony_ci if (!l2_wt_override) 14158c2ecf20Sopenharmony_ci aurora_pa_range(start, end, AURORA_CLEAN_RANGE_REG); 14168c2ecf20Sopenharmony_ci} 14178c2ecf20Sopenharmony_ci 14188c2ecf20Sopenharmony_cistatic void aurora_flush_range(unsigned long start, unsigned long end) 14198c2ecf20Sopenharmony_ci{ 14208c2ecf20Sopenharmony_ci if (l2_wt_override) 14218c2ecf20Sopenharmony_ci aurora_pa_range(start, end, AURORA_INVAL_RANGE_REG); 14228c2ecf20Sopenharmony_ci else 14238c2ecf20Sopenharmony_ci aurora_pa_range(start, end, AURORA_FLUSH_RANGE_REG); 14248c2ecf20Sopenharmony_ci} 14258c2ecf20Sopenharmony_ci 14268c2ecf20Sopenharmony_cistatic void aurora_flush_all(void) 14278c2ecf20Sopenharmony_ci{ 14288c2ecf20Sopenharmony_ci void __iomem *base = l2x0_base; 14298c2ecf20Sopenharmony_ci unsigned long flags; 14308c2ecf20Sopenharmony_ci 14318c2ecf20Sopenharmony_ci /* clean all ways */ 14328c2ecf20Sopenharmony_ci raw_spin_lock_irqsave(&l2x0_lock, flags); 14338c2ecf20Sopenharmony_ci __l2c_op_way(base + L2X0_CLEAN_INV_WAY); 14348c2ecf20Sopenharmony_ci raw_spin_unlock_irqrestore(&l2x0_lock, flags); 14358c2ecf20Sopenharmony_ci 14368c2ecf20Sopenharmony_ci writel_relaxed(0, base + AURORA_SYNC_REG); 14378c2ecf20Sopenharmony_ci} 14388c2ecf20Sopenharmony_ci 14398c2ecf20Sopenharmony_cistatic void aurora_cache_sync(void) 14408c2ecf20Sopenharmony_ci{ 14418c2ecf20Sopenharmony_ci writel_relaxed(0, l2x0_base + AURORA_SYNC_REG); 14428c2ecf20Sopenharmony_ci} 14438c2ecf20Sopenharmony_ci 14448c2ecf20Sopenharmony_cistatic void aurora_disable(void) 14458c2ecf20Sopenharmony_ci{ 14468c2ecf20Sopenharmony_ci void __iomem *base = l2x0_base; 14478c2ecf20Sopenharmony_ci unsigned long flags; 14488c2ecf20Sopenharmony_ci 14498c2ecf20Sopenharmony_ci raw_spin_lock_irqsave(&l2x0_lock, flags); 14508c2ecf20Sopenharmony_ci __l2c_op_way(base + L2X0_CLEAN_INV_WAY); 14518c2ecf20Sopenharmony_ci writel_relaxed(0, base + AURORA_SYNC_REG); 14528c2ecf20Sopenharmony_ci l2c_write_sec(0, base, L2X0_CTRL); 14538c2ecf20Sopenharmony_ci dsb(st); 14548c2ecf20Sopenharmony_ci raw_spin_unlock_irqrestore(&l2x0_lock, flags); 14558c2ecf20Sopenharmony_ci} 14568c2ecf20Sopenharmony_ci 14578c2ecf20Sopenharmony_cistatic void aurora_save(void __iomem *base) 14588c2ecf20Sopenharmony_ci{ 14598c2ecf20Sopenharmony_ci l2x0_saved_regs.ctrl = readl_relaxed(base + L2X0_CTRL); 14608c2ecf20Sopenharmony_ci l2x0_saved_regs.aux_ctrl = readl_relaxed(base + L2X0_AUX_CTRL); 14618c2ecf20Sopenharmony_ci} 14628c2ecf20Sopenharmony_ci 14638c2ecf20Sopenharmony_ci/* 14648c2ecf20Sopenharmony_ci * For Aurora cache in no outer mode, enable via the CP15 coprocessor 14658c2ecf20Sopenharmony_ci * broadcasting of cache commands to L2. 14668c2ecf20Sopenharmony_ci */ 14678c2ecf20Sopenharmony_cistatic void __init aurora_enable_no_outer(void __iomem *base, 14688c2ecf20Sopenharmony_ci unsigned num_lock) 14698c2ecf20Sopenharmony_ci{ 14708c2ecf20Sopenharmony_ci u32 u; 14718c2ecf20Sopenharmony_ci 14728c2ecf20Sopenharmony_ci asm volatile("mrc p15, 1, %0, c15, c2, 0" : "=r" (u)); 14738c2ecf20Sopenharmony_ci u |= AURORA_CTRL_FW; /* Set the FW bit */ 14748c2ecf20Sopenharmony_ci asm volatile("mcr p15, 1, %0, c15, c2, 0" : : "r" (u)); 14758c2ecf20Sopenharmony_ci 14768c2ecf20Sopenharmony_ci isb(); 14778c2ecf20Sopenharmony_ci 14788c2ecf20Sopenharmony_ci l2c_enable(base, num_lock); 14798c2ecf20Sopenharmony_ci} 14808c2ecf20Sopenharmony_ci 14818c2ecf20Sopenharmony_cistatic void __init aurora_fixup(void __iomem *base, u32 cache_id, 14828c2ecf20Sopenharmony_ci struct outer_cache_fns *fns) 14838c2ecf20Sopenharmony_ci{ 14848c2ecf20Sopenharmony_ci sync_reg_offset = AURORA_SYNC_REG; 14858c2ecf20Sopenharmony_ci} 14868c2ecf20Sopenharmony_ci 14878c2ecf20Sopenharmony_cistatic void __init aurora_of_parse(const struct device_node *np, 14888c2ecf20Sopenharmony_ci u32 *aux_val, u32 *aux_mask) 14898c2ecf20Sopenharmony_ci{ 14908c2ecf20Sopenharmony_ci u32 val = AURORA_ACR_REPLACEMENT_TYPE_SEMIPLRU; 14918c2ecf20Sopenharmony_ci u32 mask = AURORA_ACR_REPLACEMENT_MASK; 14928c2ecf20Sopenharmony_ci 14938c2ecf20Sopenharmony_ci of_property_read_u32(np, "cache-id-part", 14948c2ecf20Sopenharmony_ci &cache_id_part_number_from_dt); 14958c2ecf20Sopenharmony_ci 14968c2ecf20Sopenharmony_ci /* Determine and save the write policy */ 14978c2ecf20Sopenharmony_ci l2_wt_override = of_property_read_bool(np, "wt-override"); 14988c2ecf20Sopenharmony_ci 14998c2ecf20Sopenharmony_ci if (l2_wt_override) { 15008c2ecf20Sopenharmony_ci val |= AURORA_ACR_FORCE_WRITE_THRO_POLICY; 15018c2ecf20Sopenharmony_ci mask |= AURORA_ACR_FORCE_WRITE_POLICY_MASK; 15028c2ecf20Sopenharmony_ci } 15038c2ecf20Sopenharmony_ci 15048c2ecf20Sopenharmony_ci if (of_property_read_bool(np, "marvell,ecc-enable")) { 15058c2ecf20Sopenharmony_ci mask |= AURORA_ACR_ECC_EN; 15068c2ecf20Sopenharmony_ci val |= AURORA_ACR_ECC_EN; 15078c2ecf20Sopenharmony_ci } 15088c2ecf20Sopenharmony_ci 15098c2ecf20Sopenharmony_ci if (of_property_read_bool(np, "arm,parity-enable")) { 15108c2ecf20Sopenharmony_ci mask |= AURORA_ACR_PARITY_EN; 15118c2ecf20Sopenharmony_ci val |= AURORA_ACR_PARITY_EN; 15128c2ecf20Sopenharmony_ci } else if (of_property_read_bool(np, "arm,parity-disable")) { 15138c2ecf20Sopenharmony_ci mask |= AURORA_ACR_PARITY_EN; 15148c2ecf20Sopenharmony_ci } 15158c2ecf20Sopenharmony_ci 15168c2ecf20Sopenharmony_ci *aux_val &= ~mask; 15178c2ecf20Sopenharmony_ci *aux_val |= val; 15188c2ecf20Sopenharmony_ci *aux_mask &= ~mask; 15198c2ecf20Sopenharmony_ci} 15208c2ecf20Sopenharmony_ci 15218c2ecf20Sopenharmony_cistatic const struct l2c_init_data of_aurora_with_outer_data __initconst = { 15228c2ecf20Sopenharmony_ci .type = "Aurora", 15238c2ecf20Sopenharmony_ci .way_size_0 = SZ_4K, 15248c2ecf20Sopenharmony_ci .num_lock = 4, 15258c2ecf20Sopenharmony_ci .of_parse = aurora_of_parse, 15268c2ecf20Sopenharmony_ci .enable = l2c_enable, 15278c2ecf20Sopenharmony_ci .fixup = aurora_fixup, 15288c2ecf20Sopenharmony_ci .save = aurora_save, 15298c2ecf20Sopenharmony_ci .configure = l2c_configure, 15308c2ecf20Sopenharmony_ci .unlock = l2c_unlock, 15318c2ecf20Sopenharmony_ci .outer_cache = { 15328c2ecf20Sopenharmony_ci .inv_range = aurora_inv_range, 15338c2ecf20Sopenharmony_ci .clean_range = aurora_clean_range, 15348c2ecf20Sopenharmony_ci .flush_range = aurora_flush_range, 15358c2ecf20Sopenharmony_ci .flush_all = aurora_flush_all, 15368c2ecf20Sopenharmony_ci .disable = aurora_disable, 15378c2ecf20Sopenharmony_ci .sync = aurora_cache_sync, 15388c2ecf20Sopenharmony_ci .resume = l2c_resume, 15398c2ecf20Sopenharmony_ci }, 15408c2ecf20Sopenharmony_ci}; 15418c2ecf20Sopenharmony_ci 15428c2ecf20Sopenharmony_cistatic const struct l2c_init_data of_aurora_no_outer_data __initconst = { 15438c2ecf20Sopenharmony_ci .type = "Aurora", 15448c2ecf20Sopenharmony_ci .way_size_0 = SZ_4K, 15458c2ecf20Sopenharmony_ci .num_lock = 4, 15468c2ecf20Sopenharmony_ci .of_parse = aurora_of_parse, 15478c2ecf20Sopenharmony_ci .enable = aurora_enable_no_outer, 15488c2ecf20Sopenharmony_ci .fixup = aurora_fixup, 15498c2ecf20Sopenharmony_ci .save = aurora_save, 15508c2ecf20Sopenharmony_ci .configure = l2c_configure, 15518c2ecf20Sopenharmony_ci .unlock = l2c_unlock, 15528c2ecf20Sopenharmony_ci .outer_cache = { 15538c2ecf20Sopenharmony_ci .resume = l2c_resume, 15548c2ecf20Sopenharmony_ci }, 15558c2ecf20Sopenharmony_ci}; 15568c2ecf20Sopenharmony_ci 15578c2ecf20Sopenharmony_ci/* 15588c2ecf20Sopenharmony_ci * For certain Broadcom SoCs, depending on the address range, different offsets 15598c2ecf20Sopenharmony_ci * need to be added to the address before passing it to L2 for 15608c2ecf20Sopenharmony_ci * invalidation/clean/flush 15618c2ecf20Sopenharmony_ci * 15628c2ecf20Sopenharmony_ci * Section Address Range Offset EMI 15638c2ecf20Sopenharmony_ci * 1 0x00000000 - 0x3FFFFFFF 0x80000000 VC 15648c2ecf20Sopenharmony_ci * 2 0x40000000 - 0xBFFFFFFF 0x40000000 SYS 15658c2ecf20Sopenharmony_ci * 3 0xC0000000 - 0xFFFFFFFF 0x80000000 VC 15668c2ecf20Sopenharmony_ci * 15678c2ecf20Sopenharmony_ci * When the start and end addresses have crossed two different sections, we 15688c2ecf20Sopenharmony_ci * need to break the L2 operation into two, each within its own section. 15698c2ecf20Sopenharmony_ci * For example, if we need to invalidate addresses starts at 0xBFFF0000 and 15708c2ecf20Sopenharmony_ci * ends at 0xC0001000, we need do invalidate 1) 0xBFFF0000 - 0xBFFFFFFF and 2) 15718c2ecf20Sopenharmony_ci * 0xC0000000 - 0xC0001000 15728c2ecf20Sopenharmony_ci * 15738c2ecf20Sopenharmony_ci * Note 1: 15748c2ecf20Sopenharmony_ci * By breaking a single L2 operation into two, we may potentially suffer some 15758c2ecf20Sopenharmony_ci * performance hit, but keep in mind the cross section case is very rare 15768c2ecf20Sopenharmony_ci * 15778c2ecf20Sopenharmony_ci * Note 2: 15788c2ecf20Sopenharmony_ci * We do not need to handle the case when the start address is in 15798c2ecf20Sopenharmony_ci * Section 1 and the end address is in Section 3, since it is not a valid use 15808c2ecf20Sopenharmony_ci * case 15818c2ecf20Sopenharmony_ci * 15828c2ecf20Sopenharmony_ci * Note 3: 15838c2ecf20Sopenharmony_ci * Section 1 in practical terms can no longer be used on rev A2. Because of 15848c2ecf20Sopenharmony_ci * that the code does not need to handle section 1 at all. 15858c2ecf20Sopenharmony_ci * 15868c2ecf20Sopenharmony_ci */ 15878c2ecf20Sopenharmony_ci#define BCM_SYS_EMI_START_ADDR 0x40000000UL 15888c2ecf20Sopenharmony_ci#define BCM_VC_EMI_SEC3_START_ADDR 0xC0000000UL 15898c2ecf20Sopenharmony_ci 15908c2ecf20Sopenharmony_ci#define BCM_SYS_EMI_OFFSET 0x40000000UL 15918c2ecf20Sopenharmony_ci#define BCM_VC_EMI_OFFSET 0x80000000UL 15928c2ecf20Sopenharmony_ci 15938c2ecf20Sopenharmony_cistatic inline int bcm_addr_is_sys_emi(unsigned long addr) 15948c2ecf20Sopenharmony_ci{ 15958c2ecf20Sopenharmony_ci return (addr >= BCM_SYS_EMI_START_ADDR) && 15968c2ecf20Sopenharmony_ci (addr < BCM_VC_EMI_SEC3_START_ADDR); 15978c2ecf20Sopenharmony_ci} 15988c2ecf20Sopenharmony_ci 15998c2ecf20Sopenharmony_cistatic inline unsigned long bcm_l2_phys_addr(unsigned long addr) 16008c2ecf20Sopenharmony_ci{ 16018c2ecf20Sopenharmony_ci if (bcm_addr_is_sys_emi(addr)) 16028c2ecf20Sopenharmony_ci return addr + BCM_SYS_EMI_OFFSET; 16038c2ecf20Sopenharmony_ci else 16048c2ecf20Sopenharmony_ci return addr + BCM_VC_EMI_OFFSET; 16058c2ecf20Sopenharmony_ci} 16068c2ecf20Sopenharmony_ci 16078c2ecf20Sopenharmony_cistatic void bcm_inv_range(unsigned long start, unsigned long end) 16088c2ecf20Sopenharmony_ci{ 16098c2ecf20Sopenharmony_ci unsigned long new_start, new_end; 16108c2ecf20Sopenharmony_ci 16118c2ecf20Sopenharmony_ci BUG_ON(start < BCM_SYS_EMI_START_ADDR); 16128c2ecf20Sopenharmony_ci 16138c2ecf20Sopenharmony_ci if (unlikely(end <= start)) 16148c2ecf20Sopenharmony_ci return; 16158c2ecf20Sopenharmony_ci 16168c2ecf20Sopenharmony_ci new_start = bcm_l2_phys_addr(start); 16178c2ecf20Sopenharmony_ci new_end = bcm_l2_phys_addr(end); 16188c2ecf20Sopenharmony_ci 16198c2ecf20Sopenharmony_ci /* normal case, no cross section between start and end */ 16208c2ecf20Sopenharmony_ci if (likely(bcm_addr_is_sys_emi(end) || !bcm_addr_is_sys_emi(start))) { 16218c2ecf20Sopenharmony_ci l2c210_inv_range(new_start, new_end); 16228c2ecf20Sopenharmony_ci return; 16238c2ecf20Sopenharmony_ci } 16248c2ecf20Sopenharmony_ci 16258c2ecf20Sopenharmony_ci /* They cross sections, so it can only be a cross from section 16268c2ecf20Sopenharmony_ci * 2 to section 3 16278c2ecf20Sopenharmony_ci */ 16288c2ecf20Sopenharmony_ci l2c210_inv_range(new_start, 16298c2ecf20Sopenharmony_ci bcm_l2_phys_addr(BCM_VC_EMI_SEC3_START_ADDR-1)); 16308c2ecf20Sopenharmony_ci l2c210_inv_range(bcm_l2_phys_addr(BCM_VC_EMI_SEC3_START_ADDR), 16318c2ecf20Sopenharmony_ci new_end); 16328c2ecf20Sopenharmony_ci} 16338c2ecf20Sopenharmony_ci 16348c2ecf20Sopenharmony_cistatic void bcm_clean_range(unsigned long start, unsigned long end) 16358c2ecf20Sopenharmony_ci{ 16368c2ecf20Sopenharmony_ci unsigned long new_start, new_end; 16378c2ecf20Sopenharmony_ci 16388c2ecf20Sopenharmony_ci BUG_ON(start < BCM_SYS_EMI_START_ADDR); 16398c2ecf20Sopenharmony_ci 16408c2ecf20Sopenharmony_ci if (unlikely(end <= start)) 16418c2ecf20Sopenharmony_ci return; 16428c2ecf20Sopenharmony_ci 16438c2ecf20Sopenharmony_ci new_start = bcm_l2_phys_addr(start); 16448c2ecf20Sopenharmony_ci new_end = bcm_l2_phys_addr(end); 16458c2ecf20Sopenharmony_ci 16468c2ecf20Sopenharmony_ci /* normal case, no cross section between start and end */ 16478c2ecf20Sopenharmony_ci if (likely(bcm_addr_is_sys_emi(end) || !bcm_addr_is_sys_emi(start))) { 16488c2ecf20Sopenharmony_ci l2c210_clean_range(new_start, new_end); 16498c2ecf20Sopenharmony_ci return; 16508c2ecf20Sopenharmony_ci } 16518c2ecf20Sopenharmony_ci 16528c2ecf20Sopenharmony_ci /* They cross sections, so it can only be a cross from section 16538c2ecf20Sopenharmony_ci * 2 to section 3 16548c2ecf20Sopenharmony_ci */ 16558c2ecf20Sopenharmony_ci l2c210_clean_range(new_start, 16568c2ecf20Sopenharmony_ci bcm_l2_phys_addr(BCM_VC_EMI_SEC3_START_ADDR-1)); 16578c2ecf20Sopenharmony_ci l2c210_clean_range(bcm_l2_phys_addr(BCM_VC_EMI_SEC3_START_ADDR), 16588c2ecf20Sopenharmony_ci new_end); 16598c2ecf20Sopenharmony_ci} 16608c2ecf20Sopenharmony_ci 16618c2ecf20Sopenharmony_cistatic void bcm_flush_range(unsigned long start, unsigned long end) 16628c2ecf20Sopenharmony_ci{ 16638c2ecf20Sopenharmony_ci unsigned long new_start, new_end; 16648c2ecf20Sopenharmony_ci 16658c2ecf20Sopenharmony_ci BUG_ON(start < BCM_SYS_EMI_START_ADDR); 16668c2ecf20Sopenharmony_ci 16678c2ecf20Sopenharmony_ci if (unlikely(end <= start)) 16688c2ecf20Sopenharmony_ci return; 16698c2ecf20Sopenharmony_ci 16708c2ecf20Sopenharmony_ci if ((end - start) >= l2x0_size) { 16718c2ecf20Sopenharmony_ci outer_cache.flush_all(); 16728c2ecf20Sopenharmony_ci return; 16738c2ecf20Sopenharmony_ci } 16748c2ecf20Sopenharmony_ci 16758c2ecf20Sopenharmony_ci new_start = bcm_l2_phys_addr(start); 16768c2ecf20Sopenharmony_ci new_end = bcm_l2_phys_addr(end); 16778c2ecf20Sopenharmony_ci 16788c2ecf20Sopenharmony_ci /* normal case, no cross section between start and end */ 16798c2ecf20Sopenharmony_ci if (likely(bcm_addr_is_sys_emi(end) || !bcm_addr_is_sys_emi(start))) { 16808c2ecf20Sopenharmony_ci l2c210_flush_range(new_start, new_end); 16818c2ecf20Sopenharmony_ci return; 16828c2ecf20Sopenharmony_ci } 16838c2ecf20Sopenharmony_ci 16848c2ecf20Sopenharmony_ci /* They cross sections, so it can only be a cross from section 16858c2ecf20Sopenharmony_ci * 2 to section 3 16868c2ecf20Sopenharmony_ci */ 16878c2ecf20Sopenharmony_ci l2c210_flush_range(new_start, 16888c2ecf20Sopenharmony_ci bcm_l2_phys_addr(BCM_VC_EMI_SEC3_START_ADDR-1)); 16898c2ecf20Sopenharmony_ci l2c210_flush_range(bcm_l2_phys_addr(BCM_VC_EMI_SEC3_START_ADDR), 16908c2ecf20Sopenharmony_ci new_end); 16918c2ecf20Sopenharmony_ci} 16928c2ecf20Sopenharmony_ci 16938c2ecf20Sopenharmony_ci/* Broadcom L2C-310 start from ARMs R3P2 or later, and require no fixups */ 16948c2ecf20Sopenharmony_cistatic const struct l2c_init_data of_bcm_l2x0_data __initconst = { 16958c2ecf20Sopenharmony_ci .type = "BCM-L2C-310", 16968c2ecf20Sopenharmony_ci .way_size_0 = SZ_8K, 16978c2ecf20Sopenharmony_ci .num_lock = 8, 16988c2ecf20Sopenharmony_ci .of_parse = l2c310_of_parse, 16998c2ecf20Sopenharmony_ci .enable = l2c310_enable, 17008c2ecf20Sopenharmony_ci .save = l2c310_save, 17018c2ecf20Sopenharmony_ci .configure = l2c310_configure, 17028c2ecf20Sopenharmony_ci .unlock = l2c310_unlock, 17038c2ecf20Sopenharmony_ci .outer_cache = { 17048c2ecf20Sopenharmony_ci .inv_range = bcm_inv_range, 17058c2ecf20Sopenharmony_ci .clean_range = bcm_clean_range, 17068c2ecf20Sopenharmony_ci .flush_range = bcm_flush_range, 17078c2ecf20Sopenharmony_ci .flush_all = l2c210_flush_all, 17088c2ecf20Sopenharmony_ci .disable = l2c310_disable, 17098c2ecf20Sopenharmony_ci .sync = l2c210_sync, 17108c2ecf20Sopenharmony_ci .resume = l2c310_resume, 17118c2ecf20Sopenharmony_ci }, 17128c2ecf20Sopenharmony_ci}; 17138c2ecf20Sopenharmony_ci 17148c2ecf20Sopenharmony_cistatic void __init tauros3_save(void __iomem *base) 17158c2ecf20Sopenharmony_ci{ 17168c2ecf20Sopenharmony_ci l2c_save(base); 17178c2ecf20Sopenharmony_ci 17188c2ecf20Sopenharmony_ci l2x0_saved_regs.aux2_ctrl = 17198c2ecf20Sopenharmony_ci readl_relaxed(base + TAUROS3_AUX2_CTRL); 17208c2ecf20Sopenharmony_ci l2x0_saved_regs.prefetch_ctrl = 17218c2ecf20Sopenharmony_ci readl_relaxed(base + L310_PREFETCH_CTRL); 17228c2ecf20Sopenharmony_ci} 17238c2ecf20Sopenharmony_ci 17248c2ecf20Sopenharmony_cistatic void tauros3_configure(void __iomem *base) 17258c2ecf20Sopenharmony_ci{ 17268c2ecf20Sopenharmony_ci l2c_configure(base); 17278c2ecf20Sopenharmony_ci writel_relaxed(l2x0_saved_regs.aux2_ctrl, 17288c2ecf20Sopenharmony_ci base + TAUROS3_AUX2_CTRL); 17298c2ecf20Sopenharmony_ci writel_relaxed(l2x0_saved_regs.prefetch_ctrl, 17308c2ecf20Sopenharmony_ci base + L310_PREFETCH_CTRL); 17318c2ecf20Sopenharmony_ci} 17328c2ecf20Sopenharmony_ci 17338c2ecf20Sopenharmony_cistatic const struct l2c_init_data of_tauros3_data __initconst = { 17348c2ecf20Sopenharmony_ci .type = "Tauros3", 17358c2ecf20Sopenharmony_ci .way_size_0 = SZ_8K, 17368c2ecf20Sopenharmony_ci .num_lock = 8, 17378c2ecf20Sopenharmony_ci .enable = l2c_enable, 17388c2ecf20Sopenharmony_ci .save = tauros3_save, 17398c2ecf20Sopenharmony_ci .configure = tauros3_configure, 17408c2ecf20Sopenharmony_ci .unlock = l2c_unlock, 17418c2ecf20Sopenharmony_ci /* Tauros3 broadcasts L1 cache operations to L2 */ 17428c2ecf20Sopenharmony_ci .outer_cache = { 17438c2ecf20Sopenharmony_ci .resume = l2c_resume, 17448c2ecf20Sopenharmony_ci }, 17458c2ecf20Sopenharmony_ci}; 17468c2ecf20Sopenharmony_ci 17478c2ecf20Sopenharmony_ci#define L2C_ID(name, fns) { .compatible = name, .data = (void *)&fns } 17488c2ecf20Sopenharmony_cistatic const struct of_device_id l2x0_ids[] __initconst = { 17498c2ecf20Sopenharmony_ci L2C_ID("arm,l210-cache", of_l2c210_data), 17508c2ecf20Sopenharmony_ci L2C_ID("arm,l220-cache", of_l2c220_data), 17518c2ecf20Sopenharmony_ci L2C_ID("arm,pl310-cache", of_l2c310_data), 17528c2ecf20Sopenharmony_ci L2C_ID("brcm,bcm11351-a2-pl310-cache", of_bcm_l2x0_data), 17538c2ecf20Sopenharmony_ci L2C_ID("marvell,aurora-outer-cache", of_aurora_with_outer_data), 17548c2ecf20Sopenharmony_ci L2C_ID("marvell,aurora-system-cache", of_aurora_no_outer_data), 17558c2ecf20Sopenharmony_ci L2C_ID("marvell,tauros3-cache", of_tauros3_data), 17568c2ecf20Sopenharmony_ci /* Deprecated IDs */ 17578c2ecf20Sopenharmony_ci L2C_ID("bcm,bcm11351-a2-pl310-cache", of_bcm_l2x0_data), 17588c2ecf20Sopenharmony_ci {} 17598c2ecf20Sopenharmony_ci}; 17608c2ecf20Sopenharmony_ci 17618c2ecf20Sopenharmony_ciint __init l2x0_of_init(u32 aux_val, u32 aux_mask) 17628c2ecf20Sopenharmony_ci{ 17638c2ecf20Sopenharmony_ci const struct l2c_init_data *data; 17648c2ecf20Sopenharmony_ci struct device_node *np; 17658c2ecf20Sopenharmony_ci struct resource res; 17668c2ecf20Sopenharmony_ci u32 cache_id, old_aux; 17678c2ecf20Sopenharmony_ci u32 cache_level = 2; 17688c2ecf20Sopenharmony_ci bool nosync = false; 17698c2ecf20Sopenharmony_ci 17708c2ecf20Sopenharmony_ci np = of_find_matching_node(NULL, l2x0_ids); 17718c2ecf20Sopenharmony_ci if (!np) 17728c2ecf20Sopenharmony_ci return -ENODEV; 17738c2ecf20Sopenharmony_ci 17748c2ecf20Sopenharmony_ci if (of_address_to_resource(np, 0, &res)) 17758c2ecf20Sopenharmony_ci return -ENODEV; 17768c2ecf20Sopenharmony_ci 17778c2ecf20Sopenharmony_ci l2x0_base = ioremap(res.start, resource_size(&res)); 17788c2ecf20Sopenharmony_ci if (!l2x0_base) 17798c2ecf20Sopenharmony_ci return -ENOMEM; 17808c2ecf20Sopenharmony_ci 17818c2ecf20Sopenharmony_ci l2x0_saved_regs.phy_base = res.start; 17828c2ecf20Sopenharmony_ci 17838c2ecf20Sopenharmony_ci data = of_match_node(l2x0_ids, np)->data; 17848c2ecf20Sopenharmony_ci 17858c2ecf20Sopenharmony_ci if (of_device_is_compatible(np, "arm,pl310-cache") && 17868c2ecf20Sopenharmony_ci of_property_read_bool(np, "arm,io-coherent")) 17878c2ecf20Sopenharmony_ci data = &of_l2c310_coherent_data; 17888c2ecf20Sopenharmony_ci 17898c2ecf20Sopenharmony_ci old_aux = readl_relaxed(l2x0_base + L2X0_AUX_CTRL); 17908c2ecf20Sopenharmony_ci if (old_aux != ((old_aux & aux_mask) | aux_val)) { 17918c2ecf20Sopenharmony_ci pr_warn("L2C: platform modifies aux control register: 0x%08x -> 0x%08x\n", 17928c2ecf20Sopenharmony_ci old_aux, (old_aux & aux_mask) | aux_val); 17938c2ecf20Sopenharmony_ci } else if (aux_mask != ~0U && aux_val != 0) { 17948c2ecf20Sopenharmony_ci pr_alert("L2C: platform provided aux values match the hardware, so have no effect. Please remove them.\n"); 17958c2ecf20Sopenharmony_ci } 17968c2ecf20Sopenharmony_ci 17978c2ecf20Sopenharmony_ci /* All L2 caches are unified, so this property should be specified */ 17988c2ecf20Sopenharmony_ci if (!of_property_read_bool(np, "cache-unified")) 17998c2ecf20Sopenharmony_ci pr_err("L2C: device tree omits to specify unified cache\n"); 18008c2ecf20Sopenharmony_ci 18018c2ecf20Sopenharmony_ci if (of_property_read_u32(np, "cache-level", &cache_level)) 18028c2ecf20Sopenharmony_ci pr_err("L2C: device tree omits to specify cache-level\n"); 18038c2ecf20Sopenharmony_ci 18048c2ecf20Sopenharmony_ci if (cache_level != 2) 18058c2ecf20Sopenharmony_ci pr_err("L2C: device tree specifies invalid cache level\n"); 18068c2ecf20Sopenharmony_ci 18078c2ecf20Sopenharmony_ci nosync = of_property_read_bool(np, "arm,outer-sync-disable"); 18088c2ecf20Sopenharmony_ci 18098c2ecf20Sopenharmony_ci /* Read back current (default) hardware configuration */ 18108c2ecf20Sopenharmony_ci if (data->save) 18118c2ecf20Sopenharmony_ci data->save(l2x0_base); 18128c2ecf20Sopenharmony_ci 18138c2ecf20Sopenharmony_ci /* L2 configuration can only be changed if the cache is disabled */ 18148c2ecf20Sopenharmony_ci if (!(readl_relaxed(l2x0_base + L2X0_CTRL) & L2X0_CTRL_EN)) 18158c2ecf20Sopenharmony_ci if (data->of_parse) 18168c2ecf20Sopenharmony_ci data->of_parse(np, &aux_val, &aux_mask); 18178c2ecf20Sopenharmony_ci 18188c2ecf20Sopenharmony_ci if (cache_id_part_number_from_dt) 18198c2ecf20Sopenharmony_ci cache_id = cache_id_part_number_from_dt; 18208c2ecf20Sopenharmony_ci else 18218c2ecf20Sopenharmony_ci cache_id = readl_relaxed(l2x0_base + L2X0_CACHE_ID); 18228c2ecf20Sopenharmony_ci 18238c2ecf20Sopenharmony_ci return __l2c_init(data, aux_val, aux_mask, cache_id, nosync); 18248c2ecf20Sopenharmony_ci} 18258c2ecf20Sopenharmony_ci#endif 1826