xref: /openbmc/u-boot/arch/arc/lib/cache.c (revision 379b3280)
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 }
400