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