blob: ed8e8e74e2a19a414a5aee265605815b1c91ea22 [file] [log] [blame]
Alexey Brodkin2f16ac92014-02-04 12:56:14 +04001/*
2 * Copyright (C) 2013-2014 Synopsys, Inc. All rights reserved.
3 *
4 * SPDX-License-Identifier: GPL-2.0+
5 */
6
7#include <config.h>
Alexey Brodkinef639e62015-05-18 16:56:26 +03008#include <linux/compiler.h>
9#include <linux/kernel.h>
Alexey Brodkin2f16ac92014-02-04 12:56:14 +040010#include <asm/arcregs.h>
Alexey Brodkin205e7a72015-02-03 13:58:13 +030011#include <asm/cache.h>
Alexey Brodkin2f16ac92014-02-04 12:56:14 +040012
Alexey Brodkinef639e62015-05-18 16:56:26 +030013#define CACHE_LINE_MASK (~(CONFIG_SYS_CACHELINE_SIZE - 1))
14
Alexey Brodkin2f16ac92014-02-04 12:56:14 +040015/* 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)
Igor Guryanovf8cf3d12014-12-24 16:07:07 +030022#define CACHE_VER_NUM_MASK 0xF
Alexey Brodkin6eb15e52015-03-30 13:36:04 +030023#define SLC_CTRL_SB (1 << 2)
Alexey Brodkin2f16ac92014-02-04 12:56:14 +040024
Alexey Brodkinef639e62015-05-18 16:56:26 +030025#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 */
35int slc_line_sz __section(".data");
36int slc_exists __section(".data");
37
38static 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
54static 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
66static 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
87static 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
102static 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
114static 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
123static 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
132void 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
Alexey Brodkin2f16ac92014-02-04 12:56:14 +0400151int icache_status(void)
152{
Alexey Brodkinef639e62015-05-18 16:56:26 +0300153 if (!icache_exists())
Igor Guryanovf8cf3d12014-12-24 16:07:07 +0300154 return 0;
155
Alexey Brodkinef639e62015-05-18 16:56:26 +0300156 if (read_aux_reg(ARC_AUX_IC_CTRL) & IC_CTRL_CACHE_DISABLE)
157 return 0;
158 else
159 return 1;
Alexey Brodkin2f16ac92014-02-04 12:56:14 +0400160}
161
162void icache_enable(void)
163{
Alexey Brodkinef639e62015-05-18 16:56:26 +0300164 if (icache_exists())
165 write_aux_reg(ARC_AUX_IC_CTRL, read_aux_reg(ARC_AUX_IC_CTRL) &
166 ~IC_CTRL_CACHE_DISABLE);
Alexey Brodkin2f16ac92014-02-04 12:56:14 +0400167}
168
169void icache_disable(void)
170{
Alexey Brodkinef639e62015-05-18 16:56:26 +0300171 if (icache_exists())
172 write_aux_reg(ARC_AUX_IC_CTRL, read_aux_reg(ARC_AUX_IC_CTRL) |
173 IC_CTRL_CACHE_DISABLE);
Alexey Brodkin2f16ac92014-02-04 12:56:14 +0400174}
175
Alexey Brodkinef639e62015-05-18 16:56:26 +0300176#ifndef CONFIG_SYS_DCACHE_OFF
Alexey Brodkin2f16ac92014-02-04 12:56:14 +0400177void invalidate_icache_all(void)
178{
Alexey Brodkin2f16ac92014-02-04 12:56:14 +0400179 /* Any write to IC_IVIC register triggers invalidation of entire I$ */
Alexey Brodkinef639e62015-05-18 16:56:26 +0300180 if (icache_status()) {
181 write_aux_reg(ARC_AUX_IC_IVIC, 1);
182 read_aux_reg(ARC_AUX_IC_CTRL); /* blocks */
183 }
Alexey Brodkin2f16ac92014-02-04 12:56:14 +0400184}
Alexey Brodkinef639e62015-05-18 16:56:26 +0300185#else
186void invalidate_icache_all(void)
187{
188}
189#endif
Alexey Brodkin2f16ac92014-02-04 12:56:14 +0400190
191int dcache_status(void)
192{
Alexey Brodkinef639e62015-05-18 16:56:26 +0300193 if (!dcache_exists())
Igor Guryanovf8cf3d12014-12-24 16:07:07 +0300194 return 0;
195
Alexey Brodkinef639e62015-05-18 16:56:26 +0300196 if (read_aux_reg(ARC_AUX_DC_CTRL) & DC_CTRL_CACHE_DISABLE)
197 return 0;
198 else
199 return 1;
Alexey Brodkin2f16ac92014-02-04 12:56:14 +0400200}
201
202void dcache_enable(void)
203{
Alexey Brodkinef639e62015-05-18 16:56:26 +0300204 if (!dcache_exists())
Igor Guryanovf8cf3d12014-12-24 16:07:07 +0300205 return;
206
Alexey Brodkin2f16ac92014-02-04 12:56:14 +0400207 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
211void dcache_disable(void)
212{
Alexey Brodkinef639e62015-05-18 16:56:26 +0300213 if (!dcache_exists())
Igor Guryanovf8cf3d12014-12-24 16:07:07 +0300214 return;
215
Alexey Brodkin2f16ac92014-02-04 12:56:14 +0400216 write_aux_reg(ARC_AUX_DC_CTRL, read_aux_reg(ARC_AUX_DC_CTRL) |
217 DC_CTRL_CACHE_DISABLE);
218}
219
Alexey Brodkin2f16ac92014-02-04 12:56:14 +0400220#ifndef CONFIG_SYS_DCACHE_OFF
Alexey Brodkinef639e62015-05-18 16:56:26 +0300221/*
222 * Common Helper for Line Operations on {I,D}-Cache
223 */
224static inline void __cache_line_loop(unsigned long paddr, unsigned long sz,
225 const int cacheop)
Alexey Brodkin2f16ac92014-02-04 12:56:14 +0400226{
Alexey Brodkinef639e62015-05-18 16:56:26 +0300227 unsigned int aux_cmd;
Alexey Brodkin5ff40f32015-02-03 13:58:12 +0300228#if (CONFIG_ARC_MMU_VER == 3)
Alexey Brodkinef639e62015-05-18 16:56:26 +0300229 unsigned int aux_tag;
Alexey Brodkin2f16ac92014-02-04 12:56:14 +0400230#endif
Alexey Brodkinef639e62015-05-18 16:56:26 +0300231 int num_lines;
Alexey Brodkin2f16ac92014-02-04 12:56:14 +0400232
Alexey Brodkinef639e62015-05-18 16:56:26 +0300233 if (cacheop == OP_INV_IC) {
234 aux_cmd = ARC_AUX_IC_IVIL;
Alexey Brodkin5ff40f32015-02-03 13:58:12 +0300235#if (CONFIG_ARC_MMU_VER == 3)
Alexey Brodkinef639e62015-05-18 16:56:26 +0300236 aux_tag = ARC_AUX_IC_PTAG;
Alexey Brodkin2f16ac92014-02-04 12:56:14 +0400237#endif
Alexey Brodkinef639e62015-05-18 16:56:26 +0300238 } 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 }
Alexey Brodkin2f16ac92014-02-04 12:56:14 +0400258}
Alexey Brodkin2f16ac92014-02-04 12:56:14 +0400259
Alexey Brodkinef639e62015-05-18 16:56:26 +0300260static unsigned int __before_dc_op(const int op)
Alexey Brodkin2f16ac92014-02-04 12:56:14 +0400261{
Alexey Brodkinef639e62015-05-18 16:56:26 +0300262 unsigned int reg;
Alexey Brodkin2f16ac92014-02-04 12:56:14 +0400263
Alexey Brodkinef639e62015-05-18 16:56:26 +0300264 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 }
Alexey Brodkin2f16ac92014-02-04 12:56:14 +0400272
Alexey Brodkinef639e62015-05-18 16:56:26 +0300273 return reg;
Alexey Brodkin2f16ac92014-02-04 12:56:14 +0400274}
275
Alexey Brodkinef639e62015-05-18 16:56:26 +0300276static 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
287static 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
302static 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
Alexey Brodkin2f16ac92014-02-04 12:56:14 +0400314void invalidate_dcache_range(unsigned long start, unsigned long end)
315{
Alexey Brodkinef639e62015-05-18 16:56:26 +0300316 __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);
Alexey Brodkin2f16ac92014-02-04 12:56:14 +0400320#endif
Alexey Brodkin2f16ac92014-02-04 12:56:14 +0400321}
322
Alexey Brodkinef639e62015-05-18 16:56:26 +0300323void flush_dcache_range(unsigned long start, unsigned long end)
Alexey Brodkin2f16ac92014-02-04 12:56:14 +0400324{
Alexey Brodkinef639e62015-05-18 16:56:26 +0300325 __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
Alexey Brodkin2f16ac92014-02-04 12:56:14 +0400330}
331
332void flush_cache(unsigned long start, unsigned long size)
333{
334 flush_dcache_range(start, start + size);
335}
Alexey Brodkin6eb15e52015-03-30 13:36:04 +0300336
Alexey Brodkinef639e62015-05-18 16:56:26 +0300337void invalidate_dcache_all(void)
338{
339 __dc_entire_op(OP_INV);
Alexey Brodkin6eb15e52015-03-30 13:36:04 +0300340#ifdef CONFIG_ISA_ARCV2
Alexey Brodkinef639e62015-05-18 16:56:26 +0300341 if (slc_exists)
342 __slc_entire_op(OP_INV);
343#endif
Alexey Brodkin6eb15e52015-03-30 13:36:04 +0300344}
345
Alexey Brodkinef639e62015-05-18 16:56:26 +0300346void flush_dcache_all(void)
Alexey Brodkin6eb15e52015-03-30 13:36:04 +0300347{
Alexey Brodkinef639e62015-05-18 16:56:26 +0300348 __dc_entire_op(OP_FLUSH);
349#ifdef CONFIG_ISA_ARCV2
350 if (slc_exists)
351 __slc_entire_op(OP_FLUSH);
352#endif
Alexey Brodkin6eb15e52015-03-30 13:36:04 +0300353}