Line data Source code
1 0 : /*
2 : * Copyright 2021 Intel Corporation
3 : * SPDX-License-Identifier: Apache-2.0
4 : */
5 : #ifndef ZEPHYR_INCLUDE_ARCH_XTENSA_CACHE_H_
6 : #define ZEPHYR_INCLUDE_ARCH_XTENSA_CACHE_H_
7 :
8 : #include <xtensa/config/core-isa.h>
9 : #include <zephyr/toolchain.h>
10 : #include <zephyr/sys/util.h>
11 : #include <zephyr/debug/sparse.h>
12 : #include <xtensa/hal.h>
13 :
14 : #ifdef __cplusplus
15 : extern "C" {
16 : #endif
17 :
18 : #define Z_DCACHE_MAX (XCHAL_DCACHE_SIZE / XCHAL_DCACHE_WAYS)
19 :
20 : #if XCHAL_DCACHE_SIZE
21 : BUILD_ASSERT(Z_IS_POW2(XCHAL_DCACHE_LINESIZE));
22 : BUILD_ASSERT(Z_IS_POW2(Z_DCACHE_MAX));
23 : #endif
24 :
25 : #if defined(CONFIG_DCACHE) || defined(__DOXYGEN__)
26 :
27 : /** Implementation of @ref arch_dcache_flush_range. */
28 1 : static ALWAYS_INLINE int arch_dcache_flush_range(void *addr, size_t bytes)
29 : {
30 : #if XCHAL_DCACHE_SIZE
31 : size_t step = XCHAL_DCACHE_LINESIZE;
32 : size_t first = ROUND_DOWN(addr, step);
33 : size_t last = ROUND_UP(((long)addr) + bytes, step);
34 : size_t line;
35 :
36 : for (line = first; bytes && line < last; line += step) {
37 : __asm__ volatile("dhwb %0, 0" :: "r"(line));
38 : }
39 : #endif
40 : return 0;
41 : }
42 :
43 : /** Implementation of @ref arch_dcache_flush_and_invd_range. */
44 1 : static ALWAYS_INLINE int arch_dcache_flush_and_invd_range(void *addr, size_t bytes)
45 : {
46 : #if XCHAL_DCACHE_SIZE
47 : size_t step = XCHAL_DCACHE_LINESIZE;
48 : size_t first = ROUND_DOWN(addr, step);
49 : size_t last = ROUND_UP(((long)addr) + bytes, step);
50 : size_t line;
51 :
52 : for (line = first; bytes && line < last; line += step) {
53 : __asm__ volatile("dhwbi %0, 0" :: "r"(line));
54 : }
55 : #endif
56 : return 0;
57 : }
58 :
59 : /** Implementation of @ref arch_dcache_invd_range. */
60 1 : static ALWAYS_INLINE int arch_dcache_invd_range(void *addr, size_t bytes)
61 : {
62 : #if XCHAL_DCACHE_SIZE
63 : size_t step = XCHAL_DCACHE_LINESIZE;
64 : size_t first = ROUND_DOWN(addr, step);
65 : size_t last = ROUND_UP(((long)addr) + bytes, step);
66 : size_t line;
67 :
68 : for (line = first; bytes && line < last; line += step) {
69 : __asm__ volatile("dhi %0, 0" :: "r"(line));
70 : }
71 : #endif
72 : return 0;
73 : }
74 :
75 : /** Implementation of @ref arch_dcache_invd_all. */
76 1 : static ALWAYS_INLINE int arch_dcache_invd_all(void)
77 : {
78 : #if XCHAL_DCACHE_SIZE
79 : size_t step = XCHAL_DCACHE_LINESIZE;
80 : size_t line;
81 :
82 : for (line = 0; line < XCHAL_DCACHE_SIZE; line += step) {
83 : __asm__ volatile("dii %0, 0" :: "r"(line));
84 : }
85 : #endif
86 : return 0;
87 : }
88 :
89 : /** Implementation of @ref arch_dcache_flush_all. */
90 1 : static ALWAYS_INLINE int arch_dcache_flush_all(void)
91 : {
92 : #if XCHAL_DCACHE_SIZE
93 : size_t step = XCHAL_DCACHE_LINESIZE;
94 : size_t line;
95 :
96 : for (line = 0; line < XCHAL_DCACHE_SIZE; line += step) {
97 : __asm__ volatile("diwb %0, 0" :: "r"(line));
98 : }
99 : #endif
100 : return 0;
101 : }
102 :
103 : /** Implementation of @ref arch_dcache_flush_and_invd_all. */
104 1 : static ALWAYS_INLINE int arch_dcache_flush_and_invd_all(void)
105 : {
106 : #if XCHAL_DCACHE_SIZE
107 : size_t step = XCHAL_DCACHE_LINESIZE;
108 : size_t line;
109 :
110 : for (line = 0; line < XCHAL_DCACHE_SIZE; line += step) {
111 : __asm__ volatile("diwbi %0, 0" :: "r"(line));
112 : }
113 : #endif
114 : return 0;
115 : }
116 :
117 : /** Implementation of @ref arch_dcache_enable. */
118 1 : static ALWAYS_INLINE void arch_dcache_enable(void)
119 : {
120 : /* nothing */
121 : }
122 :
123 : /** Implementation of @ref arch_dcache_disable. */
124 1 : static ALWAYS_INLINE void arch_dcache_disable(void)
125 : {
126 : /* nothing */
127 : }
128 :
129 : #endif /* CONFIG_DCACHE */
130 :
131 : #if defined(CONFIG_ICACHE) || defined(__DOXYGEN__)
132 :
133 : /** Implementation of @ref arch_icache_line_size_get. */
134 1 : static ALWAYS_INLINE size_t arch_icache_line_size_get(void)
135 : {
136 : return -ENOTSUP;
137 : }
138 :
139 : /** Implementation of @ref arch_icache_flush_all. */
140 1 : static ALWAYS_INLINE int arch_icache_flush_all(void)
141 : {
142 : return -ENOTSUP;
143 : }
144 :
145 : /** Implementation of @ref arch_icache_invd_all. */
146 1 : static ALWAYS_INLINE int arch_icache_invd_all(void)
147 : {
148 : #if XCHAL_ICACHE_SIZE
149 : xthal_icache_all_invalidate();
150 : #endif
151 : return 0;
152 : }
153 :
154 : /** Implementation of @ref arch_icache_flush_and_invd_all. */
155 1 : static ALWAYS_INLINE int arch_icache_flush_and_invd_all(void)
156 : {
157 : return -ENOTSUP;
158 : }
159 :
160 : /** Implementation of @ref arch_icache_flush_range. */
161 1 : static ALWAYS_INLINE int arch_icache_flush_range(void *addr, size_t size)
162 : {
163 : return -ENOTSUP;
164 : }
165 :
166 : /** Implementation of @ref arch_icache_invd_range. */
167 1 : static ALWAYS_INLINE int arch_icache_invd_range(void *addr, size_t size)
168 : {
169 : #if XCHAL_ICACHE_SIZE
170 : xthal_icache_region_invalidate(addr, size);
171 : #endif
172 : return 0;
173 : }
174 :
175 : /** Implementation of @ref arch_icache_flush_and_invd_range. */
176 1 : static ALWAYS_INLINE int arch_icache_flush_and_invd_range(void *addr, size_t size)
177 : {
178 : return -ENOTSUP;
179 : }
180 :
181 : /** Implementation of @ref arch_icache_enable. */
182 1 : static ALWAYS_INLINE void arch_icache_enable(void)
183 : {
184 : /* nothing */
185 : }
186 :
187 : /** Implementation of @ref arch_icache_disable. */
188 1 : static ALWAYS_INLINE void arch_icache_disable(void)
189 : {
190 : /* nothing */
191 : }
192 :
193 : #endif /* CONFIG_ICACHE */
194 :
195 : #if defined(CONFIG_CACHE_DOUBLEMAP)
196 : /**
197 : * @brief Test if a pointer is in cached region.
198 : *
199 : * Some hardware may map the same physical memory twice
200 : * so that it can be seen in both (incoherent) cached mappings
201 : * and a coherent "shared" area. This tests if a particular
202 : * pointer is within the cached, coherent area.
203 : *
204 : * @param ptr Pointer
205 : *
206 : * @retval True if pointer is in cached region.
207 : * @retval False if pointer is not in cached region.
208 : */
209 : static inline bool arch_cache_is_ptr_cached(void *ptr)
210 : {
211 : size_t addr = (size_t) ptr;
212 :
213 : return (addr >> 29) == CONFIG_XTENSA_CACHED_REGION;
214 : }
215 :
216 : /**
217 : * @brief Test if a pointer is in un-cached region.
218 : *
219 : * Some hardware may map the same physical memory twice
220 : * so that it can be seen in both (incoherent) cached mappings
221 : * and a coherent "shared" area. This tests if a particular
222 : * pointer is within the un-cached, incoherent area.
223 : *
224 : * @param ptr Pointer
225 : *
226 : * @retval True if pointer is not in cached region.
227 : * @retval False if pointer is in cached region.
228 : */
229 : static inline bool arch_cache_is_ptr_uncached(void *ptr)
230 : {
231 : size_t addr = (size_t) ptr;
232 :
233 : return (addr >> 29) == CONFIG_XTENSA_UNCACHED_REGION;
234 : }
235 :
236 : static ALWAYS_INLINE uint32_t z_xtrpoflip(uint32_t addr, uint32_t rto, uint32_t rfrom)
237 : {
238 : /* The math here is all compile-time: when the two regions
239 : * differ by a power of two, we can convert between them by
240 : * setting or clearing just one bit. Otherwise it needs two
241 : * operations.
242 : */
243 : uint32_t rxor = (rto ^ rfrom) << 29;
244 :
245 : rto <<= 29;
246 : if (Z_IS_POW2(rxor)) {
247 : if ((rxor & rto) == 0) {
248 : return addr & ~rxor;
249 : } else {
250 : return addr | rxor;
251 : }
252 : } else {
253 : return (addr & ~(7U << 29)) | rto;
254 : }
255 : }
256 :
257 : /**
258 : * @brief Return cached pointer to a RAM address
259 : *
260 : * The Xtensa coherence architecture maps addressable RAM twice, in
261 : * two different 512MB regions whose L1 cache settings can be
262 : * controlled independently. So for any given pointer, it is possible
263 : * to convert it to and from a cached version.
264 : *
265 : * This function takes a pointer to any addressable object (either in
266 : * cacheable memory or not) and returns a pointer that can be used to
267 : * refer to the same memory through the L1 data cache. Data read
268 : * through the resulting pointer will reflect locally cached values on
269 : * the current CPU if they exist, and writes will go first into the
270 : * cache and be written back later.
271 : *
272 : * @see arch_uncached_ptr()
273 : *
274 : * @param ptr A pointer to a valid C object
275 : * @return A pointer to the same object via the L1 dcache
276 : */
277 : static inline void __sparse_cache *arch_cache_cached_ptr_get(void *ptr)
278 : {
279 : return (__sparse_force void __sparse_cache *)z_xtrpoflip((uint32_t) ptr,
280 : CONFIG_XTENSA_CACHED_REGION,
281 : CONFIG_XTENSA_UNCACHED_REGION);
282 : }
283 :
284 : /**
285 : * @brief Return uncached pointer to a RAM address
286 : *
287 : * The Xtensa coherence architecture maps addressable RAM twice, in
288 : * two different 512MB regions whose L1 cache settings can be
289 : * controlled independently. So for any given pointer, it is possible
290 : * to convert it to and from a cached version.
291 : *
292 : * This function takes a pointer to any addressable object (either in
293 : * cacheable memory or not) and returns a pointer that can be used to
294 : * refer to the same memory while bypassing the L1 data cache. Data
295 : * in the L1 cache will not be inspected nor modified by the access.
296 : *
297 : * @see arch_cached_ptr()
298 : *
299 : * @param ptr A pointer to a valid C object
300 : * @return A pointer to the same object bypassing the L1 dcache
301 : */
302 : static inline void *arch_cache_uncached_ptr_get(void __sparse_cache *ptr)
303 : {
304 : return (void *)z_xtrpoflip((__sparse_force uint32_t)ptr,
305 : CONFIG_XTENSA_UNCACHED_REGION,
306 : CONFIG_XTENSA_CACHED_REGION);
307 : }
308 : #else
309 0 : static inline bool arch_cache_is_ptr_cached(void *ptr)
310 : {
311 : ARG_UNUSED(ptr);
312 :
313 : return false;
314 : }
315 :
316 0 : static inline bool arch_cache_is_ptr_uncached(void *ptr)
317 : {
318 : ARG_UNUSED(ptr);
319 :
320 : return false;
321 : }
322 :
323 0 : static inline void *arch_cache_cached_ptr_get(void *ptr)
324 : {
325 : return ptr;
326 : }
327 :
328 0 : static inline void *arch_cache_uncached_ptr_get(void *ptr)
329 : {
330 : return ptr;
331 : }
332 : #endif
333 :
334 0 : static ALWAYS_INLINE void arch_cache_init(void)
335 : {
336 : }
337 :
338 :
339 : #ifdef __cplusplus
340 : } /* extern "C" */
341 : #endif
342 :
343 : #endif /* ZEPHYR_INCLUDE_ARCH_XTENSA_CACHE_H_ */
|