]> git.ipfire.org Git - people/ms/u-boot.git/blob - arch/arc/lib/cache.c
Merge branch 'master' of git://git.denx.de/u-boot
[people/ms/u-boot.git] / arch / arc / lib / cache.c
1 /*
2 * Copyright (C) 2013-2014 Synopsys, Inc. All rights reserved.
3 *
4 * SPDX-License-Identifier: GPL-2.0+
5 */
6
7 #include <config.h>
8 #include <linux/compiler.h>
9 #include <linux/kernel.h>
10 #include <asm/arcregs.h>
11 #include <asm/cache.h>
12
13 #define CACHE_LINE_MASK (~(CONFIG_SYS_CACHELINE_SIZE - 1))
14
15 /* Bit values in IC_CTRL */
16 #define IC_CTRL_CACHE_DISABLE (1 << 0)
17
18 /* Bit values in DC_CTRL */
19 #define DC_CTRL_CACHE_DISABLE (1 << 0)
20 #define DC_CTRL_INV_MODE_FLUSH (1 << 6)
21 #define DC_CTRL_FLUSH_STATUS (1 << 8)
22 #define CACHE_VER_NUM_MASK 0xF
23 #define SLC_CTRL_SB (1 << 2)
24
25 #define OP_INV 0x1
26 #define OP_FLUSH 0x2
27 #define OP_INV_IC 0x3
28
29 #ifdef CONFIG_ISA_ARCV2
30 /*
31 * By default that variable will fall into .bss section.
32 * But .bss section is not relocated and so it will be initilized before
33 * relocation but will be used after being zeroed.
34 */
35 int slc_line_sz __section(".data");
36 int slc_exists __section(".data");
37
38 static unsigned int __before_slc_op(const int op)
39 {
40 unsigned int reg = reg;
41
42 if (op == OP_INV) {
43 /*
44 * IM is set by default and implies Flush-n-inv
45 * Clear it here for vanilla inv
46 */
47 reg = read_aux_reg(ARC_AUX_SLC_CTRL);
48 write_aux_reg(ARC_AUX_SLC_CTRL, reg & ~DC_CTRL_INV_MODE_FLUSH);
49 }
50
51 return reg;
52 }
53
54 static void __after_slc_op(const int op, unsigned int reg)
55 {
56 if (op & OP_FLUSH) /* flush / flush-n-inv both wait */
57 while (read_aux_reg(ARC_AUX_SLC_CTRL) &
58 DC_CTRL_FLUSH_STATUS)
59 ;
60
61 /* Switch back to default Invalidate mode */
62 if (op == OP_INV)
63 write_aux_reg(ARC_AUX_SLC_CTRL, reg | DC_CTRL_INV_MODE_FLUSH);
64 }
65
66 static inline void __slc_line_loop(unsigned long paddr, unsigned long sz,
67 const int op)
68 {
69 unsigned int aux_cmd;
70 int num_lines;
71
72 #define SLC_LINE_MASK (~(slc_line_sz - 1))
73
74 aux_cmd = op & OP_INV ? ARC_AUX_SLC_IVDL : ARC_AUX_SLC_FLDL;
75
76 sz += paddr & ~SLC_LINE_MASK;
77 paddr &= SLC_LINE_MASK;
78
79 num_lines = DIV_ROUND_UP(sz, slc_line_sz);
80
81 while (num_lines-- > 0) {
82 write_aux_reg(aux_cmd, paddr);
83 paddr += slc_line_sz;
84 }
85 }
86
87 static inline void __slc_entire_op(const int cacheop)
88 {
89 int aux;
90 unsigned int ctrl_reg = __before_slc_op(cacheop);
91
92 if (cacheop & OP_INV) /* Inv or flush-n-inv use same cmd reg */
93 aux = ARC_AUX_SLC_INVALIDATE;
94 else
95 aux = ARC_AUX_SLC_FLUSH;
96
97 write_aux_reg(aux, 0x1);
98
99 __after_slc_op(cacheop, ctrl_reg);
100 }
101
102 static inline void __slc_line_op(unsigned long paddr, unsigned long sz,
103 const int cacheop)
104 {
105 unsigned int ctrl_reg = __before_slc_op(cacheop);
106 __slc_line_loop(paddr, sz, cacheop);
107 __after_slc_op(cacheop, ctrl_reg);
108 }
109 #else
110 #define __slc_entire_op(cacheop)
111 #define __slc_line_op(paddr, sz, cacheop)
112 #endif
113
114 static inline int icache_exists(void)
115 {
116 /* Check if Instruction Cache is available */
117 if (read_aux_reg(ARC_BCR_IC_BUILD) & CACHE_VER_NUM_MASK)
118 return 1;
119 else
120 return 0;
121 }
122
123 static inline int dcache_exists(void)
124 {
125 /* Check if Data Cache is available */
126 if (read_aux_reg(ARC_BCR_DC_BUILD) & CACHE_VER_NUM_MASK)
127 return 1;
128 else
129 return 0;
130 }
131
132 void cache_init(void)
133 {
134 #ifdef CONFIG_ISA_ARCV2
135 /* Check if System-Level Cache (SLC) is available */
136 if (read_aux_reg(ARC_BCR_SLC) & CACHE_VER_NUM_MASK) {
137 #define LSIZE_OFFSET 4
138 #define LSIZE_MASK 3
139 if (read_aux_reg(ARC_AUX_SLC_CONFIG) &
140 (LSIZE_MASK << LSIZE_OFFSET))
141 slc_line_sz = 64;
142 else
143 slc_line_sz = 128;
144 slc_exists = 1;
145 } else {
146 slc_exists = 0;
147 }
148 #endif
149 }
150
151 int icache_status(void)
152 {
153 if (!icache_exists())
154 return 0;
155
156 if (read_aux_reg(ARC_AUX_IC_CTRL) & IC_CTRL_CACHE_DISABLE)
157 return 0;
158 else
159 return 1;
160 }
161
162 void icache_enable(void)
163 {
164 if (icache_exists())
165 write_aux_reg(ARC_AUX_IC_CTRL, read_aux_reg(ARC_AUX_IC_CTRL) &
166 ~IC_CTRL_CACHE_DISABLE);
167 }
168
169 void icache_disable(void)
170 {
171 if (icache_exists())
172 write_aux_reg(ARC_AUX_IC_CTRL, read_aux_reg(ARC_AUX_IC_CTRL) |
173 IC_CTRL_CACHE_DISABLE);
174 }
175
176 #ifndef CONFIG_SYS_DCACHE_OFF
177 void invalidate_icache_all(void)
178 {
179 /* Any write to IC_IVIC register triggers invalidation of entire I$ */
180 if (icache_status()) {
181 write_aux_reg(ARC_AUX_IC_IVIC, 1);
182 read_aux_reg(ARC_AUX_IC_CTRL); /* blocks */
183 }
184 }
185 #else
186 void invalidate_icache_all(void)
187 {
188 }
189 #endif
190
191 int dcache_status(void)
192 {
193 if (!dcache_exists())
194 return 0;
195
196 if (read_aux_reg(ARC_AUX_DC_CTRL) & DC_CTRL_CACHE_DISABLE)
197 return 0;
198 else
199 return 1;
200 }
201
202 void dcache_enable(void)
203 {
204 if (!dcache_exists())
205 return;
206
207 write_aux_reg(ARC_AUX_DC_CTRL, read_aux_reg(ARC_AUX_DC_CTRL) &
208 ~(DC_CTRL_INV_MODE_FLUSH | DC_CTRL_CACHE_DISABLE));
209 }
210
211 void dcache_disable(void)
212 {
213 if (!dcache_exists())
214 return;
215
216 write_aux_reg(ARC_AUX_DC_CTRL, read_aux_reg(ARC_AUX_DC_CTRL) |
217 DC_CTRL_CACHE_DISABLE);
218 }
219
220 #ifndef CONFIG_SYS_DCACHE_OFF
221 /*
222 * Common Helper for Line Operations on {I,D}-Cache
223 */
224 static inline void __cache_line_loop(unsigned long paddr, unsigned long sz,
225 const int cacheop)
226 {
227 unsigned int aux_cmd;
228 #if (CONFIG_ARC_MMU_VER == 3)
229 unsigned int aux_tag;
230 #endif
231 int num_lines;
232
233 if (cacheop == OP_INV_IC) {
234 aux_cmd = ARC_AUX_IC_IVIL;
235 #if (CONFIG_ARC_MMU_VER == 3)
236 aux_tag = ARC_AUX_IC_PTAG;
237 #endif
238 } else {
239 /* d$ cmd: INV (discard or wback-n-discard) OR FLUSH (wback) */
240 aux_cmd = cacheop & OP_INV ? ARC_AUX_DC_IVDL : ARC_AUX_DC_FLDL;
241 #if (CONFIG_ARC_MMU_VER == 3)
242 aux_tag = ARC_AUX_DC_PTAG;
243 #endif
244 }
245
246 sz += paddr & ~CACHE_LINE_MASK;
247 paddr &= CACHE_LINE_MASK;
248
249 num_lines = DIV_ROUND_UP(sz, CONFIG_SYS_CACHELINE_SIZE);
250
251 while (num_lines-- > 0) {
252 #if (CONFIG_ARC_MMU_VER == 3)
253 write_aux_reg(aux_tag, paddr);
254 #endif
255 write_aux_reg(aux_cmd, paddr);
256 paddr += CONFIG_SYS_CACHELINE_SIZE;
257 }
258 }
259
260 static unsigned int __before_dc_op(const int op)
261 {
262 unsigned int reg;
263
264 if (op == OP_INV) {
265 /*
266 * IM is set by default and implies Flush-n-inv
267 * Clear it here for vanilla inv
268 */
269 reg = read_aux_reg(ARC_AUX_DC_CTRL);
270 write_aux_reg(ARC_AUX_DC_CTRL, reg & ~DC_CTRL_INV_MODE_FLUSH);
271 }
272
273 return reg;
274 }
275
276 static void __after_dc_op(const int op, unsigned int reg)
277 {
278 if (op & OP_FLUSH) /* flush / flush-n-inv both wait */
279 while (read_aux_reg(ARC_AUX_DC_CTRL) & DC_CTRL_FLUSH_STATUS)
280 ;
281
282 /* Switch back to default Invalidate mode */
283 if (op == OP_INV)
284 write_aux_reg(ARC_AUX_DC_CTRL, reg | DC_CTRL_INV_MODE_FLUSH);
285 }
286
287 static inline void __dc_entire_op(const int cacheop)
288 {
289 int aux;
290 unsigned int ctrl_reg = __before_dc_op(cacheop);
291
292 if (cacheop & OP_INV) /* Inv or flush-n-inv use same cmd reg */
293 aux = ARC_AUX_DC_IVDC;
294 else
295 aux = ARC_AUX_DC_FLSH;
296
297 write_aux_reg(aux, 0x1);
298
299 __after_dc_op(cacheop, ctrl_reg);
300 }
301
302 static inline void __dc_line_op(unsigned long paddr, unsigned long sz,
303 const int cacheop)
304 {
305 unsigned int ctrl_reg = __before_dc_op(cacheop);
306 __cache_line_loop(paddr, sz, cacheop);
307 __after_dc_op(cacheop, ctrl_reg);
308 }
309 #else
310 #define __dc_entire_op(cacheop)
311 #define __dc_line_op(paddr, sz, cacheop)
312 #endif /* !CONFIG_SYS_DCACHE_OFF */
313
314 void invalidate_dcache_range(unsigned long start, unsigned long end)
315 {
316 __dc_line_op(start, end - start, OP_INV);
317 #ifdef CONFIG_ISA_ARCV2
318 if (slc_exists)
319 __slc_line_op(start, end - start, OP_INV);
320 #endif
321 }
322
323 void flush_dcache_range(unsigned long start, unsigned long end)
324 {
325 __dc_line_op(start, end - start, OP_FLUSH);
326 #ifdef CONFIG_ISA_ARCV2
327 if (slc_exists)
328 __slc_line_op(start, end - start, OP_FLUSH);
329 #endif
330 }
331
332 void flush_cache(unsigned long start, unsigned long size)
333 {
334 flush_dcache_range(start, start + size);
335 }
336
337 void invalidate_dcache_all(void)
338 {
339 __dc_entire_op(OP_INV);
340 #ifdef CONFIG_ISA_ARCV2
341 if (slc_exists)
342 __slc_entire_op(OP_INV);
343 #endif
344 }
345
346 void flush_dcache_all(void)
347 {
348 __dc_entire_op(OP_FLUSH);
349 #ifdef CONFIG_ISA_ARCV2
350 if (slc_exists)
351 __slc_entire_op(OP_FLUSH);
352 #endif
353 }