162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Copyright (C) 2013 Imagination Technologies
462306a36Sopenharmony_ci * Author: Paul Burton <paul.burton@mips.com>
562306a36Sopenharmony_ci */
662306a36Sopenharmony_ci
762306a36Sopenharmony_ci#include <linux/errno.h>
862306a36Sopenharmony_ci#include <linux/percpu.h>
962306a36Sopenharmony_ci#include <linux/spinlock.h>
1062306a36Sopenharmony_ci
1162306a36Sopenharmony_ci#include <asm/mips-cps.h>
1262306a36Sopenharmony_ci#include <asm/mipsregs.h>
1362306a36Sopenharmony_ci
1462306a36Sopenharmony_civoid __iomem *mips_gcr_base;
1562306a36Sopenharmony_civoid __iomem *mips_cm_l2sync_base;
1662306a36Sopenharmony_ciint mips_cm_is64;
1762306a36Sopenharmony_ci
1862306a36Sopenharmony_cistatic char *cm2_tr[8] = {
1962306a36Sopenharmony_ci	"mem",	"gcr",	"gic",	"mmio",
2062306a36Sopenharmony_ci	"0x04", "cpc", "0x06", "0x07"
2162306a36Sopenharmony_ci};
2262306a36Sopenharmony_ci
2362306a36Sopenharmony_ci/* CM3 Tag ECC transaction type */
2462306a36Sopenharmony_cistatic char *cm3_tr[16] = {
2562306a36Sopenharmony_ci	[0x0] = "ReqNoData",
2662306a36Sopenharmony_ci	[0x1] = "0x1",
2762306a36Sopenharmony_ci	[0x2] = "ReqWData",
2862306a36Sopenharmony_ci	[0x3] = "0x3",
2962306a36Sopenharmony_ci	[0x4] = "IReqNoResp",
3062306a36Sopenharmony_ci	[0x5] = "IReqWResp",
3162306a36Sopenharmony_ci	[0x6] = "IReqNoRespDat",
3262306a36Sopenharmony_ci	[0x7] = "IReqWRespDat",
3362306a36Sopenharmony_ci	[0x8] = "RespNoData",
3462306a36Sopenharmony_ci	[0x9] = "RespDataFol",
3562306a36Sopenharmony_ci	[0xa] = "RespWData",
3662306a36Sopenharmony_ci	[0xb] = "RespDataOnly",
3762306a36Sopenharmony_ci	[0xc] = "IRespNoData",
3862306a36Sopenharmony_ci	[0xd] = "IRespDataFol",
3962306a36Sopenharmony_ci	[0xe] = "IRespWData",
4062306a36Sopenharmony_ci	[0xf] = "IRespDataOnly"
4162306a36Sopenharmony_ci};
4262306a36Sopenharmony_ci
4362306a36Sopenharmony_cistatic char *cm2_cmd[32] = {
4462306a36Sopenharmony_ci	[0x00] = "0x00",
4562306a36Sopenharmony_ci	[0x01] = "Legacy Write",
4662306a36Sopenharmony_ci	[0x02] = "Legacy Read",
4762306a36Sopenharmony_ci	[0x03] = "0x03",
4862306a36Sopenharmony_ci	[0x04] = "0x04",
4962306a36Sopenharmony_ci	[0x05] = "0x05",
5062306a36Sopenharmony_ci	[0x06] = "0x06",
5162306a36Sopenharmony_ci	[0x07] = "0x07",
5262306a36Sopenharmony_ci	[0x08] = "Coherent Read Own",
5362306a36Sopenharmony_ci	[0x09] = "Coherent Read Share",
5462306a36Sopenharmony_ci	[0x0a] = "Coherent Read Discard",
5562306a36Sopenharmony_ci	[0x0b] = "Coherent Ready Share Always",
5662306a36Sopenharmony_ci	[0x0c] = "Coherent Upgrade",
5762306a36Sopenharmony_ci	[0x0d] = "Coherent Writeback",
5862306a36Sopenharmony_ci	[0x0e] = "0x0e",
5962306a36Sopenharmony_ci	[0x0f] = "0x0f",
6062306a36Sopenharmony_ci	[0x10] = "Coherent Copyback",
6162306a36Sopenharmony_ci	[0x11] = "Coherent Copyback Invalidate",
6262306a36Sopenharmony_ci	[0x12] = "Coherent Invalidate",
6362306a36Sopenharmony_ci	[0x13] = "Coherent Write Invalidate",
6462306a36Sopenharmony_ci	[0x14] = "Coherent Completion Sync",
6562306a36Sopenharmony_ci	[0x15] = "0x15",
6662306a36Sopenharmony_ci	[0x16] = "0x16",
6762306a36Sopenharmony_ci	[0x17] = "0x17",
6862306a36Sopenharmony_ci	[0x18] = "0x18",
6962306a36Sopenharmony_ci	[0x19] = "0x19",
7062306a36Sopenharmony_ci	[0x1a] = "0x1a",
7162306a36Sopenharmony_ci	[0x1b] = "0x1b",
7262306a36Sopenharmony_ci	[0x1c] = "0x1c",
7362306a36Sopenharmony_ci	[0x1d] = "0x1d",
7462306a36Sopenharmony_ci	[0x1e] = "0x1e",
7562306a36Sopenharmony_ci	[0x1f] = "0x1f"
7662306a36Sopenharmony_ci};
7762306a36Sopenharmony_ci
7862306a36Sopenharmony_ci/* CM3 Tag ECC command type */
7962306a36Sopenharmony_cistatic char *cm3_cmd[16] = {
8062306a36Sopenharmony_ci	[0x0] = "Legacy Read",
8162306a36Sopenharmony_ci	[0x1] = "Legacy Write",
8262306a36Sopenharmony_ci	[0x2] = "Coherent Read Own",
8362306a36Sopenharmony_ci	[0x3] = "Coherent Read Share",
8462306a36Sopenharmony_ci	[0x4] = "Coherent Read Discard",
8562306a36Sopenharmony_ci	[0x5] = "Coherent Evicted",
8662306a36Sopenharmony_ci	[0x6] = "Coherent Upgrade",
8762306a36Sopenharmony_ci	[0x7] = "Coherent Upgrade for Store Conditional",
8862306a36Sopenharmony_ci	[0x8] = "Coherent Writeback",
8962306a36Sopenharmony_ci	[0x9] = "Coherent Write Invalidate",
9062306a36Sopenharmony_ci	[0xa] = "0xa",
9162306a36Sopenharmony_ci	[0xb] = "0xb",
9262306a36Sopenharmony_ci	[0xc] = "0xc",
9362306a36Sopenharmony_ci	[0xd] = "0xd",
9462306a36Sopenharmony_ci	[0xe] = "0xe",
9562306a36Sopenharmony_ci	[0xf] = "0xf"
9662306a36Sopenharmony_ci};
9762306a36Sopenharmony_ci
9862306a36Sopenharmony_ci/* CM3 Tag ECC command group */
9962306a36Sopenharmony_cistatic char *cm3_cmd_group[8] = {
10062306a36Sopenharmony_ci	[0x0] = "Normal",
10162306a36Sopenharmony_ci	[0x1] = "Registers",
10262306a36Sopenharmony_ci	[0x2] = "TLB",
10362306a36Sopenharmony_ci	[0x3] = "0x3",
10462306a36Sopenharmony_ci	[0x4] = "L1I",
10562306a36Sopenharmony_ci	[0x5] = "L1D",
10662306a36Sopenharmony_ci	[0x6] = "L3",
10762306a36Sopenharmony_ci	[0x7] = "L2"
10862306a36Sopenharmony_ci};
10962306a36Sopenharmony_ci
11062306a36Sopenharmony_cistatic char *cm2_core[8] = {
11162306a36Sopenharmony_ci	"Invalid/OK",	"Invalid/Data",
11262306a36Sopenharmony_ci	"Shared/OK",	"Shared/Data",
11362306a36Sopenharmony_ci	"Modified/OK",	"Modified/Data",
11462306a36Sopenharmony_ci	"Exclusive/OK", "Exclusive/Data"
11562306a36Sopenharmony_ci};
11662306a36Sopenharmony_ci
11762306a36Sopenharmony_cistatic char *cm2_l2_type[4] = {
11862306a36Sopenharmony_ci	[0x0] = "None",
11962306a36Sopenharmony_ci	[0x1] = "Tag RAM single/double ECC error",
12062306a36Sopenharmony_ci	[0x2] = "Data RAM single/double ECC error",
12162306a36Sopenharmony_ci	[0x3] = "WS RAM uncorrectable dirty parity"
12262306a36Sopenharmony_ci};
12362306a36Sopenharmony_ci
12462306a36Sopenharmony_cistatic char *cm2_l2_instr[32] = {
12562306a36Sopenharmony_ci	[0x00] = "L2_NOP",
12662306a36Sopenharmony_ci	[0x01] = "L2_ERR_CORR",
12762306a36Sopenharmony_ci	[0x02] = "L2_TAG_INV",
12862306a36Sopenharmony_ci	[0x03] = "L2_WS_CLEAN",
12962306a36Sopenharmony_ci	[0x04] = "L2_RD_MDYFY_WR",
13062306a36Sopenharmony_ci	[0x05] = "L2_WS_MRU",
13162306a36Sopenharmony_ci	[0x06] = "L2_EVICT_LN2",
13262306a36Sopenharmony_ci	[0x07] = "0x07",
13362306a36Sopenharmony_ci	[0x08] = "L2_EVICT",
13462306a36Sopenharmony_ci	[0x09] = "L2_REFL",
13562306a36Sopenharmony_ci	[0x0a] = "L2_RD",
13662306a36Sopenharmony_ci	[0x0b] = "L2_WR",
13762306a36Sopenharmony_ci	[0x0c] = "L2_EVICT_MRU",
13862306a36Sopenharmony_ci	[0x0d] = "L2_SYNC",
13962306a36Sopenharmony_ci	[0x0e] = "L2_REFL_ERR",
14062306a36Sopenharmony_ci	[0x0f] = "0x0f",
14162306a36Sopenharmony_ci	[0x10] = "L2_INDX_WB_INV",
14262306a36Sopenharmony_ci	[0x11] = "L2_INDX_LD_TAG",
14362306a36Sopenharmony_ci	[0x12] = "L2_INDX_ST_TAG",
14462306a36Sopenharmony_ci	[0x13] = "L2_INDX_ST_DATA",
14562306a36Sopenharmony_ci	[0x14] = "L2_INDX_ST_ECC",
14662306a36Sopenharmony_ci	[0x15] = "0x15",
14762306a36Sopenharmony_ci	[0x16] = "0x16",
14862306a36Sopenharmony_ci	[0x17] = "0x17",
14962306a36Sopenharmony_ci	[0x18] = "L2_FTCH_AND_LCK",
15062306a36Sopenharmony_ci	[0x19] = "L2_HIT_INV",
15162306a36Sopenharmony_ci	[0x1a] = "L2_HIT_WB_INV",
15262306a36Sopenharmony_ci	[0x1b] = "L2_HIT_WB",
15362306a36Sopenharmony_ci	[0x1c] = "0x1c",
15462306a36Sopenharmony_ci	[0x1d] = "0x1d",
15562306a36Sopenharmony_ci	[0x1e] = "0x1e",
15662306a36Sopenharmony_ci	[0x1f] = "0x1f"
15762306a36Sopenharmony_ci};
15862306a36Sopenharmony_ci
15962306a36Sopenharmony_cistatic char *cm2_causes[32] = {
16062306a36Sopenharmony_ci	"None", "GC_WR_ERR", "GC_RD_ERR", "COH_WR_ERR",
16162306a36Sopenharmony_ci	"COH_RD_ERR", "MMIO_WR_ERR", "MMIO_RD_ERR", "0x07",
16262306a36Sopenharmony_ci	"0x08", "0x09", "0x0a", "0x0b",
16362306a36Sopenharmony_ci	"0x0c", "0x0d", "0x0e", "0x0f",
16462306a36Sopenharmony_ci	"0x10", "INTVN_WR_ERR", "INTVN_RD_ERR", "0x13",
16562306a36Sopenharmony_ci	"0x14", "0x15", "0x16", "0x17",
16662306a36Sopenharmony_ci	"L2_RD_UNCORR", "L2_WR_UNCORR", "L2_CORR", "0x1b",
16762306a36Sopenharmony_ci	"0x1c", "0x1d", "0x1e", "0x1f"
16862306a36Sopenharmony_ci};
16962306a36Sopenharmony_ci
17062306a36Sopenharmony_cistatic char *cm3_causes[32] = {
17162306a36Sopenharmony_ci	"0x0", "MP_CORRECTABLE_ECC_ERR", "MP_REQUEST_DECODE_ERR",
17262306a36Sopenharmony_ci	"MP_UNCORRECTABLE_ECC_ERR", "MP_PARITY_ERR", "MP_COHERENCE_ERR",
17362306a36Sopenharmony_ci	"CMBIU_REQUEST_DECODE_ERR", "CMBIU_PARITY_ERR", "CMBIU_AXI_RESP_ERR",
17462306a36Sopenharmony_ci	"0x9", "RBI_BUS_ERR", "0xb", "0xc", "0xd", "0xe", "0xf", "0x10",
17562306a36Sopenharmony_ci	"0x11", "0x12", "0x13", "0x14", "0x15", "0x16", "0x17", "0x18",
17662306a36Sopenharmony_ci	"0x19", "0x1a", "0x1b", "0x1c", "0x1d", "0x1e", "0x1f"
17762306a36Sopenharmony_ci};
17862306a36Sopenharmony_ci
17962306a36Sopenharmony_cistatic DEFINE_PER_CPU_ALIGNED(spinlock_t, cm_core_lock);
18062306a36Sopenharmony_cistatic DEFINE_PER_CPU_ALIGNED(unsigned long, cm_core_lock_flags);
18162306a36Sopenharmony_ci
18262306a36Sopenharmony_ciphys_addr_t __mips_cm_phys_base(void)
18362306a36Sopenharmony_ci{
18462306a36Sopenharmony_ci	unsigned long cmgcr;
18562306a36Sopenharmony_ci
18662306a36Sopenharmony_ci	/* Check the CMGCRBase register is implemented */
18762306a36Sopenharmony_ci	if (!(read_c0_config() & MIPS_CONF_M))
18862306a36Sopenharmony_ci		return 0;
18962306a36Sopenharmony_ci
19062306a36Sopenharmony_ci	if (!(read_c0_config2() & MIPS_CONF_M))
19162306a36Sopenharmony_ci		return 0;
19262306a36Sopenharmony_ci
19362306a36Sopenharmony_ci	if (!(read_c0_config3() & MIPS_CONF3_CMGCR))
19462306a36Sopenharmony_ci		return 0;
19562306a36Sopenharmony_ci
19662306a36Sopenharmony_ci	/* Read the address from CMGCRBase */
19762306a36Sopenharmony_ci	cmgcr = read_c0_cmgcrbase();
19862306a36Sopenharmony_ci	return (cmgcr & MIPS_CMGCRF_BASE) << (36 - 32);
19962306a36Sopenharmony_ci}
20062306a36Sopenharmony_ci
20162306a36Sopenharmony_ciphys_addr_t mips_cm_phys_base(void)
20262306a36Sopenharmony_ci	__attribute__((weak, alias("__mips_cm_phys_base")));
20362306a36Sopenharmony_ci
20462306a36Sopenharmony_ciphys_addr_t __mips_cm_l2sync_phys_base(void)
20562306a36Sopenharmony_ci{
20662306a36Sopenharmony_ci	u32 base_reg;
20762306a36Sopenharmony_ci
20862306a36Sopenharmony_ci	/*
20962306a36Sopenharmony_ci	 * If the L2-only sync region is already enabled then leave it at it's
21062306a36Sopenharmony_ci	 * current location.
21162306a36Sopenharmony_ci	 */
21262306a36Sopenharmony_ci	base_reg = read_gcr_l2_only_sync_base();
21362306a36Sopenharmony_ci	if (base_reg & CM_GCR_L2_ONLY_SYNC_BASE_SYNCEN)
21462306a36Sopenharmony_ci		return base_reg & CM_GCR_L2_ONLY_SYNC_BASE_SYNCBASE;
21562306a36Sopenharmony_ci
21662306a36Sopenharmony_ci	/* Default to following the CM */
21762306a36Sopenharmony_ci	return mips_cm_phys_base() + MIPS_CM_GCR_SIZE;
21862306a36Sopenharmony_ci}
21962306a36Sopenharmony_ci
22062306a36Sopenharmony_ciphys_addr_t mips_cm_l2sync_phys_base(void)
22162306a36Sopenharmony_ci	__attribute__((weak, alias("__mips_cm_l2sync_phys_base")));
22262306a36Sopenharmony_ci
22362306a36Sopenharmony_cistatic void mips_cm_probe_l2sync(void)
22462306a36Sopenharmony_ci{
22562306a36Sopenharmony_ci	unsigned major_rev;
22662306a36Sopenharmony_ci	phys_addr_t addr;
22762306a36Sopenharmony_ci
22862306a36Sopenharmony_ci	/* L2-only sync was introduced with CM major revision 6 */
22962306a36Sopenharmony_ci	major_rev = FIELD_GET(CM_GCR_REV_MAJOR, read_gcr_rev());
23062306a36Sopenharmony_ci	if (major_rev < 6)
23162306a36Sopenharmony_ci		return;
23262306a36Sopenharmony_ci
23362306a36Sopenharmony_ci	/* Find a location for the L2 sync region */
23462306a36Sopenharmony_ci	addr = mips_cm_l2sync_phys_base();
23562306a36Sopenharmony_ci	BUG_ON((addr & CM_GCR_L2_ONLY_SYNC_BASE_SYNCBASE) != addr);
23662306a36Sopenharmony_ci	if (!addr)
23762306a36Sopenharmony_ci		return;
23862306a36Sopenharmony_ci
23962306a36Sopenharmony_ci	/* Set the region base address & enable it */
24062306a36Sopenharmony_ci	write_gcr_l2_only_sync_base(addr | CM_GCR_L2_ONLY_SYNC_BASE_SYNCEN);
24162306a36Sopenharmony_ci
24262306a36Sopenharmony_ci	/* Map the region */
24362306a36Sopenharmony_ci	mips_cm_l2sync_base = ioremap(addr, MIPS_CM_L2SYNC_SIZE);
24462306a36Sopenharmony_ci}
24562306a36Sopenharmony_ci
24662306a36Sopenharmony_ciint mips_cm_probe(void)
24762306a36Sopenharmony_ci{
24862306a36Sopenharmony_ci	phys_addr_t addr;
24962306a36Sopenharmony_ci	u32 base_reg;
25062306a36Sopenharmony_ci	unsigned cpu;
25162306a36Sopenharmony_ci
25262306a36Sopenharmony_ci	/*
25362306a36Sopenharmony_ci	 * No need to probe again if we have already been
25462306a36Sopenharmony_ci	 * here before.
25562306a36Sopenharmony_ci	 */
25662306a36Sopenharmony_ci	if (mips_gcr_base)
25762306a36Sopenharmony_ci		return 0;
25862306a36Sopenharmony_ci
25962306a36Sopenharmony_ci	addr = mips_cm_phys_base();
26062306a36Sopenharmony_ci	BUG_ON((addr & CM_GCR_BASE_GCRBASE) != addr);
26162306a36Sopenharmony_ci	if (!addr)
26262306a36Sopenharmony_ci		return -ENODEV;
26362306a36Sopenharmony_ci
26462306a36Sopenharmony_ci	mips_gcr_base = ioremap(addr, MIPS_CM_GCR_SIZE);
26562306a36Sopenharmony_ci	if (!mips_gcr_base)
26662306a36Sopenharmony_ci		return -ENXIO;
26762306a36Sopenharmony_ci
26862306a36Sopenharmony_ci	/* sanity check that we're looking at a CM */
26962306a36Sopenharmony_ci	base_reg = read_gcr_base();
27062306a36Sopenharmony_ci	if ((base_reg & CM_GCR_BASE_GCRBASE) != addr) {
27162306a36Sopenharmony_ci		pr_err("GCRs appear to have been moved (expected them at 0x%08lx)!\n",
27262306a36Sopenharmony_ci		       (unsigned long)addr);
27362306a36Sopenharmony_ci		iounmap(mips_gcr_base);
27462306a36Sopenharmony_ci		mips_gcr_base = NULL;
27562306a36Sopenharmony_ci		return -ENODEV;
27662306a36Sopenharmony_ci	}
27762306a36Sopenharmony_ci
27862306a36Sopenharmony_ci	/* set default target to memory */
27962306a36Sopenharmony_ci	change_gcr_base(CM_GCR_BASE_CMDEFTGT, CM_GCR_BASE_CMDEFTGT_MEM);
28062306a36Sopenharmony_ci
28162306a36Sopenharmony_ci	/* disable CM regions */
28262306a36Sopenharmony_ci	write_gcr_reg0_base(CM_GCR_REGn_BASE_BASEADDR);
28362306a36Sopenharmony_ci	write_gcr_reg0_mask(CM_GCR_REGn_MASK_ADDRMASK);
28462306a36Sopenharmony_ci	write_gcr_reg1_base(CM_GCR_REGn_BASE_BASEADDR);
28562306a36Sopenharmony_ci	write_gcr_reg1_mask(CM_GCR_REGn_MASK_ADDRMASK);
28662306a36Sopenharmony_ci	write_gcr_reg2_base(CM_GCR_REGn_BASE_BASEADDR);
28762306a36Sopenharmony_ci	write_gcr_reg2_mask(CM_GCR_REGn_MASK_ADDRMASK);
28862306a36Sopenharmony_ci	write_gcr_reg3_base(CM_GCR_REGn_BASE_BASEADDR);
28962306a36Sopenharmony_ci	write_gcr_reg3_mask(CM_GCR_REGn_MASK_ADDRMASK);
29062306a36Sopenharmony_ci
29162306a36Sopenharmony_ci	/* probe for an L2-only sync region */
29262306a36Sopenharmony_ci	mips_cm_probe_l2sync();
29362306a36Sopenharmony_ci
29462306a36Sopenharmony_ci	/* determine register width for this CM */
29562306a36Sopenharmony_ci	mips_cm_is64 = IS_ENABLED(CONFIG_64BIT) && (mips_cm_revision() >= CM_REV_CM3);
29662306a36Sopenharmony_ci
29762306a36Sopenharmony_ci	for_each_possible_cpu(cpu)
29862306a36Sopenharmony_ci		spin_lock_init(&per_cpu(cm_core_lock, cpu));
29962306a36Sopenharmony_ci
30062306a36Sopenharmony_ci	return 0;
30162306a36Sopenharmony_ci}
30262306a36Sopenharmony_ci
30362306a36Sopenharmony_civoid mips_cm_lock_other(unsigned int cluster, unsigned int core,
30462306a36Sopenharmony_ci			unsigned int vp, unsigned int block)
30562306a36Sopenharmony_ci{
30662306a36Sopenharmony_ci	unsigned int curr_core, cm_rev;
30762306a36Sopenharmony_ci	u32 val;
30862306a36Sopenharmony_ci
30962306a36Sopenharmony_ci	cm_rev = mips_cm_revision();
31062306a36Sopenharmony_ci	preempt_disable();
31162306a36Sopenharmony_ci
31262306a36Sopenharmony_ci	if (cm_rev >= CM_REV_CM3) {
31362306a36Sopenharmony_ci		val = FIELD_PREP(CM3_GCR_Cx_OTHER_CORE, core) |
31462306a36Sopenharmony_ci		      FIELD_PREP(CM3_GCR_Cx_OTHER_VP, vp);
31562306a36Sopenharmony_ci
31662306a36Sopenharmony_ci		if (cm_rev >= CM_REV_CM3_5) {
31762306a36Sopenharmony_ci			val |= CM_GCR_Cx_OTHER_CLUSTER_EN;
31862306a36Sopenharmony_ci			val |= FIELD_PREP(CM_GCR_Cx_OTHER_CLUSTER, cluster);
31962306a36Sopenharmony_ci			val |= FIELD_PREP(CM_GCR_Cx_OTHER_BLOCK, block);
32062306a36Sopenharmony_ci		} else {
32162306a36Sopenharmony_ci			WARN_ON(cluster != 0);
32262306a36Sopenharmony_ci			WARN_ON(block != CM_GCR_Cx_OTHER_BLOCK_LOCAL);
32362306a36Sopenharmony_ci		}
32462306a36Sopenharmony_ci
32562306a36Sopenharmony_ci		/*
32662306a36Sopenharmony_ci		 * We need to disable interrupts in SMP systems in order to
32762306a36Sopenharmony_ci		 * ensure that we don't interrupt the caller with code which
32862306a36Sopenharmony_ci		 * may modify the redirect register. We do so here in a
32962306a36Sopenharmony_ci		 * slightly obscure way by using a spin lock, since this has
33062306a36Sopenharmony_ci		 * the neat property of also catching any nested uses of
33162306a36Sopenharmony_ci		 * mips_cm_lock_other() leading to a deadlock or a nice warning
33262306a36Sopenharmony_ci		 * with lockdep enabled.
33362306a36Sopenharmony_ci		 */
33462306a36Sopenharmony_ci		spin_lock_irqsave(this_cpu_ptr(&cm_core_lock),
33562306a36Sopenharmony_ci				  *this_cpu_ptr(&cm_core_lock_flags));
33662306a36Sopenharmony_ci	} else {
33762306a36Sopenharmony_ci		WARN_ON(cluster != 0);
33862306a36Sopenharmony_ci		WARN_ON(block != CM_GCR_Cx_OTHER_BLOCK_LOCAL);
33962306a36Sopenharmony_ci
34062306a36Sopenharmony_ci		/*
34162306a36Sopenharmony_ci		 * We only have a GCR_CL_OTHER per core in systems with
34262306a36Sopenharmony_ci		 * CM 2.5 & older, so have to ensure other VP(E)s don't
34362306a36Sopenharmony_ci		 * race with us.
34462306a36Sopenharmony_ci		 */
34562306a36Sopenharmony_ci		curr_core = cpu_core(&current_cpu_data);
34662306a36Sopenharmony_ci		spin_lock_irqsave(&per_cpu(cm_core_lock, curr_core),
34762306a36Sopenharmony_ci				  per_cpu(cm_core_lock_flags, curr_core));
34862306a36Sopenharmony_ci
34962306a36Sopenharmony_ci		val = FIELD_PREP(CM_GCR_Cx_OTHER_CORENUM, core);
35062306a36Sopenharmony_ci	}
35162306a36Sopenharmony_ci
35262306a36Sopenharmony_ci	write_gcr_cl_other(val);
35362306a36Sopenharmony_ci
35462306a36Sopenharmony_ci	/*
35562306a36Sopenharmony_ci	 * Ensure the core-other region reflects the appropriate core &
35662306a36Sopenharmony_ci	 * VP before any accesses to it occur.
35762306a36Sopenharmony_ci	 */
35862306a36Sopenharmony_ci	mb();
35962306a36Sopenharmony_ci}
36062306a36Sopenharmony_ci
36162306a36Sopenharmony_civoid mips_cm_unlock_other(void)
36262306a36Sopenharmony_ci{
36362306a36Sopenharmony_ci	unsigned int curr_core;
36462306a36Sopenharmony_ci
36562306a36Sopenharmony_ci	if (mips_cm_revision() < CM_REV_CM3) {
36662306a36Sopenharmony_ci		curr_core = cpu_core(&current_cpu_data);
36762306a36Sopenharmony_ci		spin_unlock_irqrestore(&per_cpu(cm_core_lock, curr_core),
36862306a36Sopenharmony_ci				       per_cpu(cm_core_lock_flags, curr_core));
36962306a36Sopenharmony_ci	} else {
37062306a36Sopenharmony_ci		spin_unlock_irqrestore(this_cpu_ptr(&cm_core_lock),
37162306a36Sopenharmony_ci				       *this_cpu_ptr(&cm_core_lock_flags));
37262306a36Sopenharmony_ci	}
37362306a36Sopenharmony_ci
37462306a36Sopenharmony_ci	preempt_enable();
37562306a36Sopenharmony_ci}
37662306a36Sopenharmony_ci
37762306a36Sopenharmony_civoid mips_cm_error_report(void)
37862306a36Sopenharmony_ci{
37962306a36Sopenharmony_ci	u64 cm_error, cm_addr, cm_other;
38062306a36Sopenharmony_ci	unsigned long revision;
38162306a36Sopenharmony_ci	int ocause, cause;
38262306a36Sopenharmony_ci	char buf[256];
38362306a36Sopenharmony_ci
38462306a36Sopenharmony_ci	if (!mips_cm_present())
38562306a36Sopenharmony_ci		return;
38662306a36Sopenharmony_ci
38762306a36Sopenharmony_ci	revision = mips_cm_revision();
38862306a36Sopenharmony_ci	cm_error = read_gcr_error_cause();
38962306a36Sopenharmony_ci	cm_addr = read_gcr_error_addr();
39062306a36Sopenharmony_ci	cm_other = read_gcr_error_mult();
39162306a36Sopenharmony_ci
39262306a36Sopenharmony_ci	if (revision < CM_REV_CM3) { /* CM2 */
39362306a36Sopenharmony_ci		cause = FIELD_GET(CM_GCR_ERROR_CAUSE_ERRTYPE, cm_error);
39462306a36Sopenharmony_ci		ocause = FIELD_GET(CM_GCR_ERROR_MULT_ERR2ND, cm_other);
39562306a36Sopenharmony_ci
39662306a36Sopenharmony_ci		if (!cause)
39762306a36Sopenharmony_ci			return;
39862306a36Sopenharmony_ci
39962306a36Sopenharmony_ci		if (cause < 16) {
40062306a36Sopenharmony_ci			unsigned long cca_bits = (cm_error >> 15) & 7;
40162306a36Sopenharmony_ci			unsigned long tr_bits = (cm_error >> 12) & 7;
40262306a36Sopenharmony_ci			unsigned long cmd_bits = (cm_error >> 7) & 0x1f;
40362306a36Sopenharmony_ci			unsigned long stag_bits = (cm_error >> 3) & 15;
40462306a36Sopenharmony_ci			unsigned long sport_bits = (cm_error >> 0) & 7;
40562306a36Sopenharmony_ci
40662306a36Sopenharmony_ci			snprintf(buf, sizeof(buf),
40762306a36Sopenharmony_ci				 "CCA=%lu TR=%s MCmd=%s STag=%lu "
40862306a36Sopenharmony_ci				 "SPort=%lu\n", cca_bits, cm2_tr[tr_bits],
40962306a36Sopenharmony_ci				 cm2_cmd[cmd_bits], stag_bits, sport_bits);
41062306a36Sopenharmony_ci		} else if (cause < 24) {
41162306a36Sopenharmony_ci			/* glob state & sresp together */
41262306a36Sopenharmony_ci			unsigned long c3_bits = (cm_error >> 18) & 7;
41362306a36Sopenharmony_ci			unsigned long c2_bits = (cm_error >> 15) & 7;
41462306a36Sopenharmony_ci			unsigned long c1_bits = (cm_error >> 12) & 7;
41562306a36Sopenharmony_ci			unsigned long c0_bits = (cm_error >> 9) & 7;
41662306a36Sopenharmony_ci			unsigned long sc_bit = (cm_error >> 8) & 1;
41762306a36Sopenharmony_ci			unsigned long cmd_bits = (cm_error >> 3) & 0x1f;
41862306a36Sopenharmony_ci			unsigned long sport_bits = (cm_error >> 0) & 7;
41962306a36Sopenharmony_ci
42062306a36Sopenharmony_ci			snprintf(buf, sizeof(buf),
42162306a36Sopenharmony_ci				 "C3=%s C2=%s C1=%s C0=%s SC=%s "
42262306a36Sopenharmony_ci				 "MCmd=%s SPort=%lu\n",
42362306a36Sopenharmony_ci				 cm2_core[c3_bits], cm2_core[c2_bits],
42462306a36Sopenharmony_ci				 cm2_core[c1_bits], cm2_core[c0_bits],
42562306a36Sopenharmony_ci				 sc_bit ? "True" : "False",
42662306a36Sopenharmony_ci				 cm2_cmd[cmd_bits], sport_bits);
42762306a36Sopenharmony_ci		} else {
42862306a36Sopenharmony_ci			unsigned long muc_bit = (cm_error >> 23) & 1;
42962306a36Sopenharmony_ci			unsigned long ins_bits = (cm_error >> 18) & 0x1f;
43062306a36Sopenharmony_ci			unsigned long arr_bits = (cm_error >> 16) & 3;
43162306a36Sopenharmony_ci			unsigned long dw_bits = (cm_error >> 12) & 15;
43262306a36Sopenharmony_ci			unsigned long way_bits = (cm_error >> 9) & 7;
43362306a36Sopenharmony_ci			unsigned long mway_bit = (cm_error >> 8) & 1;
43462306a36Sopenharmony_ci			unsigned long syn_bits = (cm_error >> 0) & 0xFF;
43562306a36Sopenharmony_ci
43662306a36Sopenharmony_ci			snprintf(buf, sizeof(buf),
43762306a36Sopenharmony_ci				 "Type=%s%s Instr=%s DW=%lu Way=%lu "
43862306a36Sopenharmony_ci				 "MWay=%s Syndrome=0x%02lx",
43962306a36Sopenharmony_ci				 muc_bit ? "Multi-UC " : "",
44062306a36Sopenharmony_ci				 cm2_l2_type[arr_bits],
44162306a36Sopenharmony_ci				 cm2_l2_instr[ins_bits], dw_bits, way_bits,
44262306a36Sopenharmony_ci				 mway_bit ? "True" : "False", syn_bits);
44362306a36Sopenharmony_ci		}
44462306a36Sopenharmony_ci		pr_err("CM_ERROR=%08llx %s <%s>\n", cm_error,
44562306a36Sopenharmony_ci		       cm2_causes[cause], buf);
44662306a36Sopenharmony_ci		pr_err("CM_ADDR =%08llx\n", cm_addr);
44762306a36Sopenharmony_ci		pr_err("CM_OTHER=%08llx %s\n", cm_other, cm2_causes[ocause]);
44862306a36Sopenharmony_ci	} else { /* CM3 */
44962306a36Sopenharmony_ci		ulong core_id_bits, vp_id_bits, cmd_bits, cmd_group_bits;
45062306a36Sopenharmony_ci		ulong cm3_cca_bits, mcp_bits, cm3_tr_bits, sched_bit;
45162306a36Sopenharmony_ci
45262306a36Sopenharmony_ci		cause = FIELD_GET(CM3_GCR_ERROR_CAUSE_ERRTYPE, cm_error);
45362306a36Sopenharmony_ci		ocause = FIELD_GET(CM_GCR_ERROR_MULT_ERR2ND, cm_other);
45462306a36Sopenharmony_ci
45562306a36Sopenharmony_ci		if (!cause)
45662306a36Sopenharmony_ci			return;
45762306a36Sopenharmony_ci
45862306a36Sopenharmony_ci		/* Used by cause == {1,2,3} */
45962306a36Sopenharmony_ci		core_id_bits = (cm_error >> 22) & 0xf;
46062306a36Sopenharmony_ci		vp_id_bits = (cm_error >> 18) & 0xf;
46162306a36Sopenharmony_ci		cmd_bits = (cm_error >> 14) & 0xf;
46262306a36Sopenharmony_ci		cmd_group_bits = (cm_error >> 11) & 0xf;
46362306a36Sopenharmony_ci		cm3_cca_bits = (cm_error >> 8) & 7;
46462306a36Sopenharmony_ci		mcp_bits = (cm_error >> 5) & 0xf;
46562306a36Sopenharmony_ci		cm3_tr_bits = (cm_error >> 1) & 0xf;
46662306a36Sopenharmony_ci		sched_bit = cm_error & 0x1;
46762306a36Sopenharmony_ci
46862306a36Sopenharmony_ci		if (cause == 1 || cause == 3) { /* Tag ECC */
46962306a36Sopenharmony_ci			unsigned long tag_ecc = (cm_error >> 57) & 0x1;
47062306a36Sopenharmony_ci			unsigned long tag_way_bits = (cm_error >> 29) & 0xffff;
47162306a36Sopenharmony_ci			unsigned long dword_bits = (cm_error >> 49) & 0xff;
47262306a36Sopenharmony_ci			unsigned long data_way_bits = (cm_error >> 45) & 0xf;
47362306a36Sopenharmony_ci			unsigned long data_sets_bits = (cm_error >> 29) & 0xfff;
47462306a36Sopenharmony_ci			unsigned long bank_bit = (cm_error >> 28) & 0x1;
47562306a36Sopenharmony_ci			snprintf(buf, sizeof(buf),
47662306a36Sopenharmony_ci				 "%s ECC Error: Way=%lu (DWORD=%lu, Sets=%lu)"
47762306a36Sopenharmony_ci				 "Bank=%lu CoreID=%lu VPID=%lu Command=%s"
47862306a36Sopenharmony_ci				 "Command Group=%s CCA=%lu MCP=%d"
47962306a36Sopenharmony_ci				 "Transaction type=%s Scheduler=%lu\n",
48062306a36Sopenharmony_ci				 tag_ecc ? "TAG" : "DATA",
48162306a36Sopenharmony_ci				 tag_ecc ? (unsigned long)ffs(tag_way_bits) - 1 :
48262306a36Sopenharmony_ci				 data_way_bits, bank_bit, dword_bits,
48362306a36Sopenharmony_ci				 data_sets_bits,
48462306a36Sopenharmony_ci				 core_id_bits, vp_id_bits,
48562306a36Sopenharmony_ci				 cm3_cmd[cmd_bits],
48662306a36Sopenharmony_ci				 cm3_cmd_group[cmd_group_bits],
48762306a36Sopenharmony_ci				 cm3_cca_bits, 1 << mcp_bits,
48862306a36Sopenharmony_ci				 cm3_tr[cm3_tr_bits], sched_bit);
48962306a36Sopenharmony_ci		} else if (cause == 2) {
49062306a36Sopenharmony_ci			unsigned long data_error_type = (cm_error >> 41) & 0xfff;
49162306a36Sopenharmony_ci			unsigned long data_decode_cmd = (cm_error >> 37) & 0xf;
49262306a36Sopenharmony_ci			unsigned long data_decode_group = (cm_error >> 34) & 0x7;
49362306a36Sopenharmony_ci			unsigned long data_decode_destination_id = (cm_error >> 28) & 0x3f;
49462306a36Sopenharmony_ci
49562306a36Sopenharmony_ci			snprintf(buf, sizeof(buf),
49662306a36Sopenharmony_ci				 "Decode Request Error: Type=%lu, Command=%lu"
49762306a36Sopenharmony_ci				 "Command Group=%lu Destination ID=%lu"
49862306a36Sopenharmony_ci				 "CoreID=%lu VPID=%lu Command=%s"
49962306a36Sopenharmony_ci				 "Command Group=%s CCA=%lu MCP=%d"
50062306a36Sopenharmony_ci				 "Transaction type=%s Scheduler=%lu\n",
50162306a36Sopenharmony_ci				 data_error_type, data_decode_cmd,
50262306a36Sopenharmony_ci				 data_decode_group, data_decode_destination_id,
50362306a36Sopenharmony_ci				 core_id_bits, vp_id_bits,
50462306a36Sopenharmony_ci				 cm3_cmd[cmd_bits],
50562306a36Sopenharmony_ci				 cm3_cmd_group[cmd_group_bits],
50662306a36Sopenharmony_ci				 cm3_cca_bits, 1 << mcp_bits,
50762306a36Sopenharmony_ci				 cm3_tr[cm3_tr_bits], sched_bit);
50862306a36Sopenharmony_ci		} else {
50962306a36Sopenharmony_ci			buf[0] = 0;
51062306a36Sopenharmony_ci		}
51162306a36Sopenharmony_ci
51262306a36Sopenharmony_ci		pr_err("CM_ERROR=%llx %s <%s>\n", cm_error,
51362306a36Sopenharmony_ci		       cm3_causes[cause], buf);
51462306a36Sopenharmony_ci		pr_err("CM_ADDR =%llx\n", cm_addr);
51562306a36Sopenharmony_ci		pr_err("CM_OTHER=%llx %s\n", cm_other, cm3_causes[ocause]);
51662306a36Sopenharmony_ci	}
51762306a36Sopenharmony_ci
51862306a36Sopenharmony_ci	/* reprime cause register */
51962306a36Sopenharmony_ci	write_gcr_error_cause(cm_error);
52062306a36Sopenharmony_ci}
521