1 // SPDX-License-Identifier: GPL-2.0 2 /* 3 * Copyright (C) 2020-2022 Loongson Technology Corporation Limited 4 * 5 * Derived from MIPS: 6 * Copyright (C) 1994 - 2003, 06, 07 by Ralf Baechle (ralf@linux-mips.org) 7 * Copyright (C) 2007 MIPS Technologies, Inc. 8 */ 9 #include <linux/cacheinfo.h> 10 #include <linux/export.h> 11 #include <linux/fs.h> 12 #include <linux/highmem.h> 13 #include <linux/kernel.h> 14 #include <linux/linkage.h> 15 #include <linux/mm.h> 16 #include <linux/sched.h> 17 #include <linux/syscalls.h> 18 19 #include <asm/bootinfo.h> 20 #include <asm/cacheflush.h> 21 #include <asm/cpu.h> 22 #include <asm/cpu-features.h> 23 #include <asm/loongarch.h> 24 #include <asm/numa.h> 25 #include <asm/processor.h> 26 #include <asm/setup.h> 27 28 void cache_error_setup(void) 29 { 30 extern char __weak except_vec_cex; 31 set_merr_handler(0x0, &except_vec_cex, 0x80); 32 } 33 34 /* 35 * LoongArch maintains ICache/DCache coherency by hardware, 36 * we just need "ibar" to avoid instruction hazard here. 37 */ 38 void local_flush_icache_range(unsigned long start, unsigned long end) 39 { 40 asm volatile ("\tibar 0\n"::); 41 } 42 EXPORT_SYMBOL(local_flush_icache_range); 43 44 static void flush_cache_leaf(unsigned int leaf) 45 { 46 int i, j, nr_nodes; 47 uint64_t addr = CSR_DMW0_BASE; 48 struct cache_desc *cdesc = current_cpu_data.cache_leaves + leaf; 49 50 nr_nodes = cache_private(cdesc) ? 1 : loongson_sysconf.nr_nodes; 51 52 do { 53 for (i = 0; i < cdesc->sets; i++) { 54 for (j = 0; j < cdesc->ways; j++) { 55 flush_cache_line(leaf, addr); 56 addr++; 57 } 58 59 addr -= cdesc->ways; 60 addr += cdesc->linesz; 61 } 62 addr += (1ULL << NODE_ADDRSPACE_SHIFT); 63 } while (--nr_nodes > 0); 64 } 65 66 asmlinkage __visible void __flush_cache_all(void) 67 { 68 int leaf; 69 struct cache_desc *cdesc = current_cpu_data.cache_leaves; 70 unsigned int cache_present = current_cpu_data.cache_leaves_present; 71 72 leaf = cache_present - 1; 73 if (cache_inclusive(cdesc + leaf)) { 74 flush_cache_leaf(leaf); 75 return; 76 } 77 78 for (leaf = 0; leaf < cache_present; leaf++) 79 flush_cache_leaf(leaf); 80 } 81 82 #define L1IUPRE (1 << 0) 83 #define L1IUUNIFY (1 << 1) 84 #define L1DPRE (1 << 2) 85 86 #define LXIUPRE (1 << 0) 87 #define LXIUUNIFY (1 << 1) 88 #define LXIUPRIV (1 << 2) 89 #define LXIUINCL (1 << 3) 90 #define LXDPRE (1 << 4) 91 #define LXDPRIV (1 << 5) 92 #define LXDINCL (1 << 6) 93 94 #define populate_cache_properties(cfg0, cdesc, level, leaf) \ 95 do { \ 96 unsigned int cfg1; \ 97 \ 98 cfg1 = read_cpucfg(LOONGARCH_CPUCFG17 + leaf); \ 99 if (level == 1) { \ 100 cdesc->flags |= CACHE_PRIVATE; \ 101 } else { \ 102 if (cfg0 & LXIUPRIV) \ 103 cdesc->flags |= CACHE_PRIVATE; \ 104 if (cfg0 & LXIUINCL) \ 105 cdesc->flags |= CACHE_INCLUSIVE; \ 106 } \ 107 cdesc->level = level; \ 108 cdesc->flags |= CACHE_PRESENT; \ 109 cdesc->ways = ((cfg1 & CPUCFG_CACHE_WAYS_M) >> CPUCFG_CACHE_WAYS) + 1; \ 110 cdesc->sets = 1 << ((cfg1 & CPUCFG_CACHE_SETS_M) >> CPUCFG_CACHE_SETS); \ 111 cdesc->linesz = 1 << ((cfg1 & CPUCFG_CACHE_LSIZE_M) >> CPUCFG_CACHE_LSIZE); \ 112 cdesc++; leaf++; \ 113 } while (0) 114 115 void cpu_cache_init(void) 116 { 117 unsigned int leaf = 0, level = 1; 118 unsigned int config = read_cpucfg(LOONGARCH_CPUCFG16); 119 struct cache_desc *cdesc = current_cpu_data.cache_leaves; 120 121 if (config & L1IUPRE) { 122 if (config & L1IUUNIFY) 123 cdesc->type = CACHE_TYPE_UNIFIED; 124 else 125 cdesc->type = CACHE_TYPE_INST; 126 populate_cache_properties(config, cdesc, level, leaf); 127 } 128 129 if (config & L1DPRE) { 130 cdesc->type = CACHE_TYPE_DATA; 131 populate_cache_properties(config, cdesc, level, leaf); 132 } 133 134 config = config >> 3; 135 for (level = 2; level <= CACHE_LEVEL_MAX; level++) { 136 if (!config) 137 break; 138 139 if (config & LXIUPRE) { 140 if (config & LXIUUNIFY) 141 cdesc->type = CACHE_TYPE_UNIFIED; 142 else 143 cdesc->type = CACHE_TYPE_INST; 144 populate_cache_properties(config, cdesc, level, leaf); 145 } 146 147 if (config & LXDPRE) { 148 cdesc->type = CACHE_TYPE_DATA; 149 populate_cache_properties(config, cdesc, level, leaf); 150 } 151 152 config = config >> 7; 153 } 154 155 BUG_ON(leaf > CACHE_LEAVES_MAX); 156 157 current_cpu_data.cache_leaves_present = leaf; 158 current_cpu_data.options |= LOONGARCH_CPU_PREFETCH; 159 } 160 161 static const pgprot_t protection_map[16] = { 162 [VM_NONE] = __pgprot(_CACHE_CC | _PAGE_USER | 163 _PAGE_PROTNONE | _PAGE_NO_EXEC | 164 _PAGE_NO_READ), 165 [VM_READ] = __pgprot(_CACHE_CC | _PAGE_VALID | 166 _PAGE_USER | _PAGE_PRESENT | 167 _PAGE_NO_EXEC), 168 [VM_WRITE] = __pgprot(_CACHE_CC | _PAGE_VALID | 169 _PAGE_USER | _PAGE_PRESENT | 170 _PAGE_NO_EXEC), 171 [VM_WRITE | VM_READ] = __pgprot(_CACHE_CC | _PAGE_VALID | 172 _PAGE_USER | _PAGE_PRESENT | 173 _PAGE_NO_EXEC), 174 [VM_EXEC] = __pgprot(_CACHE_CC | _PAGE_VALID | 175 _PAGE_USER | _PAGE_PRESENT), 176 [VM_EXEC | VM_READ] = __pgprot(_CACHE_CC | _PAGE_VALID | 177 _PAGE_USER | _PAGE_PRESENT), 178 [VM_EXEC | VM_WRITE] = __pgprot(_CACHE_CC | _PAGE_VALID | 179 _PAGE_USER | _PAGE_PRESENT), 180 [VM_EXEC | VM_WRITE | VM_READ] = __pgprot(_CACHE_CC | _PAGE_VALID | 181 _PAGE_USER | _PAGE_PRESENT), 182 [VM_SHARED] = __pgprot(_CACHE_CC | _PAGE_USER | 183 _PAGE_PROTNONE | _PAGE_NO_EXEC | 184 _PAGE_NO_READ), 185 [VM_SHARED | VM_READ] = __pgprot(_CACHE_CC | _PAGE_VALID | 186 _PAGE_USER | _PAGE_PRESENT | 187 _PAGE_NO_EXEC), 188 [VM_SHARED | VM_WRITE] = __pgprot(_CACHE_CC | _PAGE_VALID | 189 _PAGE_USER | _PAGE_PRESENT | 190 _PAGE_NO_EXEC | _PAGE_WRITE), 191 [VM_SHARED | VM_WRITE | VM_READ] = __pgprot(_CACHE_CC | _PAGE_VALID | 192 _PAGE_USER | _PAGE_PRESENT | 193 _PAGE_NO_EXEC | _PAGE_WRITE), 194 [VM_SHARED | VM_EXEC] = __pgprot(_CACHE_CC | _PAGE_VALID | 195 _PAGE_USER | _PAGE_PRESENT), 196 [VM_SHARED | VM_EXEC | VM_READ] = __pgprot(_CACHE_CC | _PAGE_VALID | 197 _PAGE_USER | _PAGE_PRESENT), 198 [VM_SHARED | VM_EXEC | VM_WRITE] = __pgprot(_CACHE_CC | _PAGE_VALID | 199 _PAGE_USER | _PAGE_PRESENT | 200 _PAGE_WRITE), 201 [VM_SHARED | VM_EXEC | VM_WRITE | VM_READ] = __pgprot(_CACHE_CC | _PAGE_VALID | 202 _PAGE_USER | _PAGE_PRESENT | 203 _PAGE_WRITE) 204 }; 205 DECLARE_VM_GET_PAGE_PROT 206