xref: /openbmc/u-boot/arch/arc/lib/cache.c (revision 41cada4d)
1660d5f0dSAlexey Brodkin /*
2660d5f0dSAlexey Brodkin  * Copyright (C) 2013-2014 Synopsys, Inc. All rights reserved.
3660d5f0dSAlexey Brodkin  *
4660d5f0dSAlexey Brodkin  * SPDX-License-Identifier:	GPL-2.0+
5660d5f0dSAlexey Brodkin  */
6660d5f0dSAlexey Brodkin 
7660d5f0dSAlexey Brodkin #include <config.h>
8379b3280SAlexey Brodkin #include <common.h>
9ef639e6fSAlexey Brodkin #include <linux/compiler.h>
10ef639e6fSAlexey Brodkin #include <linux/kernel.h>
1197a63144SAlexey Brodkin #include <linux/log2.h>
12660d5f0dSAlexey Brodkin #include <asm/arcregs.h>
13205e7a7bSAlexey Brodkin #include <asm/cache.h>
14660d5f0dSAlexey Brodkin 
15660d5f0dSAlexey Brodkin /* Bit values in IC_CTRL */
16660d5f0dSAlexey Brodkin #define IC_CTRL_CACHE_DISABLE	(1 << 0)
17660d5f0dSAlexey Brodkin 
18660d5f0dSAlexey Brodkin /* Bit values in DC_CTRL */
19660d5f0dSAlexey Brodkin #define DC_CTRL_CACHE_DISABLE	(1 << 0)
20660d5f0dSAlexey Brodkin #define DC_CTRL_INV_MODE_FLUSH	(1 << 6)
21660d5f0dSAlexey Brodkin #define DC_CTRL_FLUSH_STATUS	(1 << 8)
22660d5f0dSAlexey Brodkin #define CACHE_VER_NUM_MASK	0xF
23660d5f0dSAlexey Brodkin 
24ef639e6fSAlexey Brodkin #define OP_INV		0x1
25ef639e6fSAlexey Brodkin #define OP_FLUSH	0x2
26ef639e6fSAlexey Brodkin #define OP_INV_IC	0x3
27ef639e6fSAlexey Brodkin 
28*41cada4dSEugeniy Paltsev /* Bit val in SLC_CONTROL */
29*41cada4dSEugeniy Paltsev #define SLC_CTRL_DIS		0x001
30*41cada4dSEugeniy Paltsev #define SLC_CTRL_IM		0x040
31*41cada4dSEugeniy Paltsev #define SLC_CTRL_BUSY		0x100
32*41cada4dSEugeniy Paltsev #define SLC_CTRL_RGN_OP_INV	0x200
33*41cada4dSEugeniy Paltsev 
34ef639e6fSAlexey Brodkin /*
35ef639e6fSAlexey Brodkin  * By default that variable will fall into .bss section.
36ef639e6fSAlexey Brodkin  * But .bss section is not relocated and so it will be initilized before
37ef639e6fSAlexey Brodkin  * relocation but will be used after being zeroed.
38ef639e6fSAlexey Brodkin  */
39379b3280SAlexey Brodkin int l1_line_sz __section(".data");
403cf23939SEugeniy Paltsev bool dcache_exists __section(".data") = false;
413cf23939SEugeniy Paltsev bool icache_exists __section(".data") = false;
42379b3280SAlexey Brodkin 
43379b3280SAlexey Brodkin #define CACHE_LINE_MASK		(~(l1_line_sz - 1))
44379b3280SAlexey Brodkin 
45379b3280SAlexey Brodkin #ifdef CONFIG_ISA_ARCV2
46ef639e6fSAlexey Brodkin int slc_line_sz __section(".data");
473cf23939SEugeniy Paltsev bool slc_exists __section(".data") = false;
483cf23939SEugeniy Paltsev bool ioc_exists __section(".data") = false;
49*41cada4dSEugeniy Paltsev bool pae_exists __section(".data") = false;
50ef639e6fSAlexey Brodkin 
51*41cada4dSEugeniy Paltsev void read_decode_mmu_bcr(void)
52ef639e6fSAlexey Brodkin {
53*41cada4dSEugeniy Paltsev 	/* TODO: should we compare mmu version from BCR and from CONFIG? */
54*41cada4dSEugeniy Paltsev #if (CONFIG_ARC_MMU_VER >= 4)
55*41cada4dSEugeniy Paltsev 	u32 tmp;
56ef639e6fSAlexey Brodkin 
57*41cada4dSEugeniy Paltsev 	tmp = read_aux_reg(ARC_AUX_MMU_BCR);
58ef639e6fSAlexey Brodkin 
59*41cada4dSEugeniy Paltsev 	struct bcr_mmu_4 {
60*41cada4dSEugeniy Paltsev #ifdef CONFIG_CPU_BIG_ENDIAN
61*41cada4dSEugeniy Paltsev 	unsigned int ver:8, sasid:1, sz1:4, sz0:4, res:2, pae:1,
62*41cada4dSEugeniy Paltsev 		     n_ways:2, n_entry:2, n_super:2, u_itlb:3, u_dtlb:3;
63ef639e6fSAlexey Brodkin #else
64*41cada4dSEugeniy Paltsev 	/*           DTLB      ITLB      JES        JE         JA      */
65*41cada4dSEugeniy Paltsev 	unsigned int u_dtlb:3, u_itlb:3, n_super:2, n_entry:2, n_ways:2,
66*41cada4dSEugeniy Paltsev 		     pae:1, res:2, sz0:4, sz1:4, sasid:1, ver:8;
67*41cada4dSEugeniy Paltsev #endif /* CONFIG_CPU_BIG_ENDIAN */
68*41cada4dSEugeniy Paltsev 	} *mmu4;
69*41cada4dSEugeniy Paltsev 
70*41cada4dSEugeniy Paltsev 	mmu4 = (struct bcr_mmu_4 *)&tmp;
71*41cada4dSEugeniy Paltsev 
72*41cada4dSEugeniy Paltsev 	pae_exists = !!mmu4->pae;
73*41cada4dSEugeniy Paltsev #endif /* (CONFIG_ARC_MMU_VER >= 4) */
74*41cada4dSEugeniy Paltsev }
75*41cada4dSEugeniy Paltsev 
76*41cada4dSEugeniy Paltsev static void __slc_entire_op(const int op)
77*41cada4dSEugeniy Paltsev {
78*41cada4dSEugeniy Paltsev 	unsigned int ctrl;
79*41cada4dSEugeniy Paltsev 
80*41cada4dSEugeniy Paltsev 	ctrl = read_aux_reg(ARC_AUX_SLC_CTRL);
81*41cada4dSEugeniy Paltsev 
82*41cada4dSEugeniy Paltsev 	if (!(op & OP_FLUSH))		/* i.e. OP_INV */
83*41cada4dSEugeniy Paltsev 		ctrl &= ~SLC_CTRL_IM;	/* clear IM: Disable flush before Inv */
84*41cada4dSEugeniy Paltsev 	else
85*41cada4dSEugeniy Paltsev 		ctrl |= SLC_CTRL_IM;
86*41cada4dSEugeniy Paltsev 
87*41cada4dSEugeniy Paltsev 	write_aux_reg(ARC_AUX_SLC_CTRL, ctrl);
88*41cada4dSEugeniy Paltsev 
89*41cada4dSEugeniy Paltsev 	if (op & OP_INV)	/* Inv or flush-n-inv use same cmd reg */
90*41cada4dSEugeniy Paltsev 		write_aux_reg(ARC_AUX_SLC_INVALIDATE, 0x1);
91*41cada4dSEugeniy Paltsev 	else
92*41cada4dSEugeniy Paltsev 		write_aux_reg(ARC_AUX_SLC_FLUSH, 0x1);
93*41cada4dSEugeniy Paltsev 
94*41cada4dSEugeniy Paltsev 	/* Make sure "busy" bit reports correct stataus, see STAR 9001165532 */
95*41cada4dSEugeniy Paltsev 	read_aux_reg(ARC_AUX_SLC_CTRL);
96*41cada4dSEugeniy Paltsev 
97*41cada4dSEugeniy Paltsev 	/* Important to wait for flush to complete */
98*41cada4dSEugeniy Paltsev 	while (read_aux_reg(ARC_AUX_SLC_CTRL) & SLC_CTRL_BUSY);
99*41cada4dSEugeniy Paltsev }
100*41cada4dSEugeniy Paltsev 
101*41cada4dSEugeniy Paltsev static void slc_upper_region_init(void)
102*41cada4dSEugeniy Paltsev {
103*41cada4dSEugeniy Paltsev 	/*
104*41cada4dSEugeniy Paltsev 	 * ARC_AUX_SLC_RGN_END1 and ARC_AUX_SLC_RGN_START1 are always == 0
105*41cada4dSEugeniy Paltsev 	 * as we don't use PAE40.
106*41cada4dSEugeniy Paltsev 	 */
107*41cada4dSEugeniy Paltsev 	write_aux_reg(ARC_AUX_SLC_RGN_END1, 0);
108*41cada4dSEugeniy Paltsev 	write_aux_reg(ARC_AUX_SLC_RGN_START1, 0);
109*41cada4dSEugeniy Paltsev }
110*41cada4dSEugeniy Paltsev 
111*41cada4dSEugeniy Paltsev static void __slc_rgn_op(unsigned long paddr, unsigned long sz, const int op)
112*41cada4dSEugeniy Paltsev {
113*41cada4dSEugeniy Paltsev 	unsigned int ctrl;
114*41cada4dSEugeniy Paltsev 	unsigned long end;
115*41cada4dSEugeniy Paltsev 
116*41cada4dSEugeniy Paltsev 	/*
117*41cada4dSEugeniy Paltsev 	 * The Region Flush operation is specified by CTRL.RGN_OP[11..9]
118*41cada4dSEugeniy Paltsev 	 *  - b'000 (default) is Flush,
119*41cada4dSEugeniy Paltsev 	 *  - b'001 is Invalidate if CTRL.IM == 0
120*41cada4dSEugeniy Paltsev 	 *  - b'001 is Flush-n-Invalidate if CTRL.IM == 1
121*41cada4dSEugeniy Paltsev 	 */
122*41cada4dSEugeniy Paltsev 	ctrl = read_aux_reg(ARC_AUX_SLC_CTRL);
123*41cada4dSEugeniy Paltsev 
124*41cada4dSEugeniy Paltsev 	/* Don't rely on default value of IM bit */
125*41cada4dSEugeniy Paltsev 	if (!(op & OP_FLUSH))		/* i.e. OP_INV */
126*41cada4dSEugeniy Paltsev 		ctrl &= ~SLC_CTRL_IM;	/* clear IM: Disable flush before Inv */
127*41cada4dSEugeniy Paltsev 	else
128*41cada4dSEugeniy Paltsev 		ctrl |= SLC_CTRL_IM;
129*41cada4dSEugeniy Paltsev 
130*41cada4dSEugeniy Paltsev 	if (op & OP_INV)
131*41cada4dSEugeniy Paltsev 		ctrl |= SLC_CTRL_RGN_OP_INV;	/* Inv or flush-n-inv */
132*41cada4dSEugeniy Paltsev 	else
133*41cada4dSEugeniy Paltsev 		ctrl &= ~SLC_CTRL_RGN_OP_INV;
134*41cada4dSEugeniy Paltsev 
135*41cada4dSEugeniy Paltsev 	write_aux_reg(ARC_AUX_SLC_CTRL, ctrl);
136*41cada4dSEugeniy Paltsev 
137*41cada4dSEugeniy Paltsev 	/*
138*41cada4dSEugeniy Paltsev 	 * Lower bits are ignored, no need to clip
139*41cada4dSEugeniy Paltsev 	 * END needs to be setup before START (latter triggers the operation)
140*41cada4dSEugeniy Paltsev 	 * END can't be same as START, so add (l2_line_sz - 1) to sz
141*41cada4dSEugeniy Paltsev 	 */
142*41cada4dSEugeniy Paltsev 	end = paddr + sz + slc_line_sz - 1;
143*41cada4dSEugeniy Paltsev 
144*41cada4dSEugeniy Paltsev 	/*
145*41cada4dSEugeniy Paltsev 	 * Upper addresses (ARC_AUX_SLC_RGN_END1 and ARC_AUX_SLC_RGN_START1)
146*41cada4dSEugeniy Paltsev 	 * are always == 0 as we don't use PAE40, so we only setup lower ones
147*41cada4dSEugeniy Paltsev 	 * (ARC_AUX_SLC_RGN_END and ARC_AUX_SLC_RGN_START)
148*41cada4dSEugeniy Paltsev 	 */
149*41cada4dSEugeniy Paltsev 	write_aux_reg(ARC_AUX_SLC_RGN_END, end);
150*41cada4dSEugeniy Paltsev 	write_aux_reg(ARC_AUX_SLC_RGN_START, paddr);
151*41cada4dSEugeniy Paltsev 
152*41cada4dSEugeniy Paltsev 	/* Make sure "busy" bit reports correct stataus, see STAR 9001165532 */
153*41cada4dSEugeniy Paltsev 	read_aux_reg(ARC_AUX_SLC_CTRL);
154*41cada4dSEugeniy Paltsev 
155*41cada4dSEugeniy Paltsev 	while (read_aux_reg(ARC_AUX_SLC_CTRL) & SLC_CTRL_BUSY);
156*41cada4dSEugeniy Paltsev }
157*41cada4dSEugeniy Paltsev #endif /* CONFIG_ISA_ARCV2 */
158ef639e6fSAlexey Brodkin 
159379b3280SAlexey Brodkin #ifdef CONFIG_ISA_ARCV2
160379b3280SAlexey Brodkin static void read_decode_cache_bcr_arcv2(void)
161ef639e6fSAlexey Brodkin {
162379b3280SAlexey Brodkin 	union {
163379b3280SAlexey Brodkin 		struct {
164379b3280SAlexey Brodkin #ifdef CONFIG_CPU_BIG_ENDIAN
165379b3280SAlexey Brodkin 			unsigned int pad:24, way:2, lsz:2, sz:4;
166379b3280SAlexey Brodkin #else
167379b3280SAlexey Brodkin 			unsigned int sz:4, lsz:2, way:2, pad:24;
168379b3280SAlexey Brodkin #endif
169379b3280SAlexey Brodkin 		} fields;
170379b3280SAlexey Brodkin 		unsigned int word;
171379b3280SAlexey Brodkin 	} slc_cfg;
172379b3280SAlexey Brodkin 
173379b3280SAlexey Brodkin 	union {
174379b3280SAlexey Brodkin 		struct {
175379b3280SAlexey Brodkin #ifdef CONFIG_CPU_BIG_ENDIAN
176379b3280SAlexey Brodkin 			unsigned int pad:24, ver:8;
177379b3280SAlexey Brodkin #else
178379b3280SAlexey Brodkin 			unsigned int ver:8, pad:24;
179379b3280SAlexey Brodkin #endif
180379b3280SAlexey Brodkin 		} fields;
181379b3280SAlexey Brodkin 		unsigned int word;
182379b3280SAlexey Brodkin 	} sbcr;
183379b3280SAlexey Brodkin 
184379b3280SAlexey Brodkin 	sbcr.word = read_aux_reg(ARC_BCR_SLC);
185379b3280SAlexey Brodkin 	if (sbcr.fields.ver) {
186379b3280SAlexey Brodkin 		slc_cfg.word = read_aux_reg(ARC_AUX_SLC_CONFIG);
1873cf23939SEugeniy Paltsev 		slc_exists = true;
188379b3280SAlexey Brodkin 		slc_line_sz = (slc_cfg.fields.lsz == 0) ? 128 : 64;
189379b3280SAlexey Brodkin 	}
190db6ce231SAlexey Brodkin 
191db6ce231SAlexey Brodkin 	union {
192db6ce231SAlexey Brodkin 		struct bcr_clust_cfg {
193db6ce231SAlexey Brodkin #ifdef CONFIG_CPU_BIG_ENDIAN
194db6ce231SAlexey Brodkin 			unsigned int pad:7, c:1, num_entries:8, num_cores:8, ver:8;
195db6ce231SAlexey Brodkin #else
196db6ce231SAlexey Brodkin 			unsigned int ver:8, num_cores:8, num_entries:8, c:1, pad:7;
197db6ce231SAlexey Brodkin #endif
198db6ce231SAlexey Brodkin 		} fields;
199db6ce231SAlexey Brodkin 		unsigned int word;
200db6ce231SAlexey Brodkin 	} cbcr;
201db6ce231SAlexey Brodkin 
202db6ce231SAlexey Brodkin 	cbcr.word = read_aux_reg(ARC_BCR_CLUSTER);
203db6ce231SAlexey Brodkin 	if (cbcr.fields.c)
2043cf23939SEugeniy Paltsev 		ioc_exists = true;
205379b3280SAlexey Brodkin }
206379b3280SAlexey Brodkin #endif
207379b3280SAlexey Brodkin 
208379b3280SAlexey Brodkin void read_decode_cache_bcr(void)
209379b3280SAlexey Brodkin {
210379b3280SAlexey Brodkin 	int dc_line_sz = 0, ic_line_sz = 0;
211379b3280SAlexey Brodkin 
212379b3280SAlexey Brodkin 	union {
213379b3280SAlexey Brodkin 		struct {
214379b3280SAlexey Brodkin #ifdef CONFIG_CPU_BIG_ENDIAN
215379b3280SAlexey Brodkin 			unsigned int pad:12, line_len:4, sz:4, config:4, ver:8;
216379b3280SAlexey Brodkin #else
217379b3280SAlexey Brodkin 			unsigned int ver:8, config:4, sz:4, line_len:4, pad:12;
218379b3280SAlexey Brodkin #endif
219379b3280SAlexey Brodkin 		} fields;
220379b3280SAlexey Brodkin 		unsigned int word;
221379b3280SAlexey Brodkin 	} ibcr, dbcr;
222379b3280SAlexey Brodkin 
223379b3280SAlexey Brodkin 	ibcr.word = read_aux_reg(ARC_BCR_IC_BUILD);
224379b3280SAlexey Brodkin 	if (ibcr.fields.ver) {
2253cf23939SEugeniy Paltsev 		icache_exists = true;
226379b3280SAlexey Brodkin 		l1_line_sz = ic_line_sz = 8 << ibcr.fields.line_len;
227379b3280SAlexey Brodkin 		if (!ic_line_sz)
228379b3280SAlexey Brodkin 			panic("Instruction exists but line length is 0\n");
229ef639e6fSAlexey Brodkin 	}
230ef639e6fSAlexey Brodkin 
231379b3280SAlexey Brodkin 	dbcr.word = read_aux_reg(ARC_BCR_DC_BUILD);
232379b3280SAlexey Brodkin 	if (dbcr.fields.ver){
2333cf23939SEugeniy Paltsev 		dcache_exists = true;
234379b3280SAlexey Brodkin 		l1_line_sz = dc_line_sz = 16 << dbcr.fields.line_len;
235379b3280SAlexey Brodkin 		if (!dc_line_sz)
236379b3280SAlexey Brodkin 			panic("Data cache exists but line length is 0\n");
237379b3280SAlexey Brodkin 	}
238379b3280SAlexey Brodkin 
239379b3280SAlexey Brodkin 	if (ic_line_sz && dc_line_sz && (ic_line_sz != dc_line_sz))
240379b3280SAlexey Brodkin 		panic("Instruction and data cache line lengths differ\n");
241ef639e6fSAlexey Brodkin }
242ef639e6fSAlexey Brodkin 
243ef639e6fSAlexey Brodkin void cache_init(void)
244ef639e6fSAlexey Brodkin {
245379b3280SAlexey Brodkin 	read_decode_cache_bcr();
246379b3280SAlexey Brodkin 
247ef639e6fSAlexey Brodkin #ifdef CONFIG_ISA_ARCV2
248379b3280SAlexey Brodkin 	read_decode_cache_bcr_arcv2();
249db6ce231SAlexey Brodkin 
250db6ce231SAlexey Brodkin 	if (ioc_exists) {
25197a63144SAlexey Brodkin 		/* IOC Aperture start is equal to DDR start */
25297a63144SAlexey Brodkin 		unsigned int ap_base = CONFIG_SYS_SDRAM_BASE;
25397a63144SAlexey Brodkin 		/* IOC Aperture size is equal to DDR size */
25497a63144SAlexey Brodkin 		long ap_size = CONFIG_SYS_SDRAM_SIZE;
25597a63144SAlexey Brodkin 
256a4a43fcfSAlexey Brodkin 		flush_dcache_all();
257a4a43fcfSAlexey Brodkin 		invalidate_dcache_all();
258a4a43fcfSAlexey Brodkin 
25997a63144SAlexey Brodkin 		if (!is_power_of_2(ap_size) || ap_size < 4096)
26097a63144SAlexey Brodkin 			panic("IOC Aperture size must be power of 2 and bigger 4Kib");
26197a63144SAlexey Brodkin 
26297a63144SAlexey Brodkin 		/*
26397a63144SAlexey Brodkin 		 * IOC Aperture size decoded as 2 ^ (SIZE + 2) KB,
26497a63144SAlexey Brodkin 		 * so setting 0x11 implies 512M, 0x12 implies 1G...
26597a63144SAlexey Brodkin 		 */
26697a63144SAlexey Brodkin 		write_aux_reg(ARC_AUX_IO_COH_AP0_SIZE,
26797a63144SAlexey Brodkin 			      order_base_2(ap_size/1024) - 2);
26897a63144SAlexey Brodkin 
26997a63144SAlexey Brodkin 
27097a63144SAlexey Brodkin 		/* IOC Aperture start must be aligned to the size of the aperture */
27197a63144SAlexey Brodkin 		if (ap_base % ap_size != 0)
27297a63144SAlexey Brodkin 			panic("IOC Aperture start must be aligned to the size of the aperture");
27397a63144SAlexey Brodkin 
27497a63144SAlexey Brodkin 		write_aux_reg(ARC_AUX_IO_COH_AP0_BASE, ap_base >> 12);
275db6ce231SAlexey Brodkin 		write_aux_reg(ARC_AUX_IO_COH_PARTIAL, 1);
276db6ce231SAlexey Brodkin 		write_aux_reg(ARC_AUX_IO_COH_ENABLE, 1);
27797a63144SAlexey Brodkin 
278db6ce231SAlexey Brodkin 	}
279*41cada4dSEugeniy Paltsev 
280*41cada4dSEugeniy Paltsev 	read_decode_mmu_bcr();
281*41cada4dSEugeniy Paltsev 
282*41cada4dSEugeniy Paltsev 	/*
283*41cada4dSEugeniy Paltsev 	 * ARC_AUX_SLC_RGN_START1 and ARC_AUX_SLC_RGN_END1 register exist
284*41cada4dSEugeniy Paltsev 	 * only if PAE exists in current HW. So we had to check pae_exist
285*41cada4dSEugeniy Paltsev 	 * before using them.
286*41cada4dSEugeniy Paltsev 	 */
287*41cada4dSEugeniy Paltsev 	if (slc_exists && pae_exists)
288*41cada4dSEugeniy Paltsev 		slc_upper_region_init();
289*41cada4dSEugeniy Paltsev #endif /* CONFIG_ISA_ARCV2 */
290ef639e6fSAlexey Brodkin }
291ef639e6fSAlexey Brodkin 
292660d5f0dSAlexey Brodkin int icache_status(void)
293660d5f0dSAlexey Brodkin {
294379b3280SAlexey Brodkin 	if (!icache_exists)
295660d5f0dSAlexey Brodkin 		return 0;
296660d5f0dSAlexey Brodkin 
297ef639e6fSAlexey Brodkin 	if (read_aux_reg(ARC_AUX_IC_CTRL) & IC_CTRL_CACHE_DISABLE)
298ef639e6fSAlexey Brodkin 		return 0;
299ef639e6fSAlexey Brodkin 	else
300ef639e6fSAlexey Brodkin 		return 1;
301660d5f0dSAlexey Brodkin }
302660d5f0dSAlexey Brodkin 
303660d5f0dSAlexey Brodkin void icache_enable(void)
304660d5f0dSAlexey Brodkin {
305379b3280SAlexey Brodkin 	if (icache_exists)
306660d5f0dSAlexey Brodkin 		write_aux_reg(ARC_AUX_IC_CTRL, read_aux_reg(ARC_AUX_IC_CTRL) &
307660d5f0dSAlexey Brodkin 			      ~IC_CTRL_CACHE_DISABLE);
308660d5f0dSAlexey Brodkin }
309660d5f0dSAlexey Brodkin 
310660d5f0dSAlexey Brodkin void icache_disable(void)
311660d5f0dSAlexey Brodkin {
312379b3280SAlexey Brodkin 	if (icache_exists)
313660d5f0dSAlexey Brodkin 		write_aux_reg(ARC_AUX_IC_CTRL, read_aux_reg(ARC_AUX_IC_CTRL) |
314660d5f0dSAlexey Brodkin 			      IC_CTRL_CACHE_DISABLE);
315660d5f0dSAlexey Brodkin }
316660d5f0dSAlexey Brodkin 
317660d5f0dSAlexey Brodkin void invalidate_icache_all(void)
318660d5f0dSAlexey Brodkin {
319660d5f0dSAlexey Brodkin 	/* Any write to IC_IVIC register triggers invalidation of entire I$ */
320ef639e6fSAlexey Brodkin 	if (icache_status()) {
321660d5f0dSAlexey Brodkin 		write_aux_reg(ARC_AUX_IC_IVIC, 1);
322f2a22678SAlexey Brodkin 		/*
323f2a22678SAlexey Brodkin 		 * As per ARC HS databook (see chapter 5.3.3.2)
324f2a22678SAlexey Brodkin 		 * it is required to add 3 NOPs after each write to IC_IVIC.
325f2a22678SAlexey Brodkin 		 */
326f2a22678SAlexey Brodkin 		__builtin_arc_nop();
327f2a22678SAlexey Brodkin 		__builtin_arc_nop();
328f2a22678SAlexey Brodkin 		__builtin_arc_nop();
329ef639e6fSAlexey Brodkin 		read_aux_reg(ARC_AUX_IC_CTRL);	/* blocks */
330660d5f0dSAlexey Brodkin 	}
331*41cada4dSEugeniy Paltsev 
332*41cada4dSEugeniy Paltsev #ifdef CONFIG_ISA_ARCV2
333*41cada4dSEugeniy Paltsev 	if (slc_exists)
334*41cada4dSEugeniy Paltsev 		__slc_entire_op(OP_INV);
335ef639e6fSAlexey Brodkin #endif
336*41cada4dSEugeniy Paltsev }
337660d5f0dSAlexey Brodkin 
338660d5f0dSAlexey Brodkin int dcache_status(void)
339660d5f0dSAlexey Brodkin {
340379b3280SAlexey Brodkin 	if (!dcache_exists)
341660d5f0dSAlexey Brodkin 		return 0;
342660d5f0dSAlexey Brodkin 
343ef639e6fSAlexey Brodkin 	if (read_aux_reg(ARC_AUX_DC_CTRL) & DC_CTRL_CACHE_DISABLE)
344ef639e6fSAlexey Brodkin 		return 0;
345ef639e6fSAlexey Brodkin 	else
346ef639e6fSAlexey Brodkin 		return 1;
347660d5f0dSAlexey Brodkin }
348660d5f0dSAlexey Brodkin 
349660d5f0dSAlexey Brodkin void dcache_enable(void)
350660d5f0dSAlexey Brodkin {
351379b3280SAlexey Brodkin 	if (!dcache_exists)
352660d5f0dSAlexey Brodkin 		return;
353660d5f0dSAlexey Brodkin 
354660d5f0dSAlexey Brodkin 	write_aux_reg(ARC_AUX_DC_CTRL, read_aux_reg(ARC_AUX_DC_CTRL) &
355660d5f0dSAlexey Brodkin 		      ~(DC_CTRL_INV_MODE_FLUSH | DC_CTRL_CACHE_DISABLE));
356660d5f0dSAlexey Brodkin }
357660d5f0dSAlexey Brodkin 
358660d5f0dSAlexey Brodkin void dcache_disable(void)
359660d5f0dSAlexey Brodkin {
360379b3280SAlexey Brodkin 	if (!dcache_exists)
361660d5f0dSAlexey Brodkin 		return;
362660d5f0dSAlexey Brodkin 
363660d5f0dSAlexey Brodkin 	write_aux_reg(ARC_AUX_DC_CTRL, read_aux_reg(ARC_AUX_DC_CTRL) |
364660d5f0dSAlexey Brodkin 		      DC_CTRL_CACHE_DISABLE);
365660d5f0dSAlexey Brodkin }
366660d5f0dSAlexey Brodkin 
367660d5f0dSAlexey Brodkin #ifndef CONFIG_SYS_DCACHE_OFF
368660d5f0dSAlexey Brodkin /*
369ef639e6fSAlexey Brodkin  * Common Helper for Line Operations on {I,D}-Cache
370660d5f0dSAlexey Brodkin  */
371ef639e6fSAlexey Brodkin static inline void __cache_line_loop(unsigned long paddr, unsigned long sz,
372ef639e6fSAlexey Brodkin 				     const int cacheop)
373660d5f0dSAlexey Brodkin {
374ef639e6fSAlexey Brodkin 	unsigned int aux_cmd;
375ef639e6fSAlexey Brodkin #if (CONFIG_ARC_MMU_VER == 3)
376ef639e6fSAlexey Brodkin 	unsigned int aux_tag;
377ef639e6fSAlexey Brodkin #endif
378ef639e6fSAlexey Brodkin 	int num_lines;
379660d5f0dSAlexey Brodkin 
380ef639e6fSAlexey Brodkin 	if (cacheop == OP_INV_IC) {
381ef639e6fSAlexey Brodkin 		aux_cmd = ARC_AUX_IC_IVIL;
382ef639e6fSAlexey Brodkin #if (CONFIG_ARC_MMU_VER == 3)
383ef639e6fSAlexey Brodkin 		aux_tag = ARC_AUX_IC_PTAG;
384ef639e6fSAlexey Brodkin #endif
385ef639e6fSAlexey Brodkin 	} else {
386ef639e6fSAlexey Brodkin 		/* d$ cmd: INV (discard or wback-n-discard) OR FLUSH (wback) */
387ef639e6fSAlexey Brodkin 		aux_cmd = cacheop & OP_INV ? ARC_AUX_DC_IVDL : ARC_AUX_DC_FLDL;
388ef639e6fSAlexey Brodkin #if (CONFIG_ARC_MMU_VER == 3)
389ef639e6fSAlexey Brodkin 		aux_tag = ARC_AUX_DC_PTAG;
390ef639e6fSAlexey Brodkin #endif
391660d5f0dSAlexey Brodkin 	}
392660d5f0dSAlexey Brodkin 
393ef639e6fSAlexey Brodkin 	sz += paddr & ~CACHE_LINE_MASK;
394ef639e6fSAlexey Brodkin 	paddr &= CACHE_LINE_MASK;
395ef639e6fSAlexey Brodkin 
396379b3280SAlexey Brodkin 	num_lines = DIV_ROUND_UP(sz, l1_line_sz);
397ef639e6fSAlexey Brodkin 
398ef639e6fSAlexey Brodkin 	while (num_lines-- > 0) {
399ef639e6fSAlexey Brodkin #if (CONFIG_ARC_MMU_VER == 3)
400ef639e6fSAlexey Brodkin 		write_aux_reg(aux_tag, paddr);
401ef639e6fSAlexey Brodkin #endif
402ef639e6fSAlexey Brodkin 		write_aux_reg(aux_cmd, paddr);
403379b3280SAlexey Brodkin 		paddr += l1_line_sz;
404ef639e6fSAlexey Brodkin 	}
405ef639e6fSAlexey Brodkin }
406ef639e6fSAlexey Brodkin 
407ef639e6fSAlexey Brodkin static unsigned int __before_dc_op(const int op)
408ef639e6fSAlexey Brodkin {
409ef639e6fSAlexey Brodkin 	unsigned int reg;
410ef639e6fSAlexey Brodkin 
411ef639e6fSAlexey Brodkin 	if (op == OP_INV) {
412ef639e6fSAlexey Brodkin 		/*
413ef639e6fSAlexey Brodkin 		 * IM is set by default and implies Flush-n-inv
414ef639e6fSAlexey Brodkin 		 * Clear it here for vanilla inv
415ef639e6fSAlexey Brodkin 		 */
416ef639e6fSAlexey Brodkin 		reg = read_aux_reg(ARC_AUX_DC_CTRL);
417ef639e6fSAlexey Brodkin 		write_aux_reg(ARC_AUX_DC_CTRL, reg & ~DC_CTRL_INV_MODE_FLUSH);
418ef639e6fSAlexey Brodkin 	}
419ef639e6fSAlexey Brodkin 
420ef639e6fSAlexey Brodkin 	return reg;
421ef639e6fSAlexey Brodkin }
422ef639e6fSAlexey Brodkin 
423ef639e6fSAlexey Brodkin static void __after_dc_op(const int op, unsigned int reg)
424ef639e6fSAlexey Brodkin {
425ef639e6fSAlexey Brodkin 	if (op & OP_FLUSH)	/* flush / flush-n-inv both wait */
426ef639e6fSAlexey Brodkin 		while (read_aux_reg(ARC_AUX_DC_CTRL) & DC_CTRL_FLUSH_STATUS)
427ef639e6fSAlexey Brodkin 			;
428ef639e6fSAlexey Brodkin 
429ef639e6fSAlexey Brodkin 	/* Switch back to default Invalidate mode */
430ef639e6fSAlexey Brodkin 	if (op == OP_INV)
431ef639e6fSAlexey Brodkin 		write_aux_reg(ARC_AUX_DC_CTRL, reg | DC_CTRL_INV_MODE_FLUSH);
432ef639e6fSAlexey Brodkin }
433ef639e6fSAlexey Brodkin 
434ef639e6fSAlexey Brodkin static inline void __dc_entire_op(const int cacheop)
435ef639e6fSAlexey Brodkin {
436ef639e6fSAlexey Brodkin 	int aux;
437ef639e6fSAlexey Brodkin 	unsigned int ctrl_reg = __before_dc_op(cacheop);
438ef639e6fSAlexey Brodkin 
439ef639e6fSAlexey Brodkin 	if (cacheop & OP_INV)	/* Inv or flush-n-inv use same cmd reg */
440ef639e6fSAlexey Brodkin 		aux = ARC_AUX_DC_IVDC;
441ef639e6fSAlexey Brodkin 	else
442ef639e6fSAlexey Brodkin 		aux = ARC_AUX_DC_FLSH;
443ef639e6fSAlexey Brodkin 
444ef639e6fSAlexey Brodkin 	write_aux_reg(aux, 0x1);
445ef639e6fSAlexey Brodkin 
446ef639e6fSAlexey Brodkin 	__after_dc_op(cacheop, ctrl_reg);
447ef639e6fSAlexey Brodkin }
448ef639e6fSAlexey Brodkin 
449ef639e6fSAlexey Brodkin static inline void __dc_line_op(unsigned long paddr, unsigned long sz,
450ef639e6fSAlexey Brodkin 				const int cacheop)
451ef639e6fSAlexey Brodkin {
452ef639e6fSAlexey Brodkin 	unsigned int ctrl_reg = __before_dc_op(cacheop);
453ef639e6fSAlexey Brodkin 	__cache_line_loop(paddr, sz, cacheop);
454ef639e6fSAlexey Brodkin 	__after_dc_op(cacheop, ctrl_reg);
455ef639e6fSAlexey Brodkin }
456ef639e6fSAlexey Brodkin #else
457ef639e6fSAlexey Brodkin #define __dc_entire_op(cacheop)
458ef639e6fSAlexey Brodkin #define __dc_line_op(paddr, sz, cacheop)
459ef639e6fSAlexey Brodkin #endif /* !CONFIG_SYS_DCACHE_OFF */
460ef639e6fSAlexey Brodkin 
461660d5f0dSAlexey Brodkin void invalidate_dcache_range(unsigned long start, unsigned long end)
462660d5f0dSAlexey Brodkin {
463*41cada4dSEugeniy Paltsev 	if (start >= end)
464*41cada4dSEugeniy Paltsev 		return;
465*41cada4dSEugeniy Paltsev 
466ef639e6fSAlexey Brodkin #ifdef CONFIG_ISA_ARCV2
467db6ce231SAlexey Brodkin 	if (!ioc_exists)
468db6ce231SAlexey Brodkin #endif
469db6ce231SAlexey Brodkin 		__dc_line_op(start, end - start, OP_INV);
470db6ce231SAlexey Brodkin 
471db6ce231SAlexey Brodkin #ifdef CONFIG_ISA_ARCV2
472db6ce231SAlexey Brodkin 	if (slc_exists && !ioc_exists)
473*41cada4dSEugeniy Paltsev 		__slc_rgn_op(start, end - start, OP_INV);
474660d5f0dSAlexey Brodkin #endif
475660d5f0dSAlexey Brodkin }
476660d5f0dSAlexey Brodkin 
477ef639e6fSAlexey Brodkin void flush_dcache_range(unsigned long start, unsigned long end)
478660d5f0dSAlexey Brodkin {
479*41cada4dSEugeniy Paltsev 	if (start >= end)
480*41cada4dSEugeniy Paltsev 		return;
481*41cada4dSEugeniy Paltsev 
482ef639e6fSAlexey Brodkin #ifdef CONFIG_ISA_ARCV2
483db6ce231SAlexey Brodkin 	if (!ioc_exists)
484db6ce231SAlexey Brodkin #endif
485db6ce231SAlexey Brodkin 		__dc_line_op(start, end - start, OP_FLUSH);
486db6ce231SAlexey Brodkin 
487db6ce231SAlexey Brodkin #ifdef CONFIG_ISA_ARCV2
488db6ce231SAlexey Brodkin 	if (slc_exists && !ioc_exists)
489*41cada4dSEugeniy Paltsev 		__slc_rgn_op(start, end - start, OP_FLUSH);
490ef639e6fSAlexey Brodkin #endif
491660d5f0dSAlexey Brodkin }
492660d5f0dSAlexey Brodkin 
493660d5f0dSAlexey Brodkin void flush_cache(unsigned long start, unsigned long size)
494660d5f0dSAlexey Brodkin {
495660d5f0dSAlexey Brodkin 	flush_dcache_range(start, start + size);
496660d5f0dSAlexey Brodkin }
4976eb15e50SAlexey Brodkin 
498ef639e6fSAlexey Brodkin void invalidate_dcache_all(void)
499ef639e6fSAlexey Brodkin {
500db6ce231SAlexey Brodkin 	__dc_entire_op(OP_INV);
501db6ce231SAlexey Brodkin 
502db6ce231SAlexey Brodkin #ifdef CONFIG_ISA_ARCV2
503bd91508bSAlexey Brodkin 	if (slc_exists)
504ef639e6fSAlexey Brodkin 		__slc_entire_op(OP_INV);
505ef639e6fSAlexey Brodkin #endif
5066eb15e50SAlexey Brodkin }
5076eb15e50SAlexey Brodkin 
508ef639e6fSAlexey Brodkin void flush_dcache_all(void)
5096eb15e50SAlexey Brodkin {
510db6ce231SAlexey Brodkin 	__dc_entire_op(OP_FLUSH);
511db6ce231SAlexey Brodkin 
512db6ce231SAlexey Brodkin #ifdef CONFIG_ISA_ARCV2
5132a8382c6SAlexey Brodkin 	if (slc_exists)
514ef639e6fSAlexey Brodkin 		__slc_entire_op(OP_FLUSH);
515ef639e6fSAlexey Brodkin #endif
5166eb15e50SAlexey Brodkin }
517