162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * ARC Cache Management 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (C) 2014-15 Synopsys, Inc. (www.synopsys.com) 662306a36Sopenharmony_ci * Copyright (C) 2004, 2007-2010, 2011-2012 Synopsys, Inc. (www.synopsys.com) 762306a36Sopenharmony_ci */ 862306a36Sopenharmony_ci 962306a36Sopenharmony_ci#include <linux/module.h> 1062306a36Sopenharmony_ci#include <linux/mm.h> 1162306a36Sopenharmony_ci#include <linux/sched.h> 1262306a36Sopenharmony_ci#include <linux/cache.h> 1362306a36Sopenharmony_ci#include <linux/mmu_context.h> 1462306a36Sopenharmony_ci#include <linux/syscalls.h> 1562306a36Sopenharmony_ci#include <linux/uaccess.h> 1662306a36Sopenharmony_ci#include <linux/pagemap.h> 1762306a36Sopenharmony_ci#include <asm/cacheflush.h> 1862306a36Sopenharmony_ci#include <asm/cachectl.h> 1962306a36Sopenharmony_ci#include <asm/setup.h> 2062306a36Sopenharmony_ci 2162306a36Sopenharmony_ci#ifdef CONFIG_ISA_ARCV2 2262306a36Sopenharmony_ci#define USE_RGN_FLSH 1 2362306a36Sopenharmony_ci#endif 2462306a36Sopenharmony_ci 2562306a36Sopenharmony_cistatic int l2_line_sz; 2662306a36Sopenharmony_cistatic int ioc_exists; 2762306a36Sopenharmony_ciint slc_enable = 1, ioc_enable = 1; 2862306a36Sopenharmony_ciunsigned long perip_base = ARC_UNCACHED_ADDR_SPACE; /* legacy value for boot */ 2962306a36Sopenharmony_ciunsigned long perip_end = 0xFFFFFFFF; /* legacy value */ 3062306a36Sopenharmony_ci 3162306a36Sopenharmony_cistatic struct cpuinfo_arc_cache { 3262306a36Sopenharmony_ci unsigned int sz_k, line_len, colors; 3362306a36Sopenharmony_ci} ic_info, dc_info, slc_info; 3462306a36Sopenharmony_ci 3562306a36Sopenharmony_civoid (*_cache_line_loop_ic_fn)(phys_addr_t paddr, unsigned long vaddr, 3662306a36Sopenharmony_ci unsigned long sz, const int op, const int full_page); 3762306a36Sopenharmony_ci 3862306a36Sopenharmony_civoid (*__dma_cache_wback_inv)(phys_addr_t start, unsigned long sz); 3962306a36Sopenharmony_civoid (*__dma_cache_inv)(phys_addr_t start, unsigned long sz); 4062306a36Sopenharmony_civoid (*__dma_cache_wback)(phys_addr_t start, unsigned long sz); 4162306a36Sopenharmony_ci 4262306a36Sopenharmony_cistatic int read_decode_cache_bcr_arcv2(int c, char *buf, int len) 4362306a36Sopenharmony_ci{ 4462306a36Sopenharmony_ci struct cpuinfo_arc_cache *p_slc = &slc_info; 4562306a36Sopenharmony_ci struct bcr_identity ident; 4662306a36Sopenharmony_ci struct bcr_generic sbcr; 4762306a36Sopenharmony_ci struct bcr_clust_cfg cbcr; 4862306a36Sopenharmony_ci struct bcr_volatile vol; 4962306a36Sopenharmony_ci int n = 0; 5062306a36Sopenharmony_ci 5162306a36Sopenharmony_ci READ_BCR(ARC_REG_SLC_BCR, sbcr); 5262306a36Sopenharmony_ci if (sbcr.ver) { 5362306a36Sopenharmony_ci struct bcr_slc_cfg slc_cfg; 5462306a36Sopenharmony_ci READ_BCR(ARC_REG_SLC_CFG, slc_cfg); 5562306a36Sopenharmony_ci p_slc->sz_k = 128 << slc_cfg.sz; 5662306a36Sopenharmony_ci l2_line_sz = p_slc->line_len = (slc_cfg.lsz == 0) ? 128 : 64; 5762306a36Sopenharmony_ci n += scnprintf(buf + n, len - n, 5862306a36Sopenharmony_ci "SLC\t\t: %uK, %uB Line%s\n", 5962306a36Sopenharmony_ci p_slc->sz_k, p_slc->line_len, IS_USED_RUN(slc_enable)); 6062306a36Sopenharmony_ci } 6162306a36Sopenharmony_ci 6262306a36Sopenharmony_ci READ_BCR(ARC_REG_CLUSTER_BCR, cbcr); 6362306a36Sopenharmony_ci if (cbcr.c) { 6462306a36Sopenharmony_ci ioc_exists = 1; 6562306a36Sopenharmony_ci 6662306a36Sopenharmony_ci /* 6762306a36Sopenharmony_ci * As for today we don't support both IOC and ZONE_HIGHMEM enabled 6862306a36Sopenharmony_ci * simultaneously. This happens because as of today IOC aperture covers 6962306a36Sopenharmony_ci * only ZONE_NORMAL (low mem) and any dma transactions outside this 7062306a36Sopenharmony_ci * region won't be HW coherent. 7162306a36Sopenharmony_ci * If we want to use both IOC and ZONE_HIGHMEM we can use 7262306a36Sopenharmony_ci * bounce_buffer to handle dma transactions to HIGHMEM. 7362306a36Sopenharmony_ci * Also it is possible to modify dma_direct cache ops or increase IOC 7462306a36Sopenharmony_ci * aperture size if we are planning to use HIGHMEM without PAE. 7562306a36Sopenharmony_ci */ 7662306a36Sopenharmony_ci if (IS_ENABLED(CONFIG_HIGHMEM) || is_pae40_enabled()) 7762306a36Sopenharmony_ci ioc_enable = 0; 7862306a36Sopenharmony_ci } else { 7962306a36Sopenharmony_ci ioc_enable = 0; 8062306a36Sopenharmony_ci } 8162306a36Sopenharmony_ci 8262306a36Sopenharmony_ci READ_BCR(AUX_IDENTITY, ident); 8362306a36Sopenharmony_ci 8462306a36Sopenharmony_ci /* HS 2.0 didn't have AUX_VOL */ 8562306a36Sopenharmony_ci if (ident.family > 0x51) { 8662306a36Sopenharmony_ci READ_BCR(AUX_VOL, vol); 8762306a36Sopenharmony_ci perip_base = vol.start << 28; 8862306a36Sopenharmony_ci /* HS 3.0 has limit and strict-ordering fields */ 8962306a36Sopenharmony_ci if (ident.family > 0x52) 9062306a36Sopenharmony_ci perip_end = (vol.limit << 28) - 1; 9162306a36Sopenharmony_ci } 9262306a36Sopenharmony_ci 9362306a36Sopenharmony_ci n += scnprintf(buf + n, len - n, "Peripherals\t: %#lx%s%s\n", 9462306a36Sopenharmony_ci perip_base, 9562306a36Sopenharmony_ci IS_AVAIL3(ioc_exists, ioc_enable, ", IO-Coherency (per-device) ")); 9662306a36Sopenharmony_ci 9762306a36Sopenharmony_ci return n; 9862306a36Sopenharmony_ci} 9962306a36Sopenharmony_ci 10062306a36Sopenharmony_ciint arc_cache_mumbojumbo(int c, char *buf, int len) 10162306a36Sopenharmony_ci{ 10262306a36Sopenharmony_ci struct cpuinfo_arc_cache *p_ic = &ic_info, *p_dc = &dc_info; 10362306a36Sopenharmony_ci struct bcr_cache ibcr, dbcr; 10462306a36Sopenharmony_ci int vipt, assoc; 10562306a36Sopenharmony_ci int n = 0; 10662306a36Sopenharmony_ci 10762306a36Sopenharmony_ci READ_BCR(ARC_REG_IC_BCR, ibcr); 10862306a36Sopenharmony_ci if (!ibcr.ver) 10962306a36Sopenharmony_ci goto dc_chk; 11062306a36Sopenharmony_ci 11162306a36Sopenharmony_ci if (is_isa_arcompact() && (ibcr.ver <= 3)) { 11262306a36Sopenharmony_ci BUG_ON(ibcr.config != 3); 11362306a36Sopenharmony_ci assoc = 2; /* Fixed to 2w set assoc */ 11462306a36Sopenharmony_ci } else if (is_isa_arcv2() && (ibcr.ver >= 4)) { 11562306a36Sopenharmony_ci assoc = 1 << ibcr.config; /* 1,2,4,8 */ 11662306a36Sopenharmony_ci } 11762306a36Sopenharmony_ci 11862306a36Sopenharmony_ci p_ic->line_len = 8 << ibcr.line_len; 11962306a36Sopenharmony_ci p_ic->sz_k = 1 << (ibcr.sz - 1); 12062306a36Sopenharmony_ci p_ic->colors = p_ic->sz_k/assoc/TO_KB(PAGE_SIZE); 12162306a36Sopenharmony_ci 12262306a36Sopenharmony_ci n += scnprintf(buf + n, len - n, 12362306a36Sopenharmony_ci "I-Cache\t\t: %uK, %dway/set, %uB Line, VIPT%s%s\n", 12462306a36Sopenharmony_ci p_ic->sz_k, assoc, p_ic->line_len, 12562306a36Sopenharmony_ci p_ic->colors > 1 ? " aliasing" : "", 12662306a36Sopenharmony_ci IS_USED_CFG(CONFIG_ARC_HAS_ICACHE)); 12762306a36Sopenharmony_ci 12862306a36Sopenharmony_cidc_chk: 12962306a36Sopenharmony_ci READ_BCR(ARC_REG_DC_BCR, dbcr); 13062306a36Sopenharmony_ci if (!dbcr.ver) 13162306a36Sopenharmony_ci goto slc_chk; 13262306a36Sopenharmony_ci 13362306a36Sopenharmony_ci if (is_isa_arcompact() && (dbcr.ver <= 3)) { 13462306a36Sopenharmony_ci BUG_ON(dbcr.config != 2); 13562306a36Sopenharmony_ci vipt = 1; 13662306a36Sopenharmony_ci assoc = 4; /* Fixed to 4w set assoc */ 13762306a36Sopenharmony_ci p_dc->colors = p_dc->sz_k/assoc/TO_KB(PAGE_SIZE); 13862306a36Sopenharmony_ci } else if (is_isa_arcv2() && (dbcr.ver >= 4)) { 13962306a36Sopenharmony_ci vipt = 0; 14062306a36Sopenharmony_ci assoc = 1 << dbcr.config; /* 1,2,4,8 */ 14162306a36Sopenharmony_ci p_dc->colors = 1; /* PIPT so can't VIPT alias */ 14262306a36Sopenharmony_ci } 14362306a36Sopenharmony_ci 14462306a36Sopenharmony_ci p_dc->line_len = 16 << dbcr.line_len; 14562306a36Sopenharmony_ci p_dc->sz_k = 1 << (dbcr.sz - 1); 14662306a36Sopenharmony_ci 14762306a36Sopenharmony_ci n += scnprintf(buf + n, len - n, 14862306a36Sopenharmony_ci "D-Cache\t\t: %uK, %dway/set, %uB Line, %s%s%s\n", 14962306a36Sopenharmony_ci p_dc->sz_k, assoc, p_dc->line_len, 15062306a36Sopenharmony_ci vipt ? "VIPT" : "PIPT", 15162306a36Sopenharmony_ci p_dc->colors > 1 ? " aliasing" : "", 15262306a36Sopenharmony_ci IS_USED_CFG(CONFIG_ARC_HAS_DCACHE)); 15362306a36Sopenharmony_ci 15462306a36Sopenharmony_cislc_chk: 15562306a36Sopenharmony_ci if (is_isa_arcv2()) 15662306a36Sopenharmony_ci n += read_decode_cache_bcr_arcv2(c, buf + n, len - n); 15762306a36Sopenharmony_ci 15862306a36Sopenharmony_ci return n; 15962306a36Sopenharmony_ci} 16062306a36Sopenharmony_ci 16162306a36Sopenharmony_ci/* 16262306a36Sopenharmony_ci * Line Operation on {I,D}-Cache 16362306a36Sopenharmony_ci */ 16462306a36Sopenharmony_ci 16562306a36Sopenharmony_ci#define OP_INV 0x1 16662306a36Sopenharmony_ci#define OP_FLUSH 0x2 16762306a36Sopenharmony_ci#define OP_FLUSH_N_INV 0x3 16862306a36Sopenharmony_ci#define OP_INV_IC 0x4 16962306a36Sopenharmony_ci 17062306a36Sopenharmony_ci/* 17162306a36Sopenharmony_ci * Cache Flush programming model 17262306a36Sopenharmony_ci * 17362306a36Sopenharmony_ci * ARC700 MMUv3 I$ and D$ are both VIPT and can potentially alias. 17462306a36Sopenharmony_ci * Programming model requires both paddr and vaddr irrespecive of aliasing 17562306a36Sopenharmony_ci * considerations: 17662306a36Sopenharmony_ci * - vaddr in {I,D}C_IV?L 17762306a36Sopenharmony_ci * - paddr in {I,D}C_PTAG 17862306a36Sopenharmony_ci * 17962306a36Sopenharmony_ci * In HS38x (MMUv4), D$ is PIPT, I$ is VIPT and can still alias. 18062306a36Sopenharmony_ci * Programming model is different for aliasing vs. non-aliasing I$ 18162306a36Sopenharmony_ci * - D$ / Non-aliasing I$: only paddr in {I,D}C_IV?L 18262306a36Sopenharmony_ci * - Aliasing I$: same as ARC700 above (so MMUv3 routine used for MMUv4 I$) 18362306a36Sopenharmony_ci * 18462306a36Sopenharmony_ci * - If PAE40 is enabled, independent of aliasing considerations, the higher 18562306a36Sopenharmony_ci * bits needs to be written into PTAG_HI 18662306a36Sopenharmony_ci */ 18762306a36Sopenharmony_ci 18862306a36Sopenharmony_cistatic inline 18962306a36Sopenharmony_civoid __cache_line_loop_v3(phys_addr_t paddr, unsigned long vaddr, 19062306a36Sopenharmony_ci unsigned long sz, const int op, const int full_page) 19162306a36Sopenharmony_ci{ 19262306a36Sopenharmony_ci unsigned int aux_cmd, aux_tag; 19362306a36Sopenharmony_ci int num_lines; 19462306a36Sopenharmony_ci 19562306a36Sopenharmony_ci if (op == OP_INV_IC) { 19662306a36Sopenharmony_ci aux_cmd = ARC_REG_IC_IVIL; 19762306a36Sopenharmony_ci aux_tag = ARC_REG_IC_PTAG; 19862306a36Sopenharmony_ci } else { 19962306a36Sopenharmony_ci aux_cmd = op & OP_INV ? ARC_REG_DC_IVDL : ARC_REG_DC_FLDL; 20062306a36Sopenharmony_ci aux_tag = ARC_REG_DC_PTAG; 20162306a36Sopenharmony_ci } 20262306a36Sopenharmony_ci 20362306a36Sopenharmony_ci /* Ensure we properly floor/ceil the non-line aligned/sized requests 20462306a36Sopenharmony_ci * and have @paddr - aligned to cache line and integral @num_lines. 20562306a36Sopenharmony_ci * This however can be avoided for page sized since: 20662306a36Sopenharmony_ci * -@paddr will be cache-line aligned already (being page aligned) 20762306a36Sopenharmony_ci * -@sz will be integral multiple of line size (being page sized). 20862306a36Sopenharmony_ci */ 20962306a36Sopenharmony_ci if (!full_page) { 21062306a36Sopenharmony_ci sz += paddr & ~CACHE_LINE_MASK; 21162306a36Sopenharmony_ci paddr &= CACHE_LINE_MASK; 21262306a36Sopenharmony_ci vaddr &= CACHE_LINE_MASK; 21362306a36Sopenharmony_ci } 21462306a36Sopenharmony_ci num_lines = DIV_ROUND_UP(sz, L1_CACHE_BYTES); 21562306a36Sopenharmony_ci 21662306a36Sopenharmony_ci /* 21762306a36Sopenharmony_ci * MMUv3, cache ops require paddr in PTAG reg 21862306a36Sopenharmony_ci * if V-P const for loop, PTAG can be written once outside loop 21962306a36Sopenharmony_ci */ 22062306a36Sopenharmony_ci if (full_page) 22162306a36Sopenharmony_ci write_aux_reg(aux_tag, paddr); 22262306a36Sopenharmony_ci 22362306a36Sopenharmony_ci /* 22462306a36Sopenharmony_ci * This is technically for MMU v4, using the MMU v3 programming model 22562306a36Sopenharmony_ci * Special work for HS38 aliasing I-cache configuration with PAE40 22662306a36Sopenharmony_ci * - upper 8 bits of paddr need to be written into PTAG_HI 22762306a36Sopenharmony_ci * - (and needs to be written before the lower 32 bits) 22862306a36Sopenharmony_ci * Note that PTAG_HI is hoisted outside the line loop 22962306a36Sopenharmony_ci */ 23062306a36Sopenharmony_ci if (is_pae40_enabled() && op == OP_INV_IC) 23162306a36Sopenharmony_ci write_aux_reg(ARC_REG_IC_PTAG_HI, (u64)paddr >> 32); 23262306a36Sopenharmony_ci 23362306a36Sopenharmony_ci while (num_lines-- > 0) { 23462306a36Sopenharmony_ci if (!full_page) { 23562306a36Sopenharmony_ci write_aux_reg(aux_tag, paddr); 23662306a36Sopenharmony_ci paddr += L1_CACHE_BYTES; 23762306a36Sopenharmony_ci } 23862306a36Sopenharmony_ci 23962306a36Sopenharmony_ci write_aux_reg(aux_cmd, vaddr); 24062306a36Sopenharmony_ci vaddr += L1_CACHE_BYTES; 24162306a36Sopenharmony_ci } 24262306a36Sopenharmony_ci} 24362306a36Sopenharmony_ci 24462306a36Sopenharmony_ci#ifndef USE_RGN_FLSH 24562306a36Sopenharmony_ci 24662306a36Sopenharmony_ci/* 24762306a36Sopenharmony_ci */ 24862306a36Sopenharmony_cistatic inline 24962306a36Sopenharmony_civoid __cache_line_loop_v4(phys_addr_t paddr, unsigned long vaddr, 25062306a36Sopenharmony_ci unsigned long sz, const int op, const int full_page) 25162306a36Sopenharmony_ci{ 25262306a36Sopenharmony_ci unsigned int aux_cmd; 25362306a36Sopenharmony_ci int num_lines; 25462306a36Sopenharmony_ci 25562306a36Sopenharmony_ci if (op == OP_INV_IC) { 25662306a36Sopenharmony_ci aux_cmd = ARC_REG_IC_IVIL; 25762306a36Sopenharmony_ci } else { 25862306a36Sopenharmony_ci /* d$ cmd: INV (discard or wback-n-discard) OR FLUSH (wback) */ 25962306a36Sopenharmony_ci aux_cmd = op & OP_INV ? ARC_REG_DC_IVDL : ARC_REG_DC_FLDL; 26062306a36Sopenharmony_ci } 26162306a36Sopenharmony_ci 26262306a36Sopenharmony_ci /* Ensure we properly floor/ceil the non-line aligned/sized requests 26362306a36Sopenharmony_ci * and have @paddr - aligned to cache line and integral @num_lines. 26462306a36Sopenharmony_ci * This however can be avoided for page sized since: 26562306a36Sopenharmony_ci * -@paddr will be cache-line aligned already (being page aligned) 26662306a36Sopenharmony_ci * -@sz will be integral multiple of line size (being page sized). 26762306a36Sopenharmony_ci */ 26862306a36Sopenharmony_ci if (!full_page) { 26962306a36Sopenharmony_ci sz += paddr & ~CACHE_LINE_MASK; 27062306a36Sopenharmony_ci paddr &= CACHE_LINE_MASK; 27162306a36Sopenharmony_ci } 27262306a36Sopenharmony_ci 27362306a36Sopenharmony_ci num_lines = DIV_ROUND_UP(sz, L1_CACHE_BYTES); 27462306a36Sopenharmony_ci 27562306a36Sopenharmony_ci /* 27662306a36Sopenharmony_ci * For HS38 PAE40 configuration 27762306a36Sopenharmony_ci * - upper 8 bits of paddr need to be written into PTAG_HI 27862306a36Sopenharmony_ci * - (and needs to be written before the lower 32 bits) 27962306a36Sopenharmony_ci */ 28062306a36Sopenharmony_ci if (is_pae40_enabled()) { 28162306a36Sopenharmony_ci if (op == OP_INV_IC) 28262306a36Sopenharmony_ci /* 28362306a36Sopenharmony_ci * Non aliasing I-cache in HS38, 28462306a36Sopenharmony_ci * aliasing I-cache handled in __cache_line_loop_v3() 28562306a36Sopenharmony_ci */ 28662306a36Sopenharmony_ci write_aux_reg(ARC_REG_IC_PTAG_HI, (u64)paddr >> 32); 28762306a36Sopenharmony_ci else 28862306a36Sopenharmony_ci write_aux_reg(ARC_REG_DC_PTAG_HI, (u64)paddr >> 32); 28962306a36Sopenharmony_ci } 29062306a36Sopenharmony_ci 29162306a36Sopenharmony_ci while (num_lines-- > 0) { 29262306a36Sopenharmony_ci write_aux_reg(aux_cmd, paddr); 29362306a36Sopenharmony_ci paddr += L1_CACHE_BYTES; 29462306a36Sopenharmony_ci } 29562306a36Sopenharmony_ci} 29662306a36Sopenharmony_ci 29762306a36Sopenharmony_ci#else 29862306a36Sopenharmony_ci 29962306a36Sopenharmony_ci/* 30062306a36Sopenharmony_ci * optimized flush operation which takes a region as opposed to iterating per line 30162306a36Sopenharmony_ci */ 30262306a36Sopenharmony_cistatic inline 30362306a36Sopenharmony_civoid __cache_line_loop_v4(phys_addr_t paddr, unsigned long vaddr, 30462306a36Sopenharmony_ci unsigned long sz, const int op, const int full_page) 30562306a36Sopenharmony_ci{ 30662306a36Sopenharmony_ci unsigned int s, e; 30762306a36Sopenharmony_ci 30862306a36Sopenharmony_ci /* Only for Non aliasing I-cache in HS38 */ 30962306a36Sopenharmony_ci if (op == OP_INV_IC) { 31062306a36Sopenharmony_ci s = ARC_REG_IC_IVIR; 31162306a36Sopenharmony_ci e = ARC_REG_IC_ENDR; 31262306a36Sopenharmony_ci } else { 31362306a36Sopenharmony_ci s = ARC_REG_DC_STARTR; 31462306a36Sopenharmony_ci e = ARC_REG_DC_ENDR; 31562306a36Sopenharmony_ci } 31662306a36Sopenharmony_ci 31762306a36Sopenharmony_ci if (!full_page) { 31862306a36Sopenharmony_ci /* for any leading gap between @paddr and start of cache line */ 31962306a36Sopenharmony_ci sz += paddr & ~CACHE_LINE_MASK; 32062306a36Sopenharmony_ci paddr &= CACHE_LINE_MASK; 32162306a36Sopenharmony_ci 32262306a36Sopenharmony_ci /* 32362306a36Sopenharmony_ci * account for any trailing gap to end of cache line 32462306a36Sopenharmony_ci * this is equivalent to DIV_ROUND_UP() in line ops above 32562306a36Sopenharmony_ci */ 32662306a36Sopenharmony_ci sz += L1_CACHE_BYTES - 1; 32762306a36Sopenharmony_ci } 32862306a36Sopenharmony_ci 32962306a36Sopenharmony_ci if (is_pae40_enabled()) { 33062306a36Sopenharmony_ci /* TBD: check if crossing 4TB boundary */ 33162306a36Sopenharmony_ci if (op == OP_INV_IC) 33262306a36Sopenharmony_ci write_aux_reg(ARC_REG_IC_PTAG_HI, (u64)paddr >> 32); 33362306a36Sopenharmony_ci else 33462306a36Sopenharmony_ci write_aux_reg(ARC_REG_DC_PTAG_HI, (u64)paddr >> 32); 33562306a36Sopenharmony_ci } 33662306a36Sopenharmony_ci 33762306a36Sopenharmony_ci /* ENDR needs to be set ahead of START */ 33862306a36Sopenharmony_ci write_aux_reg(e, paddr + sz); /* ENDR is exclusive */ 33962306a36Sopenharmony_ci write_aux_reg(s, paddr); 34062306a36Sopenharmony_ci 34162306a36Sopenharmony_ci /* caller waits on DC_CTRL.FS */ 34262306a36Sopenharmony_ci} 34362306a36Sopenharmony_ci 34462306a36Sopenharmony_ci#endif 34562306a36Sopenharmony_ci 34662306a36Sopenharmony_ci#ifdef CONFIG_ARC_MMU_V3 34762306a36Sopenharmony_ci#define __cache_line_loop __cache_line_loop_v3 34862306a36Sopenharmony_ci#else 34962306a36Sopenharmony_ci#define __cache_line_loop __cache_line_loop_v4 35062306a36Sopenharmony_ci#endif 35162306a36Sopenharmony_ci 35262306a36Sopenharmony_ci#ifdef CONFIG_ARC_HAS_DCACHE 35362306a36Sopenharmony_ci 35462306a36Sopenharmony_ci/*************************************************************** 35562306a36Sopenharmony_ci * Machine specific helpers for Entire D-Cache or Per Line ops 35662306a36Sopenharmony_ci */ 35762306a36Sopenharmony_ci 35862306a36Sopenharmony_ci#ifndef USE_RGN_FLSH 35962306a36Sopenharmony_ci/* 36062306a36Sopenharmony_ci * this version avoids extra read/write of DC_CTRL for flush or invalid ops 36162306a36Sopenharmony_ci * in the non region flush regime (such as for ARCompact) 36262306a36Sopenharmony_ci */ 36362306a36Sopenharmony_cistatic inline void __before_dc_op(const int op) 36462306a36Sopenharmony_ci{ 36562306a36Sopenharmony_ci if (op == OP_FLUSH_N_INV) { 36662306a36Sopenharmony_ci /* Dcache provides 2 cmd: FLUSH or INV 36762306a36Sopenharmony_ci * INV in turn has sub-modes: DISCARD or FLUSH-BEFORE 36862306a36Sopenharmony_ci * flush-n-inv is achieved by INV cmd but with IM=1 36962306a36Sopenharmony_ci * So toggle INV sub-mode depending on op request and default 37062306a36Sopenharmony_ci */ 37162306a36Sopenharmony_ci const unsigned int ctl = ARC_REG_DC_CTRL; 37262306a36Sopenharmony_ci write_aux_reg(ctl, read_aux_reg(ctl) | DC_CTRL_INV_MODE_FLUSH); 37362306a36Sopenharmony_ci } 37462306a36Sopenharmony_ci} 37562306a36Sopenharmony_ci 37662306a36Sopenharmony_ci#else 37762306a36Sopenharmony_ci 37862306a36Sopenharmony_cistatic inline void __before_dc_op(const int op) 37962306a36Sopenharmony_ci{ 38062306a36Sopenharmony_ci const unsigned int ctl = ARC_REG_DC_CTRL; 38162306a36Sopenharmony_ci unsigned int val = read_aux_reg(ctl); 38262306a36Sopenharmony_ci 38362306a36Sopenharmony_ci if (op == OP_FLUSH_N_INV) { 38462306a36Sopenharmony_ci val |= DC_CTRL_INV_MODE_FLUSH; 38562306a36Sopenharmony_ci } 38662306a36Sopenharmony_ci 38762306a36Sopenharmony_ci if (op != OP_INV_IC) { 38862306a36Sopenharmony_ci /* 38962306a36Sopenharmony_ci * Flush / Invalidate is provided by DC_CTRL.RNG_OP 0 or 1 39062306a36Sopenharmony_ci * combined Flush-n-invalidate uses DC_CTRL.IM = 1 set above 39162306a36Sopenharmony_ci */ 39262306a36Sopenharmony_ci val &= ~DC_CTRL_RGN_OP_MSK; 39362306a36Sopenharmony_ci if (op & OP_INV) 39462306a36Sopenharmony_ci val |= DC_CTRL_RGN_OP_INV; 39562306a36Sopenharmony_ci } 39662306a36Sopenharmony_ci write_aux_reg(ctl, val); 39762306a36Sopenharmony_ci} 39862306a36Sopenharmony_ci 39962306a36Sopenharmony_ci#endif 40062306a36Sopenharmony_ci 40162306a36Sopenharmony_ci 40262306a36Sopenharmony_cistatic inline void __after_dc_op(const int op) 40362306a36Sopenharmony_ci{ 40462306a36Sopenharmony_ci if (op & OP_FLUSH) { 40562306a36Sopenharmony_ci const unsigned int ctl = ARC_REG_DC_CTRL; 40662306a36Sopenharmony_ci unsigned int reg; 40762306a36Sopenharmony_ci 40862306a36Sopenharmony_ci /* flush / flush-n-inv both wait */ 40962306a36Sopenharmony_ci while ((reg = read_aux_reg(ctl)) & DC_CTRL_FLUSH_STATUS) 41062306a36Sopenharmony_ci ; 41162306a36Sopenharmony_ci 41262306a36Sopenharmony_ci /* Switch back to default Invalidate mode */ 41362306a36Sopenharmony_ci if (op == OP_FLUSH_N_INV) 41462306a36Sopenharmony_ci write_aux_reg(ctl, reg & ~DC_CTRL_INV_MODE_FLUSH); 41562306a36Sopenharmony_ci } 41662306a36Sopenharmony_ci} 41762306a36Sopenharmony_ci 41862306a36Sopenharmony_ci/* 41962306a36Sopenharmony_ci * Operation on Entire D-Cache 42062306a36Sopenharmony_ci * @op = {OP_INV, OP_FLUSH, OP_FLUSH_N_INV} 42162306a36Sopenharmony_ci * Note that constant propagation ensures all the checks are gone 42262306a36Sopenharmony_ci * in generated code 42362306a36Sopenharmony_ci */ 42462306a36Sopenharmony_cistatic inline void __dc_entire_op(const int op) 42562306a36Sopenharmony_ci{ 42662306a36Sopenharmony_ci int aux; 42762306a36Sopenharmony_ci 42862306a36Sopenharmony_ci __before_dc_op(op); 42962306a36Sopenharmony_ci 43062306a36Sopenharmony_ci if (op & OP_INV) /* Inv or flush-n-inv use same cmd reg */ 43162306a36Sopenharmony_ci aux = ARC_REG_DC_IVDC; 43262306a36Sopenharmony_ci else 43362306a36Sopenharmony_ci aux = ARC_REG_DC_FLSH; 43462306a36Sopenharmony_ci 43562306a36Sopenharmony_ci write_aux_reg(aux, 0x1); 43662306a36Sopenharmony_ci 43762306a36Sopenharmony_ci __after_dc_op(op); 43862306a36Sopenharmony_ci} 43962306a36Sopenharmony_ci 44062306a36Sopenharmony_cistatic inline void __dc_disable(void) 44162306a36Sopenharmony_ci{ 44262306a36Sopenharmony_ci const int r = ARC_REG_DC_CTRL; 44362306a36Sopenharmony_ci 44462306a36Sopenharmony_ci __dc_entire_op(OP_FLUSH_N_INV); 44562306a36Sopenharmony_ci write_aux_reg(r, read_aux_reg(r) | DC_CTRL_DIS); 44662306a36Sopenharmony_ci} 44762306a36Sopenharmony_ci 44862306a36Sopenharmony_cistatic void __dc_enable(void) 44962306a36Sopenharmony_ci{ 45062306a36Sopenharmony_ci const int r = ARC_REG_DC_CTRL; 45162306a36Sopenharmony_ci 45262306a36Sopenharmony_ci write_aux_reg(r, read_aux_reg(r) & ~DC_CTRL_DIS); 45362306a36Sopenharmony_ci} 45462306a36Sopenharmony_ci 45562306a36Sopenharmony_ci/* For kernel mappings cache operation: index is same as paddr */ 45662306a36Sopenharmony_ci#define __dc_line_op_k(p, sz, op) __dc_line_op(p, p, sz, op) 45762306a36Sopenharmony_ci 45862306a36Sopenharmony_ci/* 45962306a36Sopenharmony_ci * D-Cache Line ops: Per Line INV (discard or wback+discard) or FLUSH (wback) 46062306a36Sopenharmony_ci */ 46162306a36Sopenharmony_cistatic inline void __dc_line_op(phys_addr_t paddr, unsigned long vaddr, 46262306a36Sopenharmony_ci unsigned long sz, const int op) 46362306a36Sopenharmony_ci{ 46462306a36Sopenharmony_ci const int full_page = __builtin_constant_p(sz) && sz == PAGE_SIZE; 46562306a36Sopenharmony_ci unsigned long flags; 46662306a36Sopenharmony_ci 46762306a36Sopenharmony_ci local_irq_save(flags); 46862306a36Sopenharmony_ci 46962306a36Sopenharmony_ci __before_dc_op(op); 47062306a36Sopenharmony_ci 47162306a36Sopenharmony_ci __cache_line_loop(paddr, vaddr, sz, op, full_page); 47262306a36Sopenharmony_ci 47362306a36Sopenharmony_ci __after_dc_op(op); 47462306a36Sopenharmony_ci 47562306a36Sopenharmony_ci local_irq_restore(flags); 47662306a36Sopenharmony_ci} 47762306a36Sopenharmony_ci 47862306a36Sopenharmony_ci#else 47962306a36Sopenharmony_ci 48062306a36Sopenharmony_ci#define __dc_entire_op(op) 48162306a36Sopenharmony_ci#define __dc_disable() 48262306a36Sopenharmony_ci#define __dc_enable() 48362306a36Sopenharmony_ci#define __dc_line_op(paddr, vaddr, sz, op) 48462306a36Sopenharmony_ci#define __dc_line_op_k(paddr, sz, op) 48562306a36Sopenharmony_ci 48662306a36Sopenharmony_ci#endif /* CONFIG_ARC_HAS_DCACHE */ 48762306a36Sopenharmony_ci 48862306a36Sopenharmony_ci#ifdef CONFIG_ARC_HAS_ICACHE 48962306a36Sopenharmony_ci 49062306a36Sopenharmony_cistatic inline void __ic_entire_inv(void) 49162306a36Sopenharmony_ci{ 49262306a36Sopenharmony_ci write_aux_reg(ARC_REG_IC_IVIC, 1); 49362306a36Sopenharmony_ci read_aux_reg(ARC_REG_IC_CTRL); /* blocks */ 49462306a36Sopenharmony_ci} 49562306a36Sopenharmony_ci 49662306a36Sopenharmony_cistatic inline void 49762306a36Sopenharmony_ci__ic_line_inv_vaddr_local(phys_addr_t paddr, unsigned long vaddr, 49862306a36Sopenharmony_ci unsigned long sz) 49962306a36Sopenharmony_ci{ 50062306a36Sopenharmony_ci const int full_page = __builtin_constant_p(sz) && sz == PAGE_SIZE; 50162306a36Sopenharmony_ci unsigned long flags; 50262306a36Sopenharmony_ci 50362306a36Sopenharmony_ci local_irq_save(flags); 50462306a36Sopenharmony_ci (*_cache_line_loop_ic_fn)(paddr, vaddr, sz, OP_INV_IC, full_page); 50562306a36Sopenharmony_ci local_irq_restore(flags); 50662306a36Sopenharmony_ci} 50762306a36Sopenharmony_ci 50862306a36Sopenharmony_ci#ifndef CONFIG_SMP 50962306a36Sopenharmony_ci 51062306a36Sopenharmony_ci#define __ic_line_inv_vaddr(p, v, s) __ic_line_inv_vaddr_local(p, v, s) 51162306a36Sopenharmony_ci 51262306a36Sopenharmony_ci#else 51362306a36Sopenharmony_ci 51462306a36Sopenharmony_cistruct ic_inv_args { 51562306a36Sopenharmony_ci phys_addr_t paddr, vaddr; 51662306a36Sopenharmony_ci int sz; 51762306a36Sopenharmony_ci}; 51862306a36Sopenharmony_ci 51962306a36Sopenharmony_cistatic void __ic_line_inv_vaddr_helper(void *info) 52062306a36Sopenharmony_ci{ 52162306a36Sopenharmony_ci struct ic_inv_args *ic_inv = info; 52262306a36Sopenharmony_ci 52362306a36Sopenharmony_ci __ic_line_inv_vaddr_local(ic_inv->paddr, ic_inv->vaddr, ic_inv->sz); 52462306a36Sopenharmony_ci} 52562306a36Sopenharmony_ci 52662306a36Sopenharmony_cistatic void __ic_line_inv_vaddr(phys_addr_t paddr, unsigned long vaddr, 52762306a36Sopenharmony_ci unsigned long sz) 52862306a36Sopenharmony_ci{ 52962306a36Sopenharmony_ci struct ic_inv_args ic_inv = { 53062306a36Sopenharmony_ci .paddr = paddr, 53162306a36Sopenharmony_ci .vaddr = vaddr, 53262306a36Sopenharmony_ci .sz = sz 53362306a36Sopenharmony_ci }; 53462306a36Sopenharmony_ci 53562306a36Sopenharmony_ci on_each_cpu(__ic_line_inv_vaddr_helper, &ic_inv, 1); 53662306a36Sopenharmony_ci} 53762306a36Sopenharmony_ci 53862306a36Sopenharmony_ci#endif /* CONFIG_SMP */ 53962306a36Sopenharmony_ci 54062306a36Sopenharmony_ci#else /* !CONFIG_ARC_HAS_ICACHE */ 54162306a36Sopenharmony_ci 54262306a36Sopenharmony_ci#define __ic_entire_inv() 54362306a36Sopenharmony_ci#define __ic_line_inv_vaddr(pstart, vstart, sz) 54462306a36Sopenharmony_ci 54562306a36Sopenharmony_ci#endif /* CONFIG_ARC_HAS_ICACHE */ 54662306a36Sopenharmony_ci 54762306a36Sopenharmony_cistatic noinline void slc_op_rgn(phys_addr_t paddr, unsigned long sz, const int op) 54862306a36Sopenharmony_ci{ 54962306a36Sopenharmony_ci#ifdef CONFIG_ISA_ARCV2 55062306a36Sopenharmony_ci /* 55162306a36Sopenharmony_ci * SLC is shared between all cores and concurrent aux operations from 55262306a36Sopenharmony_ci * multiple cores need to be serialized using a spinlock 55362306a36Sopenharmony_ci * A concurrent operation can be silently ignored and/or the old/new 55462306a36Sopenharmony_ci * operation can remain incomplete forever (lockup in SLC_CTRL_BUSY loop 55562306a36Sopenharmony_ci * below) 55662306a36Sopenharmony_ci */ 55762306a36Sopenharmony_ci static DEFINE_SPINLOCK(lock); 55862306a36Sopenharmony_ci unsigned long flags; 55962306a36Sopenharmony_ci unsigned int ctrl; 56062306a36Sopenharmony_ci phys_addr_t end; 56162306a36Sopenharmony_ci 56262306a36Sopenharmony_ci spin_lock_irqsave(&lock, flags); 56362306a36Sopenharmony_ci 56462306a36Sopenharmony_ci /* 56562306a36Sopenharmony_ci * The Region Flush operation is specified by CTRL.RGN_OP[11..9] 56662306a36Sopenharmony_ci * - b'000 (default) is Flush, 56762306a36Sopenharmony_ci * - b'001 is Invalidate if CTRL.IM == 0 56862306a36Sopenharmony_ci * - b'001 is Flush-n-Invalidate if CTRL.IM == 1 56962306a36Sopenharmony_ci */ 57062306a36Sopenharmony_ci ctrl = read_aux_reg(ARC_REG_SLC_CTRL); 57162306a36Sopenharmony_ci 57262306a36Sopenharmony_ci /* Don't rely on default value of IM bit */ 57362306a36Sopenharmony_ci if (!(op & OP_FLUSH)) /* i.e. OP_INV */ 57462306a36Sopenharmony_ci ctrl &= ~SLC_CTRL_IM; /* clear IM: Disable flush before Inv */ 57562306a36Sopenharmony_ci else 57662306a36Sopenharmony_ci ctrl |= SLC_CTRL_IM; 57762306a36Sopenharmony_ci 57862306a36Sopenharmony_ci if (op & OP_INV) 57962306a36Sopenharmony_ci ctrl |= SLC_CTRL_RGN_OP_INV; /* Inv or flush-n-inv */ 58062306a36Sopenharmony_ci else 58162306a36Sopenharmony_ci ctrl &= ~SLC_CTRL_RGN_OP_INV; 58262306a36Sopenharmony_ci 58362306a36Sopenharmony_ci write_aux_reg(ARC_REG_SLC_CTRL, ctrl); 58462306a36Sopenharmony_ci 58562306a36Sopenharmony_ci /* 58662306a36Sopenharmony_ci * Lower bits are ignored, no need to clip 58762306a36Sopenharmony_ci * END needs to be setup before START (latter triggers the operation) 58862306a36Sopenharmony_ci * END can't be same as START, so add (l2_line_sz - 1) to sz 58962306a36Sopenharmony_ci */ 59062306a36Sopenharmony_ci end = paddr + sz + l2_line_sz - 1; 59162306a36Sopenharmony_ci if (is_pae40_enabled()) 59262306a36Sopenharmony_ci write_aux_reg(ARC_REG_SLC_RGN_END1, upper_32_bits(end)); 59362306a36Sopenharmony_ci 59462306a36Sopenharmony_ci write_aux_reg(ARC_REG_SLC_RGN_END, lower_32_bits(end)); 59562306a36Sopenharmony_ci 59662306a36Sopenharmony_ci if (is_pae40_enabled()) 59762306a36Sopenharmony_ci write_aux_reg(ARC_REG_SLC_RGN_START1, upper_32_bits(paddr)); 59862306a36Sopenharmony_ci 59962306a36Sopenharmony_ci write_aux_reg(ARC_REG_SLC_RGN_START, lower_32_bits(paddr)); 60062306a36Sopenharmony_ci 60162306a36Sopenharmony_ci /* Make sure "busy" bit reports correct stataus, see STAR 9001165532 */ 60262306a36Sopenharmony_ci read_aux_reg(ARC_REG_SLC_CTRL); 60362306a36Sopenharmony_ci 60462306a36Sopenharmony_ci while (read_aux_reg(ARC_REG_SLC_CTRL) & SLC_CTRL_BUSY); 60562306a36Sopenharmony_ci 60662306a36Sopenharmony_ci spin_unlock_irqrestore(&lock, flags); 60762306a36Sopenharmony_ci#endif 60862306a36Sopenharmony_ci} 60962306a36Sopenharmony_ci 61062306a36Sopenharmony_cistatic __maybe_unused noinline void slc_op_line(phys_addr_t paddr, unsigned long sz, const int op) 61162306a36Sopenharmony_ci{ 61262306a36Sopenharmony_ci#ifdef CONFIG_ISA_ARCV2 61362306a36Sopenharmony_ci /* 61462306a36Sopenharmony_ci * SLC is shared between all cores and concurrent aux operations from 61562306a36Sopenharmony_ci * multiple cores need to be serialized using a spinlock 61662306a36Sopenharmony_ci * A concurrent operation can be silently ignored and/or the old/new 61762306a36Sopenharmony_ci * operation can remain incomplete forever (lockup in SLC_CTRL_BUSY loop 61862306a36Sopenharmony_ci * below) 61962306a36Sopenharmony_ci */ 62062306a36Sopenharmony_ci static DEFINE_SPINLOCK(lock); 62162306a36Sopenharmony_ci 62262306a36Sopenharmony_ci const unsigned long SLC_LINE_MASK = ~(l2_line_sz - 1); 62362306a36Sopenharmony_ci unsigned int ctrl, cmd; 62462306a36Sopenharmony_ci unsigned long flags; 62562306a36Sopenharmony_ci int num_lines; 62662306a36Sopenharmony_ci 62762306a36Sopenharmony_ci spin_lock_irqsave(&lock, flags); 62862306a36Sopenharmony_ci 62962306a36Sopenharmony_ci ctrl = read_aux_reg(ARC_REG_SLC_CTRL); 63062306a36Sopenharmony_ci 63162306a36Sopenharmony_ci /* Don't rely on default value of IM bit */ 63262306a36Sopenharmony_ci if (!(op & OP_FLUSH)) /* i.e. OP_INV */ 63362306a36Sopenharmony_ci ctrl &= ~SLC_CTRL_IM; /* clear IM: Disable flush before Inv */ 63462306a36Sopenharmony_ci else 63562306a36Sopenharmony_ci ctrl |= SLC_CTRL_IM; 63662306a36Sopenharmony_ci 63762306a36Sopenharmony_ci write_aux_reg(ARC_REG_SLC_CTRL, ctrl); 63862306a36Sopenharmony_ci 63962306a36Sopenharmony_ci cmd = op & OP_INV ? ARC_AUX_SLC_IVDL : ARC_AUX_SLC_FLDL; 64062306a36Sopenharmony_ci 64162306a36Sopenharmony_ci sz += paddr & ~SLC_LINE_MASK; 64262306a36Sopenharmony_ci paddr &= SLC_LINE_MASK; 64362306a36Sopenharmony_ci 64462306a36Sopenharmony_ci num_lines = DIV_ROUND_UP(sz, l2_line_sz); 64562306a36Sopenharmony_ci 64662306a36Sopenharmony_ci while (num_lines-- > 0) { 64762306a36Sopenharmony_ci write_aux_reg(cmd, paddr); 64862306a36Sopenharmony_ci paddr += l2_line_sz; 64962306a36Sopenharmony_ci } 65062306a36Sopenharmony_ci 65162306a36Sopenharmony_ci /* Make sure "busy" bit reports correct stataus, see STAR 9001165532 */ 65262306a36Sopenharmony_ci read_aux_reg(ARC_REG_SLC_CTRL); 65362306a36Sopenharmony_ci 65462306a36Sopenharmony_ci while (read_aux_reg(ARC_REG_SLC_CTRL) & SLC_CTRL_BUSY); 65562306a36Sopenharmony_ci 65662306a36Sopenharmony_ci spin_unlock_irqrestore(&lock, flags); 65762306a36Sopenharmony_ci#endif 65862306a36Sopenharmony_ci} 65962306a36Sopenharmony_ci 66062306a36Sopenharmony_ci#define slc_op(paddr, sz, op) slc_op_rgn(paddr, sz, op) 66162306a36Sopenharmony_ci 66262306a36Sopenharmony_cinoinline static void slc_entire_op(const int op) 66362306a36Sopenharmony_ci{ 66462306a36Sopenharmony_ci unsigned int ctrl, r = ARC_REG_SLC_CTRL; 66562306a36Sopenharmony_ci 66662306a36Sopenharmony_ci ctrl = read_aux_reg(r); 66762306a36Sopenharmony_ci 66862306a36Sopenharmony_ci if (!(op & OP_FLUSH)) /* i.e. OP_INV */ 66962306a36Sopenharmony_ci ctrl &= ~SLC_CTRL_IM; /* clear IM: Disable flush before Inv */ 67062306a36Sopenharmony_ci else 67162306a36Sopenharmony_ci ctrl |= SLC_CTRL_IM; 67262306a36Sopenharmony_ci 67362306a36Sopenharmony_ci write_aux_reg(r, ctrl); 67462306a36Sopenharmony_ci 67562306a36Sopenharmony_ci if (op & OP_INV) /* Inv or flush-n-inv use same cmd reg */ 67662306a36Sopenharmony_ci write_aux_reg(ARC_REG_SLC_INVALIDATE, 0x1); 67762306a36Sopenharmony_ci else 67862306a36Sopenharmony_ci write_aux_reg(ARC_REG_SLC_FLUSH, 0x1); 67962306a36Sopenharmony_ci 68062306a36Sopenharmony_ci /* Make sure "busy" bit reports correct stataus, see STAR 9001165532 */ 68162306a36Sopenharmony_ci read_aux_reg(r); 68262306a36Sopenharmony_ci 68362306a36Sopenharmony_ci /* Important to wait for flush to complete */ 68462306a36Sopenharmony_ci while (read_aux_reg(r) & SLC_CTRL_BUSY); 68562306a36Sopenharmony_ci} 68662306a36Sopenharmony_ci 68762306a36Sopenharmony_cistatic inline void arc_slc_disable(void) 68862306a36Sopenharmony_ci{ 68962306a36Sopenharmony_ci const int r = ARC_REG_SLC_CTRL; 69062306a36Sopenharmony_ci 69162306a36Sopenharmony_ci slc_entire_op(OP_FLUSH_N_INV); 69262306a36Sopenharmony_ci write_aux_reg(r, read_aux_reg(r) | SLC_CTRL_DIS); 69362306a36Sopenharmony_ci} 69462306a36Sopenharmony_ci 69562306a36Sopenharmony_cistatic inline void arc_slc_enable(void) 69662306a36Sopenharmony_ci{ 69762306a36Sopenharmony_ci const int r = ARC_REG_SLC_CTRL; 69862306a36Sopenharmony_ci 69962306a36Sopenharmony_ci write_aux_reg(r, read_aux_reg(r) & ~SLC_CTRL_DIS); 70062306a36Sopenharmony_ci} 70162306a36Sopenharmony_ci 70262306a36Sopenharmony_ci/*********************************************************** 70362306a36Sopenharmony_ci * Exported APIs 70462306a36Sopenharmony_ci */ 70562306a36Sopenharmony_ci 70662306a36Sopenharmony_ci/* 70762306a36Sopenharmony_ci * Handle cache congruency of kernel and userspace mappings of page when kernel 70862306a36Sopenharmony_ci * writes-to/reads-from 70962306a36Sopenharmony_ci * 71062306a36Sopenharmony_ci * The idea is to defer flushing of kernel mapping after a WRITE, possible if: 71162306a36Sopenharmony_ci * -dcache is NOT aliasing, hence any U/K-mappings of page are congruent 71262306a36Sopenharmony_ci * -U-mapping doesn't exist yet for page (finalised in update_mmu_cache) 71362306a36Sopenharmony_ci * -In SMP, if hardware caches are coherent 71462306a36Sopenharmony_ci * 71562306a36Sopenharmony_ci * There's a corollary case, where kernel READs from a userspace mapped page. 71662306a36Sopenharmony_ci * If the U-mapping is not congruent to K-mapping, former needs flushing. 71762306a36Sopenharmony_ci */ 71862306a36Sopenharmony_civoid flush_dcache_folio(struct folio *folio) 71962306a36Sopenharmony_ci{ 72062306a36Sopenharmony_ci struct address_space *mapping; 72162306a36Sopenharmony_ci 72262306a36Sopenharmony_ci if (!cache_is_vipt_aliasing()) { 72362306a36Sopenharmony_ci clear_bit(PG_dc_clean, &folio->flags); 72462306a36Sopenharmony_ci return; 72562306a36Sopenharmony_ci } 72662306a36Sopenharmony_ci 72762306a36Sopenharmony_ci /* don't handle anon pages here */ 72862306a36Sopenharmony_ci mapping = folio_flush_mapping(folio); 72962306a36Sopenharmony_ci if (!mapping) 73062306a36Sopenharmony_ci return; 73162306a36Sopenharmony_ci 73262306a36Sopenharmony_ci /* 73362306a36Sopenharmony_ci * pagecache page, file not yet mapped to userspace 73462306a36Sopenharmony_ci * Make a note that K-mapping is dirty 73562306a36Sopenharmony_ci */ 73662306a36Sopenharmony_ci if (!mapping_mapped(mapping)) { 73762306a36Sopenharmony_ci clear_bit(PG_dc_clean, &folio->flags); 73862306a36Sopenharmony_ci } else if (folio_mapped(folio)) { 73962306a36Sopenharmony_ci /* kernel reading from page with U-mapping */ 74062306a36Sopenharmony_ci phys_addr_t paddr = (unsigned long)folio_address(folio); 74162306a36Sopenharmony_ci unsigned long vaddr = folio_pos(folio); 74262306a36Sopenharmony_ci 74362306a36Sopenharmony_ci /* 74462306a36Sopenharmony_ci * vaddr is not actually the virtual address, but is 74562306a36Sopenharmony_ci * congruent to every user mapping. 74662306a36Sopenharmony_ci */ 74762306a36Sopenharmony_ci if (addr_not_cache_congruent(paddr, vaddr)) 74862306a36Sopenharmony_ci __flush_dcache_pages(paddr, vaddr, 74962306a36Sopenharmony_ci folio_nr_pages(folio)); 75062306a36Sopenharmony_ci } 75162306a36Sopenharmony_ci} 75262306a36Sopenharmony_ciEXPORT_SYMBOL(flush_dcache_folio); 75362306a36Sopenharmony_ci 75462306a36Sopenharmony_civoid flush_dcache_page(struct page *page) 75562306a36Sopenharmony_ci{ 75662306a36Sopenharmony_ci return flush_dcache_folio(page_folio(page)); 75762306a36Sopenharmony_ci} 75862306a36Sopenharmony_ciEXPORT_SYMBOL(flush_dcache_page); 75962306a36Sopenharmony_ci 76062306a36Sopenharmony_ci/* 76162306a36Sopenharmony_ci * DMA ops for systems with L1 cache only 76262306a36Sopenharmony_ci * Make memory coherent with L1 cache by flushing/invalidating L1 lines 76362306a36Sopenharmony_ci */ 76462306a36Sopenharmony_cistatic void __dma_cache_wback_inv_l1(phys_addr_t start, unsigned long sz) 76562306a36Sopenharmony_ci{ 76662306a36Sopenharmony_ci __dc_line_op_k(start, sz, OP_FLUSH_N_INV); 76762306a36Sopenharmony_ci} 76862306a36Sopenharmony_ci 76962306a36Sopenharmony_cistatic void __dma_cache_inv_l1(phys_addr_t start, unsigned long sz) 77062306a36Sopenharmony_ci{ 77162306a36Sopenharmony_ci __dc_line_op_k(start, sz, OP_INV); 77262306a36Sopenharmony_ci} 77362306a36Sopenharmony_ci 77462306a36Sopenharmony_cistatic void __dma_cache_wback_l1(phys_addr_t start, unsigned long sz) 77562306a36Sopenharmony_ci{ 77662306a36Sopenharmony_ci __dc_line_op_k(start, sz, OP_FLUSH); 77762306a36Sopenharmony_ci} 77862306a36Sopenharmony_ci 77962306a36Sopenharmony_ci/* 78062306a36Sopenharmony_ci * DMA ops for systems with both L1 and L2 caches, but without IOC 78162306a36Sopenharmony_ci * Both L1 and L2 lines need to be explicitly flushed/invalidated 78262306a36Sopenharmony_ci */ 78362306a36Sopenharmony_cistatic void __dma_cache_wback_inv_slc(phys_addr_t start, unsigned long sz) 78462306a36Sopenharmony_ci{ 78562306a36Sopenharmony_ci __dc_line_op_k(start, sz, OP_FLUSH_N_INV); 78662306a36Sopenharmony_ci slc_op(start, sz, OP_FLUSH_N_INV); 78762306a36Sopenharmony_ci} 78862306a36Sopenharmony_ci 78962306a36Sopenharmony_cistatic void __dma_cache_inv_slc(phys_addr_t start, unsigned long sz) 79062306a36Sopenharmony_ci{ 79162306a36Sopenharmony_ci __dc_line_op_k(start, sz, OP_INV); 79262306a36Sopenharmony_ci slc_op(start, sz, OP_INV); 79362306a36Sopenharmony_ci} 79462306a36Sopenharmony_ci 79562306a36Sopenharmony_cistatic void __dma_cache_wback_slc(phys_addr_t start, unsigned long sz) 79662306a36Sopenharmony_ci{ 79762306a36Sopenharmony_ci __dc_line_op_k(start, sz, OP_FLUSH); 79862306a36Sopenharmony_ci slc_op(start, sz, OP_FLUSH); 79962306a36Sopenharmony_ci} 80062306a36Sopenharmony_ci 80162306a36Sopenharmony_ci/* 80262306a36Sopenharmony_ci * Exported DMA API 80362306a36Sopenharmony_ci */ 80462306a36Sopenharmony_civoid dma_cache_wback_inv(phys_addr_t start, unsigned long sz) 80562306a36Sopenharmony_ci{ 80662306a36Sopenharmony_ci __dma_cache_wback_inv(start, sz); 80762306a36Sopenharmony_ci} 80862306a36Sopenharmony_ciEXPORT_SYMBOL(dma_cache_wback_inv); 80962306a36Sopenharmony_ci 81062306a36Sopenharmony_civoid dma_cache_inv(phys_addr_t start, unsigned long sz) 81162306a36Sopenharmony_ci{ 81262306a36Sopenharmony_ci __dma_cache_inv(start, sz); 81362306a36Sopenharmony_ci} 81462306a36Sopenharmony_ciEXPORT_SYMBOL(dma_cache_inv); 81562306a36Sopenharmony_ci 81662306a36Sopenharmony_civoid dma_cache_wback(phys_addr_t start, unsigned long sz) 81762306a36Sopenharmony_ci{ 81862306a36Sopenharmony_ci __dma_cache_wback(start, sz); 81962306a36Sopenharmony_ci} 82062306a36Sopenharmony_ciEXPORT_SYMBOL(dma_cache_wback); 82162306a36Sopenharmony_ci 82262306a36Sopenharmony_ci/* 82362306a36Sopenharmony_ci * This is API for making I/D Caches consistent when modifying 82462306a36Sopenharmony_ci * kernel code (loadable modules, kprobes, kgdb...) 82562306a36Sopenharmony_ci * This is called on insmod, with kernel virtual address for CODE of 82662306a36Sopenharmony_ci * the module. ARC cache maintenance ops require PHY address thus we 82762306a36Sopenharmony_ci * need to convert vmalloc addr to PHY addr 82862306a36Sopenharmony_ci */ 82962306a36Sopenharmony_civoid flush_icache_range(unsigned long kstart, unsigned long kend) 83062306a36Sopenharmony_ci{ 83162306a36Sopenharmony_ci unsigned int tot_sz; 83262306a36Sopenharmony_ci 83362306a36Sopenharmony_ci WARN(kstart < TASK_SIZE, "%s() can't handle user vaddr", __func__); 83462306a36Sopenharmony_ci 83562306a36Sopenharmony_ci /* Shortcut for bigger flush ranges. 83662306a36Sopenharmony_ci * Here we don't care if this was kernel virtual or phy addr 83762306a36Sopenharmony_ci */ 83862306a36Sopenharmony_ci tot_sz = kend - kstart; 83962306a36Sopenharmony_ci if (tot_sz > PAGE_SIZE) { 84062306a36Sopenharmony_ci flush_cache_all(); 84162306a36Sopenharmony_ci return; 84262306a36Sopenharmony_ci } 84362306a36Sopenharmony_ci 84462306a36Sopenharmony_ci /* Case: Kernel Phy addr (0x8000_0000 onwards) */ 84562306a36Sopenharmony_ci if (likely(kstart > PAGE_OFFSET)) { 84662306a36Sopenharmony_ci /* 84762306a36Sopenharmony_ci * The 2nd arg despite being paddr will be used to index icache 84862306a36Sopenharmony_ci * This is OK since no alternate virtual mappings will exist 84962306a36Sopenharmony_ci * given the callers for this case: kprobe/kgdb in built-in 85062306a36Sopenharmony_ci * kernel code only. 85162306a36Sopenharmony_ci */ 85262306a36Sopenharmony_ci __sync_icache_dcache(kstart, kstart, kend - kstart); 85362306a36Sopenharmony_ci return; 85462306a36Sopenharmony_ci } 85562306a36Sopenharmony_ci 85662306a36Sopenharmony_ci /* 85762306a36Sopenharmony_ci * Case: Kernel Vaddr (0x7000_0000 to 0x7fff_ffff) 85862306a36Sopenharmony_ci * (1) ARC Cache Maintenance ops only take Phy addr, hence special 85962306a36Sopenharmony_ci * handling of kernel vaddr. 86062306a36Sopenharmony_ci * 86162306a36Sopenharmony_ci * (2) Despite @tot_sz being < PAGE_SIZE (bigger cases handled already), 86262306a36Sopenharmony_ci * it still needs to handle a 2 page scenario, where the range 86362306a36Sopenharmony_ci * straddles across 2 virtual pages and hence need for loop 86462306a36Sopenharmony_ci */ 86562306a36Sopenharmony_ci while (tot_sz > 0) { 86662306a36Sopenharmony_ci unsigned int off, sz; 86762306a36Sopenharmony_ci unsigned long phy, pfn; 86862306a36Sopenharmony_ci 86962306a36Sopenharmony_ci off = kstart % PAGE_SIZE; 87062306a36Sopenharmony_ci pfn = vmalloc_to_pfn((void *)kstart); 87162306a36Sopenharmony_ci phy = (pfn << PAGE_SHIFT) + off; 87262306a36Sopenharmony_ci sz = min_t(unsigned int, tot_sz, PAGE_SIZE - off); 87362306a36Sopenharmony_ci __sync_icache_dcache(phy, kstart, sz); 87462306a36Sopenharmony_ci kstart += sz; 87562306a36Sopenharmony_ci tot_sz -= sz; 87662306a36Sopenharmony_ci } 87762306a36Sopenharmony_ci} 87862306a36Sopenharmony_ciEXPORT_SYMBOL(flush_icache_range); 87962306a36Sopenharmony_ci 88062306a36Sopenharmony_ci/* 88162306a36Sopenharmony_ci * General purpose helper to make I and D cache lines consistent. 88262306a36Sopenharmony_ci * @paddr is phy addr of region 88362306a36Sopenharmony_ci * @vaddr is typically user vaddr (breakpoint) or kernel vaddr (vmalloc) 88462306a36Sopenharmony_ci * However in one instance, when called by kprobe (for a breakpt in 88562306a36Sopenharmony_ci * builtin kernel code) @vaddr will be paddr only, meaning CDU operation will 88662306a36Sopenharmony_ci * use a paddr to index the cache (despite VIPT). This is fine since a 88762306a36Sopenharmony_ci * builtin kernel page will not have any virtual mappings. 88862306a36Sopenharmony_ci * kprobe on loadable module will be kernel vaddr. 88962306a36Sopenharmony_ci */ 89062306a36Sopenharmony_civoid __sync_icache_dcache(phys_addr_t paddr, unsigned long vaddr, int len) 89162306a36Sopenharmony_ci{ 89262306a36Sopenharmony_ci __dc_line_op(paddr, vaddr, len, OP_FLUSH_N_INV); 89362306a36Sopenharmony_ci __ic_line_inv_vaddr(paddr, vaddr, len); 89462306a36Sopenharmony_ci} 89562306a36Sopenharmony_ci 89662306a36Sopenharmony_ci/* wrapper to compile time eliminate alignment checks in flush loop */ 89762306a36Sopenharmony_civoid __inv_icache_pages(phys_addr_t paddr, unsigned long vaddr, unsigned nr) 89862306a36Sopenharmony_ci{ 89962306a36Sopenharmony_ci __ic_line_inv_vaddr(paddr, vaddr, nr * PAGE_SIZE); 90062306a36Sopenharmony_ci} 90162306a36Sopenharmony_ci 90262306a36Sopenharmony_ci/* 90362306a36Sopenharmony_ci * wrapper to clearout kernel or userspace mappings of a page 90462306a36Sopenharmony_ci * For kernel mappings @vaddr == @paddr 90562306a36Sopenharmony_ci */ 90662306a36Sopenharmony_civoid __flush_dcache_pages(phys_addr_t paddr, unsigned long vaddr, unsigned nr) 90762306a36Sopenharmony_ci{ 90862306a36Sopenharmony_ci __dc_line_op(paddr, vaddr & PAGE_MASK, nr * PAGE_SIZE, OP_FLUSH_N_INV); 90962306a36Sopenharmony_ci} 91062306a36Sopenharmony_ci 91162306a36Sopenharmony_cinoinline void flush_cache_all(void) 91262306a36Sopenharmony_ci{ 91362306a36Sopenharmony_ci unsigned long flags; 91462306a36Sopenharmony_ci 91562306a36Sopenharmony_ci local_irq_save(flags); 91662306a36Sopenharmony_ci 91762306a36Sopenharmony_ci __ic_entire_inv(); 91862306a36Sopenharmony_ci __dc_entire_op(OP_FLUSH_N_INV); 91962306a36Sopenharmony_ci 92062306a36Sopenharmony_ci local_irq_restore(flags); 92162306a36Sopenharmony_ci 92262306a36Sopenharmony_ci} 92362306a36Sopenharmony_ci 92462306a36Sopenharmony_ci#ifdef CONFIG_ARC_CACHE_VIPT_ALIASING 92562306a36Sopenharmony_ci 92662306a36Sopenharmony_civoid flush_cache_mm(struct mm_struct *mm) 92762306a36Sopenharmony_ci{ 92862306a36Sopenharmony_ci flush_cache_all(); 92962306a36Sopenharmony_ci} 93062306a36Sopenharmony_ci 93162306a36Sopenharmony_civoid flush_cache_page(struct vm_area_struct *vma, unsigned long u_vaddr, 93262306a36Sopenharmony_ci unsigned long pfn) 93362306a36Sopenharmony_ci{ 93462306a36Sopenharmony_ci phys_addr_t paddr = pfn << PAGE_SHIFT; 93562306a36Sopenharmony_ci 93662306a36Sopenharmony_ci u_vaddr &= PAGE_MASK; 93762306a36Sopenharmony_ci 93862306a36Sopenharmony_ci __flush_dcache_pages(paddr, u_vaddr, 1); 93962306a36Sopenharmony_ci 94062306a36Sopenharmony_ci if (vma->vm_flags & VM_EXEC) 94162306a36Sopenharmony_ci __inv_icache_pages(paddr, u_vaddr, 1); 94262306a36Sopenharmony_ci} 94362306a36Sopenharmony_ci 94462306a36Sopenharmony_civoid flush_cache_range(struct vm_area_struct *vma, unsigned long start, 94562306a36Sopenharmony_ci unsigned long end) 94662306a36Sopenharmony_ci{ 94762306a36Sopenharmony_ci flush_cache_all(); 94862306a36Sopenharmony_ci} 94962306a36Sopenharmony_ci 95062306a36Sopenharmony_civoid flush_anon_page(struct vm_area_struct *vma, struct page *page, 95162306a36Sopenharmony_ci unsigned long u_vaddr) 95262306a36Sopenharmony_ci{ 95362306a36Sopenharmony_ci /* TBD: do we really need to clear the kernel mapping */ 95462306a36Sopenharmony_ci __flush_dcache_pages((phys_addr_t)page_address(page), u_vaddr, 1); 95562306a36Sopenharmony_ci __flush_dcache_pages((phys_addr_t)page_address(page), 95662306a36Sopenharmony_ci (phys_addr_t)page_address(page), 1); 95762306a36Sopenharmony_ci 95862306a36Sopenharmony_ci} 95962306a36Sopenharmony_ci 96062306a36Sopenharmony_ci#endif 96162306a36Sopenharmony_ci 96262306a36Sopenharmony_civoid copy_user_highpage(struct page *to, struct page *from, 96362306a36Sopenharmony_ci unsigned long u_vaddr, struct vm_area_struct *vma) 96462306a36Sopenharmony_ci{ 96562306a36Sopenharmony_ci struct folio *src = page_folio(from); 96662306a36Sopenharmony_ci struct folio *dst = page_folio(to); 96762306a36Sopenharmony_ci void *kfrom = kmap_atomic(from); 96862306a36Sopenharmony_ci void *kto = kmap_atomic(to); 96962306a36Sopenharmony_ci int clean_src_k_mappings = 0; 97062306a36Sopenharmony_ci 97162306a36Sopenharmony_ci /* 97262306a36Sopenharmony_ci * If SRC page was already mapped in userspace AND it's U-mapping is 97362306a36Sopenharmony_ci * not congruent with K-mapping, sync former to physical page so that 97462306a36Sopenharmony_ci * K-mapping in memcpy below, sees the right data 97562306a36Sopenharmony_ci * 97662306a36Sopenharmony_ci * Note that while @u_vaddr refers to DST page's userspace vaddr, it is 97762306a36Sopenharmony_ci * equally valid for SRC page as well 97862306a36Sopenharmony_ci * 97962306a36Sopenharmony_ci * For !VIPT cache, all of this gets compiled out as 98062306a36Sopenharmony_ci * addr_not_cache_congruent() is 0 98162306a36Sopenharmony_ci */ 98262306a36Sopenharmony_ci if (page_mapcount(from) && addr_not_cache_congruent(kfrom, u_vaddr)) { 98362306a36Sopenharmony_ci __flush_dcache_pages((unsigned long)kfrom, u_vaddr, 1); 98462306a36Sopenharmony_ci clean_src_k_mappings = 1; 98562306a36Sopenharmony_ci } 98662306a36Sopenharmony_ci 98762306a36Sopenharmony_ci copy_page(kto, kfrom); 98862306a36Sopenharmony_ci 98962306a36Sopenharmony_ci /* 99062306a36Sopenharmony_ci * Mark DST page K-mapping as dirty for a later finalization by 99162306a36Sopenharmony_ci * update_mmu_cache(). Although the finalization could have been done 99262306a36Sopenharmony_ci * here as well (given that both vaddr/paddr are available). 99362306a36Sopenharmony_ci * But update_mmu_cache() already has code to do that for other 99462306a36Sopenharmony_ci * non copied user pages (e.g. read faults which wire in pagecache page 99562306a36Sopenharmony_ci * directly). 99662306a36Sopenharmony_ci */ 99762306a36Sopenharmony_ci clear_bit(PG_dc_clean, &dst->flags); 99862306a36Sopenharmony_ci 99962306a36Sopenharmony_ci /* 100062306a36Sopenharmony_ci * if SRC was already usermapped and non-congruent to kernel mapping 100162306a36Sopenharmony_ci * sync the kernel mapping back to physical page 100262306a36Sopenharmony_ci */ 100362306a36Sopenharmony_ci if (clean_src_k_mappings) { 100462306a36Sopenharmony_ci __flush_dcache_pages((unsigned long)kfrom, 100562306a36Sopenharmony_ci (unsigned long)kfrom, 1); 100662306a36Sopenharmony_ci } else { 100762306a36Sopenharmony_ci clear_bit(PG_dc_clean, &src->flags); 100862306a36Sopenharmony_ci } 100962306a36Sopenharmony_ci 101062306a36Sopenharmony_ci kunmap_atomic(kto); 101162306a36Sopenharmony_ci kunmap_atomic(kfrom); 101262306a36Sopenharmony_ci} 101362306a36Sopenharmony_ci 101462306a36Sopenharmony_civoid clear_user_page(void *to, unsigned long u_vaddr, struct page *page) 101562306a36Sopenharmony_ci{ 101662306a36Sopenharmony_ci struct folio *folio = page_folio(page); 101762306a36Sopenharmony_ci clear_page(to); 101862306a36Sopenharmony_ci clear_bit(PG_dc_clean, &folio->flags); 101962306a36Sopenharmony_ci} 102062306a36Sopenharmony_ciEXPORT_SYMBOL(clear_user_page); 102162306a36Sopenharmony_ci 102262306a36Sopenharmony_ci/********************************************************************** 102362306a36Sopenharmony_ci * Explicit Cache flush request from user space via syscall 102462306a36Sopenharmony_ci * Needed for JITs which generate code on the fly 102562306a36Sopenharmony_ci */ 102662306a36Sopenharmony_ciSYSCALL_DEFINE3(cacheflush, uint32_t, start, uint32_t, sz, uint32_t, flags) 102762306a36Sopenharmony_ci{ 102862306a36Sopenharmony_ci /* TBD: optimize this */ 102962306a36Sopenharmony_ci flush_cache_all(); 103062306a36Sopenharmony_ci return 0; 103162306a36Sopenharmony_ci} 103262306a36Sopenharmony_ci 103362306a36Sopenharmony_ci/* 103462306a36Sopenharmony_ci * IO-Coherency (IOC) setup rules: 103562306a36Sopenharmony_ci * 103662306a36Sopenharmony_ci * 1. Needs to be at system level, so only once by Master core 103762306a36Sopenharmony_ci * Non-Masters need not be accessing caches at that time 103862306a36Sopenharmony_ci * - They are either HALT_ON_RESET and kick started much later or 103962306a36Sopenharmony_ci * - if run on reset, need to ensure that arc_platform_smp_wait_to_boot() 104062306a36Sopenharmony_ci * doesn't perturb caches or coherency unit 104162306a36Sopenharmony_ci * 104262306a36Sopenharmony_ci * 2. caches (L1 and SLC) need to be purged (flush+inv) before setting up IOC, 104362306a36Sopenharmony_ci * otherwise any straggler data might behave strangely post IOC enabling 104462306a36Sopenharmony_ci * 104562306a36Sopenharmony_ci * 3. All Caches need to be disabled when setting up IOC to elide any in-flight 104662306a36Sopenharmony_ci * Coherency transactions 104762306a36Sopenharmony_ci */ 104862306a36Sopenharmony_cistatic noinline void __init arc_ioc_setup(void) 104962306a36Sopenharmony_ci{ 105062306a36Sopenharmony_ci unsigned int ioc_base, mem_sz; 105162306a36Sopenharmony_ci 105262306a36Sopenharmony_ci /* 105362306a36Sopenharmony_ci * If IOC was already enabled (due to bootloader) it technically needs to 105462306a36Sopenharmony_ci * be reconfigured with aperture base,size corresponding to Linux memory map 105562306a36Sopenharmony_ci * which will certainly be different than uboot's. But disabling and 105662306a36Sopenharmony_ci * reenabling IOC when DMA might be potentially active is tricky business. 105762306a36Sopenharmony_ci * To avoid random memory issues later, just panic here and ask user to 105862306a36Sopenharmony_ci * upgrade bootloader to one which doesn't enable IOC 105962306a36Sopenharmony_ci */ 106062306a36Sopenharmony_ci if (read_aux_reg(ARC_REG_IO_COH_ENABLE) & ARC_IO_COH_ENABLE_BIT) 106162306a36Sopenharmony_ci panic("IOC already enabled, please upgrade bootloader!\n"); 106262306a36Sopenharmony_ci 106362306a36Sopenharmony_ci if (!ioc_enable) 106462306a36Sopenharmony_ci return; 106562306a36Sopenharmony_ci 106662306a36Sopenharmony_ci /* Flush + invalidate + disable L1 dcache */ 106762306a36Sopenharmony_ci __dc_disable(); 106862306a36Sopenharmony_ci 106962306a36Sopenharmony_ci /* Flush + invalidate SLC */ 107062306a36Sopenharmony_ci if (read_aux_reg(ARC_REG_SLC_BCR)) 107162306a36Sopenharmony_ci slc_entire_op(OP_FLUSH_N_INV); 107262306a36Sopenharmony_ci 107362306a36Sopenharmony_ci /* 107462306a36Sopenharmony_ci * currently IOC Aperture covers entire DDR 107562306a36Sopenharmony_ci * TBD: fix for PGU + 1GB of low mem 107662306a36Sopenharmony_ci * TBD: fix for PAE 107762306a36Sopenharmony_ci */ 107862306a36Sopenharmony_ci mem_sz = arc_get_mem_sz(); 107962306a36Sopenharmony_ci 108062306a36Sopenharmony_ci if (!is_power_of_2(mem_sz) || mem_sz < 4096) 108162306a36Sopenharmony_ci panic("IOC Aperture size must be power of 2 larger than 4KB"); 108262306a36Sopenharmony_ci 108362306a36Sopenharmony_ci /* 108462306a36Sopenharmony_ci * IOC Aperture size decoded as 2 ^ (SIZE + 2) KB, 108562306a36Sopenharmony_ci * so setting 0x11 implies 512MB, 0x12 implies 1GB... 108662306a36Sopenharmony_ci */ 108762306a36Sopenharmony_ci write_aux_reg(ARC_REG_IO_COH_AP0_SIZE, order_base_2(mem_sz >> 10) - 2); 108862306a36Sopenharmony_ci 108962306a36Sopenharmony_ci /* for now assume kernel base is start of IOC aperture */ 109062306a36Sopenharmony_ci ioc_base = CONFIG_LINUX_RAM_BASE; 109162306a36Sopenharmony_ci 109262306a36Sopenharmony_ci if (ioc_base % mem_sz != 0) 109362306a36Sopenharmony_ci panic("IOC Aperture start must be aligned to the size of the aperture"); 109462306a36Sopenharmony_ci 109562306a36Sopenharmony_ci write_aux_reg(ARC_REG_IO_COH_AP0_BASE, ioc_base >> 12); 109662306a36Sopenharmony_ci write_aux_reg(ARC_REG_IO_COH_PARTIAL, ARC_IO_COH_PARTIAL_BIT); 109762306a36Sopenharmony_ci write_aux_reg(ARC_REG_IO_COH_ENABLE, ARC_IO_COH_ENABLE_BIT); 109862306a36Sopenharmony_ci 109962306a36Sopenharmony_ci /* Re-enable L1 dcache */ 110062306a36Sopenharmony_ci __dc_enable(); 110162306a36Sopenharmony_ci} 110262306a36Sopenharmony_ci 110362306a36Sopenharmony_ci/* 110462306a36Sopenharmony_ci * Cache related boot time checks/setups only needed on master CPU: 110562306a36Sopenharmony_ci * - Geometry checks (kernel build and hardware agree: e.g. L1_CACHE_BYTES) 110662306a36Sopenharmony_ci * Assume SMP only, so all cores will have same cache config. A check on 110762306a36Sopenharmony_ci * one core suffices for all 110862306a36Sopenharmony_ci * - IOC setup / dma callbacks only need to be done once 110962306a36Sopenharmony_ci */ 111062306a36Sopenharmony_cistatic noinline void __init arc_cache_init_master(void) 111162306a36Sopenharmony_ci{ 111262306a36Sopenharmony_ci if (IS_ENABLED(CONFIG_ARC_HAS_ICACHE)) { 111362306a36Sopenharmony_ci struct cpuinfo_arc_cache *ic = &ic_info; 111462306a36Sopenharmony_ci 111562306a36Sopenharmony_ci if (!ic->line_len) 111662306a36Sopenharmony_ci panic("cache support enabled but non-existent cache\n"); 111762306a36Sopenharmony_ci 111862306a36Sopenharmony_ci if (ic->line_len != L1_CACHE_BYTES) 111962306a36Sopenharmony_ci panic("ICache line [%d] != kernel Config [%d]", 112062306a36Sopenharmony_ci ic->line_len, L1_CACHE_BYTES); 112162306a36Sopenharmony_ci 112262306a36Sopenharmony_ci /* 112362306a36Sopenharmony_ci * In MMU v4 (HS38x) the aliasing icache config uses IVIL/PTAG 112462306a36Sopenharmony_ci * pair to provide vaddr/paddr respectively, just as in MMU v3 112562306a36Sopenharmony_ci */ 112662306a36Sopenharmony_ci if (is_isa_arcv2() && ic->colors > 1) 112762306a36Sopenharmony_ci _cache_line_loop_ic_fn = __cache_line_loop_v3; 112862306a36Sopenharmony_ci else 112962306a36Sopenharmony_ci _cache_line_loop_ic_fn = __cache_line_loop; 113062306a36Sopenharmony_ci } 113162306a36Sopenharmony_ci 113262306a36Sopenharmony_ci if (IS_ENABLED(CONFIG_ARC_HAS_DCACHE)) { 113362306a36Sopenharmony_ci struct cpuinfo_arc_cache *dc = &dc_info; 113462306a36Sopenharmony_ci 113562306a36Sopenharmony_ci if (!dc->line_len) 113662306a36Sopenharmony_ci panic("cache support enabled but non-existent cache\n"); 113762306a36Sopenharmony_ci 113862306a36Sopenharmony_ci if (dc->line_len != L1_CACHE_BYTES) 113962306a36Sopenharmony_ci panic("DCache line [%d] != kernel Config [%d]", 114062306a36Sopenharmony_ci dc->line_len, L1_CACHE_BYTES); 114162306a36Sopenharmony_ci 114262306a36Sopenharmony_ci /* check for D-Cache aliasing on ARCompact: ARCv2 has PIPT */ 114362306a36Sopenharmony_ci if (is_isa_arcompact()) { 114462306a36Sopenharmony_ci int handled = IS_ENABLED(CONFIG_ARC_CACHE_VIPT_ALIASING); 114562306a36Sopenharmony_ci 114662306a36Sopenharmony_ci if (dc->colors > 1) { 114762306a36Sopenharmony_ci if (!handled) 114862306a36Sopenharmony_ci panic("Enable CONFIG_ARC_CACHE_VIPT_ALIASING\n"); 114962306a36Sopenharmony_ci if (CACHE_COLORS_NUM != dc->colors) 115062306a36Sopenharmony_ci panic("CACHE_COLORS_NUM not optimized for config\n"); 115162306a36Sopenharmony_ci } else if (handled && dc->colors == 1) { 115262306a36Sopenharmony_ci panic("Disable CONFIG_ARC_CACHE_VIPT_ALIASING\n"); 115362306a36Sopenharmony_ci } 115462306a36Sopenharmony_ci } 115562306a36Sopenharmony_ci } 115662306a36Sopenharmony_ci 115762306a36Sopenharmony_ci /* 115862306a36Sopenharmony_ci * Check that SMP_CACHE_BYTES (and hence ARCH_DMA_MINALIGN) is larger 115962306a36Sopenharmony_ci * or equal to any cache line length. 116062306a36Sopenharmony_ci */ 116162306a36Sopenharmony_ci BUILD_BUG_ON_MSG(L1_CACHE_BYTES > SMP_CACHE_BYTES, 116262306a36Sopenharmony_ci "SMP_CACHE_BYTES must be >= any cache line length"); 116362306a36Sopenharmony_ci if (is_isa_arcv2() && (l2_line_sz > SMP_CACHE_BYTES)) 116462306a36Sopenharmony_ci panic("L2 Cache line [%d] > kernel Config [%d]\n", 116562306a36Sopenharmony_ci l2_line_sz, SMP_CACHE_BYTES); 116662306a36Sopenharmony_ci 116762306a36Sopenharmony_ci /* Note that SLC disable not formally supported till HS 3.0 */ 116862306a36Sopenharmony_ci if (is_isa_arcv2() && l2_line_sz && !slc_enable) 116962306a36Sopenharmony_ci arc_slc_disable(); 117062306a36Sopenharmony_ci 117162306a36Sopenharmony_ci if (is_isa_arcv2() && ioc_exists) 117262306a36Sopenharmony_ci arc_ioc_setup(); 117362306a36Sopenharmony_ci 117462306a36Sopenharmony_ci if (is_isa_arcv2() && l2_line_sz && slc_enable) { 117562306a36Sopenharmony_ci __dma_cache_wback_inv = __dma_cache_wback_inv_slc; 117662306a36Sopenharmony_ci __dma_cache_inv = __dma_cache_inv_slc; 117762306a36Sopenharmony_ci __dma_cache_wback = __dma_cache_wback_slc; 117862306a36Sopenharmony_ci } else { 117962306a36Sopenharmony_ci __dma_cache_wback_inv = __dma_cache_wback_inv_l1; 118062306a36Sopenharmony_ci __dma_cache_inv = __dma_cache_inv_l1; 118162306a36Sopenharmony_ci __dma_cache_wback = __dma_cache_wback_l1; 118262306a36Sopenharmony_ci } 118362306a36Sopenharmony_ci /* 118462306a36Sopenharmony_ci * In case of IOC (say IOC+SLC case), pointers above could still be set 118562306a36Sopenharmony_ci * but end up not being relevant as the first function in chain is not 118662306a36Sopenharmony_ci * called at all for devices using coherent DMA. 118762306a36Sopenharmony_ci * arch_sync_dma_for_cpu() -> dma_cache_*() -> __dma_cache_*() 118862306a36Sopenharmony_ci */ 118962306a36Sopenharmony_ci} 119062306a36Sopenharmony_ci 119162306a36Sopenharmony_civoid __ref arc_cache_init(void) 119262306a36Sopenharmony_ci{ 119362306a36Sopenharmony_ci unsigned int __maybe_unused cpu = smp_processor_id(); 119462306a36Sopenharmony_ci 119562306a36Sopenharmony_ci if (!cpu) 119662306a36Sopenharmony_ci arc_cache_init_master(); 119762306a36Sopenharmony_ci 119862306a36Sopenharmony_ci /* 119962306a36Sopenharmony_ci * In PAE regime, TLB and cache maintenance ops take wider addresses 120062306a36Sopenharmony_ci * And even if PAE is not enabled in kernel, the upper 32-bits still need 120162306a36Sopenharmony_ci * to be zeroed to keep the ops sane. 120262306a36Sopenharmony_ci * As an optimization for more common !PAE enabled case, zero them out 120362306a36Sopenharmony_ci * once at init, rather than checking/setting to 0 for every runtime op 120462306a36Sopenharmony_ci */ 120562306a36Sopenharmony_ci if (is_isa_arcv2() && pae40_exist_but_not_enab()) { 120662306a36Sopenharmony_ci 120762306a36Sopenharmony_ci if (IS_ENABLED(CONFIG_ARC_HAS_ICACHE)) 120862306a36Sopenharmony_ci write_aux_reg(ARC_REG_IC_PTAG_HI, 0); 120962306a36Sopenharmony_ci 121062306a36Sopenharmony_ci if (IS_ENABLED(CONFIG_ARC_HAS_DCACHE)) 121162306a36Sopenharmony_ci write_aux_reg(ARC_REG_DC_PTAG_HI, 0); 121262306a36Sopenharmony_ci 121362306a36Sopenharmony_ci if (l2_line_sz) { 121462306a36Sopenharmony_ci write_aux_reg(ARC_REG_SLC_RGN_END1, 0); 121562306a36Sopenharmony_ci write_aux_reg(ARC_REG_SLC_RGN_START1, 0); 121662306a36Sopenharmony_ci } 121762306a36Sopenharmony_ci } 121862306a36Sopenharmony_ci} 1219