xref: /openbmc/u-boot/arch/arc/lib/cache.c (revision b0146f9e)
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 
2841cada4dSEugeniy Paltsev /* Bit val in SLC_CONTROL */
2941cada4dSEugeniy Paltsev #define SLC_CTRL_DIS		0x001
3041cada4dSEugeniy Paltsev #define SLC_CTRL_IM		0x040
3141cada4dSEugeniy Paltsev #define SLC_CTRL_BUSY		0x100
3241cada4dSEugeniy Paltsev #define SLC_CTRL_RGN_OP_INV	0x200
3341cada4dSEugeniy 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;
4941cada4dSEugeniy Paltsev bool pae_exists __section(".data") = false;
50ef639e6fSAlexey Brodkin 
51*b0146f9eSEugeniy Paltsev /* To force enable IOC set ioc_enable to 'true' */
52*b0146f9eSEugeniy Paltsev bool ioc_enable __section(".data") = false;
53*b0146f9eSEugeniy Paltsev 
5441cada4dSEugeniy Paltsev void read_decode_mmu_bcr(void)
55ef639e6fSAlexey Brodkin {
5641cada4dSEugeniy Paltsev 	/* TODO: should we compare mmu version from BCR and from CONFIG? */
5741cada4dSEugeniy Paltsev #if (CONFIG_ARC_MMU_VER >= 4)
5841cada4dSEugeniy Paltsev 	u32 tmp;
59ef639e6fSAlexey Brodkin 
6041cada4dSEugeniy Paltsev 	tmp = read_aux_reg(ARC_AUX_MMU_BCR);
61ef639e6fSAlexey Brodkin 
6241cada4dSEugeniy Paltsev 	struct bcr_mmu_4 {
6341cada4dSEugeniy Paltsev #ifdef CONFIG_CPU_BIG_ENDIAN
6441cada4dSEugeniy Paltsev 	unsigned int ver:8, sasid:1, sz1:4, sz0:4, res:2, pae:1,
6541cada4dSEugeniy Paltsev 		     n_ways:2, n_entry:2, n_super:2, u_itlb:3, u_dtlb:3;
66ef639e6fSAlexey Brodkin #else
6741cada4dSEugeniy Paltsev 	/*           DTLB      ITLB      JES        JE         JA      */
6841cada4dSEugeniy Paltsev 	unsigned int u_dtlb:3, u_itlb:3, n_super:2, n_entry:2, n_ways:2,
6941cada4dSEugeniy Paltsev 		     pae:1, res:2, sz0:4, sz1:4, sasid:1, ver:8;
7041cada4dSEugeniy Paltsev #endif /* CONFIG_CPU_BIG_ENDIAN */
7141cada4dSEugeniy Paltsev 	} *mmu4;
7241cada4dSEugeniy Paltsev 
7341cada4dSEugeniy Paltsev 	mmu4 = (struct bcr_mmu_4 *)&tmp;
7441cada4dSEugeniy Paltsev 
7541cada4dSEugeniy Paltsev 	pae_exists = !!mmu4->pae;
7641cada4dSEugeniy Paltsev #endif /* (CONFIG_ARC_MMU_VER >= 4) */
7741cada4dSEugeniy Paltsev }
7841cada4dSEugeniy Paltsev 
7941cada4dSEugeniy Paltsev static void __slc_entire_op(const int op)
8041cada4dSEugeniy Paltsev {
8141cada4dSEugeniy Paltsev 	unsigned int ctrl;
8241cada4dSEugeniy Paltsev 
8341cada4dSEugeniy Paltsev 	ctrl = read_aux_reg(ARC_AUX_SLC_CTRL);
8441cada4dSEugeniy Paltsev 
8541cada4dSEugeniy Paltsev 	if (!(op & OP_FLUSH))		/* i.e. OP_INV */
8641cada4dSEugeniy Paltsev 		ctrl &= ~SLC_CTRL_IM;	/* clear IM: Disable flush before Inv */
8741cada4dSEugeniy Paltsev 	else
8841cada4dSEugeniy Paltsev 		ctrl |= SLC_CTRL_IM;
8941cada4dSEugeniy Paltsev 
9041cada4dSEugeniy Paltsev 	write_aux_reg(ARC_AUX_SLC_CTRL, ctrl);
9141cada4dSEugeniy Paltsev 
9241cada4dSEugeniy Paltsev 	if (op & OP_INV)	/* Inv or flush-n-inv use same cmd reg */
9341cada4dSEugeniy Paltsev 		write_aux_reg(ARC_AUX_SLC_INVALIDATE, 0x1);
9441cada4dSEugeniy Paltsev 	else
9541cada4dSEugeniy Paltsev 		write_aux_reg(ARC_AUX_SLC_FLUSH, 0x1);
9641cada4dSEugeniy Paltsev 
9741cada4dSEugeniy Paltsev 	/* Make sure "busy" bit reports correct stataus, see STAR 9001165532 */
9841cada4dSEugeniy Paltsev 	read_aux_reg(ARC_AUX_SLC_CTRL);
9941cada4dSEugeniy Paltsev 
10041cada4dSEugeniy Paltsev 	/* Important to wait for flush to complete */
10141cada4dSEugeniy Paltsev 	while (read_aux_reg(ARC_AUX_SLC_CTRL) & SLC_CTRL_BUSY);
10241cada4dSEugeniy Paltsev }
10341cada4dSEugeniy Paltsev 
10441cada4dSEugeniy Paltsev static void slc_upper_region_init(void)
10541cada4dSEugeniy Paltsev {
10641cada4dSEugeniy Paltsev 	/*
10741cada4dSEugeniy Paltsev 	 * ARC_AUX_SLC_RGN_END1 and ARC_AUX_SLC_RGN_START1 are always == 0
10841cada4dSEugeniy Paltsev 	 * as we don't use PAE40.
10941cada4dSEugeniy Paltsev 	 */
11041cada4dSEugeniy Paltsev 	write_aux_reg(ARC_AUX_SLC_RGN_END1, 0);
11141cada4dSEugeniy Paltsev 	write_aux_reg(ARC_AUX_SLC_RGN_START1, 0);
11241cada4dSEugeniy Paltsev }
11341cada4dSEugeniy Paltsev 
11441cada4dSEugeniy Paltsev static void __slc_rgn_op(unsigned long paddr, unsigned long sz, const int op)
11541cada4dSEugeniy Paltsev {
11641cada4dSEugeniy Paltsev 	unsigned int ctrl;
11741cada4dSEugeniy Paltsev 	unsigned long end;
11841cada4dSEugeniy Paltsev 
11941cada4dSEugeniy Paltsev 	/*
12041cada4dSEugeniy Paltsev 	 * The Region Flush operation is specified by CTRL.RGN_OP[11..9]
12141cada4dSEugeniy Paltsev 	 *  - b'000 (default) is Flush,
12241cada4dSEugeniy Paltsev 	 *  - b'001 is Invalidate if CTRL.IM == 0
12341cada4dSEugeniy Paltsev 	 *  - b'001 is Flush-n-Invalidate if CTRL.IM == 1
12441cada4dSEugeniy Paltsev 	 */
12541cada4dSEugeniy Paltsev 	ctrl = read_aux_reg(ARC_AUX_SLC_CTRL);
12641cada4dSEugeniy Paltsev 
12741cada4dSEugeniy Paltsev 	/* Don't rely on default value of IM bit */
12841cada4dSEugeniy Paltsev 	if (!(op & OP_FLUSH))		/* i.e. OP_INV */
12941cada4dSEugeniy Paltsev 		ctrl &= ~SLC_CTRL_IM;	/* clear IM: Disable flush before Inv */
13041cada4dSEugeniy Paltsev 	else
13141cada4dSEugeniy Paltsev 		ctrl |= SLC_CTRL_IM;
13241cada4dSEugeniy Paltsev 
13341cada4dSEugeniy Paltsev 	if (op & OP_INV)
13441cada4dSEugeniy Paltsev 		ctrl |= SLC_CTRL_RGN_OP_INV;	/* Inv or flush-n-inv */
13541cada4dSEugeniy Paltsev 	else
13641cada4dSEugeniy Paltsev 		ctrl &= ~SLC_CTRL_RGN_OP_INV;
13741cada4dSEugeniy Paltsev 
13841cada4dSEugeniy Paltsev 	write_aux_reg(ARC_AUX_SLC_CTRL, ctrl);
13941cada4dSEugeniy Paltsev 
14041cada4dSEugeniy Paltsev 	/*
14141cada4dSEugeniy Paltsev 	 * Lower bits are ignored, no need to clip
14241cada4dSEugeniy Paltsev 	 * END needs to be setup before START (latter triggers the operation)
14341cada4dSEugeniy Paltsev 	 * END can't be same as START, so add (l2_line_sz - 1) to sz
14441cada4dSEugeniy Paltsev 	 */
14541cada4dSEugeniy Paltsev 	end = paddr + sz + slc_line_sz - 1;
14641cada4dSEugeniy Paltsev 
14741cada4dSEugeniy Paltsev 	/*
14841cada4dSEugeniy Paltsev 	 * Upper addresses (ARC_AUX_SLC_RGN_END1 and ARC_AUX_SLC_RGN_START1)
14941cada4dSEugeniy Paltsev 	 * are always == 0 as we don't use PAE40, so we only setup lower ones
15041cada4dSEugeniy Paltsev 	 * (ARC_AUX_SLC_RGN_END and ARC_AUX_SLC_RGN_START)
15141cada4dSEugeniy Paltsev 	 */
15241cada4dSEugeniy Paltsev 	write_aux_reg(ARC_AUX_SLC_RGN_END, end);
15341cada4dSEugeniy Paltsev 	write_aux_reg(ARC_AUX_SLC_RGN_START, paddr);
15441cada4dSEugeniy Paltsev 
15541cada4dSEugeniy Paltsev 	/* Make sure "busy" bit reports correct stataus, see STAR 9001165532 */
15641cada4dSEugeniy Paltsev 	read_aux_reg(ARC_AUX_SLC_CTRL);
15741cada4dSEugeniy Paltsev 
15841cada4dSEugeniy Paltsev 	while (read_aux_reg(ARC_AUX_SLC_CTRL) & SLC_CTRL_BUSY);
15941cada4dSEugeniy Paltsev }
16041cada4dSEugeniy Paltsev #endif /* CONFIG_ISA_ARCV2 */
161ef639e6fSAlexey Brodkin 
162379b3280SAlexey Brodkin #ifdef CONFIG_ISA_ARCV2
163379b3280SAlexey Brodkin static void read_decode_cache_bcr_arcv2(void)
164ef639e6fSAlexey Brodkin {
165379b3280SAlexey Brodkin 	union {
166379b3280SAlexey Brodkin 		struct {
167379b3280SAlexey Brodkin #ifdef CONFIG_CPU_BIG_ENDIAN
168379b3280SAlexey Brodkin 			unsigned int pad:24, way:2, lsz:2, sz:4;
169379b3280SAlexey Brodkin #else
170379b3280SAlexey Brodkin 			unsigned int sz:4, lsz:2, way:2, pad:24;
171379b3280SAlexey Brodkin #endif
172379b3280SAlexey Brodkin 		} fields;
173379b3280SAlexey Brodkin 		unsigned int word;
174379b3280SAlexey Brodkin 	} slc_cfg;
175379b3280SAlexey Brodkin 
176379b3280SAlexey Brodkin 	union {
177379b3280SAlexey Brodkin 		struct {
178379b3280SAlexey Brodkin #ifdef CONFIG_CPU_BIG_ENDIAN
179379b3280SAlexey Brodkin 			unsigned int pad:24, ver:8;
180379b3280SAlexey Brodkin #else
181379b3280SAlexey Brodkin 			unsigned int ver:8, pad:24;
182379b3280SAlexey Brodkin #endif
183379b3280SAlexey Brodkin 		} fields;
184379b3280SAlexey Brodkin 		unsigned int word;
185379b3280SAlexey Brodkin 	} sbcr;
186379b3280SAlexey Brodkin 
187379b3280SAlexey Brodkin 	sbcr.word = read_aux_reg(ARC_BCR_SLC);
188379b3280SAlexey Brodkin 	if (sbcr.fields.ver) {
189379b3280SAlexey Brodkin 		slc_cfg.word = read_aux_reg(ARC_AUX_SLC_CONFIG);
1903cf23939SEugeniy Paltsev 		slc_exists = true;
191379b3280SAlexey Brodkin 		slc_line_sz = (slc_cfg.fields.lsz == 0) ? 128 : 64;
192379b3280SAlexey Brodkin 	}
193db6ce231SAlexey Brodkin 
194db6ce231SAlexey Brodkin 	union {
195db6ce231SAlexey Brodkin 		struct bcr_clust_cfg {
196db6ce231SAlexey Brodkin #ifdef CONFIG_CPU_BIG_ENDIAN
197db6ce231SAlexey Brodkin 			unsigned int pad:7, c:1, num_entries:8, num_cores:8, ver:8;
198db6ce231SAlexey Brodkin #else
199db6ce231SAlexey Brodkin 			unsigned int ver:8, num_cores:8, num_entries:8, c:1, pad:7;
200db6ce231SAlexey Brodkin #endif
201db6ce231SAlexey Brodkin 		} fields;
202db6ce231SAlexey Brodkin 		unsigned int word;
203db6ce231SAlexey Brodkin 	} cbcr;
204db6ce231SAlexey Brodkin 
205db6ce231SAlexey Brodkin 	cbcr.word = read_aux_reg(ARC_BCR_CLUSTER);
206*b0146f9eSEugeniy Paltsev 	if (cbcr.fields.c && ioc_enable)
2073cf23939SEugeniy Paltsev 		ioc_exists = true;
208379b3280SAlexey Brodkin }
209379b3280SAlexey Brodkin #endif
210379b3280SAlexey Brodkin 
211379b3280SAlexey Brodkin void read_decode_cache_bcr(void)
212379b3280SAlexey Brodkin {
213379b3280SAlexey Brodkin 	int dc_line_sz = 0, ic_line_sz = 0;
214379b3280SAlexey Brodkin 
215379b3280SAlexey Brodkin 	union {
216379b3280SAlexey Brodkin 		struct {
217379b3280SAlexey Brodkin #ifdef CONFIG_CPU_BIG_ENDIAN
218379b3280SAlexey Brodkin 			unsigned int pad:12, line_len:4, sz:4, config:4, ver:8;
219379b3280SAlexey Brodkin #else
220379b3280SAlexey Brodkin 			unsigned int ver:8, config:4, sz:4, line_len:4, pad:12;
221379b3280SAlexey Brodkin #endif
222379b3280SAlexey Brodkin 		} fields;
223379b3280SAlexey Brodkin 		unsigned int word;
224379b3280SAlexey Brodkin 	} ibcr, dbcr;
225379b3280SAlexey Brodkin 
226379b3280SAlexey Brodkin 	ibcr.word = read_aux_reg(ARC_BCR_IC_BUILD);
227379b3280SAlexey Brodkin 	if (ibcr.fields.ver) {
2283cf23939SEugeniy Paltsev 		icache_exists = true;
229379b3280SAlexey Brodkin 		l1_line_sz = ic_line_sz = 8 << ibcr.fields.line_len;
230379b3280SAlexey Brodkin 		if (!ic_line_sz)
231379b3280SAlexey Brodkin 			panic("Instruction exists but line length is 0\n");
232ef639e6fSAlexey Brodkin 	}
233ef639e6fSAlexey Brodkin 
234379b3280SAlexey Brodkin 	dbcr.word = read_aux_reg(ARC_BCR_DC_BUILD);
235379b3280SAlexey Brodkin 	if (dbcr.fields.ver){
2363cf23939SEugeniy Paltsev 		dcache_exists = true;
237379b3280SAlexey Brodkin 		l1_line_sz = dc_line_sz = 16 << dbcr.fields.line_len;
238379b3280SAlexey Brodkin 		if (!dc_line_sz)
239379b3280SAlexey Brodkin 			panic("Data cache exists but line length is 0\n");
240379b3280SAlexey Brodkin 	}
241379b3280SAlexey Brodkin 
242379b3280SAlexey Brodkin 	if (ic_line_sz && dc_line_sz && (ic_line_sz != dc_line_sz))
243379b3280SAlexey Brodkin 		panic("Instruction and data cache line lengths differ\n");
244ef639e6fSAlexey Brodkin }
245ef639e6fSAlexey Brodkin 
246ef639e6fSAlexey Brodkin void cache_init(void)
247ef639e6fSAlexey Brodkin {
248379b3280SAlexey Brodkin 	read_decode_cache_bcr();
249379b3280SAlexey Brodkin 
250ef639e6fSAlexey Brodkin #ifdef CONFIG_ISA_ARCV2
251379b3280SAlexey Brodkin 	read_decode_cache_bcr_arcv2();
252db6ce231SAlexey Brodkin 
253db6ce231SAlexey Brodkin 	if (ioc_exists) {
25497a63144SAlexey Brodkin 		/* IOC Aperture start is equal to DDR start */
25597a63144SAlexey Brodkin 		unsigned int ap_base = CONFIG_SYS_SDRAM_BASE;
25697a63144SAlexey Brodkin 		/* IOC Aperture size is equal to DDR size */
25797a63144SAlexey Brodkin 		long ap_size = CONFIG_SYS_SDRAM_SIZE;
25897a63144SAlexey Brodkin 
259a4a43fcfSAlexey Brodkin 		flush_dcache_all();
260a4a43fcfSAlexey Brodkin 		invalidate_dcache_all();
261a4a43fcfSAlexey Brodkin 
26297a63144SAlexey Brodkin 		if (!is_power_of_2(ap_size) || ap_size < 4096)
26397a63144SAlexey Brodkin 			panic("IOC Aperture size must be power of 2 and bigger 4Kib");
26497a63144SAlexey Brodkin 
26597a63144SAlexey Brodkin 		/*
26697a63144SAlexey Brodkin 		 * IOC Aperture size decoded as 2 ^ (SIZE + 2) KB,
26797a63144SAlexey Brodkin 		 * so setting 0x11 implies 512M, 0x12 implies 1G...
26897a63144SAlexey Brodkin 		 */
26997a63144SAlexey Brodkin 		write_aux_reg(ARC_AUX_IO_COH_AP0_SIZE,
27097a63144SAlexey Brodkin 			      order_base_2(ap_size/1024) - 2);
27197a63144SAlexey Brodkin 
27297a63144SAlexey Brodkin 
27397a63144SAlexey Brodkin 		/* IOC Aperture start must be aligned to the size of the aperture */
27497a63144SAlexey Brodkin 		if (ap_base % ap_size != 0)
27597a63144SAlexey Brodkin 			panic("IOC Aperture start must be aligned to the size of the aperture");
27697a63144SAlexey Brodkin 
27797a63144SAlexey Brodkin 		write_aux_reg(ARC_AUX_IO_COH_AP0_BASE, ap_base >> 12);
278db6ce231SAlexey Brodkin 		write_aux_reg(ARC_AUX_IO_COH_PARTIAL, 1);
279db6ce231SAlexey Brodkin 		write_aux_reg(ARC_AUX_IO_COH_ENABLE, 1);
28097a63144SAlexey Brodkin 
281db6ce231SAlexey Brodkin 	}
28241cada4dSEugeniy Paltsev 
28341cada4dSEugeniy Paltsev 	read_decode_mmu_bcr();
28441cada4dSEugeniy Paltsev 
28541cada4dSEugeniy Paltsev 	/*
28641cada4dSEugeniy Paltsev 	 * ARC_AUX_SLC_RGN_START1 and ARC_AUX_SLC_RGN_END1 register exist
28741cada4dSEugeniy Paltsev 	 * only if PAE exists in current HW. So we had to check pae_exist
28841cada4dSEugeniy Paltsev 	 * before using them.
28941cada4dSEugeniy Paltsev 	 */
29041cada4dSEugeniy Paltsev 	if (slc_exists && pae_exists)
29141cada4dSEugeniy Paltsev 		slc_upper_region_init();
29241cada4dSEugeniy Paltsev #endif /* CONFIG_ISA_ARCV2 */
293ef639e6fSAlexey Brodkin }
294ef639e6fSAlexey Brodkin 
295660d5f0dSAlexey Brodkin int icache_status(void)
296660d5f0dSAlexey Brodkin {
297379b3280SAlexey Brodkin 	if (!icache_exists)
298660d5f0dSAlexey Brodkin 		return 0;
299660d5f0dSAlexey Brodkin 
300ef639e6fSAlexey Brodkin 	if (read_aux_reg(ARC_AUX_IC_CTRL) & IC_CTRL_CACHE_DISABLE)
301ef639e6fSAlexey Brodkin 		return 0;
302ef639e6fSAlexey Brodkin 	else
303ef639e6fSAlexey Brodkin 		return 1;
304660d5f0dSAlexey Brodkin }
305660d5f0dSAlexey Brodkin 
306660d5f0dSAlexey Brodkin void icache_enable(void)
307660d5f0dSAlexey Brodkin {
308379b3280SAlexey Brodkin 	if (icache_exists)
309660d5f0dSAlexey Brodkin 		write_aux_reg(ARC_AUX_IC_CTRL, read_aux_reg(ARC_AUX_IC_CTRL) &
310660d5f0dSAlexey Brodkin 			      ~IC_CTRL_CACHE_DISABLE);
311660d5f0dSAlexey Brodkin }
312660d5f0dSAlexey Brodkin 
313660d5f0dSAlexey Brodkin void icache_disable(void)
314660d5f0dSAlexey Brodkin {
315379b3280SAlexey Brodkin 	if (icache_exists)
316660d5f0dSAlexey Brodkin 		write_aux_reg(ARC_AUX_IC_CTRL, read_aux_reg(ARC_AUX_IC_CTRL) |
317660d5f0dSAlexey Brodkin 			      IC_CTRL_CACHE_DISABLE);
318660d5f0dSAlexey Brodkin }
319660d5f0dSAlexey Brodkin 
320660d5f0dSAlexey Brodkin void invalidate_icache_all(void)
321660d5f0dSAlexey Brodkin {
322660d5f0dSAlexey Brodkin 	/* Any write to IC_IVIC register triggers invalidation of entire I$ */
323ef639e6fSAlexey Brodkin 	if (icache_status()) {
324660d5f0dSAlexey Brodkin 		write_aux_reg(ARC_AUX_IC_IVIC, 1);
325f2a22678SAlexey Brodkin 		/*
326f2a22678SAlexey Brodkin 		 * As per ARC HS databook (see chapter 5.3.3.2)
327f2a22678SAlexey Brodkin 		 * it is required to add 3 NOPs after each write to IC_IVIC.
328f2a22678SAlexey Brodkin 		 */
329f2a22678SAlexey Brodkin 		__builtin_arc_nop();
330f2a22678SAlexey Brodkin 		__builtin_arc_nop();
331f2a22678SAlexey Brodkin 		__builtin_arc_nop();
332ef639e6fSAlexey Brodkin 		read_aux_reg(ARC_AUX_IC_CTRL);	/* blocks */
333660d5f0dSAlexey Brodkin 	}
33441cada4dSEugeniy Paltsev 
33541cada4dSEugeniy Paltsev #ifdef CONFIG_ISA_ARCV2
33641cada4dSEugeniy Paltsev 	if (slc_exists)
33741cada4dSEugeniy Paltsev 		__slc_entire_op(OP_INV);
338ef639e6fSAlexey Brodkin #endif
33941cada4dSEugeniy Paltsev }
340660d5f0dSAlexey Brodkin 
341660d5f0dSAlexey Brodkin int dcache_status(void)
342660d5f0dSAlexey Brodkin {
343379b3280SAlexey Brodkin 	if (!dcache_exists)
344660d5f0dSAlexey Brodkin 		return 0;
345660d5f0dSAlexey Brodkin 
346ef639e6fSAlexey Brodkin 	if (read_aux_reg(ARC_AUX_DC_CTRL) & DC_CTRL_CACHE_DISABLE)
347ef639e6fSAlexey Brodkin 		return 0;
348ef639e6fSAlexey Brodkin 	else
349ef639e6fSAlexey Brodkin 		return 1;
350660d5f0dSAlexey Brodkin }
351660d5f0dSAlexey Brodkin 
352660d5f0dSAlexey Brodkin void dcache_enable(void)
353660d5f0dSAlexey Brodkin {
354379b3280SAlexey Brodkin 	if (!dcache_exists)
355660d5f0dSAlexey Brodkin 		return;
356660d5f0dSAlexey Brodkin 
357660d5f0dSAlexey Brodkin 	write_aux_reg(ARC_AUX_DC_CTRL, read_aux_reg(ARC_AUX_DC_CTRL) &
358660d5f0dSAlexey Brodkin 		      ~(DC_CTRL_INV_MODE_FLUSH | DC_CTRL_CACHE_DISABLE));
359660d5f0dSAlexey Brodkin }
360660d5f0dSAlexey Brodkin 
361660d5f0dSAlexey Brodkin void dcache_disable(void)
362660d5f0dSAlexey Brodkin {
363379b3280SAlexey Brodkin 	if (!dcache_exists)
364660d5f0dSAlexey Brodkin 		return;
365660d5f0dSAlexey Brodkin 
366660d5f0dSAlexey Brodkin 	write_aux_reg(ARC_AUX_DC_CTRL, read_aux_reg(ARC_AUX_DC_CTRL) |
367660d5f0dSAlexey Brodkin 		      DC_CTRL_CACHE_DISABLE);
368660d5f0dSAlexey Brodkin }
369660d5f0dSAlexey Brodkin 
370660d5f0dSAlexey Brodkin #ifndef CONFIG_SYS_DCACHE_OFF
371660d5f0dSAlexey Brodkin /*
372ef639e6fSAlexey Brodkin  * Common Helper for Line Operations on {I,D}-Cache
373660d5f0dSAlexey Brodkin  */
374ef639e6fSAlexey Brodkin static inline void __cache_line_loop(unsigned long paddr, unsigned long sz,
375ef639e6fSAlexey Brodkin 				     const int cacheop)
376660d5f0dSAlexey Brodkin {
377ef639e6fSAlexey Brodkin 	unsigned int aux_cmd;
378ef639e6fSAlexey Brodkin #if (CONFIG_ARC_MMU_VER == 3)
379ef639e6fSAlexey Brodkin 	unsigned int aux_tag;
380ef639e6fSAlexey Brodkin #endif
381ef639e6fSAlexey Brodkin 	int num_lines;
382660d5f0dSAlexey Brodkin 
383ef639e6fSAlexey Brodkin 	if (cacheop == OP_INV_IC) {
384ef639e6fSAlexey Brodkin 		aux_cmd = ARC_AUX_IC_IVIL;
385ef639e6fSAlexey Brodkin #if (CONFIG_ARC_MMU_VER == 3)
386ef639e6fSAlexey Brodkin 		aux_tag = ARC_AUX_IC_PTAG;
387ef639e6fSAlexey Brodkin #endif
388ef639e6fSAlexey Brodkin 	} else {
389ef639e6fSAlexey Brodkin 		/* d$ cmd: INV (discard or wback-n-discard) OR FLUSH (wback) */
390ef639e6fSAlexey Brodkin 		aux_cmd = cacheop & OP_INV ? ARC_AUX_DC_IVDL : ARC_AUX_DC_FLDL;
391ef639e6fSAlexey Brodkin #if (CONFIG_ARC_MMU_VER == 3)
392ef639e6fSAlexey Brodkin 		aux_tag = ARC_AUX_DC_PTAG;
393ef639e6fSAlexey Brodkin #endif
394660d5f0dSAlexey Brodkin 	}
395660d5f0dSAlexey Brodkin 
396ef639e6fSAlexey Brodkin 	sz += paddr & ~CACHE_LINE_MASK;
397ef639e6fSAlexey Brodkin 	paddr &= CACHE_LINE_MASK;
398ef639e6fSAlexey Brodkin 
399379b3280SAlexey Brodkin 	num_lines = DIV_ROUND_UP(sz, l1_line_sz);
400ef639e6fSAlexey Brodkin 
401ef639e6fSAlexey Brodkin 	while (num_lines-- > 0) {
402ef639e6fSAlexey Brodkin #if (CONFIG_ARC_MMU_VER == 3)
403ef639e6fSAlexey Brodkin 		write_aux_reg(aux_tag, paddr);
404ef639e6fSAlexey Brodkin #endif
405ef639e6fSAlexey Brodkin 		write_aux_reg(aux_cmd, paddr);
406379b3280SAlexey Brodkin 		paddr += l1_line_sz;
407ef639e6fSAlexey Brodkin 	}
408ef639e6fSAlexey Brodkin }
409ef639e6fSAlexey Brodkin 
410ef639e6fSAlexey Brodkin static unsigned int __before_dc_op(const int op)
411ef639e6fSAlexey Brodkin {
412ef639e6fSAlexey Brodkin 	unsigned int reg;
413ef639e6fSAlexey Brodkin 
414ef639e6fSAlexey Brodkin 	if (op == OP_INV) {
415ef639e6fSAlexey Brodkin 		/*
416ef639e6fSAlexey Brodkin 		 * IM is set by default and implies Flush-n-inv
417ef639e6fSAlexey Brodkin 		 * Clear it here for vanilla inv
418ef639e6fSAlexey Brodkin 		 */
419ef639e6fSAlexey Brodkin 		reg = read_aux_reg(ARC_AUX_DC_CTRL);
420ef639e6fSAlexey Brodkin 		write_aux_reg(ARC_AUX_DC_CTRL, reg & ~DC_CTRL_INV_MODE_FLUSH);
421ef639e6fSAlexey Brodkin 	}
422ef639e6fSAlexey Brodkin 
423ef639e6fSAlexey Brodkin 	return reg;
424ef639e6fSAlexey Brodkin }
425ef639e6fSAlexey Brodkin 
426ef639e6fSAlexey Brodkin static void __after_dc_op(const int op, unsigned int reg)
427ef639e6fSAlexey Brodkin {
428ef639e6fSAlexey Brodkin 	if (op & OP_FLUSH)	/* flush / flush-n-inv both wait */
429ef639e6fSAlexey Brodkin 		while (read_aux_reg(ARC_AUX_DC_CTRL) & DC_CTRL_FLUSH_STATUS)
430ef639e6fSAlexey Brodkin 			;
431ef639e6fSAlexey Brodkin 
432ef639e6fSAlexey Brodkin 	/* Switch back to default Invalidate mode */
433ef639e6fSAlexey Brodkin 	if (op == OP_INV)
434ef639e6fSAlexey Brodkin 		write_aux_reg(ARC_AUX_DC_CTRL, reg | DC_CTRL_INV_MODE_FLUSH);
435ef639e6fSAlexey Brodkin }
436ef639e6fSAlexey Brodkin 
437ef639e6fSAlexey Brodkin static inline void __dc_entire_op(const int cacheop)
438ef639e6fSAlexey Brodkin {
439ef639e6fSAlexey Brodkin 	int aux;
440ef639e6fSAlexey Brodkin 	unsigned int ctrl_reg = __before_dc_op(cacheop);
441ef639e6fSAlexey Brodkin 
442ef639e6fSAlexey Brodkin 	if (cacheop & OP_INV)	/* Inv or flush-n-inv use same cmd reg */
443ef639e6fSAlexey Brodkin 		aux = ARC_AUX_DC_IVDC;
444ef639e6fSAlexey Brodkin 	else
445ef639e6fSAlexey Brodkin 		aux = ARC_AUX_DC_FLSH;
446ef639e6fSAlexey Brodkin 
447ef639e6fSAlexey Brodkin 	write_aux_reg(aux, 0x1);
448ef639e6fSAlexey Brodkin 
449ef639e6fSAlexey Brodkin 	__after_dc_op(cacheop, ctrl_reg);
450ef639e6fSAlexey Brodkin }
451ef639e6fSAlexey Brodkin 
452ef639e6fSAlexey Brodkin static inline void __dc_line_op(unsigned long paddr, unsigned long sz,
453ef639e6fSAlexey Brodkin 				const int cacheop)
454ef639e6fSAlexey Brodkin {
455ef639e6fSAlexey Brodkin 	unsigned int ctrl_reg = __before_dc_op(cacheop);
456ef639e6fSAlexey Brodkin 	__cache_line_loop(paddr, sz, cacheop);
457ef639e6fSAlexey Brodkin 	__after_dc_op(cacheop, ctrl_reg);
458ef639e6fSAlexey Brodkin }
459ef639e6fSAlexey Brodkin #else
460ef639e6fSAlexey Brodkin #define __dc_entire_op(cacheop)
461ef639e6fSAlexey Brodkin #define __dc_line_op(paddr, sz, cacheop)
462ef639e6fSAlexey Brodkin #endif /* !CONFIG_SYS_DCACHE_OFF */
463ef639e6fSAlexey Brodkin 
464660d5f0dSAlexey Brodkin void invalidate_dcache_range(unsigned long start, unsigned long end)
465660d5f0dSAlexey Brodkin {
46641cada4dSEugeniy Paltsev 	if (start >= end)
46741cada4dSEugeniy Paltsev 		return;
46841cada4dSEugeniy Paltsev 
469ef639e6fSAlexey Brodkin #ifdef CONFIG_ISA_ARCV2
470db6ce231SAlexey Brodkin 	if (!ioc_exists)
471db6ce231SAlexey Brodkin #endif
472db6ce231SAlexey Brodkin 		__dc_line_op(start, end - start, OP_INV);
473db6ce231SAlexey Brodkin 
474db6ce231SAlexey Brodkin #ifdef CONFIG_ISA_ARCV2
475db6ce231SAlexey Brodkin 	if (slc_exists && !ioc_exists)
47641cada4dSEugeniy Paltsev 		__slc_rgn_op(start, end - start, OP_INV);
477660d5f0dSAlexey Brodkin #endif
478660d5f0dSAlexey Brodkin }
479660d5f0dSAlexey Brodkin 
480ef639e6fSAlexey Brodkin void flush_dcache_range(unsigned long start, unsigned long end)
481660d5f0dSAlexey Brodkin {
48241cada4dSEugeniy Paltsev 	if (start >= end)
48341cada4dSEugeniy Paltsev 		return;
48441cada4dSEugeniy Paltsev 
485ef639e6fSAlexey Brodkin #ifdef CONFIG_ISA_ARCV2
486db6ce231SAlexey Brodkin 	if (!ioc_exists)
487db6ce231SAlexey Brodkin #endif
488db6ce231SAlexey Brodkin 		__dc_line_op(start, end - start, OP_FLUSH);
489db6ce231SAlexey Brodkin 
490db6ce231SAlexey Brodkin #ifdef CONFIG_ISA_ARCV2
491db6ce231SAlexey Brodkin 	if (slc_exists && !ioc_exists)
49241cada4dSEugeniy Paltsev 		__slc_rgn_op(start, end - start, OP_FLUSH);
493ef639e6fSAlexey Brodkin #endif
494660d5f0dSAlexey Brodkin }
495660d5f0dSAlexey Brodkin 
496660d5f0dSAlexey Brodkin void flush_cache(unsigned long start, unsigned long size)
497660d5f0dSAlexey Brodkin {
498660d5f0dSAlexey Brodkin 	flush_dcache_range(start, start + size);
499660d5f0dSAlexey Brodkin }
5006eb15e50SAlexey Brodkin 
501ef639e6fSAlexey Brodkin void invalidate_dcache_all(void)
502ef639e6fSAlexey Brodkin {
503db6ce231SAlexey Brodkin 	__dc_entire_op(OP_INV);
504db6ce231SAlexey Brodkin 
505db6ce231SAlexey Brodkin #ifdef CONFIG_ISA_ARCV2
506bd91508bSAlexey Brodkin 	if (slc_exists)
507ef639e6fSAlexey Brodkin 		__slc_entire_op(OP_INV);
508ef639e6fSAlexey Brodkin #endif
5096eb15e50SAlexey Brodkin }
5106eb15e50SAlexey Brodkin 
511ef639e6fSAlexey Brodkin void flush_dcache_all(void)
5126eb15e50SAlexey Brodkin {
513db6ce231SAlexey Brodkin 	__dc_entire_op(OP_FLUSH);
514db6ce231SAlexey Brodkin 
515db6ce231SAlexey Brodkin #ifdef CONFIG_ISA_ARCV2
5162a8382c6SAlexey Brodkin 	if (slc_exists)
517ef639e6fSAlexey Brodkin 		__slc_entire_op(OP_FLUSH);
518ef639e6fSAlexey Brodkin #endif
5196eb15e50SAlexey Brodkin }
520