18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * ARC Cache Management 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (C) 2014-15 Synopsys, Inc. (www.synopsys.com) 68c2ecf20Sopenharmony_ci * Copyright (C) 2004, 2007-2010, 2011-2012 Synopsys, Inc. (www.synopsys.com) 78c2ecf20Sopenharmony_ci */ 88c2ecf20Sopenharmony_ci 98c2ecf20Sopenharmony_ci#include <linux/module.h> 108c2ecf20Sopenharmony_ci#include <linux/mm.h> 118c2ecf20Sopenharmony_ci#include <linux/sched.h> 128c2ecf20Sopenharmony_ci#include <linux/cache.h> 138c2ecf20Sopenharmony_ci#include <linux/mmu_context.h> 148c2ecf20Sopenharmony_ci#include <linux/syscalls.h> 158c2ecf20Sopenharmony_ci#include <linux/uaccess.h> 168c2ecf20Sopenharmony_ci#include <linux/pagemap.h> 178c2ecf20Sopenharmony_ci#include <asm/cacheflush.h> 188c2ecf20Sopenharmony_ci#include <asm/cachectl.h> 198c2ecf20Sopenharmony_ci#include <asm/setup.h> 208c2ecf20Sopenharmony_ci 218c2ecf20Sopenharmony_ci#ifdef CONFIG_ISA_ARCV2 228c2ecf20Sopenharmony_ci#define USE_RGN_FLSH 1 238c2ecf20Sopenharmony_ci#endif 248c2ecf20Sopenharmony_ci 258c2ecf20Sopenharmony_cistatic int l2_line_sz; 268c2ecf20Sopenharmony_cistatic int ioc_exists; 278c2ecf20Sopenharmony_ciint slc_enable = 1, ioc_enable = 1; 288c2ecf20Sopenharmony_ciunsigned long perip_base = ARC_UNCACHED_ADDR_SPACE; /* legacy value for boot */ 298c2ecf20Sopenharmony_ciunsigned long perip_end = 0xFFFFFFFF; /* legacy value */ 308c2ecf20Sopenharmony_ci 318c2ecf20Sopenharmony_civoid (*_cache_line_loop_ic_fn)(phys_addr_t paddr, unsigned long vaddr, 328c2ecf20Sopenharmony_ci unsigned long sz, const int op, const int full_page); 338c2ecf20Sopenharmony_ci 348c2ecf20Sopenharmony_civoid (*__dma_cache_wback_inv)(phys_addr_t start, unsigned long sz); 358c2ecf20Sopenharmony_civoid (*__dma_cache_inv)(phys_addr_t start, unsigned long sz); 368c2ecf20Sopenharmony_civoid (*__dma_cache_wback)(phys_addr_t start, unsigned long sz); 378c2ecf20Sopenharmony_ci 388c2ecf20Sopenharmony_cichar *arc_cache_mumbojumbo(int c, char *buf, int len) 398c2ecf20Sopenharmony_ci{ 408c2ecf20Sopenharmony_ci int n = 0; 418c2ecf20Sopenharmony_ci struct cpuinfo_arc_cache *p; 428c2ecf20Sopenharmony_ci 438c2ecf20Sopenharmony_ci#define PR_CACHE(p, cfg, str) \ 448c2ecf20Sopenharmony_ci if (!(p)->line_len) \ 458c2ecf20Sopenharmony_ci n += scnprintf(buf + n, len - n, str"\t\t: N/A\n"); \ 468c2ecf20Sopenharmony_ci else \ 478c2ecf20Sopenharmony_ci n += scnprintf(buf + n, len - n, \ 488c2ecf20Sopenharmony_ci str"\t\t: %uK, %dway/set, %uB Line, %s%s%s\n", \ 498c2ecf20Sopenharmony_ci (p)->sz_k, (p)->assoc, (p)->line_len, \ 508c2ecf20Sopenharmony_ci (p)->vipt ? "VIPT" : "PIPT", \ 518c2ecf20Sopenharmony_ci (p)->alias ? " aliasing" : "", \ 528c2ecf20Sopenharmony_ci IS_USED_CFG(cfg)); 538c2ecf20Sopenharmony_ci 548c2ecf20Sopenharmony_ci PR_CACHE(&cpuinfo_arc700[c].icache, CONFIG_ARC_HAS_ICACHE, "I-Cache"); 558c2ecf20Sopenharmony_ci PR_CACHE(&cpuinfo_arc700[c].dcache, CONFIG_ARC_HAS_DCACHE, "D-Cache"); 568c2ecf20Sopenharmony_ci 578c2ecf20Sopenharmony_ci p = &cpuinfo_arc700[c].slc; 588c2ecf20Sopenharmony_ci if (p->line_len) 598c2ecf20Sopenharmony_ci n += scnprintf(buf + n, len - n, 608c2ecf20Sopenharmony_ci "SLC\t\t: %uK, %uB Line%s\n", 618c2ecf20Sopenharmony_ci p->sz_k, p->line_len, IS_USED_RUN(slc_enable)); 628c2ecf20Sopenharmony_ci 638c2ecf20Sopenharmony_ci n += scnprintf(buf + n, len - n, "Peripherals\t: %#lx%s%s\n", 648c2ecf20Sopenharmony_ci perip_base, 658c2ecf20Sopenharmony_ci IS_AVAIL3(ioc_exists, ioc_enable, ", IO-Coherency (per-device) ")); 668c2ecf20Sopenharmony_ci 678c2ecf20Sopenharmony_ci return buf; 688c2ecf20Sopenharmony_ci} 698c2ecf20Sopenharmony_ci 708c2ecf20Sopenharmony_ci/* 718c2ecf20Sopenharmony_ci * Read the Cache Build Confuration Registers, Decode them and save into 728c2ecf20Sopenharmony_ci * the cpuinfo structure for later use. 738c2ecf20Sopenharmony_ci * No Validation done here, simply read/convert the BCRs 748c2ecf20Sopenharmony_ci */ 758c2ecf20Sopenharmony_cistatic void read_decode_cache_bcr_arcv2(int cpu) 768c2ecf20Sopenharmony_ci{ 778c2ecf20Sopenharmony_ci struct cpuinfo_arc_cache *p_slc = &cpuinfo_arc700[cpu].slc; 788c2ecf20Sopenharmony_ci struct bcr_generic sbcr; 798c2ecf20Sopenharmony_ci 808c2ecf20Sopenharmony_ci struct bcr_slc_cfg { 818c2ecf20Sopenharmony_ci#ifdef CONFIG_CPU_BIG_ENDIAN 828c2ecf20Sopenharmony_ci unsigned int pad:24, way:2, lsz:2, sz:4; 838c2ecf20Sopenharmony_ci#else 848c2ecf20Sopenharmony_ci unsigned int sz:4, lsz:2, way:2, pad:24; 858c2ecf20Sopenharmony_ci#endif 868c2ecf20Sopenharmony_ci } slc_cfg; 878c2ecf20Sopenharmony_ci 888c2ecf20Sopenharmony_ci struct bcr_clust_cfg { 898c2ecf20Sopenharmony_ci#ifdef CONFIG_CPU_BIG_ENDIAN 908c2ecf20Sopenharmony_ci unsigned int pad:7, c:1, num_entries:8, num_cores:8, ver:8; 918c2ecf20Sopenharmony_ci#else 928c2ecf20Sopenharmony_ci unsigned int ver:8, num_cores:8, num_entries:8, c:1, pad:7; 938c2ecf20Sopenharmony_ci#endif 948c2ecf20Sopenharmony_ci } cbcr; 958c2ecf20Sopenharmony_ci 968c2ecf20Sopenharmony_ci struct bcr_volatile { 978c2ecf20Sopenharmony_ci#ifdef CONFIG_CPU_BIG_ENDIAN 988c2ecf20Sopenharmony_ci unsigned int start:4, limit:4, pad:22, order:1, disable:1; 998c2ecf20Sopenharmony_ci#else 1008c2ecf20Sopenharmony_ci unsigned int disable:1, order:1, pad:22, limit:4, start:4; 1018c2ecf20Sopenharmony_ci#endif 1028c2ecf20Sopenharmony_ci } vol; 1038c2ecf20Sopenharmony_ci 1048c2ecf20Sopenharmony_ci 1058c2ecf20Sopenharmony_ci READ_BCR(ARC_REG_SLC_BCR, sbcr); 1068c2ecf20Sopenharmony_ci if (sbcr.ver) { 1078c2ecf20Sopenharmony_ci READ_BCR(ARC_REG_SLC_CFG, slc_cfg); 1088c2ecf20Sopenharmony_ci p_slc->sz_k = 128 << slc_cfg.sz; 1098c2ecf20Sopenharmony_ci l2_line_sz = p_slc->line_len = (slc_cfg.lsz == 0) ? 128 : 64; 1108c2ecf20Sopenharmony_ci } 1118c2ecf20Sopenharmony_ci 1128c2ecf20Sopenharmony_ci READ_BCR(ARC_REG_CLUSTER_BCR, cbcr); 1138c2ecf20Sopenharmony_ci if (cbcr.c) { 1148c2ecf20Sopenharmony_ci ioc_exists = 1; 1158c2ecf20Sopenharmony_ci 1168c2ecf20Sopenharmony_ci /* 1178c2ecf20Sopenharmony_ci * As for today we don't support both IOC and ZONE_HIGHMEM enabled 1188c2ecf20Sopenharmony_ci * simultaneously. This happens because as of today IOC aperture covers 1198c2ecf20Sopenharmony_ci * only ZONE_NORMAL (low mem) and any dma transactions outside this 1208c2ecf20Sopenharmony_ci * region won't be HW coherent. 1218c2ecf20Sopenharmony_ci * If we want to use both IOC and ZONE_HIGHMEM we can use 1228c2ecf20Sopenharmony_ci * bounce_buffer to handle dma transactions to HIGHMEM. 1238c2ecf20Sopenharmony_ci * Also it is possible to modify dma_direct cache ops or increase IOC 1248c2ecf20Sopenharmony_ci * aperture size if we are planning to use HIGHMEM without PAE. 1258c2ecf20Sopenharmony_ci */ 1268c2ecf20Sopenharmony_ci if (IS_ENABLED(CONFIG_HIGHMEM) || is_pae40_enabled()) 1278c2ecf20Sopenharmony_ci ioc_enable = 0; 1288c2ecf20Sopenharmony_ci } else { 1298c2ecf20Sopenharmony_ci ioc_enable = 0; 1308c2ecf20Sopenharmony_ci } 1318c2ecf20Sopenharmony_ci 1328c2ecf20Sopenharmony_ci /* HS 2.0 didn't have AUX_VOL */ 1338c2ecf20Sopenharmony_ci if (cpuinfo_arc700[cpu].core.family > 0x51) { 1348c2ecf20Sopenharmony_ci READ_BCR(AUX_VOL, vol); 1358c2ecf20Sopenharmony_ci perip_base = vol.start << 28; 1368c2ecf20Sopenharmony_ci /* HS 3.0 has limit and strict-ordering fields */ 1378c2ecf20Sopenharmony_ci if (cpuinfo_arc700[cpu].core.family > 0x52) 1388c2ecf20Sopenharmony_ci perip_end = (vol.limit << 28) - 1; 1398c2ecf20Sopenharmony_ci } 1408c2ecf20Sopenharmony_ci} 1418c2ecf20Sopenharmony_ci 1428c2ecf20Sopenharmony_civoid read_decode_cache_bcr(void) 1438c2ecf20Sopenharmony_ci{ 1448c2ecf20Sopenharmony_ci struct cpuinfo_arc_cache *p_ic, *p_dc; 1458c2ecf20Sopenharmony_ci unsigned int cpu = smp_processor_id(); 1468c2ecf20Sopenharmony_ci struct bcr_cache { 1478c2ecf20Sopenharmony_ci#ifdef CONFIG_CPU_BIG_ENDIAN 1488c2ecf20Sopenharmony_ci unsigned int pad:12, line_len:4, sz:4, config:4, ver:8; 1498c2ecf20Sopenharmony_ci#else 1508c2ecf20Sopenharmony_ci unsigned int ver:8, config:4, sz:4, line_len:4, pad:12; 1518c2ecf20Sopenharmony_ci#endif 1528c2ecf20Sopenharmony_ci } ibcr, dbcr; 1538c2ecf20Sopenharmony_ci 1548c2ecf20Sopenharmony_ci p_ic = &cpuinfo_arc700[cpu].icache; 1558c2ecf20Sopenharmony_ci READ_BCR(ARC_REG_IC_BCR, ibcr); 1568c2ecf20Sopenharmony_ci 1578c2ecf20Sopenharmony_ci if (!ibcr.ver) 1588c2ecf20Sopenharmony_ci goto dc_chk; 1598c2ecf20Sopenharmony_ci 1608c2ecf20Sopenharmony_ci if (ibcr.ver <= 3) { 1618c2ecf20Sopenharmony_ci BUG_ON(ibcr.config != 3); 1628c2ecf20Sopenharmony_ci p_ic->assoc = 2; /* Fixed to 2w set assoc */ 1638c2ecf20Sopenharmony_ci } else if (ibcr.ver >= 4) { 1648c2ecf20Sopenharmony_ci p_ic->assoc = 1 << ibcr.config; /* 1,2,4,8 */ 1658c2ecf20Sopenharmony_ci } 1668c2ecf20Sopenharmony_ci 1678c2ecf20Sopenharmony_ci p_ic->line_len = 8 << ibcr.line_len; 1688c2ecf20Sopenharmony_ci p_ic->sz_k = 1 << (ibcr.sz - 1); 1698c2ecf20Sopenharmony_ci p_ic->vipt = 1; 1708c2ecf20Sopenharmony_ci p_ic->alias = p_ic->sz_k/p_ic->assoc/TO_KB(PAGE_SIZE) > 1; 1718c2ecf20Sopenharmony_ci 1728c2ecf20Sopenharmony_cidc_chk: 1738c2ecf20Sopenharmony_ci p_dc = &cpuinfo_arc700[cpu].dcache; 1748c2ecf20Sopenharmony_ci READ_BCR(ARC_REG_DC_BCR, dbcr); 1758c2ecf20Sopenharmony_ci 1768c2ecf20Sopenharmony_ci if (!dbcr.ver) 1778c2ecf20Sopenharmony_ci goto slc_chk; 1788c2ecf20Sopenharmony_ci 1798c2ecf20Sopenharmony_ci if (dbcr.ver <= 3) { 1808c2ecf20Sopenharmony_ci BUG_ON(dbcr.config != 2); 1818c2ecf20Sopenharmony_ci p_dc->assoc = 4; /* Fixed to 4w set assoc */ 1828c2ecf20Sopenharmony_ci p_dc->vipt = 1; 1838c2ecf20Sopenharmony_ci p_dc->alias = p_dc->sz_k/p_dc->assoc/TO_KB(PAGE_SIZE) > 1; 1848c2ecf20Sopenharmony_ci } else if (dbcr.ver >= 4) { 1858c2ecf20Sopenharmony_ci p_dc->assoc = 1 << dbcr.config; /* 1,2,4,8 */ 1868c2ecf20Sopenharmony_ci p_dc->vipt = 0; 1878c2ecf20Sopenharmony_ci p_dc->alias = 0; /* PIPT so can't VIPT alias */ 1888c2ecf20Sopenharmony_ci } 1898c2ecf20Sopenharmony_ci 1908c2ecf20Sopenharmony_ci p_dc->line_len = 16 << dbcr.line_len; 1918c2ecf20Sopenharmony_ci p_dc->sz_k = 1 << (dbcr.sz - 1); 1928c2ecf20Sopenharmony_ci 1938c2ecf20Sopenharmony_cislc_chk: 1948c2ecf20Sopenharmony_ci if (is_isa_arcv2()) 1958c2ecf20Sopenharmony_ci read_decode_cache_bcr_arcv2(cpu); 1968c2ecf20Sopenharmony_ci} 1978c2ecf20Sopenharmony_ci 1988c2ecf20Sopenharmony_ci/* 1998c2ecf20Sopenharmony_ci * Line Operation on {I,D}-Cache 2008c2ecf20Sopenharmony_ci */ 2018c2ecf20Sopenharmony_ci 2028c2ecf20Sopenharmony_ci#define OP_INV 0x1 2038c2ecf20Sopenharmony_ci#define OP_FLUSH 0x2 2048c2ecf20Sopenharmony_ci#define OP_FLUSH_N_INV 0x3 2058c2ecf20Sopenharmony_ci#define OP_INV_IC 0x4 2068c2ecf20Sopenharmony_ci 2078c2ecf20Sopenharmony_ci/* 2088c2ecf20Sopenharmony_ci * I-Cache Aliasing in ARC700 VIPT caches (MMU v1-v3) 2098c2ecf20Sopenharmony_ci * 2108c2ecf20Sopenharmony_ci * ARC VIPT I-cache uses vaddr to index into cache and paddr to match the tag. 2118c2ecf20Sopenharmony_ci * The orig Cache Management Module "CDU" only required paddr to invalidate a 2128c2ecf20Sopenharmony_ci * certain line since it sufficed as index in Non-Aliasing VIPT cache-geometry. 2138c2ecf20Sopenharmony_ci * Infact for distinct V1,V2,P: all of {V1-P},{V2-P},{P-P} would end up fetching 2148c2ecf20Sopenharmony_ci * the exact same line. 2158c2ecf20Sopenharmony_ci * 2168c2ecf20Sopenharmony_ci * However for larger Caches (way-size > page-size) - i.e. in Aliasing config, 2178c2ecf20Sopenharmony_ci * paddr alone could not be used to correctly index the cache. 2188c2ecf20Sopenharmony_ci * 2198c2ecf20Sopenharmony_ci * ------------------ 2208c2ecf20Sopenharmony_ci * MMU v1/v2 (Fixed Page Size 8k) 2218c2ecf20Sopenharmony_ci * ------------------ 2228c2ecf20Sopenharmony_ci * The solution was to provide CDU with these additonal vaddr bits. These 2238c2ecf20Sopenharmony_ci * would be bits [x:13], x would depend on cache-geometry, 13 comes from 2248c2ecf20Sopenharmony_ci * standard page size of 8k. 2258c2ecf20Sopenharmony_ci * H/w folks chose [17:13] to be a future safe range, and moreso these 5 bits 2268c2ecf20Sopenharmony_ci * of vaddr could easily be "stuffed" in the paddr as bits [4:0] since the 2278c2ecf20Sopenharmony_ci * orig 5 bits of paddr were anyways ignored by CDU line ops, as they 2288c2ecf20Sopenharmony_ci * represent the offset within cache-line. The adv of using this "clumsy" 2298c2ecf20Sopenharmony_ci * interface for additional info was no new reg was needed in CDU programming 2308c2ecf20Sopenharmony_ci * model. 2318c2ecf20Sopenharmony_ci * 2328c2ecf20Sopenharmony_ci * 17:13 represented the max num of bits passable, actual bits needed were 2338c2ecf20Sopenharmony_ci * fewer, based on the num-of-aliases possible. 2348c2ecf20Sopenharmony_ci * -for 2 alias possibility, only bit 13 needed (32K cache) 2358c2ecf20Sopenharmony_ci * -for 4 alias possibility, bits 14:13 needed (64K cache) 2368c2ecf20Sopenharmony_ci * 2378c2ecf20Sopenharmony_ci * ------------------ 2388c2ecf20Sopenharmony_ci * MMU v3 2398c2ecf20Sopenharmony_ci * ------------------ 2408c2ecf20Sopenharmony_ci * This ver of MMU supports variable page sizes (1k-16k): although Linux will 2418c2ecf20Sopenharmony_ci * only support 8k (default), 16k and 4k. 2428c2ecf20Sopenharmony_ci * However from hardware perspective, smaller page sizes aggravate aliasing 2438c2ecf20Sopenharmony_ci * meaning more vaddr bits needed to disambiguate the cache-line-op ; 2448c2ecf20Sopenharmony_ci * the existing scheme of piggybacking won't work for certain configurations. 2458c2ecf20Sopenharmony_ci * Two new registers IC_PTAG and DC_PTAG inttoduced. 2468c2ecf20Sopenharmony_ci * "tag" bits are provided in PTAG, index bits in existing IVIL/IVDL/FLDL regs 2478c2ecf20Sopenharmony_ci */ 2488c2ecf20Sopenharmony_ci 2498c2ecf20Sopenharmony_cistatic inline 2508c2ecf20Sopenharmony_civoid __cache_line_loop_v2(phys_addr_t paddr, unsigned long vaddr, 2518c2ecf20Sopenharmony_ci unsigned long sz, const int op, const int full_page) 2528c2ecf20Sopenharmony_ci{ 2538c2ecf20Sopenharmony_ci unsigned int aux_cmd; 2548c2ecf20Sopenharmony_ci int num_lines; 2558c2ecf20Sopenharmony_ci 2568c2ecf20Sopenharmony_ci if (op == OP_INV_IC) { 2578c2ecf20Sopenharmony_ci aux_cmd = ARC_REG_IC_IVIL; 2588c2ecf20Sopenharmony_ci } else { 2598c2ecf20Sopenharmony_ci /* d$ cmd: INV (discard or wback-n-discard) OR FLUSH (wback) */ 2608c2ecf20Sopenharmony_ci aux_cmd = op & OP_INV ? ARC_REG_DC_IVDL : ARC_REG_DC_FLDL; 2618c2ecf20Sopenharmony_ci } 2628c2ecf20Sopenharmony_ci 2638c2ecf20Sopenharmony_ci /* Ensure we properly floor/ceil the non-line aligned/sized requests 2648c2ecf20Sopenharmony_ci * and have @paddr - aligned to cache line and integral @num_lines. 2658c2ecf20Sopenharmony_ci * This however can be avoided for page sized since: 2668c2ecf20Sopenharmony_ci * -@paddr will be cache-line aligned already (being page aligned) 2678c2ecf20Sopenharmony_ci * -@sz will be integral multiple of line size (being page sized). 2688c2ecf20Sopenharmony_ci */ 2698c2ecf20Sopenharmony_ci if (!full_page) { 2708c2ecf20Sopenharmony_ci sz += paddr & ~CACHE_LINE_MASK; 2718c2ecf20Sopenharmony_ci paddr &= CACHE_LINE_MASK; 2728c2ecf20Sopenharmony_ci vaddr &= CACHE_LINE_MASK; 2738c2ecf20Sopenharmony_ci } 2748c2ecf20Sopenharmony_ci 2758c2ecf20Sopenharmony_ci num_lines = DIV_ROUND_UP(sz, L1_CACHE_BYTES); 2768c2ecf20Sopenharmony_ci 2778c2ecf20Sopenharmony_ci /* MMUv2 and before: paddr contains stuffed vaddrs bits */ 2788c2ecf20Sopenharmony_ci paddr |= (vaddr >> PAGE_SHIFT) & 0x1F; 2798c2ecf20Sopenharmony_ci 2808c2ecf20Sopenharmony_ci while (num_lines-- > 0) { 2818c2ecf20Sopenharmony_ci write_aux_reg(aux_cmd, paddr); 2828c2ecf20Sopenharmony_ci paddr += L1_CACHE_BYTES; 2838c2ecf20Sopenharmony_ci } 2848c2ecf20Sopenharmony_ci} 2858c2ecf20Sopenharmony_ci 2868c2ecf20Sopenharmony_ci/* 2878c2ecf20Sopenharmony_ci * For ARC700 MMUv3 I-cache and D-cache flushes 2888c2ecf20Sopenharmony_ci * - ARC700 programming model requires paddr and vaddr be passed in seperate 2898c2ecf20Sopenharmony_ci * AUX registers (*_IV*L and *_PTAG respectively) irrespective of whether the 2908c2ecf20Sopenharmony_ci * caches actually alias or not. 2918c2ecf20Sopenharmony_ci * - For HS38, only the aliasing I-cache configuration uses the PTAG reg 2928c2ecf20Sopenharmony_ci * (non aliasing I-cache version doesn't; while D-cache can't possibly alias) 2938c2ecf20Sopenharmony_ci */ 2948c2ecf20Sopenharmony_cistatic inline 2958c2ecf20Sopenharmony_civoid __cache_line_loop_v3(phys_addr_t paddr, unsigned long vaddr, 2968c2ecf20Sopenharmony_ci unsigned long sz, const int op, const int full_page) 2978c2ecf20Sopenharmony_ci{ 2988c2ecf20Sopenharmony_ci unsigned int aux_cmd, aux_tag; 2998c2ecf20Sopenharmony_ci int num_lines; 3008c2ecf20Sopenharmony_ci 3018c2ecf20Sopenharmony_ci if (op == OP_INV_IC) { 3028c2ecf20Sopenharmony_ci aux_cmd = ARC_REG_IC_IVIL; 3038c2ecf20Sopenharmony_ci aux_tag = ARC_REG_IC_PTAG; 3048c2ecf20Sopenharmony_ci } else { 3058c2ecf20Sopenharmony_ci aux_cmd = op & OP_INV ? ARC_REG_DC_IVDL : ARC_REG_DC_FLDL; 3068c2ecf20Sopenharmony_ci aux_tag = ARC_REG_DC_PTAG; 3078c2ecf20Sopenharmony_ci } 3088c2ecf20Sopenharmony_ci 3098c2ecf20Sopenharmony_ci /* Ensure we properly floor/ceil the non-line aligned/sized requests 3108c2ecf20Sopenharmony_ci * and have @paddr - aligned to cache line and integral @num_lines. 3118c2ecf20Sopenharmony_ci * This however can be avoided for page sized since: 3128c2ecf20Sopenharmony_ci * -@paddr will be cache-line aligned already (being page aligned) 3138c2ecf20Sopenharmony_ci * -@sz will be integral multiple of line size (being page sized). 3148c2ecf20Sopenharmony_ci */ 3158c2ecf20Sopenharmony_ci if (!full_page) { 3168c2ecf20Sopenharmony_ci sz += paddr & ~CACHE_LINE_MASK; 3178c2ecf20Sopenharmony_ci paddr &= CACHE_LINE_MASK; 3188c2ecf20Sopenharmony_ci vaddr &= CACHE_LINE_MASK; 3198c2ecf20Sopenharmony_ci } 3208c2ecf20Sopenharmony_ci num_lines = DIV_ROUND_UP(sz, L1_CACHE_BYTES); 3218c2ecf20Sopenharmony_ci 3228c2ecf20Sopenharmony_ci /* 3238c2ecf20Sopenharmony_ci * MMUv3, cache ops require paddr in PTAG reg 3248c2ecf20Sopenharmony_ci * if V-P const for loop, PTAG can be written once outside loop 3258c2ecf20Sopenharmony_ci */ 3268c2ecf20Sopenharmony_ci if (full_page) 3278c2ecf20Sopenharmony_ci write_aux_reg(aux_tag, paddr); 3288c2ecf20Sopenharmony_ci 3298c2ecf20Sopenharmony_ci /* 3308c2ecf20Sopenharmony_ci * This is technically for MMU v4, using the MMU v3 programming model 3318c2ecf20Sopenharmony_ci * Special work for HS38 aliasing I-cache configuration with PAE40 3328c2ecf20Sopenharmony_ci * - upper 8 bits of paddr need to be written into PTAG_HI 3338c2ecf20Sopenharmony_ci * - (and needs to be written before the lower 32 bits) 3348c2ecf20Sopenharmony_ci * Note that PTAG_HI is hoisted outside the line loop 3358c2ecf20Sopenharmony_ci */ 3368c2ecf20Sopenharmony_ci if (is_pae40_enabled() && op == OP_INV_IC) 3378c2ecf20Sopenharmony_ci write_aux_reg(ARC_REG_IC_PTAG_HI, (u64)paddr >> 32); 3388c2ecf20Sopenharmony_ci 3398c2ecf20Sopenharmony_ci while (num_lines-- > 0) { 3408c2ecf20Sopenharmony_ci if (!full_page) { 3418c2ecf20Sopenharmony_ci write_aux_reg(aux_tag, paddr); 3428c2ecf20Sopenharmony_ci paddr += L1_CACHE_BYTES; 3438c2ecf20Sopenharmony_ci } 3448c2ecf20Sopenharmony_ci 3458c2ecf20Sopenharmony_ci write_aux_reg(aux_cmd, vaddr); 3468c2ecf20Sopenharmony_ci vaddr += L1_CACHE_BYTES; 3478c2ecf20Sopenharmony_ci } 3488c2ecf20Sopenharmony_ci} 3498c2ecf20Sopenharmony_ci 3508c2ecf20Sopenharmony_ci#ifndef USE_RGN_FLSH 3518c2ecf20Sopenharmony_ci 3528c2ecf20Sopenharmony_ci/* 3538c2ecf20Sopenharmony_ci * In HS38x (MMU v4), I-cache is VIPT (can alias), D-cache is PIPT 3548c2ecf20Sopenharmony_ci * Here's how cache ops are implemented 3558c2ecf20Sopenharmony_ci * 3568c2ecf20Sopenharmony_ci * - D-cache: only paddr needed (in DC_IVDL/DC_FLDL) 3578c2ecf20Sopenharmony_ci * - I-cache Non Aliasing: Despite VIPT, only paddr needed (in IC_IVIL) 3588c2ecf20Sopenharmony_ci * - I-cache Aliasing: Both vaddr and paddr needed (in IC_IVIL, IC_PTAG 3598c2ecf20Sopenharmony_ci * respectively, similar to MMU v3 programming model, hence 3608c2ecf20Sopenharmony_ci * __cache_line_loop_v3() is used) 3618c2ecf20Sopenharmony_ci * 3628c2ecf20Sopenharmony_ci * If PAE40 is enabled, independent of aliasing considerations, the higher bits 3638c2ecf20Sopenharmony_ci * needs to be written into PTAG_HI 3648c2ecf20Sopenharmony_ci */ 3658c2ecf20Sopenharmony_cistatic inline 3668c2ecf20Sopenharmony_civoid __cache_line_loop_v4(phys_addr_t paddr, unsigned long vaddr, 3678c2ecf20Sopenharmony_ci unsigned long sz, const int op, const int full_page) 3688c2ecf20Sopenharmony_ci{ 3698c2ecf20Sopenharmony_ci unsigned int aux_cmd; 3708c2ecf20Sopenharmony_ci int num_lines; 3718c2ecf20Sopenharmony_ci 3728c2ecf20Sopenharmony_ci if (op == OP_INV_IC) { 3738c2ecf20Sopenharmony_ci aux_cmd = ARC_REG_IC_IVIL; 3748c2ecf20Sopenharmony_ci } else { 3758c2ecf20Sopenharmony_ci /* d$ cmd: INV (discard or wback-n-discard) OR FLUSH (wback) */ 3768c2ecf20Sopenharmony_ci aux_cmd = op & OP_INV ? ARC_REG_DC_IVDL : ARC_REG_DC_FLDL; 3778c2ecf20Sopenharmony_ci } 3788c2ecf20Sopenharmony_ci 3798c2ecf20Sopenharmony_ci /* Ensure we properly floor/ceil the non-line aligned/sized requests 3808c2ecf20Sopenharmony_ci * and have @paddr - aligned to cache line and integral @num_lines. 3818c2ecf20Sopenharmony_ci * This however can be avoided for page sized since: 3828c2ecf20Sopenharmony_ci * -@paddr will be cache-line aligned already (being page aligned) 3838c2ecf20Sopenharmony_ci * -@sz will be integral multiple of line size (being page sized). 3848c2ecf20Sopenharmony_ci */ 3858c2ecf20Sopenharmony_ci if (!full_page) { 3868c2ecf20Sopenharmony_ci sz += paddr & ~CACHE_LINE_MASK; 3878c2ecf20Sopenharmony_ci paddr &= CACHE_LINE_MASK; 3888c2ecf20Sopenharmony_ci } 3898c2ecf20Sopenharmony_ci 3908c2ecf20Sopenharmony_ci num_lines = DIV_ROUND_UP(sz, L1_CACHE_BYTES); 3918c2ecf20Sopenharmony_ci 3928c2ecf20Sopenharmony_ci /* 3938c2ecf20Sopenharmony_ci * For HS38 PAE40 configuration 3948c2ecf20Sopenharmony_ci * - upper 8 bits of paddr need to be written into PTAG_HI 3958c2ecf20Sopenharmony_ci * - (and needs to be written before the lower 32 bits) 3968c2ecf20Sopenharmony_ci */ 3978c2ecf20Sopenharmony_ci if (is_pae40_enabled()) { 3988c2ecf20Sopenharmony_ci if (op == OP_INV_IC) 3998c2ecf20Sopenharmony_ci /* 4008c2ecf20Sopenharmony_ci * Non aliasing I-cache in HS38, 4018c2ecf20Sopenharmony_ci * aliasing I-cache handled in __cache_line_loop_v3() 4028c2ecf20Sopenharmony_ci */ 4038c2ecf20Sopenharmony_ci write_aux_reg(ARC_REG_IC_PTAG_HI, (u64)paddr >> 32); 4048c2ecf20Sopenharmony_ci else 4058c2ecf20Sopenharmony_ci write_aux_reg(ARC_REG_DC_PTAG_HI, (u64)paddr >> 32); 4068c2ecf20Sopenharmony_ci } 4078c2ecf20Sopenharmony_ci 4088c2ecf20Sopenharmony_ci while (num_lines-- > 0) { 4098c2ecf20Sopenharmony_ci write_aux_reg(aux_cmd, paddr); 4108c2ecf20Sopenharmony_ci paddr += L1_CACHE_BYTES; 4118c2ecf20Sopenharmony_ci } 4128c2ecf20Sopenharmony_ci} 4138c2ecf20Sopenharmony_ci 4148c2ecf20Sopenharmony_ci#else 4158c2ecf20Sopenharmony_ci 4168c2ecf20Sopenharmony_ci/* 4178c2ecf20Sopenharmony_ci * optimized flush operation which takes a region as opposed to iterating per line 4188c2ecf20Sopenharmony_ci */ 4198c2ecf20Sopenharmony_cistatic inline 4208c2ecf20Sopenharmony_civoid __cache_line_loop_v4(phys_addr_t paddr, unsigned long vaddr, 4218c2ecf20Sopenharmony_ci unsigned long sz, const int op, const int full_page) 4228c2ecf20Sopenharmony_ci{ 4238c2ecf20Sopenharmony_ci unsigned int s, e; 4248c2ecf20Sopenharmony_ci 4258c2ecf20Sopenharmony_ci /* Only for Non aliasing I-cache in HS38 */ 4268c2ecf20Sopenharmony_ci if (op == OP_INV_IC) { 4278c2ecf20Sopenharmony_ci s = ARC_REG_IC_IVIR; 4288c2ecf20Sopenharmony_ci e = ARC_REG_IC_ENDR; 4298c2ecf20Sopenharmony_ci } else { 4308c2ecf20Sopenharmony_ci s = ARC_REG_DC_STARTR; 4318c2ecf20Sopenharmony_ci e = ARC_REG_DC_ENDR; 4328c2ecf20Sopenharmony_ci } 4338c2ecf20Sopenharmony_ci 4348c2ecf20Sopenharmony_ci if (!full_page) { 4358c2ecf20Sopenharmony_ci /* for any leading gap between @paddr and start of cache line */ 4368c2ecf20Sopenharmony_ci sz += paddr & ~CACHE_LINE_MASK; 4378c2ecf20Sopenharmony_ci paddr &= CACHE_LINE_MASK; 4388c2ecf20Sopenharmony_ci 4398c2ecf20Sopenharmony_ci /* 4408c2ecf20Sopenharmony_ci * account for any trailing gap to end of cache line 4418c2ecf20Sopenharmony_ci * this is equivalent to DIV_ROUND_UP() in line ops above 4428c2ecf20Sopenharmony_ci */ 4438c2ecf20Sopenharmony_ci sz += L1_CACHE_BYTES - 1; 4448c2ecf20Sopenharmony_ci } 4458c2ecf20Sopenharmony_ci 4468c2ecf20Sopenharmony_ci if (is_pae40_enabled()) { 4478c2ecf20Sopenharmony_ci /* TBD: check if crossing 4TB boundary */ 4488c2ecf20Sopenharmony_ci if (op == OP_INV_IC) 4498c2ecf20Sopenharmony_ci write_aux_reg(ARC_REG_IC_PTAG_HI, (u64)paddr >> 32); 4508c2ecf20Sopenharmony_ci else 4518c2ecf20Sopenharmony_ci write_aux_reg(ARC_REG_DC_PTAG_HI, (u64)paddr >> 32); 4528c2ecf20Sopenharmony_ci } 4538c2ecf20Sopenharmony_ci 4548c2ecf20Sopenharmony_ci /* ENDR needs to be set ahead of START */ 4558c2ecf20Sopenharmony_ci write_aux_reg(e, paddr + sz); /* ENDR is exclusive */ 4568c2ecf20Sopenharmony_ci write_aux_reg(s, paddr); 4578c2ecf20Sopenharmony_ci 4588c2ecf20Sopenharmony_ci /* caller waits on DC_CTRL.FS */ 4598c2ecf20Sopenharmony_ci} 4608c2ecf20Sopenharmony_ci 4618c2ecf20Sopenharmony_ci#endif 4628c2ecf20Sopenharmony_ci 4638c2ecf20Sopenharmony_ci#if (CONFIG_ARC_MMU_VER < 3) 4648c2ecf20Sopenharmony_ci#define __cache_line_loop __cache_line_loop_v2 4658c2ecf20Sopenharmony_ci#elif (CONFIG_ARC_MMU_VER == 3) 4668c2ecf20Sopenharmony_ci#define __cache_line_loop __cache_line_loop_v3 4678c2ecf20Sopenharmony_ci#elif (CONFIG_ARC_MMU_VER > 3) 4688c2ecf20Sopenharmony_ci#define __cache_line_loop __cache_line_loop_v4 4698c2ecf20Sopenharmony_ci#endif 4708c2ecf20Sopenharmony_ci 4718c2ecf20Sopenharmony_ci#ifdef CONFIG_ARC_HAS_DCACHE 4728c2ecf20Sopenharmony_ci 4738c2ecf20Sopenharmony_ci/*************************************************************** 4748c2ecf20Sopenharmony_ci * Machine specific helpers for Entire D-Cache or Per Line ops 4758c2ecf20Sopenharmony_ci */ 4768c2ecf20Sopenharmony_ci 4778c2ecf20Sopenharmony_ci#ifndef USE_RGN_FLSH 4788c2ecf20Sopenharmony_ci/* 4798c2ecf20Sopenharmony_ci * this version avoids extra read/write of DC_CTRL for flush or invalid ops 4808c2ecf20Sopenharmony_ci * in the non region flush regime (such as for ARCompact) 4818c2ecf20Sopenharmony_ci */ 4828c2ecf20Sopenharmony_cistatic inline void __before_dc_op(const int op) 4838c2ecf20Sopenharmony_ci{ 4848c2ecf20Sopenharmony_ci if (op == OP_FLUSH_N_INV) { 4858c2ecf20Sopenharmony_ci /* Dcache provides 2 cmd: FLUSH or INV 4868c2ecf20Sopenharmony_ci * INV inturn has sub-modes: DISCARD or FLUSH-BEFORE 4878c2ecf20Sopenharmony_ci * flush-n-inv is achieved by INV cmd but with IM=1 4888c2ecf20Sopenharmony_ci * So toggle INV sub-mode depending on op request and default 4898c2ecf20Sopenharmony_ci */ 4908c2ecf20Sopenharmony_ci const unsigned int ctl = ARC_REG_DC_CTRL; 4918c2ecf20Sopenharmony_ci write_aux_reg(ctl, read_aux_reg(ctl) | DC_CTRL_INV_MODE_FLUSH); 4928c2ecf20Sopenharmony_ci } 4938c2ecf20Sopenharmony_ci} 4948c2ecf20Sopenharmony_ci 4958c2ecf20Sopenharmony_ci#else 4968c2ecf20Sopenharmony_ci 4978c2ecf20Sopenharmony_cistatic inline void __before_dc_op(const int op) 4988c2ecf20Sopenharmony_ci{ 4998c2ecf20Sopenharmony_ci const unsigned int ctl = ARC_REG_DC_CTRL; 5008c2ecf20Sopenharmony_ci unsigned int val = read_aux_reg(ctl); 5018c2ecf20Sopenharmony_ci 5028c2ecf20Sopenharmony_ci if (op == OP_FLUSH_N_INV) { 5038c2ecf20Sopenharmony_ci val |= DC_CTRL_INV_MODE_FLUSH; 5048c2ecf20Sopenharmony_ci } 5058c2ecf20Sopenharmony_ci 5068c2ecf20Sopenharmony_ci if (op != OP_INV_IC) { 5078c2ecf20Sopenharmony_ci /* 5088c2ecf20Sopenharmony_ci * Flush / Invalidate is provided by DC_CTRL.RNG_OP 0 or 1 5098c2ecf20Sopenharmony_ci * combined Flush-n-invalidate uses DC_CTRL.IM = 1 set above 5108c2ecf20Sopenharmony_ci */ 5118c2ecf20Sopenharmony_ci val &= ~DC_CTRL_RGN_OP_MSK; 5128c2ecf20Sopenharmony_ci if (op & OP_INV) 5138c2ecf20Sopenharmony_ci val |= DC_CTRL_RGN_OP_INV; 5148c2ecf20Sopenharmony_ci } 5158c2ecf20Sopenharmony_ci write_aux_reg(ctl, val); 5168c2ecf20Sopenharmony_ci} 5178c2ecf20Sopenharmony_ci 5188c2ecf20Sopenharmony_ci#endif 5198c2ecf20Sopenharmony_ci 5208c2ecf20Sopenharmony_ci 5218c2ecf20Sopenharmony_cistatic inline void __after_dc_op(const int op) 5228c2ecf20Sopenharmony_ci{ 5238c2ecf20Sopenharmony_ci if (op & OP_FLUSH) { 5248c2ecf20Sopenharmony_ci const unsigned int ctl = ARC_REG_DC_CTRL; 5258c2ecf20Sopenharmony_ci unsigned int reg; 5268c2ecf20Sopenharmony_ci 5278c2ecf20Sopenharmony_ci /* flush / flush-n-inv both wait */ 5288c2ecf20Sopenharmony_ci while ((reg = read_aux_reg(ctl)) & DC_CTRL_FLUSH_STATUS) 5298c2ecf20Sopenharmony_ci ; 5308c2ecf20Sopenharmony_ci 5318c2ecf20Sopenharmony_ci /* Switch back to default Invalidate mode */ 5328c2ecf20Sopenharmony_ci if (op == OP_FLUSH_N_INV) 5338c2ecf20Sopenharmony_ci write_aux_reg(ctl, reg & ~DC_CTRL_INV_MODE_FLUSH); 5348c2ecf20Sopenharmony_ci } 5358c2ecf20Sopenharmony_ci} 5368c2ecf20Sopenharmony_ci 5378c2ecf20Sopenharmony_ci/* 5388c2ecf20Sopenharmony_ci * Operation on Entire D-Cache 5398c2ecf20Sopenharmony_ci * @op = {OP_INV, OP_FLUSH, OP_FLUSH_N_INV} 5408c2ecf20Sopenharmony_ci * Note that constant propagation ensures all the checks are gone 5418c2ecf20Sopenharmony_ci * in generated code 5428c2ecf20Sopenharmony_ci */ 5438c2ecf20Sopenharmony_cistatic inline void __dc_entire_op(const int op) 5448c2ecf20Sopenharmony_ci{ 5458c2ecf20Sopenharmony_ci int aux; 5468c2ecf20Sopenharmony_ci 5478c2ecf20Sopenharmony_ci __before_dc_op(op); 5488c2ecf20Sopenharmony_ci 5498c2ecf20Sopenharmony_ci if (op & OP_INV) /* Inv or flush-n-inv use same cmd reg */ 5508c2ecf20Sopenharmony_ci aux = ARC_REG_DC_IVDC; 5518c2ecf20Sopenharmony_ci else 5528c2ecf20Sopenharmony_ci aux = ARC_REG_DC_FLSH; 5538c2ecf20Sopenharmony_ci 5548c2ecf20Sopenharmony_ci write_aux_reg(aux, 0x1); 5558c2ecf20Sopenharmony_ci 5568c2ecf20Sopenharmony_ci __after_dc_op(op); 5578c2ecf20Sopenharmony_ci} 5588c2ecf20Sopenharmony_ci 5598c2ecf20Sopenharmony_cistatic inline void __dc_disable(void) 5608c2ecf20Sopenharmony_ci{ 5618c2ecf20Sopenharmony_ci const int r = ARC_REG_DC_CTRL; 5628c2ecf20Sopenharmony_ci 5638c2ecf20Sopenharmony_ci __dc_entire_op(OP_FLUSH_N_INV); 5648c2ecf20Sopenharmony_ci write_aux_reg(r, read_aux_reg(r) | DC_CTRL_DIS); 5658c2ecf20Sopenharmony_ci} 5668c2ecf20Sopenharmony_ci 5678c2ecf20Sopenharmony_cistatic void __dc_enable(void) 5688c2ecf20Sopenharmony_ci{ 5698c2ecf20Sopenharmony_ci const int r = ARC_REG_DC_CTRL; 5708c2ecf20Sopenharmony_ci 5718c2ecf20Sopenharmony_ci write_aux_reg(r, read_aux_reg(r) & ~DC_CTRL_DIS); 5728c2ecf20Sopenharmony_ci} 5738c2ecf20Sopenharmony_ci 5748c2ecf20Sopenharmony_ci/* For kernel mappings cache operation: index is same as paddr */ 5758c2ecf20Sopenharmony_ci#define __dc_line_op_k(p, sz, op) __dc_line_op(p, p, sz, op) 5768c2ecf20Sopenharmony_ci 5778c2ecf20Sopenharmony_ci/* 5788c2ecf20Sopenharmony_ci * D-Cache Line ops: Per Line INV (discard or wback+discard) or FLUSH (wback) 5798c2ecf20Sopenharmony_ci */ 5808c2ecf20Sopenharmony_cistatic inline void __dc_line_op(phys_addr_t paddr, unsigned long vaddr, 5818c2ecf20Sopenharmony_ci unsigned long sz, const int op) 5828c2ecf20Sopenharmony_ci{ 5838c2ecf20Sopenharmony_ci const int full_page = __builtin_constant_p(sz) && sz == PAGE_SIZE; 5848c2ecf20Sopenharmony_ci unsigned long flags; 5858c2ecf20Sopenharmony_ci 5868c2ecf20Sopenharmony_ci local_irq_save(flags); 5878c2ecf20Sopenharmony_ci 5888c2ecf20Sopenharmony_ci __before_dc_op(op); 5898c2ecf20Sopenharmony_ci 5908c2ecf20Sopenharmony_ci __cache_line_loop(paddr, vaddr, sz, op, full_page); 5918c2ecf20Sopenharmony_ci 5928c2ecf20Sopenharmony_ci __after_dc_op(op); 5938c2ecf20Sopenharmony_ci 5948c2ecf20Sopenharmony_ci local_irq_restore(flags); 5958c2ecf20Sopenharmony_ci} 5968c2ecf20Sopenharmony_ci 5978c2ecf20Sopenharmony_ci#else 5988c2ecf20Sopenharmony_ci 5998c2ecf20Sopenharmony_ci#define __dc_entire_op(op) 6008c2ecf20Sopenharmony_ci#define __dc_disable() 6018c2ecf20Sopenharmony_ci#define __dc_enable() 6028c2ecf20Sopenharmony_ci#define __dc_line_op(paddr, vaddr, sz, op) 6038c2ecf20Sopenharmony_ci#define __dc_line_op_k(paddr, sz, op) 6048c2ecf20Sopenharmony_ci 6058c2ecf20Sopenharmony_ci#endif /* CONFIG_ARC_HAS_DCACHE */ 6068c2ecf20Sopenharmony_ci 6078c2ecf20Sopenharmony_ci#ifdef CONFIG_ARC_HAS_ICACHE 6088c2ecf20Sopenharmony_ci 6098c2ecf20Sopenharmony_cistatic inline void __ic_entire_inv(void) 6108c2ecf20Sopenharmony_ci{ 6118c2ecf20Sopenharmony_ci write_aux_reg(ARC_REG_IC_IVIC, 1); 6128c2ecf20Sopenharmony_ci read_aux_reg(ARC_REG_IC_CTRL); /* blocks */ 6138c2ecf20Sopenharmony_ci} 6148c2ecf20Sopenharmony_ci 6158c2ecf20Sopenharmony_cistatic inline void 6168c2ecf20Sopenharmony_ci__ic_line_inv_vaddr_local(phys_addr_t paddr, unsigned long vaddr, 6178c2ecf20Sopenharmony_ci unsigned long sz) 6188c2ecf20Sopenharmony_ci{ 6198c2ecf20Sopenharmony_ci const int full_page = __builtin_constant_p(sz) && sz == PAGE_SIZE; 6208c2ecf20Sopenharmony_ci unsigned long flags; 6218c2ecf20Sopenharmony_ci 6228c2ecf20Sopenharmony_ci local_irq_save(flags); 6238c2ecf20Sopenharmony_ci (*_cache_line_loop_ic_fn)(paddr, vaddr, sz, OP_INV_IC, full_page); 6248c2ecf20Sopenharmony_ci local_irq_restore(flags); 6258c2ecf20Sopenharmony_ci} 6268c2ecf20Sopenharmony_ci 6278c2ecf20Sopenharmony_ci#ifndef CONFIG_SMP 6288c2ecf20Sopenharmony_ci 6298c2ecf20Sopenharmony_ci#define __ic_line_inv_vaddr(p, v, s) __ic_line_inv_vaddr_local(p, v, s) 6308c2ecf20Sopenharmony_ci 6318c2ecf20Sopenharmony_ci#else 6328c2ecf20Sopenharmony_ci 6338c2ecf20Sopenharmony_cistruct ic_inv_args { 6348c2ecf20Sopenharmony_ci phys_addr_t paddr, vaddr; 6358c2ecf20Sopenharmony_ci int sz; 6368c2ecf20Sopenharmony_ci}; 6378c2ecf20Sopenharmony_ci 6388c2ecf20Sopenharmony_cistatic void __ic_line_inv_vaddr_helper(void *info) 6398c2ecf20Sopenharmony_ci{ 6408c2ecf20Sopenharmony_ci struct ic_inv_args *ic_inv = info; 6418c2ecf20Sopenharmony_ci 6428c2ecf20Sopenharmony_ci __ic_line_inv_vaddr_local(ic_inv->paddr, ic_inv->vaddr, ic_inv->sz); 6438c2ecf20Sopenharmony_ci} 6448c2ecf20Sopenharmony_ci 6458c2ecf20Sopenharmony_cistatic void __ic_line_inv_vaddr(phys_addr_t paddr, unsigned long vaddr, 6468c2ecf20Sopenharmony_ci unsigned long sz) 6478c2ecf20Sopenharmony_ci{ 6488c2ecf20Sopenharmony_ci struct ic_inv_args ic_inv = { 6498c2ecf20Sopenharmony_ci .paddr = paddr, 6508c2ecf20Sopenharmony_ci .vaddr = vaddr, 6518c2ecf20Sopenharmony_ci .sz = sz 6528c2ecf20Sopenharmony_ci }; 6538c2ecf20Sopenharmony_ci 6548c2ecf20Sopenharmony_ci on_each_cpu(__ic_line_inv_vaddr_helper, &ic_inv, 1); 6558c2ecf20Sopenharmony_ci} 6568c2ecf20Sopenharmony_ci 6578c2ecf20Sopenharmony_ci#endif /* CONFIG_SMP */ 6588c2ecf20Sopenharmony_ci 6598c2ecf20Sopenharmony_ci#else /* !CONFIG_ARC_HAS_ICACHE */ 6608c2ecf20Sopenharmony_ci 6618c2ecf20Sopenharmony_ci#define __ic_entire_inv() 6628c2ecf20Sopenharmony_ci#define __ic_line_inv_vaddr(pstart, vstart, sz) 6638c2ecf20Sopenharmony_ci 6648c2ecf20Sopenharmony_ci#endif /* CONFIG_ARC_HAS_ICACHE */ 6658c2ecf20Sopenharmony_ci 6668c2ecf20Sopenharmony_cinoinline void slc_op_rgn(phys_addr_t paddr, unsigned long sz, const int op) 6678c2ecf20Sopenharmony_ci{ 6688c2ecf20Sopenharmony_ci#ifdef CONFIG_ISA_ARCV2 6698c2ecf20Sopenharmony_ci /* 6708c2ecf20Sopenharmony_ci * SLC is shared between all cores and concurrent aux operations from 6718c2ecf20Sopenharmony_ci * multiple cores need to be serialized using a spinlock 6728c2ecf20Sopenharmony_ci * A concurrent operation can be silently ignored and/or the old/new 6738c2ecf20Sopenharmony_ci * operation can remain incomplete forever (lockup in SLC_CTRL_BUSY loop 6748c2ecf20Sopenharmony_ci * below) 6758c2ecf20Sopenharmony_ci */ 6768c2ecf20Sopenharmony_ci static DEFINE_SPINLOCK(lock); 6778c2ecf20Sopenharmony_ci unsigned long flags; 6788c2ecf20Sopenharmony_ci unsigned int ctrl; 6798c2ecf20Sopenharmony_ci phys_addr_t end; 6808c2ecf20Sopenharmony_ci 6818c2ecf20Sopenharmony_ci spin_lock_irqsave(&lock, flags); 6828c2ecf20Sopenharmony_ci 6838c2ecf20Sopenharmony_ci /* 6848c2ecf20Sopenharmony_ci * The Region Flush operation is specified by CTRL.RGN_OP[11..9] 6858c2ecf20Sopenharmony_ci * - b'000 (default) is Flush, 6868c2ecf20Sopenharmony_ci * - b'001 is Invalidate if CTRL.IM == 0 6878c2ecf20Sopenharmony_ci * - b'001 is Flush-n-Invalidate if CTRL.IM == 1 6888c2ecf20Sopenharmony_ci */ 6898c2ecf20Sopenharmony_ci ctrl = read_aux_reg(ARC_REG_SLC_CTRL); 6908c2ecf20Sopenharmony_ci 6918c2ecf20Sopenharmony_ci /* Don't rely on default value of IM bit */ 6928c2ecf20Sopenharmony_ci if (!(op & OP_FLUSH)) /* i.e. OP_INV */ 6938c2ecf20Sopenharmony_ci ctrl &= ~SLC_CTRL_IM; /* clear IM: Disable flush before Inv */ 6948c2ecf20Sopenharmony_ci else 6958c2ecf20Sopenharmony_ci ctrl |= SLC_CTRL_IM; 6968c2ecf20Sopenharmony_ci 6978c2ecf20Sopenharmony_ci if (op & OP_INV) 6988c2ecf20Sopenharmony_ci ctrl |= SLC_CTRL_RGN_OP_INV; /* Inv or flush-n-inv */ 6998c2ecf20Sopenharmony_ci else 7008c2ecf20Sopenharmony_ci ctrl &= ~SLC_CTRL_RGN_OP_INV; 7018c2ecf20Sopenharmony_ci 7028c2ecf20Sopenharmony_ci write_aux_reg(ARC_REG_SLC_CTRL, ctrl); 7038c2ecf20Sopenharmony_ci 7048c2ecf20Sopenharmony_ci /* 7058c2ecf20Sopenharmony_ci * Lower bits are ignored, no need to clip 7068c2ecf20Sopenharmony_ci * END needs to be setup before START (latter triggers the operation) 7078c2ecf20Sopenharmony_ci * END can't be same as START, so add (l2_line_sz - 1) to sz 7088c2ecf20Sopenharmony_ci */ 7098c2ecf20Sopenharmony_ci end = paddr + sz + l2_line_sz - 1; 7108c2ecf20Sopenharmony_ci if (is_pae40_enabled()) 7118c2ecf20Sopenharmony_ci write_aux_reg(ARC_REG_SLC_RGN_END1, upper_32_bits(end)); 7128c2ecf20Sopenharmony_ci 7138c2ecf20Sopenharmony_ci write_aux_reg(ARC_REG_SLC_RGN_END, lower_32_bits(end)); 7148c2ecf20Sopenharmony_ci 7158c2ecf20Sopenharmony_ci if (is_pae40_enabled()) 7168c2ecf20Sopenharmony_ci write_aux_reg(ARC_REG_SLC_RGN_START1, upper_32_bits(paddr)); 7178c2ecf20Sopenharmony_ci 7188c2ecf20Sopenharmony_ci write_aux_reg(ARC_REG_SLC_RGN_START, lower_32_bits(paddr)); 7198c2ecf20Sopenharmony_ci 7208c2ecf20Sopenharmony_ci /* Make sure "busy" bit reports correct stataus, see STAR 9001165532 */ 7218c2ecf20Sopenharmony_ci read_aux_reg(ARC_REG_SLC_CTRL); 7228c2ecf20Sopenharmony_ci 7238c2ecf20Sopenharmony_ci while (read_aux_reg(ARC_REG_SLC_CTRL) & SLC_CTRL_BUSY); 7248c2ecf20Sopenharmony_ci 7258c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&lock, flags); 7268c2ecf20Sopenharmony_ci#endif 7278c2ecf20Sopenharmony_ci} 7288c2ecf20Sopenharmony_ci 7298c2ecf20Sopenharmony_cinoinline void slc_op_line(phys_addr_t paddr, unsigned long sz, const int op) 7308c2ecf20Sopenharmony_ci{ 7318c2ecf20Sopenharmony_ci#ifdef CONFIG_ISA_ARCV2 7328c2ecf20Sopenharmony_ci /* 7338c2ecf20Sopenharmony_ci * SLC is shared between all cores and concurrent aux operations from 7348c2ecf20Sopenharmony_ci * multiple cores need to be serialized using a spinlock 7358c2ecf20Sopenharmony_ci * A concurrent operation can be silently ignored and/or the old/new 7368c2ecf20Sopenharmony_ci * operation can remain incomplete forever (lockup in SLC_CTRL_BUSY loop 7378c2ecf20Sopenharmony_ci * below) 7388c2ecf20Sopenharmony_ci */ 7398c2ecf20Sopenharmony_ci static DEFINE_SPINLOCK(lock); 7408c2ecf20Sopenharmony_ci 7418c2ecf20Sopenharmony_ci const unsigned long SLC_LINE_MASK = ~(l2_line_sz - 1); 7428c2ecf20Sopenharmony_ci unsigned int ctrl, cmd; 7438c2ecf20Sopenharmony_ci unsigned long flags; 7448c2ecf20Sopenharmony_ci int num_lines; 7458c2ecf20Sopenharmony_ci 7468c2ecf20Sopenharmony_ci spin_lock_irqsave(&lock, flags); 7478c2ecf20Sopenharmony_ci 7488c2ecf20Sopenharmony_ci ctrl = read_aux_reg(ARC_REG_SLC_CTRL); 7498c2ecf20Sopenharmony_ci 7508c2ecf20Sopenharmony_ci /* Don't rely on default value of IM bit */ 7518c2ecf20Sopenharmony_ci if (!(op & OP_FLUSH)) /* i.e. OP_INV */ 7528c2ecf20Sopenharmony_ci ctrl &= ~SLC_CTRL_IM; /* clear IM: Disable flush before Inv */ 7538c2ecf20Sopenharmony_ci else 7548c2ecf20Sopenharmony_ci ctrl |= SLC_CTRL_IM; 7558c2ecf20Sopenharmony_ci 7568c2ecf20Sopenharmony_ci write_aux_reg(ARC_REG_SLC_CTRL, ctrl); 7578c2ecf20Sopenharmony_ci 7588c2ecf20Sopenharmony_ci cmd = op & OP_INV ? ARC_AUX_SLC_IVDL : ARC_AUX_SLC_FLDL; 7598c2ecf20Sopenharmony_ci 7608c2ecf20Sopenharmony_ci sz += paddr & ~SLC_LINE_MASK; 7618c2ecf20Sopenharmony_ci paddr &= SLC_LINE_MASK; 7628c2ecf20Sopenharmony_ci 7638c2ecf20Sopenharmony_ci num_lines = DIV_ROUND_UP(sz, l2_line_sz); 7648c2ecf20Sopenharmony_ci 7658c2ecf20Sopenharmony_ci while (num_lines-- > 0) { 7668c2ecf20Sopenharmony_ci write_aux_reg(cmd, paddr); 7678c2ecf20Sopenharmony_ci paddr += l2_line_sz; 7688c2ecf20Sopenharmony_ci } 7698c2ecf20Sopenharmony_ci 7708c2ecf20Sopenharmony_ci /* Make sure "busy" bit reports correct stataus, see STAR 9001165532 */ 7718c2ecf20Sopenharmony_ci read_aux_reg(ARC_REG_SLC_CTRL); 7728c2ecf20Sopenharmony_ci 7738c2ecf20Sopenharmony_ci while (read_aux_reg(ARC_REG_SLC_CTRL) & SLC_CTRL_BUSY); 7748c2ecf20Sopenharmony_ci 7758c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&lock, flags); 7768c2ecf20Sopenharmony_ci#endif 7778c2ecf20Sopenharmony_ci} 7788c2ecf20Sopenharmony_ci 7798c2ecf20Sopenharmony_ci#define slc_op(paddr, sz, op) slc_op_rgn(paddr, sz, op) 7808c2ecf20Sopenharmony_ci 7818c2ecf20Sopenharmony_cinoinline static void slc_entire_op(const int op) 7828c2ecf20Sopenharmony_ci{ 7838c2ecf20Sopenharmony_ci unsigned int ctrl, r = ARC_REG_SLC_CTRL; 7848c2ecf20Sopenharmony_ci 7858c2ecf20Sopenharmony_ci ctrl = read_aux_reg(r); 7868c2ecf20Sopenharmony_ci 7878c2ecf20Sopenharmony_ci if (!(op & OP_FLUSH)) /* i.e. OP_INV */ 7888c2ecf20Sopenharmony_ci ctrl &= ~SLC_CTRL_IM; /* clear IM: Disable flush before Inv */ 7898c2ecf20Sopenharmony_ci else 7908c2ecf20Sopenharmony_ci ctrl |= SLC_CTRL_IM; 7918c2ecf20Sopenharmony_ci 7928c2ecf20Sopenharmony_ci write_aux_reg(r, ctrl); 7938c2ecf20Sopenharmony_ci 7948c2ecf20Sopenharmony_ci if (op & OP_INV) /* Inv or flush-n-inv use same cmd reg */ 7958c2ecf20Sopenharmony_ci write_aux_reg(ARC_REG_SLC_INVALIDATE, 0x1); 7968c2ecf20Sopenharmony_ci else 7978c2ecf20Sopenharmony_ci write_aux_reg(ARC_REG_SLC_FLUSH, 0x1); 7988c2ecf20Sopenharmony_ci 7998c2ecf20Sopenharmony_ci /* Make sure "busy" bit reports correct stataus, see STAR 9001165532 */ 8008c2ecf20Sopenharmony_ci read_aux_reg(r); 8018c2ecf20Sopenharmony_ci 8028c2ecf20Sopenharmony_ci /* Important to wait for flush to complete */ 8038c2ecf20Sopenharmony_ci while (read_aux_reg(r) & SLC_CTRL_BUSY); 8048c2ecf20Sopenharmony_ci} 8058c2ecf20Sopenharmony_ci 8068c2ecf20Sopenharmony_cistatic inline void arc_slc_disable(void) 8078c2ecf20Sopenharmony_ci{ 8088c2ecf20Sopenharmony_ci const int r = ARC_REG_SLC_CTRL; 8098c2ecf20Sopenharmony_ci 8108c2ecf20Sopenharmony_ci slc_entire_op(OP_FLUSH_N_INV); 8118c2ecf20Sopenharmony_ci write_aux_reg(r, read_aux_reg(r) | SLC_CTRL_DIS); 8128c2ecf20Sopenharmony_ci} 8138c2ecf20Sopenharmony_ci 8148c2ecf20Sopenharmony_cistatic inline void arc_slc_enable(void) 8158c2ecf20Sopenharmony_ci{ 8168c2ecf20Sopenharmony_ci const int r = ARC_REG_SLC_CTRL; 8178c2ecf20Sopenharmony_ci 8188c2ecf20Sopenharmony_ci write_aux_reg(r, read_aux_reg(r) & ~SLC_CTRL_DIS); 8198c2ecf20Sopenharmony_ci} 8208c2ecf20Sopenharmony_ci 8218c2ecf20Sopenharmony_ci/*********************************************************** 8228c2ecf20Sopenharmony_ci * Exported APIs 8238c2ecf20Sopenharmony_ci */ 8248c2ecf20Sopenharmony_ci 8258c2ecf20Sopenharmony_ci/* 8268c2ecf20Sopenharmony_ci * Handle cache congruency of kernel and userspace mappings of page when kernel 8278c2ecf20Sopenharmony_ci * writes-to/reads-from 8288c2ecf20Sopenharmony_ci * 8298c2ecf20Sopenharmony_ci * The idea is to defer flushing of kernel mapping after a WRITE, possible if: 8308c2ecf20Sopenharmony_ci * -dcache is NOT aliasing, hence any U/K-mappings of page are congruent 8318c2ecf20Sopenharmony_ci * -U-mapping doesn't exist yet for page (finalised in update_mmu_cache) 8328c2ecf20Sopenharmony_ci * -In SMP, if hardware caches are coherent 8338c2ecf20Sopenharmony_ci * 8348c2ecf20Sopenharmony_ci * There's a corollary case, where kernel READs from a userspace mapped page. 8358c2ecf20Sopenharmony_ci * If the U-mapping is not congruent to to K-mapping, former needs flushing. 8368c2ecf20Sopenharmony_ci */ 8378c2ecf20Sopenharmony_civoid flush_dcache_page(struct page *page) 8388c2ecf20Sopenharmony_ci{ 8398c2ecf20Sopenharmony_ci struct address_space *mapping; 8408c2ecf20Sopenharmony_ci 8418c2ecf20Sopenharmony_ci if (!cache_is_vipt_aliasing()) { 8428c2ecf20Sopenharmony_ci clear_bit(PG_dc_clean, &page->flags); 8438c2ecf20Sopenharmony_ci return; 8448c2ecf20Sopenharmony_ci } 8458c2ecf20Sopenharmony_ci 8468c2ecf20Sopenharmony_ci /* don't handle anon pages here */ 8478c2ecf20Sopenharmony_ci mapping = page_mapping_file(page); 8488c2ecf20Sopenharmony_ci if (!mapping) 8498c2ecf20Sopenharmony_ci return; 8508c2ecf20Sopenharmony_ci 8518c2ecf20Sopenharmony_ci /* 8528c2ecf20Sopenharmony_ci * pagecache page, file not yet mapped to userspace 8538c2ecf20Sopenharmony_ci * Make a note that K-mapping is dirty 8548c2ecf20Sopenharmony_ci */ 8558c2ecf20Sopenharmony_ci if (!mapping_mapped(mapping)) { 8568c2ecf20Sopenharmony_ci clear_bit(PG_dc_clean, &page->flags); 8578c2ecf20Sopenharmony_ci } else if (page_mapcount(page)) { 8588c2ecf20Sopenharmony_ci 8598c2ecf20Sopenharmony_ci /* kernel reading from page with U-mapping */ 8608c2ecf20Sopenharmony_ci phys_addr_t paddr = (unsigned long)page_address(page); 8618c2ecf20Sopenharmony_ci unsigned long vaddr = page->index << PAGE_SHIFT; 8628c2ecf20Sopenharmony_ci 8638c2ecf20Sopenharmony_ci if (addr_not_cache_congruent(paddr, vaddr)) 8648c2ecf20Sopenharmony_ci __flush_dcache_page(paddr, vaddr); 8658c2ecf20Sopenharmony_ci } 8668c2ecf20Sopenharmony_ci} 8678c2ecf20Sopenharmony_ciEXPORT_SYMBOL(flush_dcache_page); 8688c2ecf20Sopenharmony_ci 8698c2ecf20Sopenharmony_ci/* 8708c2ecf20Sopenharmony_ci * DMA ops for systems with L1 cache only 8718c2ecf20Sopenharmony_ci * Make memory coherent with L1 cache by flushing/invalidating L1 lines 8728c2ecf20Sopenharmony_ci */ 8738c2ecf20Sopenharmony_cistatic void __dma_cache_wback_inv_l1(phys_addr_t start, unsigned long sz) 8748c2ecf20Sopenharmony_ci{ 8758c2ecf20Sopenharmony_ci __dc_line_op_k(start, sz, OP_FLUSH_N_INV); 8768c2ecf20Sopenharmony_ci} 8778c2ecf20Sopenharmony_ci 8788c2ecf20Sopenharmony_cistatic void __dma_cache_inv_l1(phys_addr_t start, unsigned long sz) 8798c2ecf20Sopenharmony_ci{ 8808c2ecf20Sopenharmony_ci __dc_line_op_k(start, sz, OP_INV); 8818c2ecf20Sopenharmony_ci} 8828c2ecf20Sopenharmony_ci 8838c2ecf20Sopenharmony_cistatic void __dma_cache_wback_l1(phys_addr_t start, unsigned long sz) 8848c2ecf20Sopenharmony_ci{ 8858c2ecf20Sopenharmony_ci __dc_line_op_k(start, sz, OP_FLUSH); 8868c2ecf20Sopenharmony_ci} 8878c2ecf20Sopenharmony_ci 8888c2ecf20Sopenharmony_ci/* 8898c2ecf20Sopenharmony_ci * DMA ops for systems with both L1 and L2 caches, but without IOC 8908c2ecf20Sopenharmony_ci * Both L1 and L2 lines need to be explicitly flushed/invalidated 8918c2ecf20Sopenharmony_ci */ 8928c2ecf20Sopenharmony_cistatic void __dma_cache_wback_inv_slc(phys_addr_t start, unsigned long sz) 8938c2ecf20Sopenharmony_ci{ 8948c2ecf20Sopenharmony_ci __dc_line_op_k(start, sz, OP_FLUSH_N_INV); 8958c2ecf20Sopenharmony_ci slc_op(start, sz, OP_FLUSH_N_INV); 8968c2ecf20Sopenharmony_ci} 8978c2ecf20Sopenharmony_ci 8988c2ecf20Sopenharmony_cistatic void __dma_cache_inv_slc(phys_addr_t start, unsigned long sz) 8998c2ecf20Sopenharmony_ci{ 9008c2ecf20Sopenharmony_ci __dc_line_op_k(start, sz, OP_INV); 9018c2ecf20Sopenharmony_ci slc_op(start, sz, OP_INV); 9028c2ecf20Sopenharmony_ci} 9038c2ecf20Sopenharmony_ci 9048c2ecf20Sopenharmony_cistatic void __dma_cache_wback_slc(phys_addr_t start, unsigned long sz) 9058c2ecf20Sopenharmony_ci{ 9068c2ecf20Sopenharmony_ci __dc_line_op_k(start, sz, OP_FLUSH); 9078c2ecf20Sopenharmony_ci slc_op(start, sz, OP_FLUSH); 9088c2ecf20Sopenharmony_ci} 9098c2ecf20Sopenharmony_ci 9108c2ecf20Sopenharmony_ci/* 9118c2ecf20Sopenharmony_ci * Exported DMA API 9128c2ecf20Sopenharmony_ci */ 9138c2ecf20Sopenharmony_civoid dma_cache_wback_inv(phys_addr_t start, unsigned long sz) 9148c2ecf20Sopenharmony_ci{ 9158c2ecf20Sopenharmony_ci __dma_cache_wback_inv(start, sz); 9168c2ecf20Sopenharmony_ci} 9178c2ecf20Sopenharmony_ciEXPORT_SYMBOL(dma_cache_wback_inv); 9188c2ecf20Sopenharmony_ci 9198c2ecf20Sopenharmony_civoid dma_cache_inv(phys_addr_t start, unsigned long sz) 9208c2ecf20Sopenharmony_ci{ 9218c2ecf20Sopenharmony_ci __dma_cache_inv(start, sz); 9228c2ecf20Sopenharmony_ci} 9238c2ecf20Sopenharmony_ciEXPORT_SYMBOL(dma_cache_inv); 9248c2ecf20Sopenharmony_ci 9258c2ecf20Sopenharmony_civoid dma_cache_wback(phys_addr_t start, unsigned long sz) 9268c2ecf20Sopenharmony_ci{ 9278c2ecf20Sopenharmony_ci __dma_cache_wback(start, sz); 9288c2ecf20Sopenharmony_ci} 9298c2ecf20Sopenharmony_ciEXPORT_SYMBOL(dma_cache_wback); 9308c2ecf20Sopenharmony_ci 9318c2ecf20Sopenharmony_ci/* 9328c2ecf20Sopenharmony_ci * This is API for making I/D Caches consistent when modifying 9338c2ecf20Sopenharmony_ci * kernel code (loadable modules, kprobes, kgdb...) 9348c2ecf20Sopenharmony_ci * This is called on insmod, with kernel virtual address for CODE of 9358c2ecf20Sopenharmony_ci * the module. ARC cache maintenance ops require PHY address thus we 9368c2ecf20Sopenharmony_ci * need to convert vmalloc addr to PHY addr 9378c2ecf20Sopenharmony_ci */ 9388c2ecf20Sopenharmony_civoid flush_icache_range(unsigned long kstart, unsigned long kend) 9398c2ecf20Sopenharmony_ci{ 9408c2ecf20Sopenharmony_ci unsigned int tot_sz; 9418c2ecf20Sopenharmony_ci 9428c2ecf20Sopenharmony_ci WARN(kstart < TASK_SIZE, "%s() can't handle user vaddr", __func__); 9438c2ecf20Sopenharmony_ci 9448c2ecf20Sopenharmony_ci /* Shortcut for bigger flush ranges. 9458c2ecf20Sopenharmony_ci * Here we don't care if this was kernel virtual or phy addr 9468c2ecf20Sopenharmony_ci */ 9478c2ecf20Sopenharmony_ci tot_sz = kend - kstart; 9488c2ecf20Sopenharmony_ci if (tot_sz > PAGE_SIZE) { 9498c2ecf20Sopenharmony_ci flush_cache_all(); 9508c2ecf20Sopenharmony_ci return; 9518c2ecf20Sopenharmony_ci } 9528c2ecf20Sopenharmony_ci 9538c2ecf20Sopenharmony_ci /* Case: Kernel Phy addr (0x8000_0000 onwards) */ 9548c2ecf20Sopenharmony_ci if (likely(kstart > PAGE_OFFSET)) { 9558c2ecf20Sopenharmony_ci /* 9568c2ecf20Sopenharmony_ci * The 2nd arg despite being paddr will be used to index icache 9578c2ecf20Sopenharmony_ci * This is OK since no alternate virtual mappings will exist 9588c2ecf20Sopenharmony_ci * given the callers for this case: kprobe/kgdb in built-in 9598c2ecf20Sopenharmony_ci * kernel code only. 9608c2ecf20Sopenharmony_ci */ 9618c2ecf20Sopenharmony_ci __sync_icache_dcache(kstart, kstart, kend - kstart); 9628c2ecf20Sopenharmony_ci return; 9638c2ecf20Sopenharmony_ci } 9648c2ecf20Sopenharmony_ci 9658c2ecf20Sopenharmony_ci /* 9668c2ecf20Sopenharmony_ci * Case: Kernel Vaddr (0x7000_0000 to 0x7fff_ffff) 9678c2ecf20Sopenharmony_ci * (1) ARC Cache Maintenance ops only take Phy addr, hence special 9688c2ecf20Sopenharmony_ci * handling of kernel vaddr. 9698c2ecf20Sopenharmony_ci * 9708c2ecf20Sopenharmony_ci * (2) Despite @tot_sz being < PAGE_SIZE (bigger cases handled already), 9718c2ecf20Sopenharmony_ci * it still needs to handle a 2 page scenario, where the range 9728c2ecf20Sopenharmony_ci * straddles across 2 virtual pages and hence need for loop 9738c2ecf20Sopenharmony_ci */ 9748c2ecf20Sopenharmony_ci while (tot_sz > 0) { 9758c2ecf20Sopenharmony_ci unsigned int off, sz; 9768c2ecf20Sopenharmony_ci unsigned long phy, pfn; 9778c2ecf20Sopenharmony_ci 9788c2ecf20Sopenharmony_ci off = kstart % PAGE_SIZE; 9798c2ecf20Sopenharmony_ci pfn = vmalloc_to_pfn((void *)kstart); 9808c2ecf20Sopenharmony_ci phy = (pfn << PAGE_SHIFT) + off; 9818c2ecf20Sopenharmony_ci sz = min_t(unsigned int, tot_sz, PAGE_SIZE - off); 9828c2ecf20Sopenharmony_ci __sync_icache_dcache(phy, kstart, sz); 9838c2ecf20Sopenharmony_ci kstart += sz; 9848c2ecf20Sopenharmony_ci tot_sz -= sz; 9858c2ecf20Sopenharmony_ci } 9868c2ecf20Sopenharmony_ci} 9878c2ecf20Sopenharmony_ciEXPORT_SYMBOL(flush_icache_range); 9888c2ecf20Sopenharmony_ci 9898c2ecf20Sopenharmony_ci/* 9908c2ecf20Sopenharmony_ci * General purpose helper to make I and D cache lines consistent. 9918c2ecf20Sopenharmony_ci * @paddr is phy addr of region 9928c2ecf20Sopenharmony_ci * @vaddr is typically user vaddr (breakpoint) or kernel vaddr (vmalloc) 9938c2ecf20Sopenharmony_ci * However in one instance, when called by kprobe (for a breakpt in 9948c2ecf20Sopenharmony_ci * builtin kernel code) @vaddr will be paddr only, meaning CDU operation will 9958c2ecf20Sopenharmony_ci * use a paddr to index the cache (despite VIPT). This is fine since since a 9968c2ecf20Sopenharmony_ci * builtin kernel page will not have any virtual mappings. 9978c2ecf20Sopenharmony_ci * kprobe on loadable module will be kernel vaddr. 9988c2ecf20Sopenharmony_ci */ 9998c2ecf20Sopenharmony_civoid __sync_icache_dcache(phys_addr_t paddr, unsigned long vaddr, int len) 10008c2ecf20Sopenharmony_ci{ 10018c2ecf20Sopenharmony_ci __dc_line_op(paddr, vaddr, len, OP_FLUSH_N_INV); 10028c2ecf20Sopenharmony_ci __ic_line_inv_vaddr(paddr, vaddr, len); 10038c2ecf20Sopenharmony_ci} 10048c2ecf20Sopenharmony_ci 10058c2ecf20Sopenharmony_ci/* wrapper to compile time eliminate alignment checks in flush loop */ 10068c2ecf20Sopenharmony_civoid __inv_icache_page(phys_addr_t paddr, unsigned long vaddr) 10078c2ecf20Sopenharmony_ci{ 10088c2ecf20Sopenharmony_ci __ic_line_inv_vaddr(paddr, vaddr, PAGE_SIZE); 10098c2ecf20Sopenharmony_ci} 10108c2ecf20Sopenharmony_ci 10118c2ecf20Sopenharmony_ci/* 10128c2ecf20Sopenharmony_ci * wrapper to clearout kernel or userspace mappings of a page 10138c2ecf20Sopenharmony_ci * For kernel mappings @vaddr == @paddr 10148c2ecf20Sopenharmony_ci */ 10158c2ecf20Sopenharmony_civoid __flush_dcache_page(phys_addr_t paddr, unsigned long vaddr) 10168c2ecf20Sopenharmony_ci{ 10178c2ecf20Sopenharmony_ci __dc_line_op(paddr, vaddr & PAGE_MASK, PAGE_SIZE, OP_FLUSH_N_INV); 10188c2ecf20Sopenharmony_ci} 10198c2ecf20Sopenharmony_ci 10208c2ecf20Sopenharmony_cinoinline void flush_cache_all(void) 10218c2ecf20Sopenharmony_ci{ 10228c2ecf20Sopenharmony_ci unsigned long flags; 10238c2ecf20Sopenharmony_ci 10248c2ecf20Sopenharmony_ci local_irq_save(flags); 10258c2ecf20Sopenharmony_ci 10268c2ecf20Sopenharmony_ci __ic_entire_inv(); 10278c2ecf20Sopenharmony_ci __dc_entire_op(OP_FLUSH_N_INV); 10288c2ecf20Sopenharmony_ci 10298c2ecf20Sopenharmony_ci local_irq_restore(flags); 10308c2ecf20Sopenharmony_ci 10318c2ecf20Sopenharmony_ci} 10328c2ecf20Sopenharmony_ci 10338c2ecf20Sopenharmony_ci#ifdef CONFIG_ARC_CACHE_VIPT_ALIASING 10348c2ecf20Sopenharmony_ci 10358c2ecf20Sopenharmony_civoid flush_cache_mm(struct mm_struct *mm) 10368c2ecf20Sopenharmony_ci{ 10378c2ecf20Sopenharmony_ci flush_cache_all(); 10388c2ecf20Sopenharmony_ci} 10398c2ecf20Sopenharmony_ci 10408c2ecf20Sopenharmony_civoid flush_cache_page(struct vm_area_struct *vma, unsigned long u_vaddr, 10418c2ecf20Sopenharmony_ci unsigned long pfn) 10428c2ecf20Sopenharmony_ci{ 10438c2ecf20Sopenharmony_ci phys_addr_t paddr = pfn << PAGE_SHIFT; 10448c2ecf20Sopenharmony_ci 10458c2ecf20Sopenharmony_ci u_vaddr &= PAGE_MASK; 10468c2ecf20Sopenharmony_ci 10478c2ecf20Sopenharmony_ci __flush_dcache_page(paddr, u_vaddr); 10488c2ecf20Sopenharmony_ci 10498c2ecf20Sopenharmony_ci if (vma->vm_flags & VM_EXEC) 10508c2ecf20Sopenharmony_ci __inv_icache_page(paddr, u_vaddr); 10518c2ecf20Sopenharmony_ci} 10528c2ecf20Sopenharmony_ci 10538c2ecf20Sopenharmony_civoid flush_cache_range(struct vm_area_struct *vma, unsigned long start, 10548c2ecf20Sopenharmony_ci unsigned long end) 10558c2ecf20Sopenharmony_ci{ 10568c2ecf20Sopenharmony_ci flush_cache_all(); 10578c2ecf20Sopenharmony_ci} 10588c2ecf20Sopenharmony_ci 10598c2ecf20Sopenharmony_civoid flush_anon_page(struct vm_area_struct *vma, struct page *page, 10608c2ecf20Sopenharmony_ci unsigned long u_vaddr) 10618c2ecf20Sopenharmony_ci{ 10628c2ecf20Sopenharmony_ci /* TBD: do we really need to clear the kernel mapping */ 10638c2ecf20Sopenharmony_ci __flush_dcache_page((phys_addr_t)page_address(page), u_vaddr); 10648c2ecf20Sopenharmony_ci __flush_dcache_page((phys_addr_t)page_address(page), 10658c2ecf20Sopenharmony_ci (phys_addr_t)page_address(page)); 10668c2ecf20Sopenharmony_ci 10678c2ecf20Sopenharmony_ci} 10688c2ecf20Sopenharmony_ci 10698c2ecf20Sopenharmony_ci#endif 10708c2ecf20Sopenharmony_ci 10718c2ecf20Sopenharmony_civoid copy_user_highpage(struct page *to, struct page *from, 10728c2ecf20Sopenharmony_ci unsigned long u_vaddr, struct vm_area_struct *vma) 10738c2ecf20Sopenharmony_ci{ 10748c2ecf20Sopenharmony_ci void *kfrom = kmap_atomic(from); 10758c2ecf20Sopenharmony_ci void *kto = kmap_atomic(to); 10768c2ecf20Sopenharmony_ci int clean_src_k_mappings = 0; 10778c2ecf20Sopenharmony_ci 10788c2ecf20Sopenharmony_ci /* 10798c2ecf20Sopenharmony_ci * If SRC page was already mapped in userspace AND it's U-mapping is 10808c2ecf20Sopenharmony_ci * not congruent with K-mapping, sync former to physical page so that 10818c2ecf20Sopenharmony_ci * K-mapping in memcpy below, sees the right data 10828c2ecf20Sopenharmony_ci * 10838c2ecf20Sopenharmony_ci * Note that while @u_vaddr refers to DST page's userspace vaddr, it is 10848c2ecf20Sopenharmony_ci * equally valid for SRC page as well 10858c2ecf20Sopenharmony_ci * 10868c2ecf20Sopenharmony_ci * For !VIPT cache, all of this gets compiled out as 10878c2ecf20Sopenharmony_ci * addr_not_cache_congruent() is 0 10888c2ecf20Sopenharmony_ci */ 10898c2ecf20Sopenharmony_ci if (page_mapcount(from) && addr_not_cache_congruent(kfrom, u_vaddr)) { 10908c2ecf20Sopenharmony_ci __flush_dcache_page((unsigned long)kfrom, u_vaddr); 10918c2ecf20Sopenharmony_ci clean_src_k_mappings = 1; 10928c2ecf20Sopenharmony_ci } 10938c2ecf20Sopenharmony_ci 10948c2ecf20Sopenharmony_ci copy_page(kto, kfrom); 10958c2ecf20Sopenharmony_ci 10968c2ecf20Sopenharmony_ci /* 10978c2ecf20Sopenharmony_ci * Mark DST page K-mapping as dirty for a later finalization by 10988c2ecf20Sopenharmony_ci * update_mmu_cache(). Although the finalization could have been done 10998c2ecf20Sopenharmony_ci * here as well (given that both vaddr/paddr are available). 11008c2ecf20Sopenharmony_ci * But update_mmu_cache() already has code to do that for other 11018c2ecf20Sopenharmony_ci * non copied user pages (e.g. read faults which wire in pagecache page 11028c2ecf20Sopenharmony_ci * directly). 11038c2ecf20Sopenharmony_ci */ 11048c2ecf20Sopenharmony_ci clear_bit(PG_dc_clean, &to->flags); 11058c2ecf20Sopenharmony_ci 11068c2ecf20Sopenharmony_ci /* 11078c2ecf20Sopenharmony_ci * if SRC was already usermapped and non-congruent to kernel mapping 11088c2ecf20Sopenharmony_ci * sync the kernel mapping back to physical page 11098c2ecf20Sopenharmony_ci */ 11108c2ecf20Sopenharmony_ci if (clean_src_k_mappings) { 11118c2ecf20Sopenharmony_ci __flush_dcache_page((unsigned long)kfrom, (unsigned long)kfrom); 11128c2ecf20Sopenharmony_ci set_bit(PG_dc_clean, &from->flags); 11138c2ecf20Sopenharmony_ci } else { 11148c2ecf20Sopenharmony_ci clear_bit(PG_dc_clean, &from->flags); 11158c2ecf20Sopenharmony_ci } 11168c2ecf20Sopenharmony_ci 11178c2ecf20Sopenharmony_ci kunmap_atomic(kto); 11188c2ecf20Sopenharmony_ci kunmap_atomic(kfrom); 11198c2ecf20Sopenharmony_ci} 11208c2ecf20Sopenharmony_ci 11218c2ecf20Sopenharmony_civoid clear_user_page(void *to, unsigned long u_vaddr, struct page *page) 11228c2ecf20Sopenharmony_ci{ 11238c2ecf20Sopenharmony_ci clear_page(to); 11248c2ecf20Sopenharmony_ci clear_bit(PG_dc_clean, &page->flags); 11258c2ecf20Sopenharmony_ci} 11268c2ecf20Sopenharmony_ciEXPORT_SYMBOL(clear_user_page); 11278c2ecf20Sopenharmony_ci 11288c2ecf20Sopenharmony_ci/********************************************************************** 11298c2ecf20Sopenharmony_ci * Explicit Cache flush request from user space via syscall 11308c2ecf20Sopenharmony_ci * Needed for JITs which generate code on the fly 11318c2ecf20Sopenharmony_ci */ 11328c2ecf20Sopenharmony_ciSYSCALL_DEFINE3(cacheflush, uint32_t, start, uint32_t, sz, uint32_t, flags) 11338c2ecf20Sopenharmony_ci{ 11348c2ecf20Sopenharmony_ci /* TBD: optimize this */ 11358c2ecf20Sopenharmony_ci flush_cache_all(); 11368c2ecf20Sopenharmony_ci return 0; 11378c2ecf20Sopenharmony_ci} 11388c2ecf20Sopenharmony_ci 11398c2ecf20Sopenharmony_ci/* 11408c2ecf20Sopenharmony_ci * IO-Coherency (IOC) setup rules: 11418c2ecf20Sopenharmony_ci * 11428c2ecf20Sopenharmony_ci * 1. Needs to be at system level, so only once by Master core 11438c2ecf20Sopenharmony_ci * Non-Masters need not be accessing caches at that time 11448c2ecf20Sopenharmony_ci * - They are either HALT_ON_RESET and kick started much later or 11458c2ecf20Sopenharmony_ci * - if run on reset, need to ensure that arc_platform_smp_wait_to_boot() 11468c2ecf20Sopenharmony_ci * doesn't perturb caches or coherency unit 11478c2ecf20Sopenharmony_ci * 11488c2ecf20Sopenharmony_ci * 2. caches (L1 and SLC) need to be purged (flush+inv) before setting up IOC, 11498c2ecf20Sopenharmony_ci * otherwise any straggler data might behave strangely post IOC enabling 11508c2ecf20Sopenharmony_ci * 11518c2ecf20Sopenharmony_ci * 3. All Caches need to be disabled when setting up IOC to elide any in-flight 11528c2ecf20Sopenharmony_ci * Coherency transactions 11538c2ecf20Sopenharmony_ci */ 11548c2ecf20Sopenharmony_cinoinline void __init arc_ioc_setup(void) 11558c2ecf20Sopenharmony_ci{ 11568c2ecf20Sopenharmony_ci unsigned int ioc_base, mem_sz; 11578c2ecf20Sopenharmony_ci 11588c2ecf20Sopenharmony_ci /* 11598c2ecf20Sopenharmony_ci * If IOC was already enabled (due to bootloader) it technically needs to 11608c2ecf20Sopenharmony_ci * be reconfigured with aperture base,size corresponding to Linux memory map 11618c2ecf20Sopenharmony_ci * which will certainly be different than uboot's. But disabling and 11628c2ecf20Sopenharmony_ci * reenabling IOC when DMA might be potentially active is tricky business. 11638c2ecf20Sopenharmony_ci * To avoid random memory issues later, just panic here and ask user to 11648c2ecf20Sopenharmony_ci * upgrade bootloader to one which doesn't enable IOC 11658c2ecf20Sopenharmony_ci */ 11668c2ecf20Sopenharmony_ci if (read_aux_reg(ARC_REG_IO_COH_ENABLE) & ARC_IO_COH_ENABLE_BIT) 11678c2ecf20Sopenharmony_ci panic("IOC already enabled, please upgrade bootloader!\n"); 11688c2ecf20Sopenharmony_ci 11698c2ecf20Sopenharmony_ci if (!ioc_enable) 11708c2ecf20Sopenharmony_ci return; 11718c2ecf20Sopenharmony_ci 11728c2ecf20Sopenharmony_ci /* Flush + invalidate + disable L1 dcache */ 11738c2ecf20Sopenharmony_ci __dc_disable(); 11748c2ecf20Sopenharmony_ci 11758c2ecf20Sopenharmony_ci /* Flush + invalidate SLC */ 11768c2ecf20Sopenharmony_ci if (read_aux_reg(ARC_REG_SLC_BCR)) 11778c2ecf20Sopenharmony_ci slc_entire_op(OP_FLUSH_N_INV); 11788c2ecf20Sopenharmony_ci 11798c2ecf20Sopenharmony_ci /* 11808c2ecf20Sopenharmony_ci * currently IOC Aperture covers entire DDR 11818c2ecf20Sopenharmony_ci * TBD: fix for PGU + 1GB of low mem 11828c2ecf20Sopenharmony_ci * TBD: fix for PAE 11838c2ecf20Sopenharmony_ci */ 11848c2ecf20Sopenharmony_ci mem_sz = arc_get_mem_sz(); 11858c2ecf20Sopenharmony_ci 11868c2ecf20Sopenharmony_ci if (!is_power_of_2(mem_sz) || mem_sz < 4096) 11878c2ecf20Sopenharmony_ci panic("IOC Aperture size must be power of 2 larger than 4KB"); 11888c2ecf20Sopenharmony_ci 11898c2ecf20Sopenharmony_ci /* 11908c2ecf20Sopenharmony_ci * IOC Aperture size decoded as 2 ^ (SIZE + 2) KB, 11918c2ecf20Sopenharmony_ci * so setting 0x11 implies 512MB, 0x12 implies 1GB... 11928c2ecf20Sopenharmony_ci */ 11938c2ecf20Sopenharmony_ci write_aux_reg(ARC_REG_IO_COH_AP0_SIZE, order_base_2(mem_sz >> 10) - 2); 11948c2ecf20Sopenharmony_ci 11958c2ecf20Sopenharmony_ci /* for now assume kernel base is start of IOC aperture */ 11968c2ecf20Sopenharmony_ci ioc_base = CONFIG_LINUX_RAM_BASE; 11978c2ecf20Sopenharmony_ci 11988c2ecf20Sopenharmony_ci if (ioc_base % mem_sz != 0) 11998c2ecf20Sopenharmony_ci panic("IOC Aperture start must be aligned to the size of the aperture"); 12008c2ecf20Sopenharmony_ci 12018c2ecf20Sopenharmony_ci write_aux_reg(ARC_REG_IO_COH_AP0_BASE, ioc_base >> 12); 12028c2ecf20Sopenharmony_ci write_aux_reg(ARC_REG_IO_COH_PARTIAL, ARC_IO_COH_PARTIAL_BIT); 12038c2ecf20Sopenharmony_ci write_aux_reg(ARC_REG_IO_COH_ENABLE, ARC_IO_COH_ENABLE_BIT); 12048c2ecf20Sopenharmony_ci 12058c2ecf20Sopenharmony_ci /* Re-enable L1 dcache */ 12068c2ecf20Sopenharmony_ci __dc_enable(); 12078c2ecf20Sopenharmony_ci} 12088c2ecf20Sopenharmony_ci 12098c2ecf20Sopenharmony_ci/* 12108c2ecf20Sopenharmony_ci * Cache related boot time checks/setups only needed on master CPU: 12118c2ecf20Sopenharmony_ci * - Geometry checks (kernel build and hardware agree: e.g. L1_CACHE_BYTES) 12128c2ecf20Sopenharmony_ci * Assume SMP only, so all cores will have same cache config. A check on 12138c2ecf20Sopenharmony_ci * one core suffices for all 12148c2ecf20Sopenharmony_ci * - IOC setup / dma callbacks only need to be done once 12158c2ecf20Sopenharmony_ci */ 12168c2ecf20Sopenharmony_civoid __init arc_cache_init_master(void) 12178c2ecf20Sopenharmony_ci{ 12188c2ecf20Sopenharmony_ci unsigned int __maybe_unused cpu = smp_processor_id(); 12198c2ecf20Sopenharmony_ci 12208c2ecf20Sopenharmony_ci if (IS_ENABLED(CONFIG_ARC_HAS_ICACHE)) { 12218c2ecf20Sopenharmony_ci struct cpuinfo_arc_cache *ic = &cpuinfo_arc700[cpu].icache; 12228c2ecf20Sopenharmony_ci 12238c2ecf20Sopenharmony_ci if (!ic->line_len) 12248c2ecf20Sopenharmony_ci panic("cache support enabled but non-existent cache\n"); 12258c2ecf20Sopenharmony_ci 12268c2ecf20Sopenharmony_ci if (ic->line_len != L1_CACHE_BYTES) 12278c2ecf20Sopenharmony_ci panic("ICache line [%d] != kernel Config [%d]", 12288c2ecf20Sopenharmony_ci ic->line_len, L1_CACHE_BYTES); 12298c2ecf20Sopenharmony_ci 12308c2ecf20Sopenharmony_ci /* 12318c2ecf20Sopenharmony_ci * In MMU v4 (HS38x) the aliasing icache config uses IVIL/PTAG 12328c2ecf20Sopenharmony_ci * pair to provide vaddr/paddr respectively, just as in MMU v3 12338c2ecf20Sopenharmony_ci */ 12348c2ecf20Sopenharmony_ci if (is_isa_arcv2() && ic->alias) 12358c2ecf20Sopenharmony_ci _cache_line_loop_ic_fn = __cache_line_loop_v3; 12368c2ecf20Sopenharmony_ci else 12378c2ecf20Sopenharmony_ci _cache_line_loop_ic_fn = __cache_line_loop; 12388c2ecf20Sopenharmony_ci } 12398c2ecf20Sopenharmony_ci 12408c2ecf20Sopenharmony_ci if (IS_ENABLED(CONFIG_ARC_HAS_DCACHE)) { 12418c2ecf20Sopenharmony_ci struct cpuinfo_arc_cache *dc = &cpuinfo_arc700[cpu].dcache; 12428c2ecf20Sopenharmony_ci 12438c2ecf20Sopenharmony_ci if (!dc->line_len) 12448c2ecf20Sopenharmony_ci panic("cache support enabled but non-existent cache\n"); 12458c2ecf20Sopenharmony_ci 12468c2ecf20Sopenharmony_ci if (dc->line_len != L1_CACHE_BYTES) 12478c2ecf20Sopenharmony_ci panic("DCache line [%d] != kernel Config [%d]", 12488c2ecf20Sopenharmony_ci dc->line_len, L1_CACHE_BYTES); 12498c2ecf20Sopenharmony_ci 12508c2ecf20Sopenharmony_ci /* check for D-Cache aliasing on ARCompact: ARCv2 has PIPT */ 12518c2ecf20Sopenharmony_ci if (is_isa_arcompact()) { 12528c2ecf20Sopenharmony_ci int handled = IS_ENABLED(CONFIG_ARC_CACHE_VIPT_ALIASING); 12538c2ecf20Sopenharmony_ci int num_colors = dc->sz_k/dc->assoc/TO_KB(PAGE_SIZE); 12548c2ecf20Sopenharmony_ci 12558c2ecf20Sopenharmony_ci if (dc->alias) { 12568c2ecf20Sopenharmony_ci if (!handled) 12578c2ecf20Sopenharmony_ci panic("Enable CONFIG_ARC_CACHE_VIPT_ALIASING\n"); 12588c2ecf20Sopenharmony_ci if (CACHE_COLORS_NUM != num_colors) 12598c2ecf20Sopenharmony_ci panic("CACHE_COLORS_NUM not optimized for config\n"); 12608c2ecf20Sopenharmony_ci } else if (!dc->alias && handled) { 12618c2ecf20Sopenharmony_ci panic("Disable CONFIG_ARC_CACHE_VIPT_ALIASING\n"); 12628c2ecf20Sopenharmony_ci } 12638c2ecf20Sopenharmony_ci } 12648c2ecf20Sopenharmony_ci } 12658c2ecf20Sopenharmony_ci 12668c2ecf20Sopenharmony_ci /* 12678c2ecf20Sopenharmony_ci * Check that SMP_CACHE_BYTES (and hence ARCH_DMA_MINALIGN) is larger 12688c2ecf20Sopenharmony_ci * or equal to any cache line length. 12698c2ecf20Sopenharmony_ci */ 12708c2ecf20Sopenharmony_ci BUILD_BUG_ON_MSG(L1_CACHE_BYTES > SMP_CACHE_BYTES, 12718c2ecf20Sopenharmony_ci "SMP_CACHE_BYTES must be >= any cache line length"); 12728c2ecf20Sopenharmony_ci if (is_isa_arcv2() && (l2_line_sz > SMP_CACHE_BYTES)) 12738c2ecf20Sopenharmony_ci panic("L2 Cache line [%d] > kernel Config [%d]\n", 12748c2ecf20Sopenharmony_ci l2_line_sz, SMP_CACHE_BYTES); 12758c2ecf20Sopenharmony_ci 12768c2ecf20Sopenharmony_ci /* Note that SLC disable not formally supported till HS 3.0 */ 12778c2ecf20Sopenharmony_ci if (is_isa_arcv2() && l2_line_sz && !slc_enable) 12788c2ecf20Sopenharmony_ci arc_slc_disable(); 12798c2ecf20Sopenharmony_ci 12808c2ecf20Sopenharmony_ci if (is_isa_arcv2() && ioc_exists) 12818c2ecf20Sopenharmony_ci arc_ioc_setup(); 12828c2ecf20Sopenharmony_ci 12838c2ecf20Sopenharmony_ci if (is_isa_arcv2() && l2_line_sz && slc_enable) { 12848c2ecf20Sopenharmony_ci __dma_cache_wback_inv = __dma_cache_wback_inv_slc; 12858c2ecf20Sopenharmony_ci __dma_cache_inv = __dma_cache_inv_slc; 12868c2ecf20Sopenharmony_ci __dma_cache_wback = __dma_cache_wback_slc; 12878c2ecf20Sopenharmony_ci } else { 12888c2ecf20Sopenharmony_ci __dma_cache_wback_inv = __dma_cache_wback_inv_l1; 12898c2ecf20Sopenharmony_ci __dma_cache_inv = __dma_cache_inv_l1; 12908c2ecf20Sopenharmony_ci __dma_cache_wback = __dma_cache_wback_l1; 12918c2ecf20Sopenharmony_ci } 12928c2ecf20Sopenharmony_ci /* 12938c2ecf20Sopenharmony_ci * In case of IOC (say IOC+SLC case), pointers above could still be set 12948c2ecf20Sopenharmony_ci * but end up not being relevant as the first function in chain is not 12958c2ecf20Sopenharmony_ci * called at all for devices using coherent DMA. 12968c2ecf20Sopenharmony_ci * arch_sync_dma_for_cpu() -> dma_cache_*() -> __dma_cache_*() 12978c2ecf20Sopenharmony_ci */ 12988c2ecf20Sopenharmony_ci} 12998c2ecf20Sopenharmony_ci 13008c2ecf20Sopenharmony_civoid __ref arc_cache_init(void) 13018c2ecf20Sopenharmony_ci{ 13028c2ecf20Sopenharmony_ci unsigned int __maybe_unused cpu = smp_processor_id(); 13038c2ecf20Sopenharmony_ci char str[256]; 13048c2ecf20Sopenharmony_ci 13058c2ecf20Sopenharmony_ci pr_info("%s", arc_cache_mumbojumbo(0, str, sizeof(str))); 13068c2ecf20Sopenharmony_ci 13078c2ecf20Sopenharmony_ci if (!cpu) 13088c2ecf20Sopenharmony_ci arc_cache_init_master(); 13098c2ecf20Sopenharmony_ci 13108c2ecf20Sopenharmony_ci /* 13118c2ecf20Sopenharmony_ci * In PAE regime, TLB and cache maintenance ops take wider addresses 13128c2ecf20Sopenharmony_ci * And even if PAE is not enabled in kernel, the upper 32-bits still need 13138c2ecf20Sopenharmony_ci * to be zeroed to keep the ops sane. 13148c2ecf20Sopenharmony_ci * As an optimization for more common !PAE enabled case, zero them out 13158c2ecf20Sopenharmony_ci * once at init, rather than checking/setting to 0 for every runtime op 13168c2ecf20Sopenharmony_ci */ 13178c2ecf20Sopenharmony_ci if (is_isa_arcv2() && pae40_exist_but_not_enab()) { 13188c2ecf20Sopenharmony_ci 13198c2ecf20Sopenharmony_ci if (IS_ENABLED(CONFIG_ARC_HAS_ICACHE)) 13208c2ecf20Sopenharmony_ci write_aux_reg(ARC_REG_IC_PTAG_HI, 0); 13218c2ecf20Sopenharmony_ci 13228c2ecf20Sopenharmony_ci if (IS_ENABLED(CONFIG_ARC_HAS_DCACHE)) 13238c2ecf20Sopenharmony_ci write_aux_reg(ARC_REG_DC_PTAG_HI, 0); 13248c2ecf20Sopenharmony_ci 13258c2ecf20Sopenharmony_ci if (l2_line_sz) { 13268c2ecf20Sopenharmony_ci write_aux_reg(ARC_REG_SLC_RGN_END1, 0); 13278c2ecf20Sopenharmony_ci write_aux_reg(ARC_REG_SLC_RGN_START1, 0); 13288c2ecf20Sopenharmony_ci } 13298c2ecf20Sopenharmony_ci } 13308c2ecf20Sopenharmony_ci} 1331