Merge tag 'u-boot-atmel-fixes-2020.07-a' of https://gitlab.denx.de/u-boot/custodians...
[oweals/u-boot.git] / arch / arm / cpu / armv7m / cache.c
1 // SPDX-License-Identifier: GPL-2.0+
2 /*
3  * Copyright (C) 2017, STMicroelectronics - All Rights Reserved
4  * Author(s): Vikas Manocha, <vikas.manocha@st.com> for STMicroelectronics.
5  */
6
7 #include <common.h>
8 #include <cpu_func.h>
9 #include <errno.h>
10 #include <log.h>
11 #include <asm/armv7m.h>
12 #include <asm/cache.h>
13 #include <asm/io.h>
14 #include <linux/bitops.h>
15
16 /* Cache maintenance operation registers */
17
18 #define V7M_CACHE_REG_ICIALLU           ((u32 *)(V7M_CACHE_MAINT_BASE + 0x00))
19 #define INVAL_ICACHE_POU                0
20 #define V7M_CACHE_REG_ICIMVALU          ((u32 *)(V7M_CACHE_MAINT_BASE + 0x08))
21 #define V7M_CACHE_REG_DCIMVAC           ((u32 *)(V7M_CACHE_MAINT_BASE + 0x0C))
22 #define V7M_CACHE_REG_DCISW             ((u32 *)(V7M_CACHE_MAINT_BASE + 0x10))
23 #define V7M_CACHE_REG_DCCMVAU           ((u32 *)(V7M_CACHE_MAINT_BASE + 0x14))
24 #define V7M_CACHE_REG_DCCMVAC           ((u32 *)(V7M_CACHE_MAINT_BASE + 0x18))
25 #define V7M_CACHE_REG_DCCSW             ((u32 *)(V7M_CACHE_MAINT_BASE + 0x1C))
26 #define V7M_CACHE_REG_DCCIMVAC          ((u32 *)(V7M_CACHE_MAINT_BASE + 0x20))
27 #define V7M_CACHE_REG_DCCISW            ((u32 *)(V7M_CACHE_MAINT_BASE + 0x24))
28 #define WAYS_SHIFT                      30
29 #define SETS_SHIFT                      5
30
31 /* armv7m processor feature registers */
32
33 #define V7M_PROC_REG_CLIDR              ((u32 *)(V7M_PROC_FTR_BASE + 0x00))
34 #define V7M_PROC_REG_CTR                ((u32 *)(V7M_PROC_FTR_BASE + 0x04))
35 #define V7M_PROC_REG_CCSIDR             ((u32 *)(V7M_PROC_FTR_BASE + 0x08))
36 #define MASK_NUM_WAYS                   GENMASK(12, 3)
37 #define MASK_NUM_SETS                   GENMASK(27, 13)
38 #define CLINE_SIZE_MASK                 GENMASK(2, 0)
39 #define NUM_WAYS_SHIFT                  3
40 #define NUM_SETS_SHIFT                  13
41 #define V7M_PROC_REG_CSSELR             ((u32 *)(V7M_PROC_FTR_BASE + 0x0C))
42 #define SEL_I_OR_D                      BIT(0)
43
44 enum cache_type {
45         DCACHE,
46         ICACHE,
47 };
48
49 /* PoU : Point of Unification, Poc: Point of Coherency */
50 enum cache_action {
51         INVALIDATE_POU,         /* i-cache invalidate by address */
52         INVALIDATE_POC,         /* d-cache invalidate by address */
53         INVALIDATE_SET_WAY,     /* d-cache invalidate by sets/ways */
54         FLUSH_POU,              /* d-cache clean by address to the PoU */
55         FLUSH_POC,              /* d-cache clean by address to the PoC */
56         FLUSH_SET_WAY,          /* d-cache clean by sets/ways */
57         FLUSH_INVAL_POC,        /* d-cache clean & invalidate by addr to PoC */
58         FLUSH_INVAL_SET_WAY,    /* d-cache clean & invalidate by set/ways */
59 };
60
61 #if !CONFIG_IS_ENABLED(SYS_DCACHE_OFF)
62 struct dcache_config {
63         u32 ways;
64         u32 sets;
65 };
66
67 static void get_cache_ways_sets(struct dcache_config *cache)
68 {
69         u32 cache_size_id = readl(V7M_PROC_REG_CCSIDR);
70
71         cache->ways = (cache_size_id & MASK_NUM_WAYS) >> NUM_WAYS_SHIFT;
72         cache->sets = (cache_size_id & MASK_NUM_SETS) >> NUM_SETS_SHIFT;
73 }
74
75 /*
76  * Return the io register to perform required cache action like clean or clean
77  * & invalidate by sets/ways.
78  */
79 static u32 *get_action_reg_set_ways(enum cache_action action)
80 {
81         switch (action) {
82         case INVALIDATE_SET_WAY:
83                 return V7M_CACHE_REG_DCISW;
84         case FLUSH_SET_WAY:
85                 return V7M_CACHE_REG_DCCSW;
86         case FLUSH_INVAL_SET_WAY:
87                 return V7M_CACHE_REG_DCCISW;
88         default:
89                 break;
90         };
91
92         return NULL;
93 }
94
95 /*
96  * Return the io register to perform required cache action like clean or clean
97  * & invalidate by adddress or range.
98  */
99 static u32 *get_action_reg_range(enum cache_action action)
100 {
101         switch (action) {
102         case INVALIDATE_POU:
103                 return V7M_CACHE_REG_ICIMVALU;
104         case INVALIDATE_POC:
105                 return V7M_CACHE_REG_DCIMVAC;
106         case FLUSH_POU:
107                 return V7M_CACHE_REG_DCCMVAU;
108         case FLUSH_POC:
109                 return V7M_CACHE_REG_DCCMVAC;
110         case FLUSH_INVAL_POC:
111                 return V7M_CACHE_REG_DCCIMVAC;
112         default:
113                 break;
114         }
115
116         return NULL;
117 }
118
119 static u32 get_cline_size(enum cache_type type)
120 {
121         u32 size;
122
123         if (type == DCACHE)
124                 clrbits_le32(V7M_PROC_REG_CSSELR, BIT(SEL_I_OR_D));
125         else if (type == ICACHE)
126                 setbits_le32(V7M_PROC_REG_CSSELR, BIT(SEL_I_OR_D));
127         /* Make sure cache selection is effective for next memory access */
128         dsb();
129
130         size = readl(V7M_PROC_REG_CCSIDR) & CLINE_SIZE_MASK;
131         /* Size enocoded as 2 less than log(no_of_words_in_cache_line) base 2 */
132         size = 1 << (size + 2);
133         debug("cache line size is %d\n", size);
134
135         return size;
136 }
137
138 /* Perform the action like invalidate/clean on a range of cache addresses */
139 static int action_cache_range(enum cache_action action, u32 start_addr,
140                               int64_t size)
141 {
142         u32 cline_size;
143         u32 *action_reg;
144         enum cache_type type;
145
146         action_reg = get_action_reg_range(action);
147         if (!action_reg)
148                 return -EINVAL;
149         if (action == INVALIDATE_POU)
150                 type = ICACHE;
151         else
152                 type = DCACHE;
153
154         /* Cache line size is minium size for the cache action */
155         cline_size = get_cline_size(type);
156         /* Align start address to cache line boundary */
157         start_addr &= ~(cline_size - 1);
158         debug("total size for cache action = %llx\n", size);
159         do {
160                 writel(start_addr, action_reg);
161                 size -= cline_size;
162                 start_addr += cline_size;
163         } while (size > cline_size);
164
165         /* Make sure cache action is effective for next memory access */
166         dsb();
167         isb();  /* Make sure instruction stream sees it */
168         debug("cache action on range done\n");
169
170         return 0;
171 }
172
173 /* Perform the action like invalidate/clean on all cached addresses */
174 static int action_dcache_all(enum cache_action action)
175 {
176         struct dcache_config cache;
177         u32 *action_reg;
178         int i, j;
179
180         action_reg = get_action_reg_set_ways(action);
181         if (!action_reg)
182                 return -EINVAL;
183
184         clrbits_le32(V7M_PROC_REG_CSSELR, BIT(SEL_I_OR_D));
185         /* Make sure cache selection is effective for next memory access */
186         dsb();
187
188         get_cache_ways_sets(&cache);    /* Get number of ways & sets */
189         debug("cache: ways= %d, sets= %d\n", cache.ways + 1, cache.sets + 1);
190         for (i = cache.sets; i >= 0; i--) {
191                 for (j = cache.ways; j >= 0; j--) {
192                         writel((j << WAYS_SHIFT) | (i << SETS_SHIFT),
193                                action_reg);
194                 }
195         }
196
197         /* Make sure cache action is effective for next memory access */
198         dsb();
199         isb();  /* Make sure instruction stream sees it */
200
201         return 0;
202 }
203
204 void dcache_enable(void)
205 {
206         if (dcache_status())    /* return if cache already enabled */
207                 return;
208
209         if (action_dcache_all(INVALIDATE_SET_WAY)) {
210                 printf("ERR: D-cache not enabled\n");
211                 return;
212         }
213
214         setbits_le32(&V7M_SCB->ccr, BIT(V7M_CCR_DCACHE));
215
216         /* Make sure cache action is effective for next memory access */
217         dsb();
218         isb();  /* Make sure instruction stream sees it */
219 }
220
221 void dcache_disable(void)
222 {
223         if (!dcache_status())
224                 return;
225
226         /* if dcache is enabled-> dcache disable & then flush */
227         if (action_dcache_all(FLUSH_SET_WAY)) {
228                 printf("ERR: D-cache not flushed\n");
229                 return;
230         }
231
232         clrbits_le32(&V7M_SCB->ccr, BIT(V7M_CCR_DCACHE));
233
234         /* Make sure cache action is effective for next memory access */
235         dsb();
236         isb();  /* Make sure instruction stream sees it */
237 }
238
239 int dcache_status(void)
240 {
241         return (readl(&V7M_SCB->ccr) & BIT(V7M_CCR_DCACHE)) != 0;
242 }
243
244 void invalidate_dcache_range(unsigned long start, unsigned long stop)
245 {
246         if (action_cache_range(INVALIDATE_POC, start, stop - start)) {
247                 printf("ERR: D-cache not invalidated\n");
248                 return;
249         }
250 }
251
252 void flush_dcache_range(unsigned long start, unsigned long stop)
253 {
254         if (action_cache_range(FLUSH_POC, start, stop - start)) {
255                 printf("ERR: D-cache not flushed\n");
256                 return;
257         }
258 }
259 void flush_dcache_all(void)
260 {
261         if (action_dcache_all(FLUSH_SET_WAY)) {
262                 printf("ERR: D-cache not flushed\n");
263                 return;
264         }
265 }
266
267 void invalidate_dcache_all(void)
268 {
269         if (action_dcache_all(INVALIDATE_SET_WAY)) {
270                 printf("ERR: D-cache not invalidated\n");
271                 return;
272         }
273 }
274 #else
275 void dcache_enable(void)
276 {
277         return;
278 }
279
280 void dcache_disable(void)
281 {
282         return;
283 }
284
285 int dcache_status(void)
286 {
287         return 0;
288 }
289
290 void flush_dcache_all(void)
291 {
292 }
293
294 void invalidate_dcache_all(void)
295 {
296 }
297
298 void mmu_set_region_dcache_behaviour(phys_addr_t start, size_t size,
299                                      enum dcache_option option)
300 {
301 }
302
303 #endif
304
305 #if !CONFIG_IS_ENABLED(SYS_ICACHE_OFF)
306
307 void invalidate_icache_all(void)
308 {
309         writel(INVAL_ICACHE_POU, V7M_CACHE_REG_ICIALLU);
310
311         /* Make sure cache action is effective for next memory access */
312         dsb();
313         isb();  /* Make sure instruction stream sees it */
314 }
315
316 void icache_enable(void)
317 {
318         if (icache_status())
319                 return;
320
321         invalidate_icache_all();
322         setbits_le32(&V7M_SCB->ccr, BIT(V7M_CCR_ICACHE));
323
324         /* Make sure cache action is effective for next memory access */
325         dsb();
326         isb();  /* Make sure instruction stream sees it */
327 }
328
329 int icache_status(void)
330 {
331         return (readl(&V7M_SCB->ccr) & BIT(V7M_CCR_ICACHE)) != 0;
332 }
333
334 void icache_disable(void)
335 {
336         if (!icache_status())
337                 return;
338
339         isb();  /* flush pipeline */
340         clrbits_le32(&V7M_SCB->ccr, BIT(V7M_CCR_ICACHE));
341         isb();  /* subsequent instructions fetch see cache disable effect */
342 }
343 #else
344 void invalidate_icache_all(void)
345 {
346         return;
347 }
348
349 void icache_enable(void)
350 {
351         return;
352 }
353
354 void icache_disable(void)
355 {
356         return;
357 }
358
359 int icache_status(void)
360 {
361         return 0;
362 }
363 #endif
364
365 void enable_caches(void)
366 {
367 #if !CONFIG_IS_ENABLED(SYS_ICACHE_OFF)
368         icache_enable();
369 #endif
370 #if !CONFIG_IS_ENABLED(SYS_DCACHE_OFF)
371         dcache_enable();
372 #endif
373 }