Line data Source code
1 0 : /* 2 : * Copyright 2022 Carlo Caione <ccaione@baylibre.com> 3 : * 4 : * SPDX-License-Identifier: Apache-2.0 5 : */ 6 : #ifndef ZEPHYR_INCLUDE_ARCH_ARM64_CACHE_H_ 7 : #define ZEPHYR_INCLUDE_ARCH_ARM64_CACHE_H_ 8 : 9 : #ifndef _ASMLANGUAGE 10 : 11 : #include <zephyr/types.h> 12 : #include <zephyr/sys/util.h> 13 : #include <zephyr/sys/barrier.h> 14 : #include <zephyr/arch/cpu.h> 15 : #include <errno.h> 16 : 17 : #ifdef __cplusplus 18 : extern "C" { 19 : #endif 20 : 21 0 : #define K_CACHE_WB BIT(0) 22 0 : #define K_CACHE_INVD BIT(1) 23 0 : #define K_CACHE_WB_INVD (K_CACHE_WB | K_CACHE_INVD) 24 : 25 : #if defined(CONFIG_DCACHE) 26 : 27 : #define CTR_EL0_DMINLINE_SHIFT 16 28 : #define CTR_EL0_DMINLINE_MASK BIT_MASK(4) 29 : #define CTR_EL0_CWG_SHIFT 24 30 : #define CTR_EL0_CWG_MASK BIT_MASK(4) 31 : 32 : /* clidr_el1 */ 33 : #define CLIDR_EL1_LOC_SHIFT 24 34 : #define CLIDR_EL1_LOC_MASK BIT_MASK(3) 35 : #define CLIDR_EL1_CTYPE_SHIFT(level) ((level) * 3) 36 : #define CLIDR_EL1_CTYPE_MASK BIT_MASK(3) 37 : 38 : /* ccsidr_el1 */ 39 : #define CCSIDR_EL1_LN_SZ_SHIFT 0 40 : #define CCSIDR_EL1_LN_SZ_MASK BIT_MASK(3) 41 : #define CCSIDR_EL1_WAYS_SHIFT 3 42 : #define CCSIDR_EL1_WAYS_MASK BIT_MASK(10) 43 : #define CCSIDR_EL1_SETS_SHIFT 13 44 : #define CCSIDR_EL1_SETS_MASK BIT_MASK(15) 45 : 46 : #define dc_ops(op, val) \ 47 : ({ \ 48 : __asm__ volatile ("dc " op ", %0" :: "r" (val) : "memory"); \ 49 : }) 50 : 51 : static size_t dcache_line_size; 52 : 53 : static ALWAYS_INLINE size_t arch_dcache_line_size_get(void) 54 : { 55 : uint64_t ctr_el0; 56 : uint32_t dminline; 57 : 58 : if (dcache_line_size) { 59 : return dcache_line_size; 60 : } 61 : 62 : ctr_el0 = read_sysreg(CTR_EL0); 63 : 64 : dminline = (ctr_el0 >> CTR_EL0_DMINLINE_SHIFT) & CTR_EL0_DMINLINE_MASK; 65 : 66 : dcache_line_size = 4 << dminline; 67 : 68 : return dcache_line_size; 69 : } 70 : 71 : /* 72 : * operation for data cache by virtual address to PoC 73 : * ops: K_CACHE_INVD: invalidate 74 : * K_CACHE_WB: clean 75 : * K_CACHE_WB_INVD: clean and invalidate 76 : */ 77 : static ALWAYS_INLINE int arm64_dcache_range(void *addr, size_t size, int op) 78 : { 79 : size_t line_size; 80 : uintptr_t start_addr = (uintptr_t)addr; 81 : uintptr_t end_addr = start_addr + size; 82 : 83 : if (op != K_CACHE_INVD && op != K_CACHE_WB && op != K_CACHE_WB_INVD) { 84 : return -ENOTSUP; 85 : } 86 : 87 : line_size = arch_dcache_line_size_get(); 88 : 89 : /* 90 : * For the data cache invalidate operation, clean and invalidate 91 : * the partial cache lines at both ends of the given range to 92 : * prevent data corruption. 93 : * 94 : * For example (assume cache line size is 64 bytes): 95 : * There are 2 consecutive 32-byte buffers, which can be cached in 96 : * one line like below. 97 : * +------------------+------------------+ 98 : * Cache line: | buffer 0 (dirty) | buffer 1 | 99 : * +------------------+------------------+ 100 : * For the start address not aligned case, when invalidate the 101 : * buffer 1, the full cache line will be invalidated, if the buffer 102 : * 0 is dirty, its data will be lost. 103 : * The same logic applies to the not aligned end address. 104 : */ 105 : if (op == K_CACHE_INVD) { 106 : if (end_addr & (line_size - 1)) { 107 : end_addr &= ~(line_size - 1); 108 : dc_ops("civac", end_addr); 109 : } 110 : 111 : if (start_addr & (line_size - 1)) { 112 : start_addr &= ~(line_size - 1); 113 : if (start_addr == end_addr) { 114 : goto done; 115 : } 116 : dc_ops("civac", start_addr); 117 : start_addr += line_size; 118 : } 119 : } 120 : 121 : /* Align address to line size */ 122 : start_addr &= ~(line_size - 1); 123 : 124 : while (start_addr < end_addr) { 125 : if (op == K_CACHE_INVD) { 126 : dc_ops("ivac", start_addr); 127 : } else if (op == K_CACHE_WB) { 128 : dc_ops("cvac", start_addr); 129 : } else if (op == K_CACHE_WB_INVD) { 130 : dc_ops("civac", start_addr); 131 : } 132 : 133 : start_addr += line_size; 134 : } 135 : 136 : done: 137 : barrier_dsync_fence_full(); 138 : 139 : return 0; 140 : } 141 : 142 : static ALWAYS_INLINE int arch_dcache_flush_all(void) 143 : { 144 : return -ENOTSUP; 145 : } 146 : 147 : static ALWAYS_INLINE int arch_dcache_invd_all(void) 148 : { 149 : return -ENOTSUP; 150 : } 151 : 152 : static ALWAYS_INLINE int arch_dcache_flush_and_invd_all(void) 153 : { 154 : return -ENOTSUP; 155 : } 156 : 157 : static ALWAYS_INLINE int arch_dcache_flush_range(void *addr, size_t size) 158 : { 159 : return arm64_dcache_range(addr, size, K_CACHE_WB); 160 : } 161 : 162 : static ALWAYS_INLINE int arch_dcache_invd_range(void *addr, size_t size) 163 : { 164 : return arm64_dcache_range(addr, size, K_CACHE_INVD); 165 : } 166 : 167 : static ALWAYS_INLINE int arch_dcache_flush_and_invd_range(void *addr, size_t size) 168 : { 169 : return arm64_dcache_range(addr, size, K_CACHE_WB_INVD); 170 : } 171 : 172 : static ALWAYS_INLINE void arch_dcache_enable(void) 173 : { 174 : /* nothing */ 175 : } 176 : 177 : static ALWAYS_INLINE void arch_dcache_disable(void) 178 : { 179 : /* nothing */ 180 : } 181 : 182 : #endif /* CONFIG_DCACHE */ 183 : 184 : #if defined(CONFIG_ICACHE) 185 : 186 : static ALWAYS_INLINE size_t arch_icache_line_size_get(void) 187 : { 188 : return -ENOTSUP; 189 : } 190 : 191 : static ALWAYS_INLINE int arch_icache_flush_all(void) 192 : { 193 : return -ENOTSUP; 194 : } 195 : 196 : static ALWAYS_INLINE int arch_icache_invd_all(void) 197 : { 198 : return -ENOTSUP; 199 : } 200 : 201 : static ALWAYS_INLINE int arch_icache_flush_and_invd_all(void) 202 : { 203 : return -ENOTSUP; 204 : } 205 : 206 : static ALWAYS_INLINE int arch_icache_flush_range(void *addr, size_t size) 207 : { 208 : ARG_UNUSED(addr); 209 : ARG_UNUSED(size); 210 : return -ENOTSUP; 211 : } 212 : 213 : static ALWAYS_INLINE int arch_icache_invd_range(void *addr, size_t size) 214 : { 215 : ARG_UNUSED(addr); 216 : ARG_UNUSED(size); 217 : return -ENOTSUP; 218 : } 219 : 220 : static ALWAYS_INLINE int arch_icache_flush_and_invd_range(void *addr, size_t size) 221 : { 222 : ARG_UNUSED(addr); 223 : ARG_UNUSED(size); 224 : return -ENOTSUP; 225 : } 226 : 227 : static ALWAYS_INLINE void arch_icache_enable(void) 228 : { 229 : /* nothing */ 230 : } 231 : 232 : static ALWAYS_INLINE void arch_icache_disable(void) 233 : { 234 : /* nothing */ 235 : } 236 : 237 : #endif /* CONFIG_ICACHE */ 238 : 239 0 : static ALWAYS_INLINE void arch_cache_init(void) 240 : { 241 : } 242 : 243 : #ifdef __cplusplus 244 : } 245 : #endif 246 : 247 : #endif /* _ASMLANGUAGE */ 248 : 249 : #endif /* ZEPHYR_INCLUDE_ARCH_ARM64_CACHE_H_ */