8089f9df1e2602fbde9cdaf7f5f931893f40ca4e
[platform/kernel/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 }