Zephyr Project API 4.5.0-rc2
A Scalable Open Source RTOS
Loading...
Searching...
No Matches
cache.h
Go to the documentation of this file.
1/*
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
18extern "C" {
19#endif
20
21#define K_CACHE_WB BIT(0)
22#define K_CACHE_INVD BIT(1)
23#define K_CACHE_WB_INVD (K_CACHE_WB | K_CACHE_INVD)
24
25#define CTR_EL0_DMINLINE_SHIFT 16
26#define CTR_EL0_DMINLINE_MASK BIT_MASK(4)
27#define CTR_EL0_CWG_SHIFT 24
28#define CTR_EL0_CWG_MASK BIT_MASK(4)
29
30/* clidr_el1 */
31#define CLIDR_EL1_LOC_SHIFT 24
32#define CLIDR_EL1_LOC_MASK BIT_MASK(3)
33#define CLIDR_EL1_CTYPE_SHIFT(level) ((level) * 3)
34#define CLIDR_EL1_CTYPE_MASK BIT_MASK(3)
35
36/* ccsidr_el1 */
37#define CCSIDR_EL1_LN_SZ_SHIFT 0
38#define CCSIDR_EL1_LN_SZ_MASK BIT_MASK(3)
39#define CCSIDR_EL1_WAYS_SHIFT 3
40#define CCSIDR_EL1_WAYS_MASK BIT_MASK(10)
41#define CCSIDR_EL1_SETS_SHIFT 13
42#define CCSIDR_EL1_SETS_MASK BIT_MASK(15)
43
44#define dc_ops(op, val) \
45({ \
46 __asm__ volatile ("dc " op ", %0" :: "r" (val) : "memory"); \
47})
48
49static size_t dcache_line_size;
50
52{
53 uint64_t ctr_el0;
54 uint32_t dminline;
55
56 if (dcache_line_size) {
57 return dcache_line_size;
58 }
59
60 ctr_el0 = read_sysreg(CTR_EL0);
61
62 dminline = (ctr_el0 >> CTR_EL0_DMINLINE_SHIFT) & CTR_EL0_DMINLINE_MASK;
63
64 dcache_line_size = 4 << dminline;
65
66 return dcache_line_size;
67}
68
69/*
70 * operation for data cache by virtual address to PoC
71 * ops: K_CACHE_INVD: invalidate
72 * K_CACHE_WB: clean
73 * K_CACHE_WB_INVD: clean and invalidate
74 */
75static ALWAYS_INLINE int arm64_dcache_range(void *addr, size_t size, int op)
76{
77 size_t line_size;
78 uintptr_t start_addr = (uintptr_t)addr;
79 uintptr_t end_addr = start_addr + size;
80
81 if (op != K_CACHE_INVD && op != K_CACHE_WB && op != K_CACHE_WB_INVD) {
82 return -ENOTSUP;
83 }
84
85 line_size = arch_dcache_line_size_get();
86
87 /*
88 * For the data cache invalidate operation, clean and invalidate
89 * the partial cache lines at both ends of the given range to
90 * prevent data corruption.
91 *
92 * For example (assume cache line size is 64 bytes):
93 * There are 2 consecutive 32-byte buffers, which can be cached in
94 * one line like below.
95 * +------------------+------------------+
96 * Cache line: | buffer 0 (dirty) | buffer 1 |
97 * +------------------+------------------+
98 * For the start address not aligned case, when invalidate the
99 * buffer 1, the full cache line will be invalidated, if the buffer
100 * 0 is dirty, its data will be lost.
101 * The same logic applies to the not aligned end address.
102 */
103 if (op == K_CACHE_INVD) {
104 if (end_addr & (line_size - 1)) {
105 end_addr &= ~(line_size - 1);
106 dc_ops("civac", end_addr);
107 }
108
109 if (start_addr & (line_size - 1)) {
110 start_addr &= ~(line_size - 1);
111 if (start_addr == end_addr) {
112 goto done;
113 }
114 dc_ops("civac", start_addr);
115 start_addr += line_size;
116 }
117 }
118
119 /* Align address to line size */
120 start_addr &= ~(line_size - 1);
121
122 while (start_addr < end_addr) {
123 if (op == K_CACHE_INVD) {
124 dc_ops("ivac", start_addr);
125 } else if (op == K_CACHE_WB) {
126 dc_ops("cvac", start_addr);
127 } else if (op == K_CACHE_WB_INVD) {
128 dc_ops("civac", start_addr);
129 }
130
131 start_addr += line_size;
132 }
133
134done:
136
137 return 0;
138}
139
140#ifdef CONFIG_ARM64_DCACHE_ALL_OPS
141
142/*
143 * operation for all data cache
144 * ops: K_CACHE_INVD: invalidate
145 * K_CACHE_WB: clean
146 * K_CACHE_WB_INVD: clean and invalidate
147 */
148static ALWAYS_INLINE int arm64_dcache_all(int op)
149{
150 uint32_t clidr_el1, csselr_el1, ccsidr_el1;
151 uint8_t loc, ctype, cache_level, line_size, way_pos;
152 uint32_t max_ways, max_sets, set, way;
153 uint64_t dc_val;
154
155 if (op != K_CACHE_INVD && op != K_CACHE_WB && op != K_CACHE_WB_INVD) {
156 return -ENOTSUP;
157 }
158
159 /* Data barrier before start */
161
162 clidr_el1 = read_clidr_el1();
163
164 loc = (clidr_el1 >> CLIDR_EL1_LOC_SHIFT) & CLIDR_EL1_LOC_MASK;
165 if (!loc) {
166 return 0;
167 }
168
169 for (cache_level = 0; cache_level < loc; cache_level++) {
170 ctype = (clidr_el1 >> CLIDR_EL1_CTYPE_SHIFT(cache_level)) & CLIDR_EL1_CTYPE_MASK;
171 /* No data cache, continue */
172 if (ctype < 2) {
173 continue;
174 }
175
176 /* select cache level */
177 csselr_el1 = cache_level << 1;
178 write_csselr_el1(csselr_el1);
180
181 ccsidr_el1 = read_ccsidr_el1();
182 line_size = (ccsidr_el1 >> CCSIDR_EL1_LN_SZ_SHIFT & CCSIDR_EL1_LN_SZ_MASK) + 4;
183 max_ways = (ccsidr_el1 >> CCSIDR_EL1_WAYS_SHIFT) & CCSIDR_EL1_WAYS_MASK;
184 max_sets = (ccsidr_el1 >> CCSIDR_EL1_SETS_SHIFT) & CCSIDR_EL1_SETS_MASK;
185 /* 32-log2(ways), bit position of way in DC operand */
186 way_pos = __builtin_clz(max_ways);
187
188 for (set = 0; set <= max_sets; set++) {
189 for (way = 0; way <= max_ways; way++) {
190 /* way number, aligned to pos in DC operand */
191 dc_val = way << way_pos;
192 /* cache level, aligned to pos in DC operand */
193 dc_val |= csselr_el1;
194 /* set number, aligned to pos in DC operand */
195 dc_val |= set << line_size;
196
197 if (op == K_CACHE_INVD) {
198 dc_ops("isw", dc_val);
199 } else if (op == K_CACHE_WB_INVD) {
200 dc_ops("cisw", dc_val);
201 } else if (op == K_CACHE_WB) {
202 dc_ops("csw", dc_val);
203 }
204 }
205 }
206 }
207
208 /* Restore csselr_el1 to level 0 */
209 write_csselr_el1(0);
212
213 return 0;
214}
215
217{
218 return arm64_dcache_all(K_CACHE_WB);
219}
220
221static ALWAYS_INLINE int arch_dcache_invd_all(void)
222{
223 return arm64_dcache_all(K_CACHE_INVD);
224}
225
227{
228 return arm64_dcache_all(K_CACHE_WB_INVD);
229}
230
231#else
232
234{
235 return -ENOTSUP;
236}
237
239{
240 return -ENOTSUP;
241}
242
244{
245 return -ENOTSUP;
246}
247
248#endif /* CONFIG_ARM64_DCACHE_ALL_OPS */
249
250static ALWAYS_INLINE int arch_dcache_flush_range(void *addr, size_t size)
251{
252 return arm64_dcache_range(addr, size, K_CACHE_WB);
253}
254
255static ALWAYS_INLINE int arch_dcache_invd_range(void *addr, size_t size)
256{
257 return arm64_dcache_range(addr, size, K_CACHE_INVD);
258}
259
260static ALWAYS_INLINE int arch_dcache_flush_and_invd_range(void *addr, size_t size)
261{
262 return arm64_dcache_range(addr, size, K_CACHE_WB_INVD);
263}
264
266{
267 /* nothing */
268}
269
271{
272 /* nothing */
273}
274
275#if defined(CONFIG_ICACHE)
276
277#define CTR_EL0_IMINLINE_MASK BIT_MASK(4)
278
279#define ic_va_ops(op, val) \
280({ \
281 __asm__ volatile ("ic " op ", %0" :: "r" (val) : "memory"); \
282})
283
284static size_t icache_line_size;
285
287{
288 uint64_t ctr_el0;
289 uint32_t iminline;
290
291 if (icache_line_size) {
292 return icache_line_size;
293 }
294
295 ctr_el0 = read_sysreg(CTR_EL0);
296
297 iminline = ctr_el0 & CTR_EL0_IMINLINE_MASK;
298
299 icache_line_size = 4 << iminline;
300
301 return icache_line_size;
302}
303
304/* An I-cache holds no dirty data: "flush" is meaningless, so only the
305 * invalidate operations exist. Callers modifying code must clean the
306 * D-side to the point of unification before invalidating here.
307 */
309{
310 return -ENOTSUP;
311}
312
313static ALWAYS_INLINE int arch_icache_invd_all(void)
314{
315 __asm__ volatile ("ic ialluis" ::: "memory");
318
319 return 0;
320}
321
323{
324 return arch_icache_invd_all();
325}
326
327static ALWAYS_INLINE int arch_icache_flush_range(void *addr, size_t size)
328{
329 ARG_UNUSED(addr);
330 ARG_UNUSED(size);
331 return -ENOTSUP;
332}
333
334static ALWAYS_INLINE int arch_icache_invd_range(void *addr, size_t size)
335{
336 size_t line_size;
337 uintptr_t start_addr = (uintptr_t)addr;
338 uintptr_t end_addr = start_addr + size;
339
340 line_size = arch_icache_line_size_get();
341
342 start_addr &= ~(line_size - 1);
343
344 while (start_addr < end_addr) {
345 ic_va_ops("ivau", start_addr);
346 start_addr += line_size;
347 }
348
351
352 return 0;
353}
354
355static ALWAYS_INLINE int arch_icache_flush_and_invd_range(void *addr, size_t size)
356{
357 return arch_icache_invd_range(addr, size);
358}
359
360static ALWAYS_INLINE void arch_icache_enable(void)
361{
362 /* nothing */
363}
364
365static ALWAYS_INLINE void arch_icache_disable(void)
366{
367 /* nothing */
368}
369
370#endif /* CONFIG_ICACHE */
371
373{
374}
375
376#ifdef __cplusplus
377}
378#endif
379
380#endif /* _ASMLANGUAGE */
381
382#endif /* ZEPHYR_INCLUDE_ARCH_ARM64_CACHE_H_ */
#define CTR_EL0_DMINLINE_SHIFT
Definition cache.h:25
static ALWAYS_INLINE void arch_cache_init(void)
Definition cache.h:372
static ALWAYS_INLINE void arch_dcache_enable(void)
Definition cache.h:265
static ALWAYS_INLINE size_t arch_dcache_line_size_get(void)
Definition cache.h:51
static ALWAYS_INLINE int arch_dcache_flush_range(void *addr, size_t size)
Definition cache.h:250
static ALWAYS_INLINE int arch_dcache_flush_all(void)
Definition cache.h:233
#define CLIDR_EL1_LOC_SHIFT
Definition cache.h:31
static ALWAYS_INLINE int arm64_dcache_range(void *addr, size_t size, int op)
Definition cache.h:75
#define CCSIDR_EL1_WAYS_MASK
Definition cache.h:40
#define K_CACHE_INVD
Definition cache.h:22
static ALWAYS_INLINE int arch_dcache_flush_and_invd_all(void)
Definition cache.h:243
#define CLIDR_EL1_CTYPE_MASK
Definition cache.h:34
#define K_CACHE_WB
Definition cache.h:21
#define CCSIDR_EL1_SETS_MASK
Definition cache.h:42
#define CLIDR_EL1_LOC_MASK
Definition cache.h:32
#define dc_ops(op, val)
Definition cache.h:44
static ALWAYS_INLINE int arch_dcache_invd_all(void)
Definition cache.h:238
#define CCSIDR_EL1_SETS_SHIFT
Definition cache.h:41
#define CCSIDR_EL1_LN_SZ_MASK
Definition cache.h:38
#define CTR_EL0_DMINLINE_MASK
Definition cache.h:26
#define CCSIDR_EL1_WAYS_SHIFT
Definition cache.h:39
#define K_CACHE_WB_INVD
Definition cache.h:23
static ALWAYS_INLINE int arch_dcache_invd_range(void *addr, size_t size)
Definition cache.h:255
#define CLIDR_EL1_CTYPE_SHIFT(level)
Definition cache.h:33
static size_t dcache_line_size
Definition cache.h:49
#define CCSIDR_EL1_LN_SZ_SHIFT
Definition cache.h:37
static ALWAYS_INLINE void arch_dcache_disable(void)
Definition cache.h:270
static ALWAYS_INLINE int arch_dcache_flush_and_invd_range(void *addr, size_t size)
Definition cache.h:260
#define read_sysreg(reg)
Definition lib_helpers.h:100
System error numbers.
void arch_icache_disable(void)
Disable the i-cache.
int arch_icache_flush_and_invd_all(void)
Flush and Invalidate the i-cache.
int arch_icache_flush_all(void)
Flush the i-cache.
int arch_icache_invd_all(void)
Invalidate the i-cache.
size_t arch_icache_line_size_get(void)
Get the i-cache line size.
int arch_icache_flush_range(void *addr, size_t size)
Flush an address range in the i-cache.
int arch_icache_invd_range(void *addr, size_t size)
Invalidate an address range in the i-cache.
int arch_icache_flush_and_invd_range(void *addr, size_t size)
Flush and Invalidate an address range in the i-cache.
void arch_icache_enable(void)
Enable the i-cache.
static ALWAYS_INLINE void barrier_isync_fence_full(void)
Full/sequentially-consistent instruction synchronization barrier.
Definition barrier.h:100
static ALWAYS_INLINE void barrier_dsync_fence_full(void)
Full/sequentially-consistent data synchronization barrier.
Definition barrier.h:81
#define ENOTSUP
Unsupported value.
Definition errno.h:115
#define ALWAYS_INLINE
Definition common.h:186
__UINT32_TYPE__ uint32_t
Definition stdint.h:90
__UINT64_TYPE__ uint64_t
Definition stdint.h:91
__UINT8_TYPE__ uint8_t
Definition stdint.h:88
__UINTPTR_TYPE__ uintptr_t
Definition stdint.h:105
Memory barrier operations.
Misc utilities.