]> git.ipfire.org Git - people/ms/u-boot.git/blame - arch/arc/lib/cache.c
arc: cache - accommodate different L1 cache line lengths
[people/ms/u-boot.git] / arch / arc / lib / cache.c
CommitLineData
2f16ac9d
AB
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>
379b3280 8#include <common.h>
ef639e6f
AB
9#include <linux/compiler.h>
10#include <linux/kernel.h>
2f16ac9d 11#include <asm/arcregs.h>
205e7a7b 12#include <asm/cache.h>
2f16ac9d
AB
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)
f8cf3d1e 21#define CACHE_VER_NUM_MASK 0xF
6eb15e50 22#define SLC_CTRL_SB (1 << 2)
2f16ac9d 23
ef639e6f
AB
24#define OP_INV 0x1
25#define OP_FLUSH 0x2
26#define OP_INV_IC 0x3
27
ef639e6f
AB
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 */
379b3280
AB
33int l1_line_sz __section(".data");
34int dcache_exists __section(".data");
35int icache_exists __section(".data");
36
37#define CACHE_LINE_MASK (~(l1_line_sz - 1))
38
39#ifdef CONFIG_ISA_ARCV2
ef639e6f
AB
40int slc_line_sz __section(".data");
41int slc_exists __section(".data");
42
43static 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
59static 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
71static 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
92static 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
107static 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
379b3280
AB
119#ifdef CONFIG_ISA_ARCV2
120static void read_decode_cache_bcr_arcv2(void)
ef639e6f 121{
379b3280
AB
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 }
ef639e6f 150}
379b3280 151#endif
ef639e6f 152
379b3280 153void read_decode_cache_bcr(void)
ef639e6f 154{
379b3280
AB
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");
ef639e6f
AB
186}
187
188void cache_init(void)
189{
379b3280
AB
190 read_decode_cache_bcr();
191
ef639e6f 192#ifdef CONFIG_ISA_ARCV2
379b3280 193 read_decode_cache_bcr_arcv2();
ef639e6f
AB
194#endif
195}
196
2f16ac9d
AB
197int icache_status(void)
198{
379b3280 199 if (!icache_exists)
f8cf3d1e
IG
200 return 0;
201
ef639e6f
AB
202 if (read_aux_reg(ARC_AUX_IC_CTRL) & IC_CTRL_CACHE_DISABLE)
203 return 0;
204 else
205 return 1;
2f16ac9d
AB
206}
207
208void icache_enable(void)
209{
379b3280 210 if (icache_exists)
ef639e6f
AB
211 write_aux_reg(ARC_AUX_IC_CTRL, read_aux_reg(ARC_AUX_IC_CTRL) &
212 ~IC_CTRL_CACHE_DISABLE);
2f16ac9d
AB
213}
214
215void icache_disable(void)
216{
379b3280 217 if (icache_exists)
ef639e6f
AB
218 write_aux_reg(ARC_AUX_IC_CTRL, read_aux_reg(ARC_AUX_IC_CTRL) |
219 IC_CTRL_CACHE_DISABLE);
2f16ac9d
AB
220}
221
ef639e6f 222#ifndef CONFIG_SYS_DCACHE_OFF
2f16ac9d
AB
223void invalidate_icache_all(void)
224{
2f16ac9d 225 /* Any write to IC_IVIC register triggers invalidation of entire I$ */
ef639e6f
AB
226 if (icache_status()) {
227 write_aux_reg(ARC_AUX_IC_IVIC, 1);
228 read_aux_reg(ARC_AUX_IC_CTRL); /* blocks */
229 }
2f16ac9d 230}
ef639e6f
AB
231#else
232void invalidate_icache_all(void)
233{
234}
235#endif
2f16ac9d
AB
236
237int dcache_status(void)
238{
379b3280 239 if (!dcache_exists)
f8cf3d1e
IG
240 return 0;
241
ef639e6f
AB
242 if (read_aux_reg(ARC_AUX_DC_CTRL) & DC_CTRL_CACHE_DISABLE)
243 return 0;
244 else
245 return 1;
2f16ac9d
AB
246}
247
248void dcache_enable(void)
249{
379b3280 250 if (!dcache_exists)
f8cf3d1e
IG
251 return;
252
2f16ac9d
AB
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
257void dcache_disable(void)
258{
379b3280 259 if (!dcache_exists)
f8cf3d1e
IG
260 return;
261
2f16ac9d
AB
262 write_aux_reg(ARC_AUX_DC_CTRL, read_aux_reg(ARC_AUX_DC_CTRL) |
263 DC_CTRL_CACHE_DISABLE);
264}
265
2f16ac9d 266#ifndef CONFIG_SYS_DCACHE_OFF
ef639e6f
AB
267/*
268 * Common Helper for Line Operations on {I,D}-Cache
269 */
270static inline void __cache_line_loop(unsigned long paddr, unsigned long sz,
271 const int cacheop)
2f16ac9d 272{
ef639e6f 273 unsigned int aux_cmd;
5ff40f3d 274#if (CONFIG_ARC_MMU_VER == 3)
ef639e6f 275 unsigned int aux_tag;
2f16ac9d 276#endif
ef639e6f 277 int num_lines;
2f16ac9d 278
ef639e6f
AB
279 if (cacheop == OP_INV_IC) {
280 aux_cmd = ARC_AUX_IC_IVIL;
5ff40f3d 281#if (CONFIG_ARC_MMU_VER == 3)
ef639e6f 282 aux_tag = ARC_AUX_IC_PTAG;
2f16ac9d 283#endif
ef639e6f
AB
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 }
2f16ac9d 291
ef639e6f
AB
292 sz += paddr & ~CACHE_LINE_MASK;
293 paddr &= CACHE_LINE_MASK;
2f16ac9d 294
379b3280 295 num_lines = DIV_ROUND_UP(sz, l1_line_sz);
2f16ac9d 296
ef639e6f 297 while (num_lines-- > 0) {
5ff40f3d 298#if (CONFIG_ARC_MMU_VER == 3)
ef639e6f 299 write_aux_reg(aux_tag, paddr);
2f16ac9d 300#endif
ef639e6f 301 write_aux_reg(aux_cmd, paddr);
379b3280 302 paddr += l1_line_sz;
2f16ac9d 303 }
2f16ac9d
AB
304}
305
ef639e6f 306static unsigned int __before_dc_op(const int op)
2f16ac9d 307{
ef639e6f
AB
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 }
ae4a351a 318
ef639e6f 319 return reg;
2f16ac9d
AB
320}
321
ef639e6f 322static void __after_dc_op(const int op, unsigned int reg)
2f16ac9d 323{
ef639e6f
AB
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);
2f16ac9d 331}
6eb15e50 332
ef639e6f 333static inline void __dc_entire_op(const int cacheop)
6eb15e50 334{
ef639e6f
AB
335 int aux;
336 unsigned int ctrl_reg = __before_dc_op(cacheop);
6eb15e50 337
ef639e6f
AB
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;
6eb15e50 342
ef639e6f 343 write_aux_reg(aux, 0x1);
6eb15e50 344
ef639e6f 345 __after_dc_op(cacheop, ctrl_reg);
6eb15e50
AB
346}
347
ef639e6f
AB
348static inline void __dc_line_op(unsigned long paddr, unsigned long sz,
349 const int cacheop)
6eb15e50 350{
ef639e6f
AB
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 */
6eb15e50 359
ef639e6f
AB
360void 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}
6eb15e50 368
ef639e6f
AB
369void 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
6eb15e50
AB
376}
377
ef639e6f 378void flush_cache(unsigned long start, unsigned long size)
6eb15e50 379{
ef639e6f
AB
380 flush_dcache_range(start, start + size);
381}
6eb15e50 382
ef639e6f
AB
383void 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
6eb15e50
AB
390}
391
ef639e6f
AB
392void 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}