blob: b6ec83112cd8aa5264d9572c9ef97fbf0f9462ce [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 Brodkin379b3282015-12-14 17:14:46 +03008#include <common.h>
Alexey Brodkinef639e62015-05-18 16:56:26 +03009#include <linux/compiler.h>
10#include <linux/kernel.h>
Alexey Brodkin2f16ac92014-02-04 12:56:14 +040011#include <asm/arcregs.h>
Alexey Brodkin205e7a72015-02-03 13:58:13 +030012#include <asm/cache.h>
Alexey Brodkin2f16ac92014-02-04 12:56:14 +040013
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)
Igor Guryanovf8cf3d12014-12-24 16:07:07 +030021#define CACHE_VER_NUM_MASK 0xF
Alexey Brodkin6eb15e52015-03-30 13:36:04 +030022#define SLC_CTRL_SB (1 << 2)
Alexey Brodkin2f16ac92014-02-04 12:56:14 +040023
Alexey Brodkinef639e62015-05-18 16:56:26 +030024#define OP_INV 0x1
25#define OP_FLUSH 0x2
26#define OP_INV_IC 0x3
27
Alexey Brodkinef639e62015-05-18 16:56:26 +030028/*
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 */
Alexey Brodkin379b3282015-12-14 17:14:46 +030033int 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
Alexey Brodkinef639e62015-05-18 16:56:26 +030040int slc_line_sz __section(".data");
41int slc_exists __section(".data");
Alexey Brodkindb6ce232015-12-14 17:15:13 +030042int ioc_exists __section(".data");
Alexey Brodkinef639e62015-05-18 16:56:26 +030043
44static unsigned int __before_slc_op(const int op)
45{
46 unsigned int reg = reg;
47
48 if (op == OP_INV) {
49 /*
50 * IM is set by default and implies Flush-n-inv
51 * Clear it here for vanilla inv
52 */
53 reg = read_aux_reg(ARC_AUX_SLC_CTRL);
54 write_aux_reg(ARC_AUX_SLC_CTRL, reg & ~DC_CTRL_INV_MODE_FLUSH);
55 }
56
57 return reg;
58}
59
60static void __after_slc_op(const int op, unsigned int reg)
61{
62 if (op & OP_FLUSH) /* flush / flush-n-inv both wait */
63 while (read_aux_reg(ARC_AUX_SLC_CTRL) &
64 DC_CTRL_FLUSH_STATUS)
65 ;
66
67 /* Switch back to default Invalidate mode */
68 if (op == OP_INV)
69 write_aux_reg(ARC_AUX_SLC_CTRL, reg | DC_CTRL_INV_MODE_FLUSH);
70}
71
72static inline void __slc_line_loop(unsigned long paddr, unsigned long sz,
73 const int op)
74{
75 unsigned int aux_cmd;
76 int num_lines;
77
78#define SLC_LINE_MASK (~(slc_line_sz - 1))
79
80 aux_cmd = op & OP_INV ? ARC_AUX_SLC_IVDL : ARC_AUX_SLC_FLDL;
81
82 sz += paddr & ~SLC_LINE_MASK;
83 paddr &= SLC_LINE_MASK;
84
85 num_lines = DIV_ROUND_UP(sz, slc_line_sz);
86
87 while (num_lines-- > 0) {
88 write_aux_reg(aux_cmd, paddr);
89 paddr += slc_line_sz;
90 }
91}
92
93static inline void __slc_entire_op(const int cacheop)
94{
95 int aux;
96 unsigned int ctrl_reg = __before_slc_op(cacheop);
97
98 if (cacheop & OP_INV) /* Inv or flush-n-inv use same cmd reg */
99 aux = ARC_AUX_SLC_INVALIDATE;
100 else
101 aux = ARC_AUX_SLC_FLUSH;
102
103 write_aux_reg(aux, 0x1);
104
105 __after_slc_op(cacheop, ctrl_reg);
106}
107
108static inline void __slc_line_op(unsigned long paddr, unsigned long sz,
109 const int cacheop)
110{
111 unsigned int ctrl_reg = __before_slc_op(cacheop);
112 __slc_line_loop(paddr, sz, cacheop);
113 __after_slc_op(cacheop, ctrl_reg);
114}
115#else
116#define __slc_entire_op(cacheop)
117#define __slc_line_op(paddr, sz, cacheop)
118#endif
119
Alexey Brodkin379b3282015-12-14 17:14:46 +0300120#ifdef CONFIG_ISA_ARCV2
121static void read_decode_cache_bcr_arcv2(void)
Alexey Brodkinef639e62015-05-18 16:56:26 +0300122{
Alexey Brodkin379b3282015-12-14 17:14:46 +0300123 union {
124 struct {
125#ifdef CONFIG_CPU_BIG_ENDIAN
126 unsigned int pad:24, way:2, lsz:2, sz:4;
127#else
128 unsigned int sz:4, lsz:2, way:2, pad:24;
129#endif
130 } fields;
131 unsigned int word;
132 } slc_cfg;
Alexey Brodkinef639e62015-05-18 16:56:26 +0300133
Alexey Brodkin379b3282015-12-14 17:14:46 +0300134 union {
135 struct {
136#ifdef CONFIG_CPU_BIG_ENDIAN
137 unsigned int pad:24, ver:8;
138#else
139 unsigned int ver:8, pad:24;
140#endif
141 } fields;
142 unsigned int word;
143 } sbcr;
144
145 sbcr.word = read_aux_reg(ARC_BCR_SLC);
146 if (sbcr.fields.ver) {
147 slc_cfg.word = read_aux_reg(ARC_AUX_SLC_CONFIG);
148 slc_exists = 1;
149 slc_line_sz = (slc_cfg.fields.lsz == 0) ? 128 : 64;
150 }
Alexey Brodkindb6ce232015-12-14 17:15:13 +0300151
152 union {
153 struct bcr_clust_cfg {
154#ifdef CONFIG_CPU_BIG_ENDIAN
155 unsigned int pad:7, c:1, num_entries:8, num_cores:8, ver:8;
156#else
157 unsigned int ver:8, num_cores:8, num_entries:8, c:1, pad:7;
158#endif
159 } fields;
160 unsigned int word;
161 } cbcr;
162
163 cbcr.word = read_aux_reg(ARC_BCR_CLUSTER);
164 if (cbcr.fields.c)
165 ioc_exists = 1;
Alexey Brodkin379b3282015-12-14 17:14:46 +0300166}
167#endif
168
169void read_decode_cache_bcr(void)
Alexey Brodkinef639e62015-05-18 16:56:26 +0300170{
Alexey Brodkin379b3282015-12-14 17:14:46 +0300171 int dc_line_sz = 0, ic_line_sz = 0;
172
173 union {
174 struct {
175#ifdef CONFIG_CPU_BIG_ENDIAN
176 unsigned int pad:12, line_len:4, sz:4, config:4, ver:8;
177#else
178 unsigned int ver:8, config:4, sz:4, line_len:4, pad:12;
179#endif
180 } fields;
181 unsigned int word;
182 } ibcr, dbcr;
183
184 ibcr.word = read_aux_reg(ARC_BCR_IC_BUILD);
185 if (ibcr.fields.ver) {
186 icache_exists = 1;
187 l1_line_sz = ic_line_sz = 8 << ibcr.fields.line_len;
188 if (!ic_line_sz)
189 panic("Instruction exists but line length is 0\n");
190 }
191
192 dbcr.word = read_aux_reg(ARC_BCR_DC_BUILD);
193 if (dbcr.fields.ver){
194 dcache_exists = 1;
195 l1_line_sz = dc_line_sz = 16 << dbcr.fields.line_len;
196 if (!dc_line_sz)
197 panic("Data cache exists but line length is 0\n");
198 }
199
200 if (ic_line_sz && dc_line_sz && (ic_line_sz != dc_line_sz))
201 panic("Instruction and data cache line lengths differ\n");
Alexey Brodkinef639e62015-05-18 16:56:26 +0300202}
203
204void cache_init(void)
205{
Alexey Brodkin379b3282015-12-14 17:14:46 +0300206 read_decode_cache_bcr();
207
Alexey Brodkinef639e62015-05-18 16:56:26 +0300208#ifdef CONFIG_ISA_ARCV2
Alexey Brodkin379b3282015-12-14 17:14:46 +0300209 read_decode_cache_bcr_arcv2();
Alexey Brodkindb6ce232015-12-14 17:15:13 +0300210
211 if (ioc_exists) {
Alexey Brodkina4a43fc2016-06-08 08:04:03 +0300212 flush_dcache_all();
213 invalidate_dcache_all();
214
Alexey Brodkindb6ce232015-12-14 17:15:13 +0300215 /* IO coherency base - 0x8z */
216 write_aux_reg(ARC_AUX_IO_COH_AP0_BASE, 0x80000);
217 /* IO coherency aperture size - 512Mb: 0x8z-0xAz */
218 write_aux_reg(ARC_AUX_IO_COH_AP0_SIZE, 0x11);
219 /* Enable partial writes */
220 write_aux_reg(ARC_AUX_IO_COH_PARTIAL, 1);
221 /* Enable IO coherency */
222 write_aux_reg(ARC_AUX_IO_COH_ENABLE, 1);
223 }
Alexey Brodkinef639e62015-05-18 16:56:26 +0300224#endif
225}
226
Alexey Brodkin2f16ac92014-02-04 12:56:14 +0400227int icache_status(void)
228{
Alexey Brodkin379b3282015-12-14 17:14:46 +0300229 if (!icache_exists)
Igor Guryanovf8cf3d12014-12-24 16:07:07 +0300230 return 0;
231
Alexey Brodkinef639e62015-05-18 16:56:26 +0300232 if (read_aux_reg(ARC_AUX_IC_CTRL) & IC_CTRL_CACHE_DISABLE)
233 return 0;
234 else
235 return 1;
Alexey Brodkin2f16ac92014-02-04 12:56:14 +0400236}
237
238void icache_enable(void)
239{
Alexey Brodkin379b3282015-12-14 17:14:46 +0300240 if (icache_exists)
Alexey Brodkinef639e62015-05-18 16:56:26 +0300241 write_aux_reg(ARC_AUX_IC_CTRL, read_aux_reg(ARC_AUX_IC_CTRL) &
242 ~IC_CTRL_CACHE_DISABLE);
Alexey Brodkin2f16ac92014-02-04 12:56:14 +0400243}
244
245void icache_disable(void)
246{
Alexey Brodkin379b3282015-12-14 17:14:46 +0300247 if (icache_exists)
Alexey Brodkinef639e62015-05-18 16:56:26 +0300248 write_aux_reg(ARC_AUX_IC_CTRL, read_aux_reg(ARC_AUX_IC_CTRL) |
249 IC_CTRL_CACHE_DISABLE);
Alexey Brodkin2f16ac92014-02-04 12:56:14 +0400250}
251
Alexey Brodkinef639e62015-05-18 16:56:26 +0300252#ifndef CONFIG_SYS_DCACHE_OFF
Alexey Brodkin2f16ac92014-02-04 12:56:14 +0400253void invalidate_icache_all(void)
254{
Alexey Brodkin2f16ac92014-02-04 12:56:14 +0400255 /* Any write to IC_IVIC register triggers invalidation of entire I$ */
Alexey Brodkinef639e62015-05-18 16:56:26 +0300256 if (icache_status()) {
257 write_aux_reg(ARC_AUX_IC_IVIC, 1);
258 read_aux_reg(ARC_AUX_IC_CTRL); /* blocks */
259 }
Alexey Brodkin2f16ac92014-02-04 12:56:14 +0400260}
Alexey Brodkinef639e62015-05-18 16:56:26 +0300261#else
262void invalidate_icache_all(void)
263{
264}
265#endif
Alexey Brodkin2f16ac92014-02-04 12:56:14 +0400266
267int dcache_status(void)
268{
Alexey Brodkin379b3282015-12-14 17:14:46 +0300269 if (!dcache_exists)
Igor Guryanovf8cf3d12014-12-24 16:07:07 +0300270 return 0;
271
Alexey Brodkinef639e62015-05-18 16:56:26 +0300272 if (read_aux_reg(ARC_AUX_DC_CTRL) & DC_CTRL_CACHE_DISABLE)
273 return 0;
274 else
275 return 1;
Alexey Brodkin2f16ac92014-02-04 12:56:14 +0400276}
277
278void dcache_enable(void)
279{
Alexey Brodkin379b3282015-12-14 17:14:46 +0300280 if (!dcache_exists)
Igor Guryanovf8cf3d12014-12-24 16:07:07 +0300281 return;
282
Alexey Brodkin2f16ac92014-02-04 12:56:14 +0400283 write_aux_reg(ARC_AUX_DC_CTRL, read_aux_reg(ARC_AUX_DC_CTRL) &
284 ~(DC_CTRL_INV_MODE_FLUSH | DC_CTRL_CACHE_DISABLE));
285}
286
287void dcache_disable(void)
288{
Alexey Brodkin379b3282015-12-14 17:14:46 +0300289 if (!dcache_exists)
Igor Guryanovf8cf3d12014-12-24 16:07:07 +0300290 return;
291
Alexey Brodkin2f16ac92014-02-04 12:56:14 +0400292 write_aux_reg(ARC_AUX_DC_CTRL, read_aux_reg(ARC_AUX_DC_CTRL) |
293 DC_CTRL_CACHE_DISABLE);
294}
295
Alexey Brodkin2f16ac92014-02-04 12:56:14 +0400296#ifndef CONFIG_SYS_DCACHE_OFF
Alexey Brodkinef639e62015-05-18 16:56:26 +0300297/*
298 * Common Helper for Line Operations on {I,D}-Cache
299 */
300static inline void __cache_line_loop(unsigned long paddr, unsigned long sz,
301 const int cacheop)
Alexey Brodkin2f16ac92014-02-04 12:56:14 +0400302{
Alexey Brodkinef639e62015-05-18 16:56:26 +0300303 unsigned int aux_cmd;
Alexey Brodkin5ff40f32015-02-03 13:58:12 +0300304#if (CONFIG_ARC_MMU_VER == 3)
Alexey Brodkinef639e62015-05-18 16:56:26 +0300305 unsigned int aux_tag;
Alexey Brodkin2f16ac92014-02-04 12:56:14 +0400306#endif
Alexey Brodkinef639e62015-05-18 16:56:26 +0300307 int num_lines;
Alexey Brodkin2f16ac92014-02-04 12:56:14 +0400308
Alexey Brodkinef639e62015-05-18 16:56:26 +0300309 if (cacheop == OP_INV_IC) {
310 aux_cmd = ARC_AUX_IC_IVIL;
Alexey Brodkin5ff40f32015-02-03 13:58:12 +0300311#if (CONFIG_ARC_MMU_VER == 3)
Alexey Brodkinef639e62015-05-18 16:56:26 +0300312 aux_tag = ARC_AUX_IC_PTAG;
Alexey Brodkin2f16ac92014-02-04 12:56:14 +0400313#endif
Alexey Brodkinef639e62015-05-18 16:56:26 +0300314 } else {
315 /* d$ cmd: INV (discard or wback-n-discard) OR FLUSH (wback) */
316 aux_cmd = cacheop & OP_INV ? ARC_AUX_DC_IVDL : ARC_AUX_DC_FLDL;
317#if (CONFIG_ARC_MMU_VER == 3)
318 aux_tag = ARC_AUX_DC_PTAG;
319#endif
320 }
321
322 sz += paddr & ~CACHE_LINE_MASK;
323 paddr &= CACHE_LINE_MASK;
324
Alexey Brodkin379b3282015-12-14 17:14:46 +0300325 num_lines = DIV_ROUND_UP(sz, l1_line_sz);
Alexey Brodkinef639e62015-05-18 16:56:26 +0300326
327 while (num_lines-- > 0) {
328#if (CONFIG_ARC_MMU_VER == 3)
329 write_aux_reg(aux_tag, paddr);
330#endif
331 write_aux_reg(aux_cmd, paddr);
Alexey Brodkin379b3282015-12-14 17:14:46 +0300332 paddr += l1_line_sz;
Alexey Brodkinef639e62015-05-18 16:56:26 +0300333 }
Alexey Brodkin2f16ac92014-02-04 12:56:14 +0400334}
Alexey Brodkin2f16ac92014-02-04 12:56:14 +0400335
Alexey Brodkinef639e62015-05-18 16:56:26 +0300336static unsigned int __before_dc_op(const int op)
Alexey Brodkin2f16ac92014-02-04 12:56:14 +0400337{
Alexey Brodkinef639e62015-05-18 16:56:26 +0300338 unsigned int reg;
Alexey Brodkin2f16ac92014-02-04 12:56:14 +0400339
Alexey Brodkinef639e62015-05-18 16:56:26 +0300340 if (op == OP_INV) {
341 /*
342 * IM is set by default and implies Flush-n-inv
343 * Clear it here for vanilla inv
344 */
345 reg = read_aux_reg(ARC_AUX_DC_CTRL);
346 write_aux_reg(ARC_AUX_DC_CTRL, reg & ~DC_CTRL_INV_MODE_FLUSH);
347 }
Alexey Brodkin2f16ac92014-02-04 12:56:14 +0400348
Alexey Brodkinef639e62015-05-18 16:56:26 +0300349 return reg;
Alexey Brodkin2f16ac92014-02-04 12:56:14 +0400350}
351
Alexey Brodkinef639e62015-05-18 16:56:26 +0300352static void __after_dc_op(const int op, unsigned int reg)
353{
354 if (op & OP_FLUSH) /* flush / flush-n-inv both wait */
355 while (read_aux_reg(ARC_AUX_DC_CTRL) & DC_CTRL_FLUSH_STATUS)
356 ;
357
358 /* Switch back to default Invalidate mode */
359 if (op == OP_INV)
360 write_aux_reg(ARC_AUX_DC_CTRL, reg | DC_CTRL_INV_MODE_FLUSH);
361}
362
363static inline void __dc_entire_op(const int cacheop)
364{
365 int aux;
366 unsigned int ctrl_reg = __before_dc_op(cacheop);
367
368 if (cacheop & OP_INV) /* Inv or flush-n-inv use same cmd reg */
369 aux = ARC_AUX_DC_IVDC;
370 else
371 aux = ARC_AUX_DC_FLSH;
372
373 write_aux_reg(aux, 0x1);
374
375 __after_dc_op(cacheop, ctrl_reg);
376}
377
378static inline void __dc_line_op(unsigned long paddr, unsigned long sz,
379 const int cacheop)
380{
381 unsigned int ctrl_reg = __before_dc_op(cacheop);
382 __cache_line_loop(paddr, sz, cacheop);
383 __after_dc_op(cacheop, ctrl_reg);
384}
385#else
386#define __dc_entire_op(cacheop)
387#define __dc_line_op(paddr, sz, cacheop)
388#endif /* !CONFIG_SYS_DCACHE_OFF */
389
Alexey Brodkin2f16ac92014-02-04 12:56:14 +0400390void invalidate_dcache_range(unsigned long start, unsigned long end)
391{
Alexey Brodkinef639e62015-05-18 16:56:26 +0300392#ifdef CONFIG_ISA_ARCV2
Alexey Brodkindb6ce232015-12-14 17:15:13 +0300393 if (!ioc_exists)
394#endif
395 __dc_line_op(start, end - start, OP_INV);
396
397#ifdef CONFIG_ISA_ARCV2
398 if (slc_exists && !ioc_exists)
Alexey Brodkinef639e62015-05-18 16:56:26 +0300399 __slc_line_op(start, end - start, OP_INV);
Alexey Brodkin2f16ac92014-02-04 12:56:14 +0400400#endif
Alexey Brodkin2f16ac92014-02-04 12:56:14 +0400401}
402
Alexey Brodkinef639e62015-05-18 16:56:26 +0300403void flush_dcache_range(unsigned long start, unsigned long end)
Alexey Brodkin2f16ac92014-02-04 12:56:14 +0400404{
Alexey Brodkinef639e62015-05-18 16:56:26 +0300405#ifdef CONFIG_ISA_ARCV2
Alexey Brodkindb6ce232015-12-14 17:15:13 +0300406 if (!ioc_exists)
407#endif
408 __dc_line_op(start, end - start, OP_FLUSH);
409
410#ifdef CONFIG_ISA_ARCV2
411 if (slc_exists && !ioc_exists)
Alexey Brodkinef639e62015-05-18 16:56:26 +0300412 __slc_line_op(start, end - start, OP_FLUSH);
413#endif
Alexey Brodkin2f16ac92014-02-04 12:56:14 +0400414}
415
416void flush_cache(unsigned long start, unsigned long size)
417{
418 flush_dcache_range(start, start + size);
419}
Alexey Brodkin6eb15e52015-03-30 13:36:04 +0300420
Alexey Brodkinef639e62015-05-18 16:56:26 +0300421void invalidate_dcache_all(void)
422{
Alexey Brodkinbd915082016-06-08 07:57:19 +0300423 __dc_entire_op(OP_INV);
Alexey Brodkindb6ce232015-12-14 17:15:13 +0300424
425#ifdef CONFIG_ISA_ARCV2
Alexey Brodkinbd915082016-06-08 07:57:19 +0300426 if (slc_exists)
Alexey Brodkinef639e62015-05-18 16:56:26 +0300427 __slc_entire_op(OP_INV);
428#endif
Alexey Brodkin6eb15e52015-03-30 13:36:04 +0300429}
430
Alexey Brodkinef639e62015-05-18 16:56:26 +0300431void flush_dcache_all(void)
Alexey Brodkin6eb15e52015-03-30 13:36:04 +0300432{
Alexey Brodkin2a8382c2016-04-16 15:28:30 +0300433 __dc_entire_op(OP_FLUSH);
Alexey Brodkindb6ce232015-12-14 17:15:13 +0300434
435#ifdef CONFIG_ISA_ARCV2
Alexey Brodkin2a8382c2016-04-16 15:28:30 +0300436 if (slc_exists)
Alexey Brodkinef639e62015-05-18 16:56:26 +0300437 __slc_entire_op(OP_FLUSH);
438#endif
Alexey Brodkin6eb15e52015-03-30 13:36:04 +0300439}