xref: /openbmc/u-boot/arch/arc/lib/cache.c (revision c4ef14d2)
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 
2741cada4dSEugeniy Paltsev /* Bit val in SLC_CONTROL */
2841cada4dSEugeniy Paltsev #define SLC_CTRL_DIS		0x001
2941cada4dSEugeniy Paltsev #define SLC_CTRL_IM		0x040
3041cada4dSEugeniy Paltsev #define SLC_CTRL_BUSY		0x100
3141cada4dSEugeniy Paltsev #define SLC_CTRL_RGN_OP_INV	0x200
3241cada4dSEugeniy Paltsev 
33ef639e6fSAlexey Brodkin /*
34ef639e6fSAlexey Brodkin  * By default that variable will fall into .bss section.
35ef639e6fSAlexey Brodkin  * But .bss section is not relocated and so it will be initilized before
36ef639e6fSAlexey Brodkin  * relocation but will be used after being zeroed.
37ef639e6fSAlexey Brodkin  */
38379b3280SAlexey Brodkin int l1_line_sz __section(".data");
393cf23939SEugeniy Paltsev bool dcache_exists __section(".data") = false;
403cf23939SEugeniy Paltsev bool icache_exists __section(".data") = false;
41379b3280SAlexey Brodkin 
42379b3280SAlexey Brodkin #define CACHE_LINE_MASK		(~(l1_line_sz - 1))
43379b3280SAlexey Brodkin 
44379b3280SAlexey Brodkin #ifdef CONFIG_ISA_ARCV2
45ef639e6fSAlexey Brodkin int slc_line_sz __section(".data");
463cf23939SEugeniy Paltsev bool slc_exists __section(".data") = false;
473cf23939SEugeniy Paltsev bool ioc_exists __section(".data") = false;
4841cada4dSEugeniy Paltsev bool pae_exists __section(".data") = false;
49ef639e6fSAlexey Brodkin 
50b0146f9eSEugeniy Paltsev /* To force enable IOC set ioc_enable to 'true' */
51b0146f9eSEugeniy Paltsev bool ioc_enable __section(".data") = false;
52b0146f9eSEugeniy Paltsev 
5341cada4dSEugeniy Paltsev void read_decode_mmu_bcr(void)
54ef639e6fSAlexey Brodkin {
5541cada4dSEugeniy Paltsev 	/* TODO: should we compare mmu version from BCR and from CONFIG? */
5641cada4dSEugeniy Paltsev #if (CONFIG_ARC_MMU_VER >= 4)
5741cada4dSEugeniy Paltsev 	u32 tmp;
58ef639e6fSAlexey Brodkin 
5941cada4dSEugeniy Paltsev 	tmp = read_aux_reg(ARC_AUX_MMU_BCR);
60ef639e6fSAlexey Brodkin 
6141cada4dSEugeniy Paltsev 	struct bcr_mmu_4 {
6241cada4dSEugeniy Paltsev #ifdef CONFIG_CPU_BIG_ENDIAN
6341cada4dSEugeniy Paltsev 	unsigned int ver:8, sasid:1, sz1:4, sz0:4, res:2, pae:1,
6441cada4dSEugeniy Paltsev 		     n_ways:2, n_entry:2, n_super:2, u_itlb:3, u_dtlb:3;
65ef639e6fSAlexey Brodkin #else
6641cada4dSEugeniy Paltsev 	/*           DTLB      ITLB      JES        JE         JA      */
6741cada4dSEugeniy Paltsev 	unsigned int u_dtlb:3, u_itlb:3, n_super:2, n_entry:2, n_ways:2,
6841cada4dSEugeniy Paltsev 		     pae:1, res:2, sz0:4, sz1:4, sasid:1, ver:8;
6941cada4dSEugeniy Paltsev #endif /* CONFIG_CPU_BIG_ENDIAN */
7041cada4dSEugeniy Paltsev 	} *mmu4;
7141cada4dSEugeniy Paltsev 
7241cada4dSEugeniy Paltsev 	mmu4 = (struct bcr_mmu_4 *)&tmp;
7341cada4dSEugeniy Paltsev 
7441cada4dSEugeniy Paltsev 	pae_exists = !!mmu4->pae;
7541cada4dSEugeniy Paltsev #endif /* (CONFIG_ARC_MMU_VER >= 4) */
7641cada4dSEugeniy Paltsev }
7741cada4dSEugeniy Paltsev 
7841cada4dSEugeniy Paltsev static void __slc_entire_op(const int op)
7941cada4dSEugeniy Paltsev {
8041cada4dSEugeniy Paltsev 	unsigned int ctrl;
8141cada4dSEugeniy Paltsev 
8241cada4dSEugeniy Paltsev 	ctrl = read_aux_reg(ARC_AUX_SLC_CTRL);
8341cada4dSEugeniy Paltsev 
8441cada4dSEugeniy Paltsev 	if (!(op & OP_FLUSH))		/* i.e. OP_INV */
8541cada4dSEugeniy Paltsev 		ctrl &= ~SLC_CTRL_IM;	/* clear IM: Disable flush before Inv */
8641cada4dSEugeniy Paltsev 	else
8741cada4dSEugeniy Paltsev 		ctrl |= SLC_CTRL_IM;
8841cada4dSEugeniy Paltsev 
8941cada4dSEugeniy Paltsev 	write_aux_reg(ARC_AUX_SLC_CTRL, ctrl);
9041cada4dSEugeniy Paltsev 
9141cada4dSEugeniy Paltsev 	if (op & OP_INV)	/* Inv or flush-n-inv use same cmd reg */
9241cada4dSEugeniy Paltsev 		write_aux_reg(ARC_AUX_SLC_INVALIDATE, 0x1);
9341cada4dSEugeniy Paltsev 	else
9441cada4dSEugeniy Paltsev 		write_aux_reg(ARC_AUX_SLC_FLUSH, 0x1);
9541cada4dSEugeniy Paltsev 
9641cada4dSEugeniy Paltsev 	/* Make sure "busy" bit reports correct stataus, see STAR 9001165532 */
9741cada4dSEugeniy Paltsev 	read_aux_reg(ARC_AUX_SLC_CTRL);
9841cada4dSEugeniy Paltsev 
9941cada4dSEugeniy Paltsev 	/* Important to wait for flush to complete */
10041cada4dSEugeniy Paltsev 	while (read_aux_reg(ARC_AUX_SLC_CTRL) & SLC_CTRL_BUSY);
10141cada4dSEugeniy Paltsev }
10241cada4dSEugeniy Paltsev 
10341cada4dSEugeniy Paltsev static void slc_upper_region_init(void)
10441cada4dSEugeniy Paltsev {
10541cada4dSEugeniy Paltsev 	/*
10641cada4dSEugeniy Paltsev 	 * ARC_AUX_SLC_RGN_END1 and ARC_AUX_SLC_RGN_START1 are always == 0
10741cada4dSEugeniy Paltsev 	 * as we don't use PAE40.
10841cada4dSEugeniy Paltsev 	 */
10941cada4dSEugeniy Paltsev 	write_aux_reg(ARC_AUX_SLC_RGN_END1, 0);
11041cada4dSEugeniy Paltsev 	write_aux_reg(ARC_AUX_SLC_RGN_START1, 0);
11141cada4dSEugeniy Paltsev }
11241cada4dSEugeniy Paltsev 
11341cada4dSEugeniy Paltsev static void __slc_rgn_op(unsigned long paddr, unsigned long sz, const int op)
11441cada4dSEugeniy Paltsev {
11541cada4dSEugeniy Paltsev 	unsigned int ctrl;
11641cada4dSEugeniy Paltsev 	unsigned long end;
11741cada4dSEugeniy Paltsev 
11841cada4dSEugeniy Paltsev 	/*
11941cada4dSEugeniy Paltsev 	 * The Region Flush operation is specified by CTRL.RGN_OP[11..9]
12041cada4dSEugeniy Paltsev 	 *  - b'000 (default) is Flush,
12141cada4dSEugeniy Paltsev 	 *  - b'001 is Invalidate if CTRL.IM == 0
12241cada4dSEugeniy Paltsev 	 *  - b'001 is Flush-n-Invalidate if CTRL.IM == 1
12341cada4dSEugeniy Paltsev 	 */
12441cada4dSEugeniy Paltsev 	ctrl = read_aux_reg(ARC_AUX_SLC_CTRL);
12541cada4dSEugeniy Paltsev 
12641cada4dSEugeniy Paltsev 	/* Don't rely on default value of IM bit */
12741cada4dSEugeniy Paltsev 	if (!(op & OP_FLUSH))		/* i.e. OP_INV */
12841cada4dSEugeniy Paltsev 		ctrl &= ~SLC_CTRL_IM;	/* clear IM: Disable flush before Inv */
12941cada4dSEugeniy Paltsev 	else
13041cada4dSEugeniy Paltsev 		ctrl |= SLC_CTRL_IM;
13141cada4dSEugeniy Paltsev 
13241cada4dSEugeniy Paltsev 	if (op & OP_INV)
13341cada4dSEugeniy Paltsev 		ctrl |= SLC_CTRL_RGN_OP_INV;	/* Inv or flush-n-inv */
13441cada4dSEugeniy Paltsev 	else
13541cada4dSEugeniy Paltsev 		ctrl &= ~SLC_CTRL_RGN_OP_INV;
13641cada4dSEugeniy Paltsev 
13741cada4dSEugeniy Paltsev 	write_aux_reg(ARC_AUX_SLC_CTRL, ctrl);
13841cada4dSEugeniy Paltsev 
13941cada4dSEugeniy Paltsev 	/*
14041cada4dSEugeniy Paltsev 	 * Lower bits are ignored, no need to clip
14141cada4dSEugeniy Paltsev 	 * END needs to be setup before START (latter triggers the operation)
14241cada4dSEugeniy Paltsev 	 * END can't be same as START, so add (l2_line_sz - 1) to sz
14341cada4dSEugeniy Paltsev 	 */
14441cada4dSEugeniy Paltsev 	end = paddr + sz + slc_line_sz - 1;
14541cada4dSEugeniy Paltsev 
14641cada4dSEugeniy Paltsev 	/*
14741cada4dSEugeniy Paltsev 	 * Upper addresses (ARC_AUX_SLC_RGN_END1 and ARC_AUX_SLC_RGN_START1)
14841cada4dSEugeniy Paltsev 	 * are always == 0 as we don't use PAE40, so we only setup lower ones
14941cada4dSEugeniy Paltsev 	 * (ARC_AUX_SLC_RGN_END and ARC_AUX_SLC_RGN_START)
15041cada4dSEugeniy Paltsev 	 */
15141cada4dSEugeniy Paltsev 	write_aux_reg(ARC_AUX_SLC_RGN_END, end);
15241cada4dSEugeniy Paltsev 	write_aux_reg(ARC_AUX_SLC_RGN_START, paddr);
15341cada4dSEugeniy Paltsev 
15441cada4dSEugeniy Paltsev 	/* Make sure "busy" bit reports correct stataus, see STAR 9001165532 */
15541cada4dSEugeniy Paltsev 	read_aux_reg(ARC_AUX_SLC_CTRL);
15641cada4dSEugeniy Paltsev 
15741cada4dSEugeniy Paltsev 	while (read_aux_reg(ARC_AUX_SLC_CTRL) & SLC_CTRL_BUSY);
15841cada4dSEugeniy Paltsev }
15941cada4dSEugeniy Paltsev #endif /* CONFIG_ISA_ARCV2 */
160ef639e6fSAlexey Brodkin 
161379b3280SAlexey Brodkin #ifdef CONFIG_ISA_ARCV2
162379b3280SAlexey Brodkin static void read_decode_cache_bcr_arcv2(void)
163ef639e6fSAlexey Brodkin {
164379b3280SAlexey Brodkin 	union {
165379b3280SAlexey Brodkin 		struct {
166379b3280SAlexey Brodkin #ifdef CONFIG_CPU_BIG_ENDIAN
167379b3280SAlexey Brodkin 			unsigned int pad:24, way:2, lsz:2, sz:4;
168379b3280SAlexey Brodkin #else
169379b3280SAlexey Brodkin 			unsigned int sz:4, lsz:2, way:2, pad:24;
170379b3280SAlexey Brodkin #endif
171379b3280SAlexey Brodkin 		} fields;
172379b3280SAlexey Brodkin 		unsigned int word;
173379b3280SAlexey Brodkin 	} slc_cfg;
174379b3280SAlexey Brodkin 
175379b3280SAlexey Brodkin 	union {
176379b3280SAlexey Brodkin 		struct {
177379b3280SAlexey Brodkin #ifdef CONFIG_CPU_BIG_ENDIAN
178379b3280SAlexey Brodkin 			unsigned int pad:24, ver:8;
179379b3280SAlexey Brodkin #else
180379b3280SAlexey Brodkin 			unsigned int ver:8, pad:24;
181379b3280SAlexey Brodkin #endif
182379b3280SAlexey Brodkin 		} fields;
183379b3280SAlexey Brodkin 		unsigned int word;
184379b3280SAlexey Brodkin 	} sbcr;
185379b3280SAlexey Brodkin 
186379b3280SAlexey Brodkin 	sbcr.word = read_aux_reg(ARC_BCR_SLC);
187379b3280SAlexey Brodkin 	if (sbcr.fields.ver) {
188379b3280SAlexey Brodkin 		slc_cfg.word = read_aux_reg(ARC_AUX_SLC_CONFIG);
1893cf23939SEugeniy Paltsev 		slc_exists = true;
190379b3280SAlexey Brodkin 		slc_line_sz = (slc_cfg.fields.lsz == 0) ? 128 : 64;
191379b3280SAlexey Brodkin 	}
192db6ce231SAlexey Brodkin 
193db6ce231SAlexey Brodkin 	union {
194db6ce231SAlexey Brodkin 		struct bcr_clust_cfg {
195db6ce231SAlexey Brodkin #ifdef CONFIG_CPU_BIG_ENDIAN
196db6ce231SAlexey Brodkin 			unsigned int pad:7, c:1, num_entries:8, num_cores:8, ver:8;
197db6ce231SAlexey Brodkin #else
198db6ce231SAlexey Brodkin 			unsigned int ver:8, num_cores:8, num_entries:8, c:1, pad:7;
199db6ce231SAlexey Brodkin #endif
200db6ce231SAlexey Brodkin 		} fields;
201db6ce231SAlexey Brodkin 		unsigned int word;
202db6ce231SAlexey Brodkin 	} cbcr;
203db6ce231SAlexey Brodkin 
204db6ce231SAlexey Brodkin 	cbcr.word = read_aux_reg(ARC_BCR_CLUSTER);
205b0146f9eSEugeniy Paltsev 	if (cbcr.fields.c && ioc_enable)
2063cf23939SEugeniy Paltsev 		ioc_exists = true;
207379b3280SAlexey Brodkin }
208379b3280SAlexey Brodkin #endif
209379b3280SAlexey Brodkin 
210379b3280SAlexey Brodkin void read_decode_cache_bcr(void)
211379b3280SAlexey Brodkin {
212379b3280SAlexey Brodkin 	int dc_line_sz = 0, ic_line_sz = 0;
213379b3280SAlexey Brodkin 
214379b3280SAlexey Brodkin 	union {
215379b3280SAlexey Brodkin 		struct {
216379b3280SAlexey Brodkin #ifdef CONFIG_CPU_BIG_ENDIAN
217379b3280SAlexey Brodkin 			unsigned int pad:12, line_len:4, sz:4, config:4, ver:8;
218379b3280SAlexey Brodkin #else
219379b3280SAlexey Brodkin 			unsigned int ver:8, config:4, sz:4, line_len:4, pad:12;
220379b3280SAlexey Brodkin #endif
221379b3280SAlexey Brodkin 		} fields;
222379b3280SAlexey Brodkin 		unsigned int word;
223379b3280SAlexey Brodkin 	} ibcr, dbcr;
224379b3280SAlexey Brodkin 
225379b3280SAlexey Brodkin 	ibcr.word = read_aux_reg(ARC_BCR_IC_BUILD);
226379b3280SAlexey Brodkin 	if (ibcr.fields.ver) {
2273cf23939SEugeniy Paltsev 		icache_exists = true;
228379b3280SAlexey Brodkin 		l1_line_sz = ic_line_sz = 8 << ibcr.fields.line_len;
229379b3280SAlexey Brodkin 		if (!ic_line_sz)
230379b3280SAlexey Brodkin 			panic("Instruction exists but line length is 0\n");
231ef639e6fSAlexey Brodkin 	}
232ef639e6fSAlexey Brodkin 
233379b3280SAlexey Brodkin 	dbcr.word = read_aux_reg(ARC_BCR_DC_BUILD);
234379b3280SAlexey Brodkin 	if (dbcr.fields.ver) {
2353cf23939SEugeniy Paltsev 		dcache_exists = true;
236379b3280SAlexey Brodkin 		l1_line_sz = dc_line_sz = 16 << dbcr.fields.line_len;
237379b3280SAlexey Brodkin 		if (!dc_line_sz)
238379b3280SAlexey Brodkin 			panic("Data cache exists but line length is 0\n");
239379b3280SAlexey Brodkin 	}
240379b3280SAlexey Brodkin 
241379b3280SAlexey Brodkin 	if (ic_line_sz && dc_line_sz && (ic_line_sz != dc_line_sz))
242379b3280SAlexey Brodkin 		panic("Instruction and data cache line lengths differ\n");
243ef639e6fSAlexey Brodkin }
244ef639e6fSAlexey Brodkin 
245ef639e6fSAlexey Brodkin void cache_init(void)
246ef639e6fSAlexey Brodkin {
247379b3280SAlexey Brodkin 	read_decode_cache_bcr();
248379b3280SAlexey Brodkin 
249ef639e6fSAlexey Brodkin #ifdef CONFIG_ISA_ARCV2
250379b3280SAlexey Brodkin 	read_decode_cache_bcr_arcv2();
251db6ce231SAlexey Brodkin 
252db6ce231SAlexey Brodkin 	if (ioc_exists) {
25397a63144SAlexey Brodkin 		/* IOC Aperture start is equal to DDR start */
25497a63144SAlexey Brodkin 		unsigned int ap_base = CONFIG_SYS_SDRAM_BASE;
25597a63144SAlexey Brodkin 		/* IOC Aperture size is equal to DDR size */
25697a63144SAlexey Brodkin 		long ap_size = CONFIG_SYS_SDRAM_SIZE;
25797a63144SAlexey Brodkin 
258a4a43fcfSAlexey Brodkin 		flush_dcache_all();
259a4a43fcfSAlexey Brodkin 		invalidate_dcache_all();
260a4a43fcfSAlexey Brodkin 
26197a63144SAlexey Brodkin 		if (!is_power_of_2(ap_size) || ap_size < 4096)
26297a63144SAlexey Brodkin 			panic("IOC Aperture size must be power of 2 and bigger 4Kib");
26397a63144SAlexey Brodkin 
26497a63144SAlexey Brodkin 		/*
26597a63144SAlexey Brodkin 		 * IOC Aperture size decoded as 2 ^ (SIZE + 2) KB,
26697a63144SAlexey Brodkin 		 * so setting 0x11 implies 512M, 0x12 implies 1G...
26797a63144SAlexey Brodkin 		 */
26897a63144SAlexey Brodkin 		write_aux_reg(ARC_AUX_IO_COH_AP0_SIZE,
26997a63144SAlexey Brodkin 			      order_base_2(ap_size / 1024) - 2);
27097a63144SAlexey Brodkin 
27197a63144SAlexey Brodkin 		/* IOC Aperture start must be aligned to the size of the aperture */
27297a63144SAlexey Brodkin 		if (ap_base % ap_size != 0)
27397a63144SAlexey Brodkin 			panic("IOC Aperture start must be aligned to the size of the aperture");
27497a63144SAlexey Brodkin 
27597a63144SAlexey Brodkin 		write_aux_reg(ARC_AUX_IO_COH_AP0_BASE, ap_base >> 12);
276db6ce231SAlexey Brodkin 		write_aux_reg(ARC_AUX_IO_COH_PARTIAL, 1);
277db6ce231SAlexey Brodkin 		write_aux_reg(ARC_AUX_IO_COH_ENABLE, 1);
278db6ce231SAlexey Brodkin 	}
27941cada4dSEugeniy Paltsev 
28041cada4dSEugeniy Paltsev 	read_decode_mmu_bcr();
28141cada4dSEugeniy Paltsev 
28241cada4dSEugeniy Paltsev 	/*
28341cada4dSEugeniy Paltsev 	 * ARC_AUX_SLC_RGN_START1 and ARC_AUX_SLC_RGN_END1 register exist
28441cada4dSEugeniy Paltsev 	 * only if PAE exists in current HW. So we had to check pae_exist
28541cada4dSEugeniy Paltsev 	 * before using them.
28641cada4dSEugeniy Paltsev 	 */
28741cada4dSEugeniy Paltsev 	if (slc_exists && pae_exists)
28841cada4dSEugeniy Paltsev 		slc_upper_region_init();
28941cada4dSEugeniy 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 
31716aeee81SEugeniy Paltsev /* IC supports only invalidation */
31816aeee81SEugeniy Paltsev static inline void __ic_entire_invalidate(void)
319660d5f0dSAlexey Brodkin {
32016aeee81SEugeniy Paltsev 	if (!icache_status())
32116aeee81SEugeniy Paltsev 		return;
32216aeee81SEugeniy Paltsev 
323660d5f0dSAlexey Brodkin 	/* Any write to IC_IVIC register triggers invalidation of entire I$ */
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 
33516aeee81SEugeniy Paltsev void invalidate_icache_all(void)
33616aeee81SEugeniy Paltsev {
33716aeee81SEugeniy Paltsev 	__ic_entire_invalidate();
33816aeee81SEugeniy Paltsev 
33941cada4dSEugeniy Paltsev #ifdef CONFIG_ISA_ARCV2
34041cada4dSEugeniy Paltsev 	if (slc_exists)
34141cada4dSEugeniy Paltsev 		__slc_entire_op(OP_INV);
342ef639e6fSAlexey Brodkin #endif
34341cada4dSEugeniy Paltsev }
344660d5f0dSAlexey Brodkin 
345660d5f0dSAlexey Brodkin int dcache_status(void)
346660d5f0dSAlexey Brodkin {
347379b3280SAlexey Brodkin 	if (!dcache_exists)
348660d5f0dSAlexey Brodkin 		return 0;
349660d5f0dSAlexey Brodkin 
350ef639e6fSAlexey Brodkin 	if (read_aux_reg(ARC_AUX_DC_CTRL) & DC_CTRL_CACHE_DISABLE)
351ef639e6fSAlexey Brodkin 		return 0;
352ef639e6fSAlexey Brodkin 	else
353ef639e6fSAlexey Brodkin 		return 1;
354660d5f0dSAlexey Brodkin }
355660d5f0dSAlexey Brodkin 
356660d5f0dSAlexey Brodkin void dcache_enable(void)
357660d5f0dSAlexey Brodkin {
358379b3280SAlexey Brodkin 	if (!dcache_exists)
359660d5f0dSAlexey Brodkin 		return;
360660d5f0dSAlexey Brodkin 
361660d5f0dSAlexey Brodkin 	write_aux_reg(ARC_AUX_DC_CTRL, read_aux_reg(ARC_AUX_DC_CTRL) &
362660d5f0dSAlexey Brodkin 		      ~(DC_CTRL_INV_MODE_FLUSH | DC_CTRL_CACHE_DISABLE));
363660d5f0dSAlexey Brodkin }
364660d5f0dSAlexey Brodkin 
365660d5f0dSAlexey Brodkin void dcache_disable(void)
366660d5f0dSAlexey Brodkin {
367379b3280SAlexey Brodkin 	if (!dcache_exists)
368660d5f0dSAlexey Brodkin 		return;
369660d5f0dSAlexey Brodkin 
370660d5f0dSAlexey Brodkin 	write_aux_reg(ARC_AUX_DC_CTRL, read_aux_reg(ARC_AUX_DC_CTRL) |
371660d5f0dSAlexey Brodkin 		      DC_CTRL_CACHE_DISABLE);
372660d5f0dSAlexey Brodkin }
373660d5f0dSAlexey Brodkin 
374660d5f0dSAlexey Brodkin #ifndef CONFIG_SYS_DCACHE_OFF
375*c4ef14d2SEugeniy Paltsev /* Common Helper for Line Operations on D-cache */
376*c4ef14d2SEugeniy Paltsev static inline void __dcache_line_loop(unsigned long paddr, unsigned long sz,
377ef639e6fSAlexey Brodkin 				      const int cacheop)
378660d5f0dSAlexey Brodkin {
379ef639e6fSAlexey Brodkin 	unsigned int aux_cmd;
380ef639e6fSAlexey Brodkin 	int num_lines;
381660d5f0dSAlexey Brodkin 
382ef639e6fSAlexey Brodkin 	/* d$ cmd: INV (discard or wback-n-discard) OR FLUSH (wback) */
383ef639e6fSAlexey Brodkin 	aux_cmd = cacheop & OP_INV ? ARC_AUX_DC_IVDL : ARC_AUX_DC_FLDL;
384660d5f0dSAlexey Brodkin 
385ef639e6fSAlexey Brodkin 	sz += paddr & ~CACHE_LINE_MASK;
386ef639e6fSAlexey Brodkin 	paddr &= CACHE_LINE_MASK;
387ef639e6fSAlexey Brodkin 
388379b3280SAlexey Brodkin 	num_lines = DIV_ROUND_UP(sz, l1_line_sz);
389ef639e6fSAlexey Brodkin 
390ef639e6fSAlexey Brodkin 	while (num_lines-- > 0) {
391ef639e6fSAlexey Brodkin #if (CONFIG_ARC_MMU_VER == 3)
392*c4ef14d2SEugeniy Paltsev 		write_aux_reg(ARC_AUX_DC_PTAG, paddr);
393ef639e6fSAlexey Brodkin #endif
394ef639e6fSAlexey Brodkin 		write_aux_reg(aux_cmd, paddr);
395379b3280SAlexey Brodkin 		paddr += l1_line_sz;
396ef639e6fSAlexey Brodkin 	}
397ef639e6fSAlexey Brodkin }
398ef639e6fSAlexey Brodkin 
399ef639e6fSAlexey Brodkin static unsigned int __before_dc_op(const int op)
400ef639e6fSAlexey Brodkin {
401ef639e6fSAlexey Brodkin 	unsigned int reg;
402ef639e6fSAlexey Brodkin 
403ef639e6fSAlexey Brodkin 	if (op == OP_INV) {
404ef639e6fSAlexey Brodkin 		/*
405ef639e6fSAlexey Brodkin 		 * IM is set by default and implies Flush-n-inv
406ef639e6fSAlexey Brodkin 		 * Clear it here for vanilla inv
407ef639e6fSAlexey Brodkin 		 */
408ef639e6fSAlexey Brodkin 		reg = read_aux_reg(ARC_AUX_DC_CTRL);
409ef639e6fSAlexey Brodkin 		write_aux_reg(ARC_AUX_DC_CTRL, reg & ~DC_CTRL_INV_MODE_FLUSH);
410ef639e6fSAlexey Brodkin 	}
411ef639e6fSAlexey Brodkin 
412ef639e6fSAlexey Brodkin 	return reg;
413ef639e6fSAlexey Brodkin }
414ef639e6fSAlexey Brodkin 
415ef639e6fSAlexey Brodkin static void __after_dc_op(const int op, unsigned int reg)
416ef639e6fSAlexey Brodkin {
417ef639e6fSAlexey Brodkin 	if (op & OP_FLUSH)	/* flush / flush-n-inv both wait */
41819b10a42SEugeniy Paltsev 		while (read_aux_reg(ARC_AUX_DC_CTRL) & DC_CTRL_FLUSH_STATUS);
419ef639e6fSAlexey Brodkin 
420ef639e6fSAlexey Brodkin 	/* Switch back to default Invalidate mode */
421ef639e6fSAlexey Brodkin 	if (op == OP_INV)
422ef639e6fSAlexey Brodkin 		write_aux_reg(ARC_AUX_DC_CTRL, reg | DC_CTRL_INV_MODE_FLUSH);
423ef639e6fSAlexey Brodkin }
424ef639e6fSAlexey Brodkin 
425ef639e6fSAlexey Brodkin static inline void __dc_entire_op(const int cacheop)
426ef639e6fSAlexey Brodkin {
427ef639e6fSAlexey Brodkin 	int aux;
428ef639e6fSAlexey Brodkin 	unsigned int ctrl_reg = __before_dc_op(cacheop);
429ef639e6fSAlexey Brodkin 
430ef639e6fSAlexey Brodkin 	if (cacheop & OP_INV)	/* Inv or flush-n-inv use same cmd reg */
431ef639e6fSAlexey Brodkin 		aux = ARC_AUX_DC_IVDC;
432ef639e6fSAlexey Brodkin 	else
433ef639e6fSAlexey Brodkin 		aux = ARC_AUX_DC_FLSH;
434ef639e6fSAlexey Brodkin 
435ef639e6fSAlexey Brodkin 	write_aux_reg(aux, 0x1);
436ef639e6fSAlexey Brodkin 
437ef639e6fSAlexey Brodkin 	__after_dc_op(cacheop, ctrl_reg);
438ef639e6fSAlexey Brodkin }
439ef639e6fSAlexey Brodkin 
440ef639e6fSAlexey Brodkin static inline void __dc_line_op(unsigned long paddr, unsigned long sz,
441ef639e6fSAlexey Brodkin 				const int cacheop)
442ef639e6fSAlexey Brodkin {
443ef639e6fSAlexey Brodkin 	unsigned int ctrl_reg = __before_dc_op(cacheop);
44419b10a42SEugeniy Paltsev 
445*c4ef14d2SEugeniy Paltsev 	__dcache_line_loop(paddr, sz, cacheop);
446ef639e6fSAlexey Brodkin 	__after_dc_op(cacheop, ctrl_reg);
447ef639e6fSAlexey Brodkin }
448ef639e6fSAlexey Brodkin #else
449ef639e6fSAlexey Brodkin #define __dc_entire_op(cacheop)
450ef639e6fSAlexey Brodkin #define __dc_line_op(paddr, sz, cacheop)
451ef639e6fSAlexey Brodkin #endif /* !CONFIG_SYS_DCACHE_OFF */
452ef639e6fSAlexey Brodkin 
453660d5f0dSAlexey Brodkin void invalidate_dcache_range(unsigned long start, unsigned long end)
454660d5f0dSAlexey Brodkin {
45541cada4dSEugeniy Paltsev 	if (start >= end)
45641cada4dSEugeniy Paltsev 		return;
45741cada4dSEugeniy Paltsev 
458ef639e6fSAlexey Brodkin #ifdef CONFIG_ISA_ARCV2
459db6ce231SAlexey Brodkin 	if (!ioc_exists)
460db6ce231SAlexey Brodkin #endif
461db6ce231SAlexey Brodkin 		__dc_line_op(start, end - start, OP_INV);
462db6ce231SAlexey Brodkin 
463db6ce231SAlexey Brodkin #ifdef CONFIG_ISA_ARCV2
464db6ce231SAlexey Brodkin 	if (slc_exists && !ioc_exists)
46541cada4dSEugeniy Paltsev 		__slc_rgn_op(start, end - start, OP_INV);
466660d5f0dSAlexey Brodkin #endif
467660d5f0dSAlexey Brodkin }
468660d5f0dSAlexey Brodkin 
469ef639e6fSAlexey Brodkin void flush_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_FLUSH);
478db6ce231SAlexey Brodkin 
479db6ce231SAlexey Brodkin #ifdef CONFIG_ISA_ARCV2
480db6ce231SAlexey Brodkin 	if (slc_exists && !ioc_exists)
48141cada4dSEugeniy Paltsev 		__slc_rgn_op(start, end - start, OP_FLUSH);
482ef639e6fSAlexey Brodkin #endif
483660d5f0dSAlexey Brodkin }
484660d5f0dSAlexey Brodkin 
485660d5f0dSAlexey Brodkin void flush_cache(unsigned long start, unsigned long size)
486660d5f0dSAlexey Brodkin {
487660d5f0dSAlexey Brodkin 	flush_dcache_range(start, start + size);
488660d5f0dSAlexey Brodkin }
4896eb15e50SAlexey Brodkin 
490ef639e6fSAlexey Brodkin void invalidate_dcache_all(void)
491ef639e6fSAlexey Brodkin {
492db6ce231SAlexey Brodkin 	__dc_entire_op(OP_INV);
493db6ce231SAlexey Brodkin 
494db6ce231SAlexey Brodkin #ifdef CONFIG_ISA_ARCV2
495bd91508bSAlexey Brodkin 	if (slc_exists)
496ef639e6fSAlexey Brodkin 		__slc_entire_op(OP_INV);
497ef639e6fSAlexey Brodkin #endif
4986eb15e50SAlexey Brodkin }
4996eb15e50SAlexey Brodkin 
500ef639e6fSAlexey Brodkin void flush_dcache_all(void)
5016eb15e50SAlexey Brodkin {
502db6ce231SAlexey Brodkin 	__dc_entire_op(OP_FLUSH);
503db6ce231SAlexey Brodkin 
504db6ce231SAlexey Brodkin #ifdef CONFIG_ISA_ARCV2
5052a8382c6SAlexey Brodkin 	if (slc_exists)
506ef639e6fSAlexey Brodkin 		__slc_entire_op(OP_FLUSH);
507ef639e6fSAlexey Brodkin #endif
5086eb15e50SAlexey Brodkin }
509