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