xref: /kernel/linux/linux-6.6/arch/arc/mm/cache.c (revision 62306a36)
162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * ARC Cache Management
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Copyright (C) 2014-15 Synopsys, Inc. (www.synopsys.com)
662306a36Sopenharmony_ci * Copyright (C) 2004, 2007-2010, 2011-2012 Synopsys, Inc. (www.synopsys.com)
762306a36Sopenharmony_ci */
862306a36Sopenharmony_ci
962306a36Sopenharmony_ci#include <linux/module.h>
1062306a36Sopenharmony_ci#include <linux/mm.h>
1162306a36Sopenharmony_ci#include <linux/sched.h>
1262306a36Sopenharmony_ci#include <linux/cache.h>
1362306a36Sopenharmony_ci#include <linux/mmu_context.h>
1462306a36Sopenharmony_ci#include <linux/syscalls.h>
1562306a36Sopenharmony_ci#include <linux/uaccess.h>
1662306a36Sopenharmony_ci#include <linux/pagemap.h>
1762306a36Sopenharmony_ci#include <asm/cacheflush.h>
1862306a36Sopenharmony_ci#include <asm/cachectl.h>
1962306a36Sopenharmony_ci#include <asm/setup.h>
2062306a36Sopenharmony_ci
2162306a36Sopenharmony_ci#ifdef CONFIG_ISA_ARCV2
2262306a36Sopenharmony_ci#define USE_RGN_FLSH	1
2362306a36Sopenharmony_ci#endif
2462306a36Sopenharmony_ci
2562306a36Sopenharmony_cistatic int l2_line_sz;
2662306a36Sopenharmony_cistatic int ioc_exists;
2762306a36Sopenharmony_ciint slc_enable = 1, ioc_enable = 1;
2862306a36Sopenharmony_ciunsigned long perip_base = ARC_UNCACHED_ADDR_SPACE; /* legacy value for boot */
2962306a36Sopenharmony_ciunsigned long perip_end = 0xFFFFFFFF; /* legacy value */
3062306a36Sopenharmony_ci
3162306a36Sopenharmony_cistatic struct cpuinfo_arc_cache {
3262306a36Sopenharmony_ci	unsigned int sz_k, line_len, colors;
3362306a36Sopenharmony_ci} ic_info, dc_info, slc_info;
3462306a36Sopenharmony_ci
3562306a36Sopenharmony_civoid (*_cache_line_loop_ic_fn)(phys_addr_t paddr, unsigned long vaddr,
3662306a36Sopenharmony_ci			       unsigned long sz, const int op, const int full_page);
3762306a36Sopenharmony_ci
3862306a36Sopenharmony_civoid (*__dma_cache_wback_inv)(phys_addr_t start, unsigned long sz);
3962306a36Sopenharmony_civoid (*__dma_cache_inv)(phys_addr_t start, unsigned long sz);
4062306a36Sopenharmony_civoid (*__dma_cache_wback)(phys_addr_t start, unsigned long sz);
4162306a36Sopenharmony_ci
4262306a36Sopenharmony_cistatic int read_decode_cache_bcr_arcv2(int c, char *buf, int len)
4362306a36Sopenharmony_ci{
4462306a36Sopenharmony_ci	struct cpuinfo_arc_cache *p_slc = &slc_info;
4562306a36Sopenharmony_ci	struct bcr_identity ident;
4662306a36Sopenharmony_ci	struct bcr_generic sbcr;
4762306a36Sopenharmony_ci	struct bcr_clust_cfg cbcr;
4862306a36Sopenharmony_ci	struct bcr_volatile vol;
4962306a36Sopenharmony_ci	int n = 0;
5062306a36Sopenharmony_ci
5162306a36Sopenharmony_ci	READ_BCR(ARC_REG_SLC_BCR, sbcr);
5262306a36Sopenharmony_ci	if (sbcr.ver) {
5362306a36Sopenharmony_ci		struct bcr_slc_cfg  slc_cfg;
5462306a36Sopenharmony_ci		READ_BCR(ARC_REG_SLC_CFG, slc_cfg);
5562306a36Sopenharmony_ci		p_slc->sz_k = 128 << slc_cfg.sz;
5662306a36Sopenharmony_ci		l2_line_sz = p_slc->line_len = (slc_cfg.lsz == 0) ? 128 : 64;
5762306a36Sopenharmony_ci		n += scnprintf(buf + n, len - n,
5862306a36Sopenharmony_ci			       "SLC\t\t: %uK, %uB Line%s\n",
5962306a36Sopenharmony_ci			       p_slc->sz_k, p_slc->line_len, IS_USED_RUN(slc_enable));
6062306a36Sopenharmony_ci	}
6162306a36Sopenharmony_ci
6262306a36Sopenharmony_ci	READ_BCR(ARC_REG_CLUSTER_BCR, cbcr);
6362306a36Sopenharmony_ci	if (cbcr.c) {
6462306a36Sopenharmony_ci		ioc_exists = 1;
6562306a36Sopenharmony_ci
6662306a36Sopenharmony_ci		/*
6762306a36Sopenharmony_ci		 * As for today we don't support both IOC and ZONE_HIGHMEM enabled
6862306a36Sopenharmony_ci		 * simultaneously. This happens because as of today IOC aperture covers
6962306a36Sopenharmony_ci		 * only ZONE_NORMAL (low mem) and any dma transactions outside this
7062306a36Sopenharmony_ci		 * region won't be HW coherent.
7162306a36Sopenharmony_ci		 * If we want to use both IOC and ZONE_HIGHMEM we can use
7262306a36Sopenharmony_ci		 * bounce_buffer to handle dma transactions to HIGHMEM.
7362306a36Sopenharmony_ci		 * Also it is possible to modify dma_direct cache ops or increase IOC
7462306a36Sopenharmony_ci		 * aperture size if we are planning to use HIGHMEM without PAE.
7562306a36Sopenharmony_ci		 */
7662306a36Sopenharmony_ci		if (IS_ENABLED(CONFIG_HIGHMEM) || is_pae40_enabled())
7762306a36Sopenharmony_ci			ioc_enable = 0;
7862306a36Sopenharmony_ci	} else {
7962306a36Sopenharmony_ci		ioc_enable = 0;
8062306a36Sopenharmony_ci	}
8162306a36Sopenharmony_ci
8262306a36Sopenharmony_ci	READ_BCR(AUX_IDENTITY, ident);
8362306a36Sopenharmony_ci
8462306a36Sopenharmony_ci	/* HS 2.0 didn't have AUX_VOL */
8562306a36Sopenharmony_ci	if (ident.family > 0x51) {
8662306a36Sopenharmony_ci		READ_BCR(AUX_VOL, vol);
8762306a36Sopenharmony_ci		perip_base = vol.start << 28;
8862306a36Sopenharmony_ci		/* HS 3.0 has limit and strict-ordering fields */
8962306a36Sopenharmony_ci		if (ident.family > 0x52)
9062306a36Sopenharmony_ci			perip_end = (vol.limit << 28) - 1;
9162306a36Sopenharmony_ci	}
9262306a36Sopenharmony_ci
9362306a36Sopenharmony_ci	n += scnprintf(buf + n, len - n, "Peripherals\t: %#lx%s%s\n",
9462306a36Sopenharmony_ci		       perip_base,
9562306a36Sopenharmony_ci		       IS_AVAIL3(ioc_exists, ioc_enable, ", IO-Coherency (per-device) "));
9662306a36Sopenharmony_ci
9762306a36Sopenharmony_ci	return n;
9862306a36Sopenharmony_ci}
9962306a36Sopenharmony_ci
10062306a36Sopenharmony_ciint arc_cache_mumbojumbo(int c, char *buf, int len)
10162306a36Sopenharmony_ci{
10262306a36Sopenharmony_ci	struct cpuinfo_arc_cache *p_ic = &ic_info, *p_dc = &dc_info;
10362306a36Sopenharmony_ci	struct bcr_cache ibcr, dbcr;
10462306a36Sopenharmony_ci	int vipt, assoc;
10562306a36Sopenharmony_ci	int n = 0;
10662306a36Sopenharmony_ci
10762306a36Sopenharmony_ci	READ_BCR(ARC_REG_IC_BCR, ibcr);
10862306a36Sopenharmony_ci	if (!ibcr.ver)
10962306a36Sopenharmony_ci		goto dc_chk;
11062306a36Sopenharmony_ci
11162306a36Sopenharmony_ci	if (is_isa_arcompact() && (ibcr.ver <= 3)) {
11262306a36Sopenharmony_ci		BUG_ON(ibcr.config != 3);
11362306a36Sopenharmony_ci		assoc = 2;		/* Fixed to 2w set assoc */
11462306a36Sopenharmony_ci	} else if (is_isa_arcv2() && (ibcr.ver >= 4)) {
11562306a36Sopenharmony_ci		assoc = 1 << ibcr.config;	/* 1,2,4,8 */
11662306a36Sopenharmony_ci	}
11762306a36Sopenharmony_ci
11862306a36Sopenharmony_ci	p_ic->line_len = 8 << ibcr.line_len;
11962306a36Sopenharmony_ci	p_ic->sz_k = 1 << (ibcr.sz - 1);
12062306a36Sopenharmony_ci	p_ic->colors = p_ic->sz_k/assoc/TO_KB(PAGE_SIZE);
12162306a36Sopenharmony_ci
12262306a36Sopenharmony_ci	n += scnprintf(buf + n, len - n,
12362306a36Sopenharmony_ci			"I-Cache\t\t: %uK, %dway/set, %uB Line, VIPT%s%s\n",
12462306a36Sopenharmony_ci			p_ic->sz_k, assoc, p_ic->line_len,
12562306a36Sopenharmony_ci			p_ic->colors > 1 ? " aliasing" : "",
12662306a36Sopenharmony_ci			IS_USED_CFG(CONFIG_ARC_HAS_ICACHE));
12762306a36Sopenharmony_ci
12862306a36Sopenharmony_cidc_chk:
12962306a36Sopenharmony_ci	READ_BCR(ARC_REG_DC_BCR, dbcr);
13062306a36Sopenharmony_ci	if (!dbcr.ver)
13162306a36Sopenharmony_ci		goto slc_chk;
13262306a36Sopenharmony_ci
13362306a36Sopenharmony_ci	if (is_isa_arcompact() && (dbcr.ver <= 3)) {
13462306a36Sopenharmony_ci		BUG_ON(dbcr.config != 2);
13562306a36Sopenharmony_ci		vipt = 1;
13662306a36Sopenharmony_ci		assoc = 4;		/* Fixed to 4w set assoc */
13762306a36Sopenharmony_ci		p_dc->colors = p_dc->sz_k/assoc/TO_KB(PAGE_SIZE);
13862306a36Sopenharmony_ci	} else if (is_isa_arcv2() && (dbcr.ver >= 4)) {
13962306a36Sopenharmony_ci		vipt = 0;
14062306a36Sopenharmony_ci		assoc = 1 << dbcr.config;	/* 1,2,4,8 */
14162306a36Sopenharmony_ci		p_dc->colors = 1;		/* PIPT so can't VIPT alias */
14262306a36Sopenharmony_ci	}
14362306a36Sopenharmony_ci
14462306a36Sopenharmony_ci	p_dc->line_len = 16 << dbcr.line_len;
14562306a36Sopenharmony_ci	p_dc->sz_k = 1 << (dbcr.sz - 1);
14662306a36Sopenharmony_ci
14762306a36Sopenharmony_ci	n += scnprintf(buf + n, len - n,
14862306a36Sopenharmony_ci			"D-Cache\t\t: %uK, %dway/set, %uB Line, %s%s%s\n",
14962306a36Sopenharmony_ci			p_dc->sz_k, assoc, p_dc->line_len,
15062306a36Sopenharmony_ci			vipt ? "VIPT" : "PIPT",
15162306a36Sopenharmony_ci			p_dc->colors > 1 ? " aliasing" : "",
15262306a36Sopenharmony_ci			IS_USED_CFG(CONFIG_ARC_HAS_DCACHE));
15362306a36Sopenharmony_ci
15462306a36Sopenharmony_cislc_chk:
15562306a36Sopenharmony_ci	if (is_isa_arcv2())
15662306a36Sopenharmony_ci		n += read_decode_cache_bcr_arcv2(c, buf + n, len - n);
15762306a36Sopenharmony_ci
15862306a36Sopenharmony_ci	return n;
15962306a36Sopenharmony_ci}
16062306a36Sopenharmony_ci
16162306a36Sopenharmony_ci/*
16262306a36Sopenharmony_ci * Line Operation on {I,D}-Cache
16362306a36Sopenharmony_ci */
16462306a36Sopenharmony_ci
16562306a36Sopenharmony_ci#define OP_INV		0x1
16662306a36Sopenharmony_ci#define OP_FLUSH	0x2
16762306a36Sopenharmony_ci#define OP_FLUSH_N_INV	0x3
16862306a36Sopenharmony_ci#define OP_INV_IC	0x4
16962306a36Sopenharmony_ci
17062306a36Sopenharmony_ci/*
17162306a36Sopenharmony_ci * Cache Flush programming model
17262306a36Sopenharmony_ci *
17362306a36Sopenharmony_ci * ARC700 MMUv3 I$ and D$ are both VIPT and can potentially alias.
17462306a36Sopenharmony_ci * Programming model requires both paddr and vaddr irrespecive of aliasing
17562306a36Sopenharmony_ci * considerations:
17662306a36Sopenharmony_ci *  - vaddr in {I,D}C_IV?L
17762306a36Sopenharmony_ci *  - paddr in {I,D}C_PTAG
17862306a36Sopenharmony_ci *
17962306a36Sopenharmony_ci * In HS38x (MMUv4), D$ is PIPT, I$ is VIPT and can still alias.
18062306a36Sopenharmony_ci * Programming model is different for aliasing vs. non-aliasing I$
18162306a36Sopenharmony_ci *  - D$ / Non-aliasing I$: only paddr in {I,D}C_IV?L
18262306a36Sopenharmony_ci *  - Aliasing I$: same as ARC700 above (so MMUv3 routine used for MMUv4 I$)
18362306a36Sopenharmony_ci *
18462306a36Sopenharmony_ci *  - If PAE40 is enabled, independent of aliasing considerations, the higher
18562306a36Sopenharmony_ci *    bits needs to be written into PTAG_HI
18662306a36Sopenharmony_ci */
18762306a36Sopenharmony_ci
18862306a36Sopenharmony_cistatic inline
18962306a36Sopenharmony_civoid __cache_line_loop_v3(phys_addr_t paddr, unsigned long vaddr,
19062306a36Sopenharmony_ci			  unsigned long sz, const int op, const int full_page)
19162306a36Sopenharmony_ci{
19262306a36Sopenharmony_ci	unsigned int aux_cmd, aux_tag;
19362306a36Sopenharmony_ci	int num_lines;
19462306a36Sopenharmony_ci
19562306a36Sopenharmony_ci	if (op == OP_INV_IC) {
19662306a36Sopenharmony_ci		aux_cmd = ARC_REG_IC_IVIL;
19762306a36Sopenharmony_ci		aux_tag = ARC_REG_IC_PTAG;
19862306a36Sopenharmony_ci	} else {
19962306a36Sopenharmony_ci		aux_cmd = op & OP_INV ? ARC_REG_DC_IVDL : ARC_REG_DC_FLDL;
20062306a36Sopenharmony_ci		aux_tag = ARC_REG_DC_PTAG;
20162306a36Sopenharmony_ci	}
20262306a36Sopenharmony_ci
20362306a36Sopenharmony_ci	/* Ensure we properly floor/ceil the non-line aligned/sized requests
20462306a36Sopenharmony_ci	 * and have @paddr - aligned to cache line and integral @num_lines.
20562306a36Sopenharmony_ci	 * This however can be avoided for page sized since:
20662306a36Sopenharmony_ci	 *  -@paddr will be cache-line aligned already (being page aligned)
20762306a36Sopenharmony_ci	 *  -@sz will be integral multiple of line size (being page sized).
20862306a36Sopenharmony_ci	 */
20962306a36Sopenharmony_ci	if (!full_page) {
21062306a36Sopenharmony_ci		sz += paddr & ~CACHE_LINE_MASK;
21162306a36Sopenharmony_ci		paddr &= CACHE_LINE_MASK;
21262306a36Sopenharmony_ci		vaddr &= CACHE_LINE_MASK;
21362306a36Sopenharmony_ci	}
21462306a36Sopenharmony_ci	num_lines = DIV_ROUND_UP(sz, L1_CACHE_BYTES);
21562306a36Sopenharmony_ci
21662306a36Sopenharmony_ci	/*
21762306a36Sopenharmony_ci	 * MMUv3, cache ops require paddr in PTAG reg
21862306a36Sopenharmony_ci	 * if V-P const for loop, PTAG can be written once outside loop
21962306a36Sopenharmony_ci	 */
22062306a36Sopenharmony_ci	if (full_page)
22162306a36Sopenharmony_ci		write_aux_reg(aux_tag, paddr);
22262306a36Sopenharmony_ci
22362306a36Sopenharmony_ci	/*
22462306a36Sopenharmony_ci	 * This is technically for MMU v4, using the MMU v3 programming model
22562306a36Sopenharmony_ci	 * Special work for HS38 aliasing I-cache configuration with PAE40
22662306a36Sopenharmony_ci	 *   - upper 8 bits of paddr need to be written into PTAG_HI
22762306a36Sopenharmony_ci	 *   - (and needs to be written before the lower 32 bits)
22862306a36Sopenharmony_ci	 * Note that PTAG_HI is hoisted outside the line loop
22962306a36Sopenharmony_ci	 */
23062306a36Sopenharmony_ci	if (is_pae40_enabled() && op == OP_INV_IC)
23162306a36Sopenharmony_ci		write_aux_reg(ARC_REG_IC_PTAG_HI, (u64)paddr >> 32);
23262306a36Sopenharmony_ci
23362306a36Sopenharmony_ci	while (num_lines-- > 0) {
23462306a36Sopenharmony_ci		if (!full_page) {
23562306a36Sopenharmony_ci			write_aux_reg(aux_tag, paddr);
23662306a36Sopenharmony_ci			paddr += L1_CACHE_BYTES;
23762306a36Sopenharmony_ci		}
23862306a36Sopenharmony_ci
23962306a36Sopenharmony_ci		write_aux_reg(aux_cmd, vaddr);
24062306a36Sopenharmony_ci		vaddr += L1_CACHE_BYTES;
24162306a36Sopenharmony_ci	}
24262306a36Sopenharmony_ci}
24362306a36Sopenharmony_ci
24462306a36Sopenharmony_ci#ifndef USE_RGN_FLSH
24562306a36Sopenharmony_ci
24662306a36Sopenharmony_ci/*
24762306a36Sopenharmony_ci */
24862306a36Sopenharmony_cistatic inline
24962306a36Sopenharmony_civoid __cache_line_loop_v4(phys_addr_t paddr, unsigned long vaddr,
25062306a36Sopenharmony_ci			  unsigned long sz, const int op, const int full_page)
25162306a36Sopenharmony_ci{
25262306a36Sopenharmony_ci	unsigned int aux_cmd;
25362306a36Sopenharmony_ci	int num_lines;
25462306a36Sopenharmony_ci
25562306a36Sopenharmony_ci	if (op == OP_INV_IC) {
25662306a36Sopenharmony_ci		aux_cmd = ARC_REG_IC_IVIL;
25762306a36Sopenharmony_ci	} else {
25862306a36Sopenharmony_ci		/* d$ cmd: INV (discard or wback-n-discard) OR FLUSH (wback) */
25962306a36Sopenharmony_ci		aux_cmd = op & OP_INV ? ARC_REG_DC_IVDL : ARC_REG_DC_FLDL;
26062306a36Sopenharmony_ci	}
26162306a36Sopenharmony_ci
26262306a36Sopenharmony_ci	/* Ensure we properly floor/ceil the non-line aligned/sized requests
26362306a36Sopenharmony_ci	 * and have @paddr - aligned to cache line and integral @num_lines.
26462306a36Sopenharmony_ci	 * This however can be avoided for page sized since:
26562306a36Sopenharmony_ci	 *  -@paddr will be cache-line aligned already (being page aligned)
26662306a36Sopenharmony_ci	 *  -@sz will be integral multiple of line size (being page sized).
26762306a36Sopenharmony_ci	 */
26862306a36Sopenharmony_ci	if (!full_page) {
26962306a36Sopenharmony_ci		sz += paddr & ~CACHE_LINE_MASK;
27062306a36Sopenharmony_ci		paddr &= CACHE_LINE_MASK;
27162306a36Sopenharmony_ci	}
27262306a36Sopenharmony_ci
27362306a36Sopenharmony_ci	num_lines = DIV_ROUND_UP(sz, L1_CACHE_BYTES);
27462306a36Sopenharmony_ci
27562306a36Sopenharmony_ci	/*
27662306a36Sopenharmony_ci	 * For HS38 PAE40 configuration
27762306a36Sopenharmony_ci	 *   - upper 8 bits of paddr need to be written into PTAG_HI
27862306a36Sopenharmony_ci	 *   - (and needs to be written before the lower 32 bits)
27962306a36Sopenharmony_ci	 */
28062306a36Sopenharmony_ci	if (is_pae40_enabled()) {
28162306a36Sopenharmony_ci		if (op == OP_INV_IC)
28262306a36Sopenharmony_ci			/*
28362306a36Sopenharmony_ci			 * Non aliasing I-cache in HS38,
28462306a36Sopenharmony_ci			 * aliasing I-cache handled in __cache_line_loop_v3()
28562306a36Sopenharmony_ci			 */
28662306a36Sopenharmony_ci			write_aux_reg(ARC_REG_IC_PTAG_HI, (u64)paddr >> 32);
28762306a36Sopenharmony_ci		else
28862306a36Sopenharmony_ci			write_aux_reg(ARC_REG_DC_PTAG_HI, (u64)paddr >> 32);
28962306a36Sopenharmony_ci	}
29062306a36Sopenharmony_ci
29162306a36Sopenharmony_ci	while (num_lines-- > 0) {
29262306a36Sopenharmony_ci		write_aux_reg(aux_cmd, paddr);
29362306a36Sopenharmony_ci		paddr += L1_CACHE_BYTES;
29462306a36Sopenharmony_ci	}
29562306a36Sopenharmony_ci}
29662306a36Sopenharmony_ci
29762306a36Sopenharmony_ci#else
29862306a36Sopenharmony_ci
29962306a36Sopenharmony_ci/*
30062306a36Sopenharmony_ci * optimized flush operation which takes a region as opposed to iterating per line
30162306a36Sopenharmony_ci */
30262306a36Sopenharmony_cistatic inline
30362306a36Sopenharmony_civoid __cache_line_loop_v4(phys_addr_t paddr, unsigned long vaddr,
30462306a36Sopenharmony_ci			  unsigned long sz, const int op, const int full_page)
30562306a36Sopenharmony_ci{
30662306a36Sopenharmony_ci	unsigned int s, e;
30762306a36Sopenharmony_ci
30862306a36Sopenharmony_ci	/* Only for Non aliasing I-cache in HS38 */
30962306a36Sopenharmony_ci	if (op == OP_INV_IC) {
31062306a36Sopenharmony_ci		s = ARC_REG_IC_IVIR;
31162306a36Sopenharmony_ci		e = ARC_REG_IC_ENDR;
31262306a36Sopenharmony_ci	} else {
31362306a36Sopenharmony_ci		s = ARC_REG_DC_STARTR;
31462306a36Sopenharmony_ci		e = ARC_REG_DC_ENDR;
31562306a36Sopenharmony_ci	}
31662306a36Sopenharmony_ci
31762306a36Sopenharmony_ci	if (!full_page) {
31862306a36Sopenharmony_ci		/* for any leading gap between @paddr and start of cache line */
31962306a36Sopenharmony_ci		sz += paddr & ~CACHE_LINE_MASK;
32062306a36Sopenharmony_ci		paddr &= CACHE_LINE_MASK;
32162306a36Sopenharmony_ci
32262306a36Sopenharmony_ci		/*
32362306a36Sopenharmony_ci		 *  account for any trailing gap to end of cache line
32462306a36Sopenharmony_ci		 *  this is equivalent to DIV_ROUND_UP() in line ops above
32562306a36Sopenharmony_ci		 */
32662306a36Sopenharmony_ci		sz += L1_CACHE_BYTES - 1;
32762306a36Sopenharmony_ci	}
32862306a36Sopenharmony_ci
32962306a36Sopenharmony_ci	if (is_pae40_enabled()) {
33062306a36Sopenharmony_ci		/* TBD: check if crossing 4TB boundary */
33162306a36Sopenharmony_ci		if (op == OP_INV_IC)
33262306a36Sopenharmony_ci			write_aux_reg(ARC_REG_IC_PTAG_HI, (u64)paddr >> 32);
33362306a36Sopenharmony_ci		else
33462306a36Sopenharmony_ci			write_aux_reg(ARC_REG_DC_PTAG_HI, (u64)paddr >> 32);
33562306a36Sopenharmony_ci	}
33662306a36Sopenharmony_ci
33762306a36Sopenharmony_ci	/* ENDR needs to be set ahead of START */
33862306a36Sopenharmony_ci	write_aux_reg(e, paddr + sz);	/* ENDR is exclusive */
33962306a36Sopenharmony_ci	write_aux_reg(s, paddr);
34062306a36Sopenharmony_ci
34162306a36Sopenharmony_ci	/* caller waits on DC_CTRL.FS */
34262306a36Sopenharmony_ci}
34362306a36Sopenharmony_ci
34462306a36Sopenharmony_ci#endif
34562306a36Sopenharmony_ci
34662306a36Sopenharmony_ci#ifdef CONFIG_ARC_MMU_V3
34762306a36Sopenharmony_ci#define __cache_line_loop	__cache_line_loop_v3
34862306a36Sopenharmony_ci#else
34962306a36Sopenharmony_ci#define __cache_line_loop	__cache_line_loop_v4
35062306a36Sopenharmony_ci#endif
35162306a36Sopenharmony_ci
35262306a36Sopenharmony_ci#ifdef CONFIG_ARC_HAS_DCACHE
35362306a36Sopenharmony_ci
35462306a36Sopenharmony_ci/***************************************************************
35562306a36Sopenharmony_ci * Machine specific helpers for Entire D-Cache or Per Line ops
35662306a36Sopenharmony_ci */
35762306a36Sopenharmony_ci
35862306a36Sopenharmony_ci#ifndef USE_RGN_FLSH
35962306a36Sopenharmony_ci/*
36062306a36Sopenharmony_ci * this version avoids extra read/write of DC_CTRL for flush or invalid ops
36162306a36Sopenharmony_ci * in the non region flush regime (such as for ARCompact)
36262306a36Sopenharmony_ci */
36362306a36Sopenharmony_cistatic inline void __before_dc_op(const int op)
36462306a36Sopenharmony_ci{
36562306a36Sopenharmony_ci	if (op == OP_FLUSH_N_INV) {
36662306a36Sopenharmony_ci		/* Dcache provides 2 cmd: FLUSH or INV
36762306a36Sopenharmony_ci		 * INV in turn has sub-modes: DISCARD or FLUSH-BEFORE
36862306a36Sopenharmony_ci		 * flush-n-inv is achieved by INV cmd but with IM=1
36962306a36Sopenharmony_ci		 * So toggle INV sub-mode depending on op request and default
37062306a36Sopenharmony_ci		 */
37162306a36Sopenharmony_ci		const unsigned int ctl = ARC_REG_DC_CTRL;
37262306a36Sopenharmony_ci		write_aux_reg(ctl, read_aux_reg(ctl) | DC_CTRL_INV_MODE_FLUSH);
37362306a36Sopenharmony_ci	}
37462306a36Sopenharmony_ci}
37562306a36Sopenharmony_ci
37662306a36Sopenharmony_ci#else
37762306a36Sopenharmony_ci
37862306a36Sopenharmony_cistatic inline void __before_dc_op(const int op)
37962306a36Sopenharmony_ci{
38062306a36Sopenharmony_ci	const unsigned int ctl = ARC_REG_DC_CTRL;
38162306a36Sopenharmony_ci	unsigned int val = read_aux_reg(ctl);
38262306a36Sopenharmony_ci
38362306a36Sopenharmony_ci	if (op == OP_FLUSH_N_INV) {
38462306a36Sopenharmony_ci		val |= DC_CTRL_INV_MODE_FLUSH;
38562306a36Sopenharmony_ci	}
38662306a36Sopenharmony_ci
38762306a36Sopenharmony_ci	if (op != OP_INV_IC) {
38862306a36Sopenharmony_ci		/*
38962306a36Sopenharmony_ci		 * Flush / Invalidate is provided by DC_CTRL.RNG_OP 0 or 1
39062306a36Sopenharmony_ci		 * combined Flush-n-invalidate uses DC_CTRL.IM = 1 set above
39162306a36Sopenharmony_ci		 */
39262306a36Sopenharmony_ci		val &= ~DC_CTRL_RGN_OP_MSK;
39362306a36Sopenharmony_ci		if (op & OP_INV)
39462306a36Sopenharmony_ci			val |= DC_CTRL_RGN_OP_INV;
39562306a36Sopenharmony_ci	}
39662306a36Sopenharmony_ci	write_aux_reg(ctl, val);
39762306a36Sopenharmony_ci}
39862306a36Sopenharmony_ci
39962306a36Sopenharmony_ci#endif
40062306a36Sopenharmony_ci
40162306a36Sopenharmony_ci
40262306a36Sopenharmony_cistatic inline void __after_dc_op(const int op)
40362306a36Sopenharmony_ci{
40462306a36Sopenharmony_ci	if (op & OP_FLUSH) {
40562306a36Sopenharmony_ci		const unsigned int ctl = ARC_REG_DC_CTRL;
40662306a36Sopenharmony_ci		unsigned int reg;
40762306a36Sopenharmony_ci
40862306a36Sopenharmony_ci		/* flush / flush-n-inv both wait */
40962306a36Sopenharmony_ci		while ((reg = read_aux_reg(ctl)) & DC_CTRL_FLUSH_STATUS)
41062306a36Sopenharmony_ci			;
41162306a36Sopenharmony_ci
41262306a36Sopenharmony_ci		/* Switch back to default Invalidate mode */
41362306a36Sopenharmony_ci		if (op == OP_FLUSH_N_INV)
41462306a36Sopenharmony_ci			write_aux_reg(ctl, reg & ~DC_CTRL_INV_MODE_FLUSH);
41562306a36Sopenharmony_ci	}
41662306a36Sopenharmony_ci}
41762306a36Sopenharmony_ci
41862306a36Sopenharmony_ci/*
41962306a36Sopenharmony_ci * Operation on Entire D-Cache
42062306a36Sopenharmony_ci * @op = {OP_INV, OP_FLUSH, OP_FLUSH_N_INV}
42162306a36Sopenharmony_ci * Note that constant propagation ensures all the checks are gone
42262306a36Sopenharmony_ci * in generated code
42362306a36Sopenharmony_ci */
42462306a36Sopenharmony_cistatic inline void __dc_entire_op(const int op)
42562306a36Sopenharmony_ci{
42662306a36Sopenharmony_ci	int aux;
42762306a36Sopenharmony_ci
42862306a36Sopenharmony_ci	__before_dc_op(op);
42962306a36Sopenharmony_ci
43062306a36Sopenharmony_ci	if (op & OP_INV)	/* Inv or flush-n-inv use same cmd reg */
43162306a36Sopenharmony_ci		aux = ARC_REG_DC_IVDC;
43262306a36Sopenharmony_ci	else
43362306a36Sopenharmony_ci		aux = ARC_REG_DC_FLSH;
43462306a36Sopenharmony_ci
43562306a36Sopenharmony_ci	write_aux_reg(aux, 0x1);
43662306a36Sopenharmony_ci
43762306a36Sopenharmony_ci	__after_dc_op(op);
43862306a36Sopenharmony_ci}
43962306a36Sopenharmony_ci
44062306a36Sopenharmony_cistatic inline void __dc_disable(void)
44162306a36Sopenharmony_ci{
44262306a36Sopenharmony_ci	const int r = ARC_REG_DC_CTRL;
44362306a36Sopenharmony_ci
44462306a36Sopenharmony_ci	__dc_entire_op(OP_FLUSH_N_INV);
44562306a36Sopenharmony_ci	write_aux_reg(r, read_aux_reg(r) | DC_CTRL_DIS);
44662306a36Sopenharmony_ci}
44762306a36Sopenharmony_ci
44862306a36Sopenharmony_cistatic void __dc_enable(void)
44962306a36Sopenharmony_ci{
45062306a36Sopenharmony_ci	const int r = ARC_REG_DC_CTRL;
45162306a36Sopenharmony_ci
45262306a36Sopenharmony_ci	write_aux_reg(r, read_aux_reg(r) & ~DC_CTRL_DIS);
45362306a36Sopenharmony_ci}
45462306a36Sopenharmony_ci
45562306a36Sopenharmony_ci/* For kernel mappings cache operation: index is same as paddr */
45662306a36Sopenharmony_ci#define __dc_line_op_k(p, sz, op)	__dc_line_op(p, p, sz, op)
45762306a36Sopenharmony_ci
45862306a36Sopenharmony_ci/*
45962306a36Sopenharmony_ci * D-Cache Line ops: Per Line INV (discard or wback+discard) or FLUSH (wback)
46062306a36Sopenharmony_ci */
46162306a36Sopenharmony_cistatic inline void __dc_line_op(phys_addr_t paddr, unsigned long vaddr,
46262306a36Sopenharmony_ci				unsigned long sz, const int op)
46362306a36Sopenharmony_ci{
46462306a36Sopenharmony_ci	const int full_page = __builtin_constant_p(sz) && sz == PAGE_SIZE;
46562306a36Sopenharmony_ci	unsigned long flags;
46662306a36Sopenharmony_ci
46762306a36Sopenharmony_ci	local_irq_save(flags);
46862306a36Sopenharmony_ci
46962306a36Sopenharmony_ci	__before_dc_op(op);
47062306a36Sopenharmony_ci
47162306a36Sopenharmony_ci	__cache_line_loop(paddr, vaddr, sz, op, full_page);
47262306a36Sopenharmony_ci
47362306a36Sopenharmony_ci	__after_dc_op(op);
47462306a36Sopenharmony_ci
47562306a36Sopenharmony_ci	local_irq_restore(flags);
47662306a36Sopenharmony_ci}
47762306a36Sopenharmony_ci
47862306a36Sopenharmony_ci#else
47962306a36Sopenharmony_ci
48062306a36Sopenharmony_ci#define __dc_entire_op(op)
48162306a36Sopenharmony_ci#define __dc_disable()
48262306a36Sopenharmony_ci#define __dc_enable()
48362306a36Sopenharmony_ci#define __dc_line_op(paddr, vaddr, sz, op)
48462306a36Sopenharmony_ci#define __dc_line_op_k(paddr, sz, op)
48562306a36Sopenharmony_ci
48662306a36Sopenharmony_ci#endif /* CONFIG_ARC_HAS_DCACHE */
48762306a36Sopenharmony_ci
48862306a36Sopenharmony_ci#ifdef CONFIG_ARC_HAS_ICACHE
48962306a36Sopenharmony_ci
49062306a36Sopenharmony_cistatic inline void __ic_entire_inv(void)
49162306a36Sopenharmony_ci{
49262306a36Sopenharmony_ci	write_aux_reg(ARC_REG_IC_IVIC, 1);
49362306a36Sopenharmony_ci	read_aux_reg(ARC_REG_IC_CTRL);	/* blocks */
49462306a36Sopenharmony_ci}
49562306a36Sopenharmony_ci
49662306a36Sopenharmony_cistatic inline void
49762306a36Sopenharmony_ci__ic_line_inv_vaddr_local(phys_addr_t paddr, unsigned long vaddr,
49862306a36Sopenharmony_ci			  unsigned long sz)
49962306a36Sopenharmony_ci{
50062306a36Sopenharmony_ci	const int full_page = __builtin_constant_p(sz) && sz == PAGE_SIZE;
50162306a36Sopenharmony_ci	unsigned long flags;
50262306a36Sopenharmony_ci
50362306a36Sopenharmony_ci	local_irq_save(flags);
50462306a36Sopenharmony_ci	(*_cache_line_loop_ic_fn)(paddr, vaddr, sz, OP_INV_IC, full_page);
50562306a36Sopenharmony_ci	local_irq_restore(flags);
50662306a36Sopenharmony_ci}
50762306a36Sopenharmony_ci
50862306a36Sopenharmony_ci#ifndef CONFIG_SMP
50962306a36Sopenharmony_ci
51062306a36Sopenharmony_ci#define __ic_line_inv_vaddr(p, v, s)	__ic_line_inv_vaddr_local(p, v, s)
51162306a36Sopenharmony_ci
51262306a36Sopenharmony_ci#else
51362306a36Sopenharmony_ci
51462306a36Sopenharmony_cistruct ic_inv_args {
51562306a36Sopenharmony_ci	phys_addr_t paddr, vaddr;
51662306a36Sopenharmony_ci	int sz;
51762306a36Sopenharmony_ci};
51862306a36Sopenharmony_ci
51962306a36Sopenharmony_cistatic void __ic_line_inv_vaddr_helper(void *info)
52062306a36Sopenharmony_ci{
52162306a36Sopenharmony_ci        struct ic_inv_args *ic_inv = info;
52262306a36Sopenharmony_ci
52362306a36Sopenharmony_ci        __ic_line_inv_vaddr_local(ic_inv->paddr, ic_inv->vaddr, ic_inv->sz);
52462306a36Sopenharmony_ci}
52562306a36Sopenharmony_ci
52662306a36Sopenharmony_cistatic void __ic_line_inv_vaddr(phys_addr_t paddr, unsigned long vaddr,
52762306a36Sopenharmony_ci				unsigned long sz)
52862306a36Sopenharmony_ci{
52962306a36Sopenharmony_ci	struct ic_inv_args ic_inv = {
53062306a36Sopenharmony_ci		.paddr = paddr,
53162306a36Sopenharmony_ci		.vaddr = vaddr,
53262306a36Sopenharmony_ci		.sz    = sz
53362306a36Sopenharmony_ci	};
53462306a36Sopenharmony_ci
53562306a36Sopenharmony_ci	on_each_cpu(__ic_line_inv_vaddr_helper, &ic_inv, 1);
53662306a36Sopenharmony_ci}
53762306a36Sopenharmony_ci
53862306a36Sopenharmony_ci#endif	/* CONFIG_SMP */
53962306a36Sopenharmony_ci
54062306a36Sopenharmony_ci#else	/* !CONFIG_ARC_HAS_ICACHE */
54162306a36Sopenharmony_ci
54262306a36Sopenharmony_ci#define __ic_entire_inv()
54362306a36Sopenharmony_ci#define __ic_line_inv_vaddr(pstart, vstart, sz)
54462306a36Sopenharmony_ci
54562306a36Sopenharmony_ci#endif /* CONFIG_ARC_HAS_ICACHE */
54662306a36Sopenharmony_ci
54762306a36Sopenharmony_cistatic noinline void slc_op_rgn(phys_addr_t paddr, unsigned long sz, const int op)
54862306a36Sopenharmony_ci{
54962306a36Sopenharmony_ci#ifdef CONFIG_ISA_ARCV2
55062306a36Sopenharmony_ci	/*
55162306a36Sopenharmony_ci	 * SLC is shared between all cores and concurrent aux operations from
55262306a36Sopenharmony_ci	 * multiple cores need to be serialized using a spinlock
55362306a36Sopenharmony_ci	 * A concurrent operation can be silently ignored and/or the old/new
55462306a36Sopenharmony_ci	 * operation can remain incomplete forever (lockup in SLC_CTRL_BUSY loop
55562306a36Sopenharmony_ci	 * below)
55662306a36Sopenharmony_ci	 */
55762306a36Sopenharmony_ci	static DEFINE_SPINLOCK(lock);
55862306a36Sopenharmony_ci	unsigned long flags;
55962306a36Sopenharmony_ci	unsigned int ctrl;
56062306a36Sopenharmony_ci	phys_addr_t end;
56162306a36Sopenharmony_ci
56262306a36Sopenharmony_ci	spin_lock_irqsave(&lock, flags);
56362306a36Sopenharmony_ci
56462306a36Sopenharmony_ci	/*
56562306a36Sopenharmony_ci	 * The Region Flush operation is specified by CTRL.RGN_OP[11..9]
56662306a36Sopenharmony_ci	 *  - b'000 (default) is Flush,
56762306a36Sopenharmony_ci	 *  - b'001 is Invalidate if CTRL.IM == 0
56862306a36Sopenharmony_ci	 *  - b'001 is Flush-n-Invalidate if CTRL.IM == 1
56962306a36Sopenharmony_ci	 */
57062306a36Sopenharmony_ci	ctrl = read_aux_reg(ARC_REG_SLC_CTRL);
57162306a36Sopenharmony_ci
57262306a36Sopenharmony_ci	/* Don't rely on default value of IM bit */
57362306a36Sopenharmony_ci	if (!(op & OP_FLUSH))		/* i.e. OP_INV */
57462306a36Sopenharmony_ci		ctrl &= ~SLC_CTRL_IM;	/* clear IM: Disable flush before Inv */
57562306a36Sopenharmony_ci	else
57662306a36Sopenharmony_ci		ctrl |= SLC_CTRL_IM;
57762306a36Sopenharmony_ci
57862306a36Sopenharmony_ci	if (op & OP_INV)
57962306a36Sopenharmony_ci		ctrl |= SLC_CTRL_RGN_OP_INV;	/* Inv or flush-n-inv */
58062306a36Sopenharmony_ci	else
58162306a36Sopenharmony_ci		ctrl &= ~SLC_CTRL_RGN_OP_INV;
58262306a36Sopenharmony_ci
58362306a36Sopenharmony_ci	write_aux_reg(ARC_REG_SLC_CTRL, ctrl);
58462306a36Sopenharmony_ci
58562306a36Sopenharmony_ci	/*
58662306a36Sopenharmony_ci	 * Lower bits are ignored, no need to clip
58762306a36Sopenharmony_ci	 * END needs to be setup before START (latter triggers the operation)
58862306a36Sopenharmony_ci	 * END can't be same as START, so add (l2_line_sz - 1) to sz
58962306a36Sopenharmony_ci	 */
59062306a36Sopenharmony_ci	end = paddr + sz + l2_line_sz - 1;
59162306a36Sopenharmony_ci	if (is_pae40_enabled())
59262306a36Sopenharmony_ci		write_aux_reg(ARC_REG_SLC_RGN_END1, upper_32_bits(end));
59362306a36Sopenharmony_ci
59462306a36Sopenharmony_ci	write_aux_reg(ARC_REG_SLC_RGN_END, lower_32_bits(end));
59562306a36Sopenharmony_ci
59662306a36Sopenharmony_ci	if (is_pae40_enabled())
59762306a36Sopenharmony_ci		write_aux_reg(ARC_REG_SLC_RGN_START1, upper_32_bits(paddr));
59862306a36Sopenharmony_ci
59962306a36Sopenharmony_ci	write_aux_reg(ARC_REG_SLC_RGN_START, lower_32_bits(paddr));
60062306a36Sopenharmony_ci
60162306a36Sopenharmony_ci	/* Make sure "busy" bit reports correct stataus, see STAR 9001165532 */
60262306a36Sopenharmony_ci	read_aux_reg(ARC_REG_SLC_CTRL);
60362306a36Sopenharmony_ci
60462306a36Sopenharmony_ci	while (read_aux_reg(ARC_REG_SLC_CTRL) & SLC_CTRL_BUSY);
60562306a36Sopenharmony_ci
60662306a36Sopenharmony_ci	spin_unlock_irqrestore(&lock, flags);
60762306a36Sopenharmony_ci#endif
60862306a36Sopenharmony_ci}
60962306a36Sopenharmony_ci
61062306a36Sopenharmony_cistatic __maybe_unused noinline void slc_op_line(phys_addr_t paddr, unsigned long sz, const int op)
61162306a36Sopenharmony_ci{
61262306a36Sopenharmony_ci#ifdef CONFIG_ISA_ARCV2
61362306a36Sopenharmony_ci	/*
61462306a36Sopenharmony_ci	 * SLC is shared between all cores and concurrent aux operations from
61562306a36Sopenharmony_ci	 * multiple cores need to be serialized using a spinlock
61662306a36Sopenharmony_ci	 * A concurrent operation can be silently ignored and/or the old/new
61762306a36Sopenharmony_ci	 * operation can remain incomplete forever (lockup in SLC_CTRL_BUSY loop
61862306a36Sopenharmony_ci	 * below)
61962306a36Sopenharmony_ci	 */
62062306a36Sopenharmony_ci	static DEFINE_SPINLOCK(lock);
62162306a36Sopenharmony_ci
62262306a36Sopenharmony_ci	const unsigned long SLC_LINE_MASK = ~(l2_line_sz - 1);
62362306a36Sopenharmony_ci	unsigned int ctrl, cmd;
62462306a36Sopenharmony_ci	unsigned long flags;
62562306a36Sopenharmony_ci	int num_lines;
62662306a36Sopenharmony_ci
62762306a36Sopenharmony_ci	spin_lock_irqsave(&lock, flags);
62862306a36Sopenharmony_ci
62962306a36Sopenharmony_ci	ctrl = read_aux_reg(ARC_REG_SLC_CTRL);
63062306a36Sopenharmony_ci
63162306a36Sopenharmony_ci	/* Don't rely on default value of IM bit */
63262306a36Sopenharmony_ci	if (!(op & OP_FLUSH))		/* i.e. OP_INV */
63362306a36Sopenharmony_ci		ctrl &= ~SLC_CTRL_IM;	/* clear IM: Disable flush before Inv */
63462306a36Sopenharmony_ci	else
63562306a36Sopenharmony_ci		ctrl |= SLC_CTRL_IM;
63662306a36Sopenharmony_ci
63762306a36Sopenharmony_ci	write_aux_reg(ARC_REG_SLC_CTRL, ctrl);
63862306a36Sopenharmony_ci
63962306a36Sopenharmony_ci	cmd = op & OP_INV ? ARC_AUX_SLC_IVDL : ARC_AUX_SLC_FLDL;
64062306a36Sopenharmony_ci
64162306a36Sopenharmony_ci	sz += paddr & ~SLC_LINE_MASK;
64262306a36Sopenharmony_ci	paddr &= SLC_LINE_MASK;
64362306a36Sopenharmony_ci
64462306a36Sopenharmony_ci	num_lines = DIV_ROUND_UP(sz, l2_line_sz);
64562306a36Sopenharmony_ci
64662306a36Sopenharmony_ci	while (num_lines-- > 0) {
64762306a36Sopenharmony_ci		write_aux_reg(cmd, paddr);
64862306a36Sopenharmony_ci		paddr += l2_line_sz;
64962306a36Sopenharmony_ci	}
65062306a36Sopenharmony_ci
65162306a36Sopenharmony_ci	/* Make sure "busy" bit reports correct stataus, see STAR 9001165532 */
65262306a36Sopenharmony_ci	read_aux_reg(ARC_REG_SLC_CTRL);
65362306a36Sopenharmony_ci
65462306a36Sopenharmony_ci	while (read_aux_reg(ARC_REG_SLC_CTRL) & SLC_CTRL_BUSY);
65562306a36Sopenharmony_ci
65662306a36Sopenharmony_ci	spin_unlock_irqrestore(&lock, flags);
65762306a36Sopenharmony_ci#endif
65862306a36Sopenharmony_ci}
65962306a36Sopenharmony_ci
66062306a36Sopenharmony_ci#define slc_op(paddr, sz, op)	slc_op_rgn(paddr, sz, op)
66162306a36Sopenharmony_ci
66262306a36Sopenharmony_cinoinline static void slc_entire_op(const int op)
66362306a36Sopenharmony_ci{
66462306a36Sopenharmony_ci	unsigned int ctrl, r = ARC_REG_SLC_CTRL;
66562306a36Sopenharmony_ci
66662306a36Sopenharmony_ci	ctrl = read_aux_reg(r);
66762306a36Sopenharmony_ci
66862306a36Sopenharmony_ci	if (!(op & OP_FLUSH))		/* i.e. OP_INV */
66962306a36Sopenharmony_ci		ctrl &= ~SLC_CTRL_IM;	/* clear IM: Disable flush before Inv */
67062306a36Sopenharmony_ci	else
67162306a36Sopenharmony_ci		ctrl |= SLC_CTRL_IM;
67262306a36Sopenharmony_ci
67362306a36Sopenharmony_ci	write_aux_reg(r, ctrl);
67462306a36Sopenharmony_ci
67562306a36Sopenharmony_ci	if (op & OP_INV)	/* Inv or flush-n-inv use same cmd reg */
67662306a36Sopenharmony_ci		write_aux_reg(ARC_REG_SLC_INVALIDATE, 0x1);
67762306a36Sopenharmony_ci	else
67862306a36Sopenharmony_ci		write_aux_reg(ARC_REG_SLC_FLUSH, 0x1);
67962306a36Sopenharmony_ci
68062306a36Sopenharmony_ci	/* Make sure "busy" bit reports correct stataus, see STAR 9001165532 */
68162306a36Sopenharmony_ci	read_aux_reg(r);
68262306a36Sopenharmony_ci
68362306a36Sopenharmony_ci	/* Important to wait for flush to complete */
68462306a36Sopenharmony_ci	while (read_aux_reg(r) & SLC_CTRL_BUSY);
68562306a36Sopenharmony_ci}
68662306a36Sopenharmony_ci
68762306a36Sopenharmony_cistatic inline void arc_slc_disable(void)
68862306a36Sopenharmony_ci{
68962306a36Sopenharmony_ci	const int r = ARC_REG_SLC_CTRL;
69062306a36Sopenharmony_ci
69162306a36Sopenharmony_ci	slc_entire_op(OP_FLUSH_N_INV);
69262306a36Sopenharmony_ci	write_aux_reg(r, read_aux_reg(r) | SLC_CTRL_DIS);
69362306a36Sopenharmony_ci}
69462306a36Sopenharmony_ci
69562306a36Sopenharmony_cistatic inline void arc_slc_enable(void)
69662306a36Sopenharmony_ci{
69762306a36Sopenharmony_ci	const int r = ARC_REG_SLC_CTRL;
69862306a36Sopenharmony_ci
69962306a36Sopenharmony_ci	write_aux_reg(r, read_aux_reg(r) & ~SLC_CTRL_DIS);
70062306a36Sopenharmony_ci}
70162306a36Sopenharmony_ci
70262306a36Sopenharmony_ci/***********************************************************
70362306a36Sopenharmony_ci * Exported APIs
70462306a36Sopenharmony_ci */
70562306a36Sopenharmony_ci
70662306a36Sopenharmony_ci/*
70762306a36Sopenharmony_ci * Handle cache congruency of kernel and userspace mappings of page when kernel
70862306a36Sopenharmony_ci * writes-to/reads-from
70962306a36Sopenharmony_ci *
71062306a36Sopenharmony_ci * The idea is to defer flushing of kernel mapping after a WRITE, possible if:
71162306a36Sopenharmony_ci *  -dcache is NOT aliasing, hence any U/K-mappings of page are congruent
71262306a36Sopenharmony_ci *  -U-mapping doesn't exist yet for page (finalised in update_mmu_cache)
71362306a36Sopenharmony_ci *  -In SMP, if hardware caches are coherent
71462306a36Sopenharmony_ci *
71562306a36Sopenharmony_ci * There's a corollary case, where kernel READs from a userspace mapped page.
71662306a36Sopenharmony_ci * If the U-mapping is not congruent to K-mapping, former needs flushing.
71762306a36Sopenharmony_ci */
71862306a36Sopenharmony_civoid flush_dcache_folio(struct folio *folio)
71962306a36Sopenharmony_ci{
72062306a36Sopenharmony_ci	struct address_space *mapping;
72162306a36Sopenharmony_ci
72262306a36Sopenharmony_ci	if (!cache_is_vipt_aliasing()) {
72362306a36Sopenharmony_ci		clear_bit(PG_dc_clean, &folio->flags);
72462306a36Sopenharmony_ci		return;
72562306a36Sopenharmony_ci	}
72662306a36Sopenharmony_ci
72762306a36Sopenharmony_ci	/* don't handle anon pages here */
72862306a36Sopenharmony_ci	mapping = folio_flush_mapping(folio);
72962306a36Sopenharmony_ci	if (!mapping)
73062306a36Sopenharmony_ci		return;
73162306a36Sopenharmony_ci
73262306a36Sopenharmony_ci	/*
73362306a36Sopenharmony_ci	 * pagecache page, file not yet mapped to userspace
73462306a36Sopenharmony_ci	 * Make a note that K-mapping is dirty
73562306a36Sopenharmony_ci	 */
73662306a36Sopenharmony_ci	if (!mapping_mapped(mapping)) {
73762306a36Sopenharmony_ci		clear_bit(PG_dc_clean, &folio->flags);
73862306a36Sopenharmony_ci	} else if (folio_mapped(folio)) {
73962306a36Sopenharmony_ci		/* kernel reading from page with U-mapping */
74062306a36Sopenharmony_ci		phys_addr_t paddr = (unsigned long)folio_address(folio);
74162306a36Sopenharmony_ci		unsigned long vaddr = folio_pos(folio);
74262306a36Sopenharmony_ci
74362306a36Sopenharmony_ci		/*
74462306a36Sopenharmony_ci		 * vaddr is not actually the virtual address, but is
74562306a36Sopenharmony_ci		 * congruent to every user mapping.
74662306a36Sopenharmony_ci		 */
74762306a36Sopenharmony_ci		if (addr_not_cache_congruent(paddr, vaddr))
74862306a36Sopenharmony_ci			__flush_dcache_pages(paddr, vaddr,
74962306a36Sopenharmony_ci						folio_nr_pages(folio));
75062306a36Sopenharmony_ci	}
75162306a36Sopenharmony_ci}
75262306a36Sopenharmony_ciEXPORT_SYMBOL(flush_dcache_folio);
75362306a36Sopenharmony_ci
75462306a36Sopenharmony_civoid flush_dcache_page(struct page *page)
75562306a36Sopenharmony_ci{
75662306a36Sopenharmony_ci	return flush_dcache_folio(page_folio(page));
75762306a36Sopenharmony_ci}
75862306a36Sopenharmony_ciEXPORT_SYMBOL(flush_dcache_page);
75962306a36Sopenharmony_ci
76062306a36Sopenharmony_ci/*
76162306a36Sopenharmony_ci * DMA ops for systems with L1 cache only
76262306a36Sopenharmony_ci * Make memory coherent with L1 cache by flushing/invalidating L1 lines
76362306a36Sopenharmony_ci */
76462306a36Sopenharmony_cistatic void __dma_cache_wback_inv_l1(phys_addr_t start, unsigned long sz)
76562306a36Sopenharmony_ci{
76662306a36Sopenharmony_ci	__dc_line_op_k(start, sz, OP_FLUSH_N_INV);
76762306a36Sopenharmony_ci}
76862306a36Sopenharmony_ci
76962306a36Sopenharmony_cistatic void __dma_cache_inv_l1(phys_addr_t start, unsigned long sz)
77062306a36Sopenharmony_ci{
77162306a36Sopenharmony_ci	__dc_line_op_k(start, sz, OP_INV);
77262306a36Sopenharmony_ci}
77362306a36Sopenharmony_ci
77462306a36Sopenharmony_cistatic void __dma_cache_wback_l1(phys_addr_t start, unsigned long sz)
77562306a36Sopenharmony_ci{
77662306a36Sopenharmony_ci	__dc_line_op_k(start, sz, OP_FLUSH);
77762306a36Sopenharmony_ci}
77862306a36Sopenharmony_ci
77962306a36Sopenharmony_ci/*
78062306a36Sopenharmony_ci * DMA ops for systems with both L1 and L2 caches, but without IOC
78162306a36Sopenharmony_ci * Both L1 and L2 lines need to be explicitly flushed/invalidated
78262306a36Sopenharmony_ci */
78362306a36Sopenharmony_cistatic void __dma_cache_wback_inv_slc(phys_addr_t start, unsigned long sz)
78462306a36Sopenharmony_ci{
78562306a36Sopenharmony_ci	__dc_line_op_k(start, sz, OP_FLUSH_N_INV);
78662306a36Sopenharmony_ci	slc_op(start, sz, OP_FLUSH_N_INV);
78762306a36Sopenharmony_ci}
78862306a36Sopenharmony_ci
78962306a36Sopenharmony_cistatic void __dma_cache_inv_slc(phys_addr_t start, unsigned long sz)
79062306a36Sopenharmony_ci{
79162306a36Sopenharmony_ci	__dc_line_op_k(start, sz, OP_INV);
79262306a36Sopenharmony_ci	slc_op(start, sz, OP_INV);
79362306a36Sopenharmony_ci}
79462306a36Sopenharmony_ci
79562306a36Sopenharmony_cistatic void __dma_cache_wback_slc(phys_addr_t start, unsigned long sz)
79662306a36Sopenharmony_ci{
79762306a36Sopenharmony_ci	__dc_line_op_k(start, sz, OP_FLUSH);
79862306a36Sopenharmony_ci	slc_op(start, sz, OP_FLUSH);
79962306a36Sopenharmony_ci}
80062306a36Sopenharmony_ci
80162306a36Sopenharmony_ci/*
80262306a36Sopenharmony_ci * Exported DMA API
80362306a36Sopenharmony_ci */
80462306a36Sopenharmony_civoid dma_cache_wback_inv(phys_addr_t start, unsigned long sz)
80562306a36Sopenharmony_ci{
80662306a36Sopenharmony_ci	__dma_cache_wback_inv(start, sz);
80762306a36Sopenharmony_ci}
80862306a36Sopenharmony_ciEXPORT_SYMBOL(dma_cache_wback_inv);
80962306a36Sopenharmony_ci
81062306a36Sopenharmony_civoid dma_cache_inv(phys_addr_t start, unsigned long sz)
81162306a36Sopenharmony_ci{
81262306a36Sopenharmony_ci	__dma_cache_inv(start, sz);
81362306a36Sopenharmony_ci}
81462306a36Sopenharmony_ciEXPORT_SYMBOL(dma_cache_inv);
81562306a36Sopenharmony_ci
81662306a36Sopenharmony_civoid dma_cache_wback(phys_addr_t start, unsigned long sz)
81762306a36Sopenharmony_ci{
81862306a36Sopenharmony_ci	__dma_cache_wback(start, sz);
81962306a36Sopenharmony_ci}
82062306a36Sopenharmony_ciEXPORT_SYMBOL(dma_cache_wback);
82162306a36Sopenharmony_ci
82262306a36Sopenharmony_ci/*
82362306a36Sopenharmony_ci * This is API for making I/D Caches consistent when modifying
82462306a36Sopenharmony_ci * kernel code (loadable modules, kprobes, kgdb...)
82562306a36Sopenharmony_ci * This is called on insmod, with kernel virtual address for CODE of
82662306a36Sopenharmony_ci * the module. ARC cache maintenance ops require PHY address thus we
82762306a36Sopenharmony_ci * need to convert vmalloc addr to PHY addr
82862306a36Sopenharmony_ci */
82962306a36Sopenharmony_civoid flush_icache_range(unsigned long kstart, unsigned long kend)
83062306a36Sopenharmony_ci{
83162306a36Sopenharmony_ci	unsigned int tot_sz;
83262306a36Sopenharmony_ci
83362306a36Sopenharmony_ci	WARN(kstart < TASK_SIZE, "%s() can't handle user vaddr", __func__);
83462306a36Sopenharmony_ci
83562306a36Sopenharmony_ci	/* Shortcut for bigger flush ranges.
83662306a36Sopenharmony_ci	 * Here we don't care if this was kernel virtual or phy addr
83762306a36Sopenharmony_ci	 */
83862306a36Sopenharmony_ci	tot_sz = kend - kstart;
83962306a36Sopenharmony_ci	if (tot_sz > PAGE_SIZE) {
84062306a36Sopenharmony_ci		flush_cache_all();
84162306a36Sopenharmony_ci		return;
84262306a36Sopenharmony_ci	}
84362306a36Sopenharmony_ci
84462306a36Sopenharmony_ci	/* Case: Kernel Phy addr (0x8000_0000 onwards) */
84562306a36Sopenharmony_ci	if (likely(kstart > PAGE_OFFSET)) {
84662306a36Sopenharmony_ci		/*
84762306a36Sopenharmony_ci		 * The 2nd arg despite being paddr will be used to index icache
84862306a36Sopenharmony_ci		 * This is OK since no alternate virtual mappings will exist
84962306a36Sopenharmony_ci		 * given the callers for this case: kprobe/kgdb in built-in
85062306a36Sopenharmony_ci		 * kernel code only.
85162306a36Sopenharmony_ci		 */
85262306a36Sopenharmony_ci		__sync_icache_dcache(kstart, kstart, kend - kstart);
85362306a36Sopenharmony_ci		return;
85462306a36Sopenharmony_ci	}
85562306a36Sopenharmony_ci
85662306a36Sopenharmony_ci	/*
85762306a36Sopenharmony_ci	 * Case: Kernel Vaddr (0x7000_0000 to 0x7fff_ffff)
85862306a36Sopenharmony_ci	 * (1) ARC Cache Maintenance ops only take Phy addr, hence special
85962306a36Sopenharmony_ci	 *     handling of kernel vaddr.
86062306a36Sopenharmony_ci	 *
86162306a36Sopenharmony_ci	 * (2) Despite @tot_sz being < PAGE_SIZE (bigger cases handled already),
86262306a36Sopenharmony_ci	 *     it still needs to handle  a 2 page scenario, where the range
86362306a36Sopenharmony_ci	 *     straddles across 2 virtual pages and hence need for loop
86462306a36Sopenharmony_ci	 */
86562306a36Sopenharmony_ci	while (tot_sz > 0) {
86662306a36Sopenharmony_ci		unsigned int off, sz;
86762306a36Sopenharmony_ci		unsigned long phy, pfn;
86862306a36Sopenharmony_ci
86962306a36Sopenharmony_ci		off = kstart % PAGE_SIZE;
87062306a36Sopenharmony_ci		pfn = vmalloc_to_pfn((void *)kstart);
87162306a36Sopenharmony_ci		phy = (pfn << PAGE_SHIFT) + off;
87262306a36Sopenharmony_ci		sz = min_t(unsigned int, tot_sz, PAGE_SIZE - off);
87362306a36Sopenharmony_ci		__sync_icache_dcache(phy, kstart, sz);
87462306a36Sopenharmony_ci		kstart += sz;
87562306a36Sopenharmony_ci		tot_sz -= sz;
87662306a36Sopenharmony_ci	}
87762306a36Sopenharmony_ci}
87862306a36Sopenharmony_ciEXPORT_SYMBOL(flush_icache_range);
87962306a36Sopenharmony_ci
88062306a36Sopenharmony_ci/*
88162306a36Sopenharmony_ci * General purpose helper to make I and D cache lines consistent.
88262306a36Sopenharmony_ci * @paddr is phy addr of region
88362306a36Sopenharmony_ci * @vaddr is typically user vaddr (breakpoint) or kernel vaddr (vmalloc)
88462306a36Sopenharmony_ci *    However in one instance, when called by kprobe (for a breakpt in
88562306a36Sopenharmony_ci *    builtin kernel code) @vaddr will be paddr only, meaning CDU operation will
88662306a36Sopenharmony_ci *    use a paddr to index the cache (despite VIPT). This is fine since a
88762306a36Sopenharmony_ci *    builtin kernel page will not have any virtual mappings.
88862306a36Sopenharmony_ci *    kprobe on loadable module will be kernel vaddr.
88962306a36Sopenharmony_ci */
89062306a36Sopenharmony_civoid __sync_icache_dcache(phys_addr_t paddr, unsigned long vaddr, int len)
89162306a36Sopenharmony_ci{
89262306a36Sopenharmony_ci	__dc_line_op(paddr, vaddr, len, OP_FLUSH_N_INV);
89362306a36Sopenharmony_ci	__ic_line_inv_vaddr(paddr, vaddr, len);
89462306a36Sopenharmony_ci}
89562306a36Sopenharmony_ci
89662306a36Sopenharmony_ci/* wrapper to compile time eliminate alignment checks in flush loop */
89762306a36Sopenharmony_civoid __inv_icache_pages(phys_addr_t paddr, unsigned long vaddr, unsigned nr)
89862306a36Sopenharmony_ci{
89962306a36Sopenharmony_ci	__ic_line_inv_vaddr(paddr, vaddr, nr * PAGE_SIZE);
90062306a36Sopenharmony_ci}
90162306a36Sopenharmony_ci
90262306a36Sopenharmony_ci/*
90362306a36Sopenharmony_ci * wrapper to clearout kernel or userspace mappings of a page
90462306a36Sopenharmony_ci * For kernel mappings @vaddr == @paddr
90562306a36Sopenharmony_ci */
90662306a36Sopenharmony_civoid __flush_dcache_pages(phys_addr_t paddr, unsigned long vaddr, unsigned nr)
90762306a36Sopenharmony_ci{
90862306a36Sopenharmony_ci	__dc_line_op(paddr, vaddr & PAGE_MASK, nr * PAGE_SIZE, OP_FLUSH_N_INV);
90962306a36Sopenharmony_ci}
91062306a36Sopenharmony_ci
91162306a36Sopenharmony_cinoinline void flush_cache_all(void)
91262306a36Sopenharmony_ci{
91362306a36Sopenharmony_ci	unsigned long flags;
91462306a36Sopenharmony_ci
91562306a36Sopenharmony_ci	local_irq_save(flags);
91662306a36Sopenharmony_ci
91762306a36Sopenharmony_ci	__ic_entire_inv();
91862306a36Sopenharmony_ci	__dc_entire_op(OP_FLUSH_N_INV);
91962306a36Sopenharmony_ci
92062306a36Sopenharmony_ci	local_irq_restore(flags);
92162306a36Sopenharmony_ci
92262306a36Sopenharmony_ci}
92362306a36Sopenharmony_ci
92462306a36Sopenharmony_ci#ifdef CONFIG_ARC_CACHE_VIPT_ALIASING
92562306a36Sopenharmony_ci
92662306a36Sopenharmony_civoid flush_cache_mm(struct mm_struct *mm)
92762306a36Sopenharmony_ci{
92862306a36Sopenharmony_ci	flush_cache_all();
92962306a36Sopenharmony_ci}
93062306a36Sopenharmony_ci
93162306a36Sopenharmony_civoid flush_cache_page(struct vm_area_struct *vma, unsigned long u_vaddr,
93262306a36Sopenharmony_ci		      unsigned long pfn)
93362306a36Sopenharmony_ci{
93462306a36Sopenharmony_ci	phys_addr_t paddr = pfn << PAGE_SHIFT;
93562306a36Sopenharmony_ci
93662306a36Sopenharmony_ci	u_vaddr &= PAGE_MASK;
93762306a36Sopenharmony_ci
93862306a36Sopenharmony_ci	__flush_dcache_pages(paddr, u_vaddr, 1);
93962306a36Sopenharmony_ci
94062306a36Sopenharmony_ci	if (vma->vm_flags & VM_EXEC)
94162306a36Sopenharmony_ci		__inv_icache_pages(paddr, u_vaddr, 1);
94262306a36Sopenharmony_ci}
94362306a36Sopenharmony_ci
94462306a36Sopenharmony_civoid flush_cache_range(struct vm_area_struct *vma, unsigned long start,
94562306a36Sopenharmony_ci		       unsigned long end)
94662306a36Sopenharmony_ci{
94762306a36Sopenharmony_ci	flush_cache_all();
94862306a36Sopenharmony_ci}
94962306a36Sopenharmony_ci
95062306a36Sopenharmony_civoid flush_anon_page(struct vm_area_struct *vma, struct page *page,
95162306a36Sopenharmony_ci		     unsigned long u_vaddr)
95262306a36Sopenharmony_ci{
95362306a36Sopenharmony_ci	/* TBD: do we really need to clear the kernel mapping */
95462306a36Sopenharmony_ci	__flush_dcache_pages((phys_addr_t)page_address(page), u_vaddr, 1);
95562306a36Sopenharmony_ci	__flush_dcache_pages((phys_addr_t)page_address(page),
95662306a36Sopenharmony_ci			    (phys_addr_t)page_address(page), 1);
95762306a36Sopenharmony_ci
95862306a36Sopenharmony_ci}
95962306a36Sopenharmony_ci
96062306a36Sopenharmony_ci#endif
96162306a36Sopenharmony_ci
96262306a36Sopenharmony_civoid copy_user_highpage(struct page *to, struct page *from,
96362306a36Sopenharmony_ci	unsigned long u_vaddr, struct vm_area_struct *vma)
96462306a36Sopenharmony_ci{
96562306a36Sopenharmony_ci	struct folio *src = page_folio(from);
96662306a36Sopenharmony_ci	struct folio *dst = page_folio(to);
96762306a36Sopenharmony_ci	void *kfrom = kmap_atomic(from);
96862306a36Sopenharmony_ci	void *kto = kmap_atomic(to);
96962306a36Sopenharmony_ci	int clean_src_k_mappings = 0;
97062306a36Sopenharmony_ci
97162306a36Sopenharmony_ci	/*
97262306a36Sopenharmony_ci	 * If SRC page was already mapped in userspace AND it's U-mapping is
97362306a36Sopenharmony_ci	 * not congruent with K-mapping, sync former to physical page so that
97462306a36Sopenharmony_ci	 * K-mapping in memcpy below, sees the right data
97562306a36Sopenharmony_ci	 *
97662306a36Sopenharmony_ci	 * Note that while @u_vaddr refers to DST page's userspace vaddr, it is
97762306a36Sopenharmony_ci	 * equally valid for SRC page as well
97862306a36Sopenharmony_ci	 *
97962306a36Sopenharmony_ci	 * For !VIPT cache, all of this gets compiled out as
98062306a36Sopenharmony_ci	 * addr_not_cache_congruent() is 0
98162306a36Sopenharmony_ci	 */
98262306a36Sopenharmony_ci	if (page_mapcount(from) && addr_not_cache_congruent(kfrom, u_vaddr)) {
98362306a36Sopenharmony_ci		__flush_dcache_pages((unsigned long)kfrom, u_vaddr, 1);
98462306a36Sopenharmony_ci		clean_src_k_mappings = 1;
98562306a36Sopenharmony_ci	}
98662306a36Sopenharmony_ci
98762306a36Sopenharmony_ci	copy_page(kto, kfrom);
98862306a36Sopenharmony_ci
98962306a36Sopenharmony_ci	/*
99062306a36Sopenharmony_ci	 * Mark DST page K-mapping as dirty for a later finalization by
99162306a36Sopenharmony_ci	 * update_mmu_cache(). Although the finalization could have been done
99262306a36Sopenharmony_ci	 * here as well (given that both vaddr/paddr are available).
99362306a36Sopenharmony_ci	 * But update_mmu_cache() already has code to do that for other
99462306a36Sopenharmony_ci	 * non copied user pages (e.g. read faults which wire in pagecache page
99562306a36Sopenharmony_ci	 * directly).
99662306a36Sopenharmony_ci	 */
99762306a36Sopenharmony_ci	clear_bit(PG_dc_clean, &dst->flags);
99862306a36Sopenharmony_ci
99962306a36Sopenharmony_ci	/*
100062306a36Sopenharmony_ci	 * if SRC was already usermapped and non-congruent to kernel mapping
100162306a36Sopenharmony_ci	 * sync the kernel mapping back to physical page
100262306a36Sopenharmony_ci	 */
100362306a36Sopenharmony_ci	if (clean_src_k_mappings) {
100462306a36Sopenharmony_ci		__flush_dcache_pages((unsigned long)kfrom,
100562306a36Sopenharmony_ci					(unsigned long)kfrom, 1);
100662306a36Sopenharmony_ci	} else {
100762306a36Sopenharmony_ci		clear_bit(PG_dc_clean, &src->flags);
100862306a36Sopenharmony_ci	}
100962306a36Sopenharmony_ci
101062306a36Sopenharmony_ci	kunmap_atomic(kto);
101162306a36Sopenharmony_ci	kunmap_atomic(kfrom);
101262306a36Sopenharmony_ci}
101362306a36Sopenharmony_ci
101462306a36Sopenharmony_civoid clear_user_page(void *to, unsigned long u_vaddr, struct page *page)
101562306a36Sopenharmony_ci{
101662306a36Sopenharmony_ci	struct folio *folio = page_folio(page);
101762306a36Sopenharmony_ci	clear_page(to);
101862306a36Sopenharmony_ci	clear_bit(PG_dc_clean, &folio->flags);
101962306a36Sopenharmony_ci}
102062306a36Sopenharmony_ciEXPORT_SYMBOL(clear_user_page);
102162306a36Sopenharmony_ci
102262306a36Sopenharmony_ci/**********************************************************************
102362306a36Sopenharmony_ci * Explicit Cache flush request from user space via syscall
102462306a36Sopenharmony_ci * Needed for JITs which generate code on the fly
102562306a36Sopenharmony_ci */
102662306a36Sopenharmony_ciSYSCALL_DEFINE3(cacheflush, uint32_t, start, uint32_t, sz, uint32_t, flags)
102762306a36Sopenharmony_ci{
102862306a36Sopenharmony_ci	/* TBD: optimize this */
102962306a36Sopenharmony_ci	flush_cache_all();
103062306a36Sopenharmony_ci	return 0;
103162306a36Sopenharmony_ci}
103262306a36Sopenharmony_ci
103362306a36Sopenharmony_ci/*
103462306a36Sopenharmony_ci * IO-Coherency (IOC) setup rules:
103562306a36Sopenharmony_ci *
103662306a36Sopenharmony_ci * 1. Needs to be at system level, so only once by Master core
103762306a36Sopenharmony_ci *    Non-Masters need not be accessing caches at that time
103862306a36Sopenharmony_ci *    - They are either HALT_ON_RESET and kick started much later or
103962306a36Sopenharmony_ci *    - if run on reset, need to ensure that arc_platform_smp_wait_to_boot()
104062306a36Sopenharmony_ci *      doesn't perturb caches or coherency unit
104162306a36Sopenharmony_ci *
104262306a36Sopenharmony_ci * 2. caches (L1 and SLC) need to be purged (flush+inv) before setting up IOC,
104362306a36Sopenharmony_ci *    otherwise any straggler data might behave strangely post IOC enabling
104462306a36Sopenharmony_ci *
104562306a36Sopenharmony_ci * 3. All Caches need to be disabled when setting up IOC to elide any in-flight
104662306a36Sopenharmony_ci *    Coherency transactions
104762306a36Sopenharmony_ci */
104862306a36Sopenharmony_cistatic noinline void __init arc_ioc_setup(void)
104962306a36Sopenharmony_ci{
105062306a36Sopenharmony_ci	unsigned int ioc_base, mem_sz;
105162306a36Sopenharmony_ci
105262306a36Sopenharmony_ci	/*
105362306a36Sopenharmony_ci	 * If IOC was already enabled (due to bootloader) it technically needs to
105462306a36Sopenharmony_ci	 * be reconfigured with aperture base,size corresponding to Linux memory map
105562306a36Sopenharmony_ci	 * which will certainly be different than uboot's. But disabling and
105662306a36Sopenharmony_ci	 * reenabling IOC when DMA might be potentially active is tricky business.
105762306a36Sopenharmony_ci	 * To avoid random memory issues later, just panic here and ask user to
105862306a36Sopenharmony_ci	 * upgrade bootloader to one which doesn't enable IOC
105962306a36Sopenharmony_ci	 */
106062306a36Sopenharmony_ci	if (read_aux_reg(ARC_REG_IO_COH_ENABLE) & ARC_IO_COH_ENABLE_BIT)
106162306a36Sopenharmony_ci		panic("IOC already enabled, please upgrade bootloader!\n");
106262306a36Sopenharmony_ci
106362306a36Sopenharmony_ci	if (!ioc_enable)
106462306a36Sopenharmony_ci		return;
106562306a36Sopenharmony_ci
106662306a36Sopenharmony_ci	/* Flush + invalidate + disable L1 dcache */
106762306a36Sopenharmony_ci	__dc_disable();
106862306a36Sopenharmony_ci
106962306a36Sopenharmony_ci	/* Flush + invalidate SLC */
107062306a36Sopenharmony_ci	if (read_aux_reg(ARC_REG_SLC_BCR))
107162306a36Sopenharmony_ci		slc_entire_op(OP_FLUSH_N_INV);
107262306a36Sopenharmony_ci
107362306a36Sopenharmony_ci	/*
107462306a36Sopenharmony_ci	 * currently IOC Aperture covers entire DDR
107562306a36Sopenharmony_ci	 * TBD: fix for PGU + 1GB of low mem
107662306a36Sopenharmony_ci	 * TBD: fix for PAE
107762306a36Sopenharmony_ci	 */
107862306a36Sopenharmony_ci	mem_sz = arc_get_mem_sz();
107962306a36Sopenharmony_ci
108062306a36Sopenharmony_ci	if (!is_power_of_2(mem_sz) || mem_sz < 4096)
108162306a36Sopenharmony_ci		panic("IOC Aperture size must be power of 2 larger than 4KB");
108262306a36Sopenharmony_ci
108362306a36Sopenharmony_ci	/*
108462306a36Sopenharmony_ci	 * IOC Aperture size decoded as 2 ^ (SIZE + 2) KB,
108562306a36Sopenharmony_ci	 * so setting 0x11 implies 512MB, 0x12 implies 1GB...
108662306a36Sopenharmony_ci	 */
108762306a36Sopenharmony_ci	write_aux_reg(ARC_REG_IO_COH_AP0_SIZE, order_base_2(mem_sz >> 10) - 2);
108862306a36Sopenharmony_ci
108962306a36Sopenharmony_ci	/* for now assume kernel base is start of IOC aperture */
109062306a36Sopenharmony_ci	ioc_base = CONFIG_LINUX_RAM_BASE;
109162306a36Sopenharmony_ci
109262306a36Sopenharmony_ci	if (ioc_base % mem_sz != 0)
109362306a36Sopenharmony_ci		panic("IOC Aperture start must be aligned to the size of the aperture");
109462306a36Sopenharmony_ci
109562306a36Sopenharmony_ci	write_aux_reg(ARC_REG_IO_COH_AP0_BASE, ioc_base >> 12);
109662306a36Sopenharmony_ci	write_aux_reg(ARC_REG_IO_COH_PARTIAL, ARC_IO_COH_PARTIAL_BIT);
109762306a36Sopenharmony_ci	write_aux_reg(ARC_REG_IO_COH_ENABLE, ARC_IO_COH_ENABLE_BIT);
109862306a36Sopenharmony_ci
109962306a36Sopenharmony_ci	/* Re-enable L1 dcache */
110062306a36Sopenharmony_ci	__dc_enable();
110162306a36Sopenharmony_ci}
110262306a36Sopenharmony_ci
110362306a36Sopenharmony_ci/*
110462306a36Sopenharmony_ci * Cache related boot time checks/setups only needed on master CPU:
110562306a36Sopenharmony_ci *  - Geometry checks (kernel build and hardware agree: e.g. L1_CACHE_BYTES)
110662306a36Sopenharmony_ci *    Assume SMP only, so all cores will have same cache config. A check on
110762306a36Sopenharmony_ci *    one core suffices for all
110862306a36Sopenharmony_ci *  - IOC setup / dma callbacks only need to be done once
110962306a36Sopenharmony_ci */
111062306a36Sopenharmony_cistatic noinline void __init arc_cache_init_master(void)
111162306a36Sopenharmony_ci{
111262306a36Sopenharmony_ci	if (IS_ENABLED(CONFIG_ARC_HAS_ICACHE)) {
111362306a36Sopenharmony_ci		struct cpuinfo_arc_cache *ic = &ic_info;
111462306a36Sopenharmony_ci
111562306a36Sopenharmony_ci		if (!ic->line_len)
111662306a36Sopenharmony_ci			panic("cache support enabled but non-existent cache\n");
111762306a36Sopenharmony_ci
111862306a36Sopenharmony_ci		if (ic->line_len != L1_CACHE_BYTES)
111962306a36Sopenharmony_ci			panic("ICache line [%d] != kernel Config [%d]",
112062306a36Sopenharmony_ci			      ic->line_len, L1_CACHE_BYTES);
112162306a36Sopenharmony_ci
112262306a36Sopenharmony_ci		/*
112362306a36Sopenharmony_ci		 * In MMU v4 (HS38x) the aliasing icache config uses IVIL/PTAG
112462306a36Sopenharmony_ci		 * pair to provide vaddr/paddr respectively, just as in MMU v3
112562306a36Sopenharmony_ci		 */
112662306a36Sopenharmony_ci		if (is_isa_arcv2() && ic->colors > 1)
112762306a36Sopenharmony_ci			_cache_line_loop_ic_fn = __cache_line_loop_v3;
112862306a36Sopenharmony_ci		else
112962306a36Sopenharmony_ci			_cache_line_loop_ic_fn = __cache_line_loop;
113062306a36Sopenharmony_ci	}
113162306a36Sopenharmony_ci
113262306a36Sopenharmony_ci	if (IS_ENABLED(CONFIG_ARC_HAS_DCACHE)) {
113362306a36Sopenharmony_ci		struct cpuinfo_arc_cache *dc = &dc_info;
113462306a36Sopenharmony_ci
113562306a36Sopenharmony_ci		if (!dc->line_len)
113662306a36Sopenharmony_ci			panic("cache support enabled but non-existent cache\n");
113762306a36Sopenharmony_ci
113862306a36Sopenharmony_ci		if (dc->line_len != L1_CACHE_BYTES)
113962306a36Sopenharmony_ci			panic("DCache line [%d] != kernel Config [%d]",
114062306a36Sopenharmony_ci			      dc->line_len, L1_CACHE_BYTES);
114162306a36Sopenharmony_ci
114262306a36Sopenharmony_ci		/* check for D-Cache aliasing on ARCompact: ARCv2 has PIPT */
114362306a36Sopenharmony_ci		if (is_isa_arcompact()) {
114462306a36Sopenharmony_ci			int handled = IS_ENABLED(CONFIG_ARC_CACHE_VIPT_ALIASING);
114562306a36Sopenharmony_ci
114662306a36Sopenharmony_ci			if (dc->colors > 1) {
114762306a36Sopenharmony_ci				if (!handled)
114862306a36Sopenharmony_ci					panic("Enable CONFIG_ARC_CACHE_VIPT_ALIASING\n");
114962306a36Sopenharmony_ci				if (CACHE_COLORS_NUM != dc->colors)
115062306a36Sopenharmony_ci					panic("CACHE_COLORS_NUM not optimized for config\n");
115162306a36Sopenharmony_ci			} else if (handled && dc->colors == 1) {
115262306a36Sopenharmony_ci				panic("Disable CONFIG_ARC_CACHE_VIPT_ALIASING\n");
115362306a36Sopenharmony_ci			}
115462306a36Sopenharmony_ci		}
115562306a36Sopenharmony_ci	}
115662306a36Sopenharmony_ci
115762306a36Sopenharmony_ci	/*
115862306a36Sopenharmony_ci	 * Check that SMP_CACHE_BYTES (and hence ARCH_DMA_MINALIGN) is larger
115962306a36Sopenharmony_ci	 * or equal to any cache line length.
116062306a36Sopenharmony_ci	 */
116162306a36Sopenharmony_ci	BUILD_BUG_ON_MSG(L1_CACHE_BYTES > SMP_CACHE_BYTES,
116262306a36Sopenharmony_ci			 "SMP_CACHE_BYTES must be >= any cache line length");
116362306a36Sopenharmony_ci	if (is_isa_arcv2() && (l2_line_sz > SMP_CACHE_BYTES))
116462306a36Sopenharmony_ci		panic("L2 Cache line [%d] > kernel Config [%d]\n",
116562306a36Sopenharmony_ci		      l2_line_sz, SMP_CACHE_BYTES);
116662306a36Sopenharmony_ci
116762306a36Sopenharmony_ci	/* Note that SLC disable not formally supported till HS 3.0 */
116862306a36Sopenharmony_ci	if (is_isa_arcv2() && l2_line_sz && !slc_enable)
116962306a36Sopenharmony_ci		arc_slc_disable();
117062306a36Sopenharmony_ci
117162306a36Sopenharmony_ci	if (is_isa_arcv2() && ioc_exists)
117262306a36Sopenharmony_ci		arc_ioc_setup();
117362306a36Sopenharmony_ci
117462306a36Sopenharmony_ci	if (is_isa_arcv2() && l2_line_sz && slc_enable) {
117562306a36Sopenharmony_ci		__dma_cache_wback_inv = __dma_cache_wback_inv_slc;
117662306a36Sopenharmony_ci		__dma_cache_inv = __dma_cache_inv_slc;
117762306a36Sopenharmony_ci		__dma_cache_wback = __dma_cache_wback_slc;
117862306a36Sopenharmony_ci	} else {
117962306a36Sopenharmony_ci		__dma_cache_wback_inv = __dma_cache_wback_inv_l1;
118062306a36Sopenharmony_ci		__dma_cache_inv = __dma_cache_inv_l1;
118162306a36Sopenharmony_ci		__dma_cache_wback = __dma_cache_wback_l1;
118262306a36Sopenharmony_ci	}
118362306a36Sopenharmony_ci	/*
118462306a36Sopenharmony_ci	 * In case of IOC (say IOC+SLC case), pointers above could still be set
118562306a36Sopenharmony_ci	 * but end up not being relevant as the first function in chain is not
118662306a36Sopenharmony_ci	 * called at all for devices using coherent DMA.
118762306a36Sopenharmony_ci	 *     arch_sync_dma_for_cpu() -> dma_cache_*() -> __dma_cache_*()
118862306a36Sopenharmony_ci	 */
118962306a36Sopenharmony_ci}
119062306a36Sopenharmony_ci
119162306a36Sopenharmony_civoid __ref arc_cache_init(void)
119262306a36Sopenharmony_ci{
119362306a36Sopenharmony_ci	unsigned int __maybe_unused cpu = smp_processor_id();
119462306a36Sopenharmony_ci
119562306a36Sopenharmony_ci	if (!cpu)
119662306a36Sopenharmony_ci		arc_cache_init_master();
119762306a36Sopenharmony_ci
119862306a36Sopenharmony_ci	/*
119962306a36Sopenharmony_ci	 * In PAE regime, TLB and cache maintenance ops take wider addresses
120062306a36Sopenharmony_ci	 * And even if PAE is not enabled in kernel, the upper 32-bits still need
120162306a36Sopenharmony_ci	 * to be zeroed to keep the ops sane.
120262306a36Sopenharmony_ci	 * As an optimization for more common !PAE enabled case, zero them out
120362306a36Sopenharmony_ci	 * once at init, rather than checking/setting to 0 for every runtime op
120462306a36Sopenharmony_ci	 */
120562306a36Sopenharmony_ci	if (is_isa_arcv2() && pae40_exist_but_not_enab()) {
120662306a36Sopenharmony_ci
120762306a36Sopenharmony_ci		if (IS_ENABLED(CONFIG_ARC_HAS_ICACHE))
120862306a36Sopenharmony_ci			write_aux_reg(ARC_REG_IC_PTAG_HI, 0);
120962306a36Sopenharmony_ci
121062306a36Sopenharmony_ci		if (IS_ENABLED(CONFIG_ARC_HAS_DCACHE))
121162306a36Sopenharmony_ci			write_aux_reg(ARC_REG_DC_PTAG_HI, 0);
121262306a36Sopenharmony_ci
121362306a36Sopenharmony_ci		if (l2_line_sz) {
121462306a36Sopenharmony_ci			write_aux_reg(ARC_REG_SLC_RGN_END1, 0);
121562306a36Sopenharmony_ci			write_aux_reg(ARC_REG_SLC_RGN_START1, 0);
121662306a36Sopenharmony_ci		}
121762306a36Sopenharmony_ci	}
121862306a36Sopenharmony_ci}
1219