xref: /openbmc/u-boot/arch/arc/lib/cache.c (revision 16aeee81)
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 */
1619b10a42SEugeniy Paltsev #define IC_CTRL_CACHE_DISABLE	BIT(0)
17660d5f0dSAlexey Brodkin 
18660d5f0dSAlexey Brodkin /* Bit values in DC_CTRL */
1919b10a42SEugeniy Paltsev #define DC_CTRL_CACHE_DISABLE	BIT(0)
2019b10a42SEugeniy Paltsev #define DC_CTRL_INV_MODE_FLUSH	BIT(6)
2119b10a42SEugeniy Paltsev #define DC_CTRL_FLUSH_STATUS	BIT(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 
51b0146f9eSEugeniy Paltsev /* To force enable IOC set ioc_enable to 'true' */
52b0146f9eSEugeniy Paltsev bool ioc_enable __section(".data") = false;
53b0146f9eSEugeniy 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);
206b0146f9eSEugeniy 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 		/* IOC Aperture start must be aligned to the size of the aperture */
27397a63144SAlexey Brodkin 		if (ap_base % ap_size != 0)
27497a63144SAlexey Brodkin 			panic("IOC Aperture start must be aligned to the size of the aperture");
27597a63144SAlexey Brodkin 
27697a63144SAlexey Brodkin 		write_aux_reg(ARC_AUX_IO_COH_AP0_BASE, ap_base >> 12);
277db6ce231SAlexey Brodkin 		write_aux_reg(ARC_AUX_IO_COH_PARTIAL, 1);
278db6ce231SAlexey Brodkin 		write_aux_reg(ARC_AUX_IO_COH_ENABLE, 1);
279db6ce231SAlexey Brodkin 	}
28041cada4dSEugeniy Paltsev 
28141cada4dSEugeniy Paltsev 	read_decode_mmu_bcr();
28241cada4dSEugeniy Paltsev 
28341cada4dSEugeniy Paltsev 	/*
28441cada4dSEugeniy Paltsev 	 * ARC_AUX_SLC_RGN_START1 and ARC_AUX_SLC_RGN_END1 register exist
28541cada4dSEugeniy Paltsev 	 * only if PAE exists in current HW. So we had to check pae_exist
28641cada4dSEugeniy Paltsev 	 * before using them.
28741cada4dSEugeniy Paltsev 	 */
28841cada4dSEugeniy Paltsev 	if (slc_exists && pae_exists)
28941cada4dSEugeniy Paltsev 		slc_upper_region_init();
29041cada4dSEugeniy Paltsev #endif /* CONFIG_ISA_ARCV2 */
291ef639e6fSAlexey Brodkin }
292ef639e6fSAlexey Brodkin 
293660d5f0dSAlexey Brodkin int icache_status(void)
294660d5f0dSAlexey Brodkin {
295379b3280SAlexey Brodkin 	if (!icache_exists)
296660d5f0dSAlexey Brodkin 		return 0;
297660d5f0dSAlexey Brodkin 
298ef639e6fSAlexey Brodkin 	if (read_aux_reg(ARC_AUX_IC_CTRL) & IC_CTRL_CACHE_DISABLE)
299ef639e6fSAlexey Brodkin 		return 0;
300ef639e6fSAlexey Brodkin 	else
301ef639e6fSAlexey Brodkin 		return 1;
302660d5f0dSAlexey Brodkin }
303660d5f0dSAlexey Brodkin 
304660d5f0dSAlexey Brodkin void icache_enable(void)
305660d5f0dSAlexey Brodkin {
306379b3280SAlexey Brodkin 	if (icache_exists)
307660d5f0dSAlexey Brodkin 		write_aux_reg(ARC_AUX_IC_CTRL, read_aux_reg(ARC_AUX_IC_CTRL) &
308660d5f0dSAlexey Brodkin 			      ~IC_CTRL_CACHE_DISABLE);
309660d5f0dSAlexey Brodkin }
310660d5f0dSAlexey Brodkin 
311660d5f0dSAlexey Brodkin void icache_disable(void)
312660d5f0dSAlexey Brodkin {
313379b3280SAlexey Brodkin 	if (icache_exists)
314660d5f0dSAlexey Brodkin 		write_aux_reg(ARC_AUX_IC_CTRL, read_aux_reg(ARC_AUX_IC_CTRL) |
315660d5f0dSAlexey Brodkin 			      IC_CTRL_CACHE_DISABLE);
316660d5f0dSAlexey Brodkin }
317660d5f0dSAlexey Brodkin 
318*16aeee81SEugeniy Paltsev /* IC supports only invalidation */
319*16aeee81SEugeniy Paltsev static inline void __ic_entire_invalidate(void)
320660d5f0dSAlexey Brodkin {
321*16aeee81SEugeniy Paltsev 	if (!icache_status())
322*16aeee81SEugeniy Paltsev 		return;
323*16aeee81SEugeniy Paltsev 
324660d5f0dSAlexey Brodkin 	/* Any write to IC_IVIC register triggers invalidation of entire I$ */
325660d5f0dSAlexey Brodkin 	write_aux_reg(ARC_AUX_IC_IVIC, 1);
326f2a22678SAlexey Brodkin 	/*
327f2a22678SAlexey Brodkin 	 * As per ARC HS databook (see chapter 5.3.3.2)
328f2a22678SAlexey Brodkin 	 * it is required to add 3 NOPs after each write to IC_IVIC.
329f2a22678SAlexey Brodkin 	 */
330f2a22678SAlexey Brodkin 	__builtin_arc_nop();
331f2a22678SAlexey Brodkin 	__builtin_arc_nop();
332f2a22678SAlexey Brodkin 	__builtin_arc_nop();
333ef639e6fSAlexey Brodkin 	read_aux_reg(ARC_AUX_IC_CTRL);  /* blocks */
334660d5f0dSAlexey Brodkin }
33541cada4dSEugeniy Paltsev 
336*16aeee81SEugeniy Paltsev void invalidate_icache_all(void)
337*16aeee81SEugeniy Paltsev {
338*16aeee81SEugeniy Paltsev 	__ic_entire_invalidate();
339*16aeee81SEugeniy Paltsev 
34041cada4dSEugeniy Paltsev #ifdef CONFIG_ISA_ARCV2
34141cada4dSEugeniy Paltsev 	if (slc_exists)
34241cada4dSEugeniy Paltsev 		__slc_entire_op(OP_INV);
343ef639e6fSAlexey Brodkin #endif
34441cada4dSEugeniy Paltsev }
345660d5f0dSAlexey Brodkin 
346660d5f0dSAlexey Brodkin int dcache_status(void)
347660d5f0dSAlexey Brodkin {
348379b3280SAlexey Brodkin 	if (!dcache_exists)
349660d5f0dSAlexey Brodkin 		return 0;
350660d5f0dSAlexey Brodkin 
351ef639e6fSAlexey Brodkin 	if (read_aux_reg(ARC_AUX_DC_CTRL) & DC_CTRL_CACHE_DISABLE)
352ef639e6fSAlexey Brodkin 		return 0;
353ef639e6fSAlexey Brodkin 	else
354ef639e6fSAlexey Brodkin 		return 1;
355660d5f0dSAlexey Brodkin }
356660d5f0dSAlexey Brodkin 
357660d5f0dSAlexey Brodkin void dcache_enable(void)
358660d5f0dSAlexey Brodkin {
359379b3280SAlexey Brodkin 	if (!dcache_exists)
360660d5f0dSAlexey Brodkin 		return;
361660d5f0dSAlexey Brodkin 
362660d5f0dSAlexey Brodkin 	write_aux_reg(ARC_AUX_DC_CTRL, read_aux_reg(ARC_AUX_DC_CTRL) &
363660d5f0dSAlexey Brodkin 		      ~(DC_CTRL_INV_MODE_FLUSH | DC_CTRL_CACHE_DISABLE));
364660d5f0dSAlexey Brodkin }
365660d5f0dSAlexey Brodkin 
366660d5f0dSAlexey Brodkin void dcache_disable(void)
367660d5f0dSAlexey Brodkin {
368379b3280SAlexey Brodkin 	if (!dcache_exists)
369660d5f0dSAlexey Brodkin 		return;
370660d5f0dSAlexey Brodkin 
371660d5f0dSAlexey Brodkin 	write_aux_reg(ARC_AUX_DC_CTRL, read_aux_reg(ARC_AUX_DC_CTRL) |
372660d5f0dSAlexey Brodkin 		      DC_CTRL_CACHE_DISABLE);
373660d5f0dSAlexey Brodkin }
374660d5f0dSAlexey Brodkin 
375660d5f0dSAlexey Brodkin #ifndef CONFIG_SYS_DCACHE_OFF
376660d5f0dSAlexey Brodkin /*
377ef639e6fSAlexey Brodkin  * Common Helper for Line Operations on {I,D}-Cache
378660d5f0dSAlexey Brodkin  */
379ef639e6fSAlexey Brodkin static inline void __cache_line_loop(unsigned long paddr, unsigned long sz,
380ef639e6fSAlexey Brodkin 				     const int cacheop)
381660d5f0dSAlexey Brodkin {
382ef639e6fSAlexey Brodkin 	unsigned int aux_cmd;
383ef639e6fSAlexey Brodkin #if (CONFIG_ARC_MMU_VER == 3)
384ef639e6fSAlexey Brodkin 	unsigned int aux_tag;
385ef639e6fSAlexey Brodkin #endif
386ef639e6fSAlexey Brodkin 	int num_lines;
387660d5f0dSAlexey Brodkin 
388ef639e6fSAlexey Brodkin 	if (cacheop == OP_INV_IC) {
389ef639e6fSAlexey Brodkin 		aux_cmd = ARC_AUX_IC_IVIL;
390ef639e6fSAlexey Brodkin #if (CONFIG_ARC_MMU_VER == 3)
391ef639e6fSAlexey Brodkin 		aux_tag = ARC_AUX_IC_PTAG;
392ef639e6fSAlexey Brodkin #endif
393ef639e6fSAlexey Brodkin 	} else {
394ef639e6fSAlexey Brodkin 		/* d$ cmd: INV (discard or wback-n-discard) OR FLUSH (wback) */
395ef639e6fSAlexey Brodkin 		aux_cmd = cacheop & OP_INV ? ARC_AUX_DC_IVDL : ARC_AUX_DC_FLDL;
396ef639e6fSAlexey Brodkin #if (CONFIG_ARC_MMU_VER == 3)
397ef639e6fSAlexey Brodkin 		aux_tag = ARC_AUX_DC_PTAG;
398ef639e6fSAlexey Brodkin #endif
399660d5f0dSAlexey Brodkin 	}
400660d5f0dSAlexey Brodkin 
401ef639e6fSAlexey Brodkin 	sz += paddr & ~CACHE_LINE_MASK;
402ef639e6fSAlexey Brodkin 	paddr &= CACHE_LINE_MASK;
403ef639e6fSAlexey Brodkin 
404379b3280SAlexey Brodkin 	num_lines = DIV_ROUND_UP(sz, l1_line_sz);
405ef639e6fSAlexey Brodkin 
406ef639e6fSAlexey Brodkin 	while (num_lines-- > 0) {
407ef639e6fSAlexey Brodkin #if (CONFIG_ARC_MMU_VER == 3)
408ef639e6fSAlexey Brodkin 		write_aux_reg(aux_tag, paddr);
409ef639e6fSAlexey Brodkin #endif
410ef639e6fSAlexey Brodkin 		write_aux_reg(aux_cmd, paddr);
411379b3280SAlexey Brodkin 		paddr += l1_line_sz;
412ef639e6fSAlexey Brodkin 	}
413ef639e6fSAlexey Brodkin }
414ef639e6fSAlexey Brodkin 
415ef639e6fSAlexey Brodkin static unsigned int __before_dc_op(const int op)
416ef639e6fSAlexey Brodkin {
417ef639e6fSAlexey Brodkin 	unsigned int reg;
418ef639e6fSAlexey Brodkin 
419ef639e6fSAlexey Brodkin 	if (op == OP_INV) {
420ef639e6fSAlexey Brodkin 		/*
421ef639e6fSAlexey Brodkin 		 * IM is set by default and implies Flush-n-inv
422ef639e6fSAlexey Brodkin 		 * Clear it here for vanilla inv
423ef639e6fSAlexey Brodkin 		 */
424ef639e6fSAlexey Brodkin 		reg = read_aux_reg(ARC_AUX_DC_CTRL);
425ef639e6fSAlexey Brodkin 		write_aux_reg(ARC_AUX_DC_CTRL, reg & ~DC_CTRL_INV_MODE_FLUSH);
426ef639e6fSAlexey Brodkin 	}
427ef639e6fSAlexey Brodkin 
428ef639e6fSAlexey Brodkin 	return reg;
429ef639e6fSAlexey Brodkin }
430ef639e6fSAlexey Brodkin 
431ef639e6fSAlexey Brodkin static void __after_dc_op(const int op, unsigned int reg)
432ef639e6fSAlexey Brodkin {
433ef639e6fSAlexey Brodkin 	if (op & OP_FLUSH)	/* flush / flush-n-inv both wait */
43419b10a42SEugeniy Paltsev 		while (read_aux_reg(ARC_AUX_DC_CTRL) & DC_CTRL_FLUSH_STATUS);
435ef639e6fSAlexey Brodkin 
436ef639e6fSAlexey Brodkin 	/* Switch back to default Invalidate mode */
437ef639e6fSAlexey Brodkin 	if (op == OP_INV)
438ef639e6fSAlexey Brodkin 		write_aux_reg(ARC_AUX_DC_CTRL, reg | DC_CTRL_INV_MODE_FLUSH);
439ef639e6fSAlexey Brodkin }
440ef639e6fSAlexey Brodkin 
441ef639e6fSAlexey Brodkin static inline void __dc_entire_op(const int cacheop)
442ef639e6fSAlexey Brodkin {
443ef639e6fSAlexey Brodkin 	int aux;
444ef639e6fSAlexey Brodkin 	unsigned int ctrl_reg = __before_dc_op(cacheop);
445ef639e6fSAlexey Brodkin 
446ef639e6fSAlexey Brodkin 	if (cacheop & OP_INV)	/* Inv or flush-n-inv use same cmd reg */
447ef639e6fSAlexey Brodkin 		aux = ARC_AUX_DC_IVDC;
448ef639e6fSAlexey Brodkin 	else
449ef639e6fSAlexey Brodkin 		aux = ARC_AUX_DC_FLSH;
450ef639e6fSAlexey Brodkin 
451ef639e6fSAlexey Brodkin 	write_aux_reg(aux, 0x1);
452ef639e6fSAlexey Brodkin 
453ef639e6fSAlexey Brodkin 	__after_dc_op(cacheop, ctrl_reg);
454ef639e6fSAlexey Brodkin }
455ef639e6fSAlexey Brodkin 
456ef639e6fSAlexey Brodkin static inline void __dc_line_op(unsigned long paddr, unsigned long sz,
457ef639e6fSAlexey Brodkin 				const int cacheop)
458ef639e6fSAlexey Brodkin {
459ef639e6fSAlexey Brodkin 	unsigned int ctrl_reg = __before_dc_op(cacheop);
46019b10a42SEugeniy Paltsev 
461ef639e6fSAlexey Brodkin 	__cache_line_loop(paddr, sz, cacheop);
462ef639e6fSAlexey Brodkin 	__after_dc_op(cacheop, ctrl_reg);
463ef639e6fSAlexey Brodkin }
464ef639e6fSAlexey Brodkin #else
465ef639e6fSAlexey Brodkin #define __dc_entire_op(cacheop)
466ef639e6fSAlexey Brodkin #define __dc_line_op(paddr, sz, cacheop)
467ef639e6fSAlexey Brodkin #endif /* !CONFIG_SYS_DCACHE_OFF */
468ef639e6fSAlexey Brodkin 
469660d5f0dSAlexey Brodkin void invalidate_dcache_range(unsigned long start, unsigned long end)
470660d5f0dSAlexey Brodkin {
47141cada4dSEugeniy Paltsev 	if (start >= end)
47241cada4dSEugeniy Paltsev 		return;
47341cada4dSEugeniy Paltsev 
474ef639e6fSAlexey Brodkin #ifdef CONFIG_ISA_ARCV2
475db6ce231SAlexey Brodkin 	if (!ioc_exists)
476db6ce231SAlexey Brodkin #endif
477db6ce231SAlexey Brodkin 		__dc_line_op(start, end - start, OP_INV);
478db6ce231SAlexey Brodkin 
479db6ce231SAlexey Brodkin #ifdef CONFIG_ISA_ARCV2
480db6ce231SAlexey Brodkin 	if (slc_exists && !ioc_exists)
48141cada4dSEugeniy Paltsev 		__slc_rgn_op(start, end - start, OP_INV);
482660d5f0dSAlexey Brodkin #endif
483660d5f0dSAlexey Brodkin }
484660d5f0dSAlexey Brodkin 
485ef639e6fSAlexey Brodkin void flush_dcache_range(unsigned long start, unsigned long end)
486660d5f0dSAlexey Brodkin {
48741cada4dSEugeniy Paltsev 	if (start >= end)
48841cada4dSEugeniy Paltsev 		return;
48941cada4dSEugeniy Paltsev 
490ef639e6fSAlexey Brodkin #ifdef CONFIG_ISA_ARCV2
491db6ce231SAlexey Brodkin 	if (!ioc_exists)
492db6ce231SAlexey Brodkin #endif
493db6ce231SAlexey Brodkin 		__dc_line_op(start, end - start, OP_FLUSH);
494db6ce231SAlexey Brodkin 
495db6ce231SAlexey Brodkin #ifdef CONFIG_ISA_ARCV2
496db6ce231SAlexey Brodkin 	if (slc_exists && !ioc_exists)
49741cada4dSEugeniy Paltsev 		__slc_rgn_op(start, end - start, OP_FLUSH);
498ef639e6fSAlexey Brodkin #endif
499660d5f0dSAlexey Brodkin }
500660d5f0dSAlexey Brodkin 
501660d5f0dSAlexey Brodkin void flush_cache(unsigned long start, unsigned long size)
502660d5f0dSAlexey Brodkin {
503660d5f0dSAlexey Brodkin 	flush_dcache_range(start, start + size);
504660d5f0dSAlexey Brodkin }
5056eb15e50SAlexey Brodkin 
506ef639e6fSAlexey Brodkin void invalidate_dcache_all(void)
507ef639e6fSAlexey Brodkin {
508db6ce231SAlexey Brodkin 	__dc_entire_op(OP_INV);
509db6ce231SAlexey Brodkin 
510db6ce231SAlexey Brodkin #ifdef CONFIG_ISA_ARCV2
511bd91508bSAlexey Brodkin 	if (slc_exists)
512ef639e6fSAlexey Brodkin 		__slc_entire_op(OP_INV);
513ef639e6fSAlexey Brodkin #endif
5146eb15e50SAlexey Brodkin }
5156eb15e50SAlexey Brodkin 
516ef639e6fSAlexey Brodkin void flush_dcache_all(void)
5176eb15e50SAlexey Brodkin {
518db6ce231SAlexey Brodkin 	__dc_entire_op(OP_FLUSH);
519db6ce231SAlexey Brodkin 
520db6ce231SAlexey Brodkin #ifdef CONFIG_ISA_ARCV2
5212a8382c6SAlexey Brodkin 	if (slc_exists)
522ef639e6fSAlexey Brodkin 		__slc_entire_op(OP_FLUSH);
523ef639e6fSAlexey Brodkin #endif
5246eb15e50SAlexey Brodkin }
525