162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Copyright (C) 2006 Chris Dearman (chris@mips.com),
462306a36Sopenharmony_ci */
562306a36Sopenharmony_ci#include <linux/init.h>
662306a36Sopenharmony_ci#include <linux/kernel.h>
762306a36Sopenharmony_ci#include <linux/sched.h>
862306a36Sopenharmony_ci#include <linux/mm.h>
962306a36Sopenharmony_ci
1062306a36Sopenharmony_ci#include <asm/cpu-type.h>
1162306a36Sopenharmony_ci#include <asm/mipsregs.h>
1262306a36Sopenharmony_ci#include <asm/bcache.h>
1362306a36Sopenharmony_ci#include <asm/cacheops.h>
1462306a36Sopenharmony_ci#include <asm/page.h>
1562306a36Sopenharmony_ci#include <asm/mmu_context.h>
1662306a36Sopenharmony_ci#include <asm/r4kcache.h>
1762306a36Sopenharmony_ci#include <asm/mips-cps.h>
1862306a36Sopenharmony_ci#include <asm/bootinfo.h>
1962306a36Sopenharmony_ci
2062306a36Sopenharmony_ci/*
2162306a36Sopenharmony_ci * MIPS32/MIPS64 L2 cache handling
2262306a36Sopenharmony_ci */
2362306a36Sopenharmony_ci
2462306a36Sopenharmony_ci/*
2562306a36Sopenharmony_ci * Writeback and invalidate the secondary cache before DMA.
2662306a36Sopenharmony_ci */
2762306a36Sopenharmony_cistatic void mips_sc_wback_inv(unsigned long addr, unsigned long size)
2862306a36Sopenharmony_ci{
2962306a36Sopenharmony_ci	blast_scache_range(addr, addr + size);
3062306a36Sopenharmony_ci}
3162306a36Sopenharmony_ci
3262306a36Sopenharmony_ci/*
3362306a36Sopenharmony_ci * Invalidate the secondary cache before DMA.
3462306a36Sopenharmony_ci */
3562306a36Sopenharmony_cistatic void mips_sc_inv(unsigned long addr, unsigned long size)
3662306a36Sopenharmony_ci{
3762306a36Sopenharmony_ci	unsigned long lsize = cpu_scache_line_size();
3862306a36Sopenharmony_ci	unsigned long almask = ~(lsize - 1);
3962306a36Sopenharmony_ci
4062306a36Sopenharmony_ci	cache_op(Hit_Writeback_Inv_SD, addr & almask);
4162306a36Sopenharmony_ci	cache_op(Hit_Writeback_Inv_SD, (addr + size - 1) & almask);
4262306a36Sopenharmony_ci	blast_inv_scache_range(addr, addr + size);
4362306a36Sopenharmony_ci}
4462306a36Sopenharmony_ci
4562306a36Sopenharmony_cistatic void mips_sc_enable(void)
4662306a36Sopenharmony_ci{
4762306a36Sopenharmony_ci	/* L2 cache is permanently enabled */
4862306a36Sopenharmony_ci}
4962306a36Sopenharmony_ci
5062306a36Sopenharmony_cistatic void mips_sc_disable(void)
5162306a36Sopenharmony_ci{
5262306a36Sopenharmony_ci	/* L2 cache is permanently enabled */
5362306a36Sopenharmony_ci}
5462306a36Sopenharmony_ci
5562306a36Sopenharmony_cistatic void mips_sc_prefetch_enable(void)
5662306a36Sopenharmony_ci{
5762306a36Sopenharmony_ci	unsigned long pftctl;
5862306a36Sopenharmony_ci
5962306a36Sopenharmony_ci	if (mips_cm_revision() < CM_REV_CM2_5)
6062306a36Sopenharmony_ci		return;
6162306a36Sopenharmony_ci
6262306a36Sopenharmony_ci	/*
6362306a36Sopenharmony_ci	 * If there is one or more L2 prefetch unit present then enable
6462306a36Sopenharmony_ci	 * prefetching for both code & data, for all ports.
6562306a36Sopenharmony_ci	 */
6662306a36Sopenharmony_ci	pftctl = read_gcr_l2_pft_control();
6762306a36Sopenharmony_ci	if (pftctl & CM_GCR_L2_PFT_CONTROL_NPFT) {
6862306a36Sopenharmony_ci		pftctl &= ~CM_GCR_L2_PFT_CONTROL_PAGEMASK;
6962306a36Sopenharmony_ci		pftctl |= PAGE_MASK & CM_GCR_L2_PFT_CONTROL_PAGEMASK;
7062306a36Sopenharmony_ci		pftctl |= CM_GCR_L2_PFT_CONTROL_PFTEN;
7162306a36Sopenharmony_ci		write_gcr_l2_pft_control(pftctl);
7262306a36Sopenharmony_ci
7362306a36Sopenharmony_ci		set_gcr_l2_pft_control_b(CM_GCR_L2_PFT_CONTROL_B_PORTID |
7462306a36Sopenharmony_ci					 CM_GCR_L2_PFT_CONTROL_B_CEN);
7562306a36Sopenharmony_ci	}
7662306a36Sopenharmony_ci}
7762306a36Sopenharmony_ci
7862306a36Sopenharmony_cistatic void mips_sc_prefetch_disable(void)
7962306a36Sopenharmony_ci{
8062306a36Sopenharmony_ci	if (mips_cm_revision() < CM_REV_CM2_5)
8162306a36Sopenharmony_ci		return;
8262306a36Sopenharmony_ci
8362306a36Sopenharmony_ci	clear_gcr_l2_pft_control(CM_GCR_L2_PFT_CONTROL_PFTEN);
8462306a36Sopenharmony_ci	clear_gcr_l2_pft_control_b(CM_GCR_L2_PFT_CONTROL_B_PORTID |
8562306a36Sopenharmony_ci				   CM_GCR_L2_PFT_CONTROL_B_CEN);
8662306a36Sopenharmony_ci}
8762306a36Sopenharmony_ci
8862306a36Sopenharmony_cistatic bool mips_sc_prefetch_is_enabled(void)
8962306a36Sopenharmony_ci{
9062306a36Sopenharmony_ci	unsigned long pftctl;
9162306a36Sopenharmony_ci
9262306a36Sopenharmony_ci	if (mips_cm_revision() < CM_REV_CM2_5)
9362306a36Sopenharmony_ci		return false;
9462306a36Sopenharmony_ci
9562306a36Sopenharmony_ci	pftctl = read_gcr_l2_pft_control();
9662306a36Sopenharmony_ci	if (!(pftctl & CM_GCR_L2_PFT_CONTROL_NPFT))
9762306a36Sopenharmony_ci		return false;
9862306a36Sopenharmony_ci	return !!(pftctl & CM_GCR_L2_PFT_CONTROL_PFTEN);
9962306a36Sopenharmony_ci}
10062306a36Sopenharmony_ci
10162306a36Sopenharmony_cistatic struct bcache_ops mips_sc_ops = {
10262306a36Sopenharmony_ci	.bc_enable = mips_sc_enable,
10362306a36Sopenharmony_ci	.bc_disable = mips_sc_disable,
10462306a36Sopenharmony_ci	.bc_wback_inv = mips_sc_wback_inv,
10562306a36Sopenharmony_ci	.bc_inv = mips_sc_inv,
10662306a36Sopenharmony_ci	.bc_prefetch_enable = mips_sc_prefetch_enable,
10762306a36Sopenharmony_ci	.bc_prefetch_disable = mips_sc_prefetch_disable,
10862306a36Sopenharmony_ci	.bc_prefetch_is_enabled = mips_sc_prefetch_is_enabled,
10962306a36Sopenharmony_ci};
11062306a36Sopenharmony_ci
11162306a36Sopenharmony_ci/*
11262306a36Sopenharmony_ci * Check if the L2 cache controller is activated on a particular platform.
11362306a36Sopenharmony_ci * MTI's L2 controller and the L2 cache controller of Broadcom's BMIPS
11462306a36Sopenharmony_ci * cores both use c0_config2's bit 12 as "L2 Bypass" bit, that is the
11562306a36Sopenharmony_ci * cache being disabled.  However there is no guarantee for this to be
11662306a36Sopenharmony_ci * true on all platforms.  In an act of stupidity the spec defined bits
11762306a36Sopenharmony_ci * 12..15 as implementation defined so below function will eventually have
11862306a36Sopenharmony_ci * to be replaced by a platform specific probe.
11962306a36Sopenharmony_ci */
12062306a36Sopenharmony_cistatic inline int mips_sc_is_activated(struct cpuinfo_mips *c)
12162306a36Sopenharmony_ci{
12262306a36Sopenharmony_ci	unsigned int config2 = read_c0_config2();
12362306a36Sopenharmony_ci	unsigned int tmp;
12462306a36Sopenharmony_ci
12562306a36Sopenharmony_ci	/* Check the bypass bit (L2B) */
12662306a36Sopenharmony_ci	switch (current_cpu_type()) {
12762306a36Sopenharmony_ci	case CPU_34K:
12862306a36Sopenharmony_ci	case CPU_74K:
12962306a36Sopenharmony_ci	case CPU_1004K:
13062306a36Sopenharmony_ci	case CPU_1074K:
13162306a36Sopenharmony_ci	case CPU_INTERAPTIV:
13262306a36Sopenharmony_ci	case CPU_PROAPTIV:
13362306a36Sopenharmony_ci	case CPU_P5600:
13462306a36Sopenharmony_ci	case CPU_BMIPS5000:
13562306a36Sopenharmony_ci	case CPU_QEMU_GENERIC:
13662306a36Sopenharmony_ci	case CPU_P6600:
13762306a36Sopenharmony_ci		if (config2 & (1 << 12))
13862306a36Sopenharmony_ci			return 0;
13962306a36Sopenharmony_ci	}
14062306a36Sopenharmony_ci
14162306a36Sopenharmony_ci	tmp = (config2 >> 4) & 0x0f;
14262306a36Sopenharmony_ci	if (0 < tmp && tmp <= 7)
14362306a36Sopenharmony_ci		c->scache.linesz = 2 << tmp;
14462306a36Sopenharmony_ci	else
14562306a36Sopenharmony_ci		return 0;
14662306a36Sopenharmony_ci	return 1;
14762306a36Sopenharmony_ci}
14862306a36Sopenharmony_ci
14962306a36Sopenharmony_cistatic int mips_sc_probe_cm3(void)
15062306a36Sopenharmony_ci{
15162306a36Sopenharmony_ci	struct cpuinfo_mips *c = &current_cpu_data;
15262306a36Sopenharmony_ci	unsigned long cfg = read_gcr_l2_config();
15362306a36Sopenharmony_ci	unsigned long sets, line_sz, assoc;
15462306a36Sopenharmony_ci
15562306a36Sopenharmony_ci	if (cfg & CM_GCR_L2_CONFIG_BYPASS)
15662306a36Sopenharmony_ci		return 0;
15762306a36Sopenharmony_ci
15862306a36Sopenharmony_ci	sets = cfg & CM_GCR_L2_CONFIG_SET_SIZE;
15962306a36Sopenharmony_ci	sets >>= __ffs(CM_GCR_L2_CONFIG_SET_SIZE);
16062306a36Sopenharmony_ci	if (sets)
16162306a36Sopenharmony_ci		c->scache.sets = 64 << sets;
16262306a36Sopenharmony_ci
16362306a36Sopenharmony_ci	line_sz = cfg & CM_GCR_L2_CONFIG_LINE_SIZE;
16462306a36Sopenharmony_ci	line_sz >>= __ffs(CM_GCR_L2_CONFIG_LINE_SIZE);
16562306a36Sopenharmony_ci	if (line_sz)
16662306a36Sopenharmony_ci		c->scache.linesz = 2 << line_sz;
16762306a36Sopenharmony_ci
16862306a36Sopenharmony_ci	assoc = cfg & CM_GCR_L2_CONFIG_ASSOC;
16962306a36Sopenharmony_ci	assoc >>= __ffs(CM_GCR_L2_CONFIG_ASSOC);
17062306a36Sopenharmony_ci	c->scache.ways = assoc + 1;
17162306a36Sopenharmony_ci	c->scache.waysize = c->scache.sets * c->scache.linesz;
17262306a36Sopenharmony_ci	c->scache.waybit = __ffs(c->scache.waysize);
17362306a36Sopenharmony_ci
17462306a36Sopenharmony_ci	if (c->scache.linesz) {
17562306a36Sopenharmony_ci		c->scache.flags &= ~MIPS_CACHE_NOT_PRESENT;
17662306a36Sopenharmony_ci		c->options |= MIPS_CPU_INCLUSIVE_CACHES;
17762306a36Sopenharmony_ci		return 1;
17862306a36Sopenharmony_ci	}
17962306a36Sopenharmony_ci
18062306a36Sopenharmony_ci	return 0;
18162306a36Sopenharmony_ci}
18262306a36Sopenharmony_ci
18362306a36Sopenharmony_cistatic inline int mips_sc_probe(void)
18462306a36Sopenharmony_ci{
18562306a36Sopenharmony_ci	struct cpuinfo_mips *c = &current_cpu_data;
18662306a36Sopenharmony_ci	unsigned int config1, config2;
18762306a36Sopenharmony_ci	unsigned int tmp;
18862306a36Sopenharmony_ci
18962306a36Sopenharmony_ci	/* Mark as not present until probe completed */
19062306a36Sopenharmony_ci	c->scache.flags |= MIPS_CACHE_NOT_PRESENT;
19162306a36Sopenharmony_ci
19262306a36Sopenharmony_ci	if (mips_cm_revision() >= CM_REV_CM3)
19362306a36Sopenharmony_ci		return mips_sc_probe_cm3();
19462306a36Sopenharmony_ci
19562306a36Sopenharmony_ci	/* Ignore anything but MIPSxx processors */
19662306a36Sopenharmony_ci	if (!(c->isa_level & (MIPS_CPU_ISA_M32R1 | MIPS_CPU_ISA_M64R1 |
19762306a36Sopenharmony_ci			      MIPS_CPU_ISA_M32R2 | MIPS_CPU_ISA_M64R2 |
19862306a36Sopenharmony_ci			      MIPS_CPU_ISA_M32R5 | MIPS_CPU_ISA_M64R5 |
19962306a36Sopenharmony_ci			      MIPS_CPU_ISA_M32R6 | MIPS_CPU_ISA_M64R6)))
20062306a36Sopenharmony_ci		return 0;
20162306a36Sopenharmony_ci
20262306a36Sopenharmony_ci	/* Does this MIPS32/MIPS64 CPU have a config2 register? */
20362306a36Sopenharmony_ci	config1 = read_c0_config1();
20462306a36Sopenharmony_ci	if (!(config1 & MIPS_CONF_M))
20562306a36Sopenharmony_ci		return 0;
20662306a36Sopenharmony_ci
20762306a36Sopenharmony_ci	config2 = read_c0_config2();
20862306a36Sopenharmony_ci
20962306a36Sopenharmony_ci	if (!mips_sc_is_activated(c))
21062306a36Sopenharmony_ci		return 0;
21162306a36Sopenharmony_ci
21262306a36Sopenharmony_ci	tmp = (config2 >> 8) & 0x0f;
21362306a36Sopenharmony_ci	if (tmp <= 7)
21462306a36Sopenharmony_ci		c->scache.sets = 64 << tmp;
21562306a36Sopenharmony_ci	else
21662306a36Sopenharmony_ci		return 0;
21762306a36Sopenharmony_ci
21862306a36Sopenharmony_ci	tmp = (config2 >> 0) & 0x0f;
21962306a36Sopenharmony_ci	if (tmp <= 7)
22062306a36Sopenharmony_ci		c->scache.ways = tmp + 1;
22162306a36Sopenharmony_ci	else
22262306a36Sopenharmony_ci		return 0;
22362306a36Sopenharmony_ci
22462306a36Sopenharmony_ci	if (current_cpu_type() == CPU_XBURST) {
22562306a36Sopenharmony_ci		switch (mips_machtype) {
22662306a36Sopenharmony_ci		/*
22762306a36Sopenharmony_ci		 * According to config2 it would be 5-ways, but that is
22862306a36Sopenharmony_ci		 * contradicted by all documentation.
22962306a36Sopenharmony_ci		 */
23062306a36Sopenharmony_ci		case MACH_INGENIC_JZ4770:
23162306a36Sopenharmony_ci		case MACH_INGENIC_JZ4775:
23262306a36Sopenharmony_ci			c->scache.ways = 4;
23362306a36Sopenharmony_ci			break;
23462306a36Sopenharmony_ci
23562306a36Sopenharmony_ci		/*
23662306a36Sopenharmony_ci		 * According to config2 it would be 5-ways and 512-sets,
23762306a36Sopenharmony_ci		 * but that is contradicted by all documentation.
23862306a36Sopenharmony_ci		 */
23962306a36Sopenharmony_ci		case MACH_INGENIC_X1000:
24062306a36Sopenharmony_ci		case MACH_INGENIC_X1000E:
24162306a36Sopenharmony_ci			c->scache.sets = 256;
24262306a36Sopenharmony_ci			c->scache.ways = 4;
24362306a36Sopenharmony_ci			break;
24462306a36Sopenharmony_ci		}
24562306a36Sopenharmony_ci	}
24662306a36Sopenharmony_ci
24762306a36Sopenharmony_ci	c->scache.waysize = c->scache.sets * c->scache.linesz;
24862306a36Sopenharmony_ci	c->scache.waybit = __ffs(c->scache.waysize);
24962306a36Sopenharmony_ci
25062306a36Sopenharmony_ci	c->scache.flags &= ~MIPS_CACHE_NOT_PRESENT;
25162306a36Sopenharmony_ci
25262306a36Sopenharmony_ci	return 1;
25362306a36Sopenharmony_ci}
25462306a36Sopenharmony_ci
25562306a36Sopenharmony_ciint mips_sc_init(void)
25662306a36Sopenharmony_ci{
25762306a36Sopenharmony_ci	int found = mips_sc_probe();
25862306a36Sopenharmony_ci	if (found) {
25962306a36Sopenharmony_ci		mips_sc_enable();
26062306a36Sopenharmony_ci		mips_sc_prefetch_enable();
26162306a36Sopenharmony_ci		bcops = &mips_sc_ops;
26262306a36Sopenharmony_ci	}
26362306a36Sopenharmony_ci	return found;
26462306a36Sopenharmony_ci}
265