18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * Copyright (C) 2006 Chris Dearman (chris@mips.com),
48c2ecf20Sopenharmony_ci */
58c2ecf20Sopenharmony_ci#include <linux/init.h>
68c2ecf20Sopenharmony_ci#include <linux/kernel.h>
78c2ecf20Sopenharmony_ci#include <linux/sched.h>
88c2ecf20Sopenharmony_ci#include <linux/mm.h>
98c2ecf20Sopenharmony_ci
108c2ecf20Sopenharmony_ci#include <asm/cpu-type.h>
118c2ecf20Sopenharmony_ci#include <asm/mipsregs.h>
128c2ecf20Sopenharmony_ci#include <asm/bcache.h>
138c2ecf20Sopenharmony_ci#include <asm/cacheops.h>
148c2ecf20Sopenharmony_ci#include <asm/page.h>
158c2ecf20Sopenharmony_ci#include <asm/mmu_context.h>
168c2ecf20Sopenharmony_ci#include <asm/r4kcache.h>
178c2ecf20Sopenharmony_ci#include <asm/mips-cps.h>
188c2ecf20Sopenharmony_ci#include <asm/bootinfo.h>
198c2ecf20Sopenharmony_ci
208c2ecf20Sopenharmony_ci/*
218c2ecf20Sopenharmony_ci * MIPS32/MIPS64 L2 cache handling
228c2ecf20Sopenharmony_ci */
238c2ecf20Sopenharmony_ci
248c2ecf20Sopenharmony_ci/*
258c2ecf20Sopenharmony_ci * Writeback and invalidate the secondary cache before DMA.
268c2ecf20Sopenharmony_ci */
278c2ecf20Sopenharmony_cistatic void mips_sc_wback_inv(unsigned long addr, unsigned long size)
288c2ecf20Sopenharmony_ci{
298c2ecf20Sopenharmony_ci	blast_scache_range(addr, addr + size);
308c2ecf20Sopenharmony_ci}
318c2ecf20Sopenharmony_ci
328c2ecf20Sopenharmony_ci/*
338c2ecf20Sopenharmony_ci * Invalidate the secondary cache before DMA.
348c2ecf20Sopenharmony_ci */
358c2ecf20Sopenharmony_cistatic void mips_sc_inv(unsigned long addr, unsigned long size)
368c2ecf20Sopenharmony_ci{
378c2ecf20Sopenharmony_ci	unsigned long lsize = cpu_scache_line_size();
388c2ecf20Sopenharmony_ci	unsigned long almask = ~(lsize - 1);
398c2ecf20Sopenharmony_ci
408c2ecf20Sopenharmony_ci	cache_op(Hit_Writeback_Inv_SD, addr & almask);
418c2ecf20Sopenharmony_ci	cache_op(Hit_Writeback_Inv_SD, (addr + size - 1) & almask);
428c2ecf20Sopenharmony_ci	blast_inv_scache_range(addr, addr + size);
438c2ecf20Sopenharmony_ci}
448c2ecf20Sopenharmony_ci
458c2ecf20Sopenharmony_cistatic void mips_sc_enable(void)
468c2ecf20Sopenharmony_ci{
478c2ecf20Sopenharmony_ci	/* L2 cache is permanently enabled */
488c2ecf20Sopenharmony_ci}
498c2ecf20Sopenharmony_ci
508c2ecf20Sopenharmony_cistatic void mips_sc_disable(void)
518c2ecf20Sopenharmony_ci{
528c2ecf20Sopenharmony_ci	/* L2 cache is permanently enabled */
538c2ecf20Sopenharmony_ci}
548c2ecf20Sopenharmony_ci
558c2ecf20Sopenharmony_cistatic void mips_sc_prefetch_enable(void)
568c2ecf20Sopenharmony_ci{
578c2ecf20Sopenharmony_ci	unsigned long pftctl;
588c2ecf20Sopenharmony_ci
598c2ecf20Sopenharmony_ci	if (mips_cm_revision() < CM_REV_CM2_5)
608c2ecf20Sopenharmony_ci		return;
618c2ecf20Sopenharmony_ci
628c2ecf20Sopenharmony_ci	/*
638c2ecf20Sopenharmony_ci	 * If there is one or more L2 prefetch unit present then enable
648c2ecf20Sopenharmony_ci	 * prefetching for both code & data, for all ports.
658c2ecf20Sopenharmony_ci	 */
668c2ecf20Sopenharmony_ci	pftctl = read_gcr_l2_pft_control();
678c2ecf20Sopenharmony_ci	if (pftctl & CM_GCR_L2_PFT_CONTROL_NPFT) {
688c2ecf20Sopenharmony_ci		pftctl &= ~CM_GCR_L2_PFT_CONTROL_PAGEMASK;
698c2ecf20Sopenharmony_ci		pftctl |= PAGE_MASK & CM_GCR_L2_PFT_CONTROL_PAGEMASK;
708c2ecf20Sopenharmony_ci		pftctl |= CM_GCR_L2_PFT_CONTROL_PFTEN;
718c2ecf20Sopenharmony_ci		write_gcr_l2_pft_control(pftctl);
728c2ecf20Sopenharmony_ci
738c2ecf20Sopenharmony_ci		set_gcr_l2_pft_control_b(CM_GCR_L2_PFT_CONTROL_B_PORTID |
748c2ecf20Sopenharmony_ci					 CM_GCR_L2_PFT_CONTROL_B_CEN);
758c2ecf20Sopenharmony_ci	}
768c2ecf20Sopenharmony_ci}
778c2ecf20Sopenharmony_ci
788c2ecf20Sopenharmony_cistatic void mips_sc_prefetch_disable(void)
798c2ecf20Sopenharmony_ci{
808c2ecf20Sopenharmony_ci	if (mips_cm_revision() < CM_REV_CM2_5)
818c2ecf20Sopenharmony_ci		return;
828c2ecf20Sopenharmony_ci
838c2ecf20Sopenharmony_ci	clear_gcr_l2_pft_control(CM_GCR_L2_PFT_CONTROL_PFTEN);
848c2ecf20Sopenharmony_ci	clear_gcr_l2_pft_control_b(CM_GCR_L2_PFT_CONTROL_B_PORTID |
858c2ecf20Sopenharmony_ci				   CM_GCR_L2_PFT_CONTROL_B_CEN);
868c2ecf20Sopenharmony_ci}
878c2ecf20Sopenharmony_ci
888c2ecf20Sopenharmony_cistatic bool mips_sc_prefetch_is_enabled(void)
898c2ecf20Sopenharmony_ci{
908c2ecf20Sopenharmony_ci	unsigned long pftctl;
918c2ecf20Sopenharmony_ci
928c2ecf20Sopenharmony_ci	if (mips_cm_revision() < CM_REV_CM2_5)
938c2ecf20Sopenharmony_ci		return false;
948c2ecf20Sopenharmony_ci
958c2ecf20Sopenharmony_ci	pftctl = read_gcr_l2_pft_control();
968c2ecf20Sopenharmony_ci	if (!(pftctl & CM_GCR_L2_PFT_CONTROL_NPFT))
978c2ecf20Sopenharmony_ci		return false;
988c2ecf20Sopenharmony_ci	return !!(pftctl & CM_GCR_L2_PFT_CONTROL_PFTEN);
998c2ecf20Sopenharmony_ci}
1008c2ecf20Sopenharmony_ci
1018c2ecf20Sopenharmony_cistatic struct bcache_ops mips_sc_ops = {
1028c2ecf20Sopenharmony_ci	.bc_enable = mips_sc_enable,
1038c2ecf20Sopenharmony_ci	.bc_disable = mips_sc_disable,
1048c2ecf20Sopenharmony_ci	.bc_wback_inv = mips_sc_wback_inv,
1058c2ecf20Sopenharmony_ci	.bc_inv = mips_sc_inv,
1068c2ecf20Sopenharmony_ci	.bc_prefetch_enable = mips_sc_prefetch_enable,
1078c2ecf20Sopenharmony_ci	.bc_prefetch_disable = mips_sc_prefetch_disable,
1088c2ecf20Sopenharmony_ci	.bc_prefetch_is_enabled = mips_sc_prefetch_is_enabled,
1098c2ecf20Sopenharmony_ci};
1108c2ecf20Sopenharmony_ci
1118c2ecf20Sopenharmony_ci/*
1128c2ecf20Sopenharmony_ci * Check if the L2 cache controller is activated on a particular platform.
1138c2ecf20Sopenharmony_ci * MTI's L2 controller and the L2 cache controller of Broadcom's BMIPS
1148c2ecf20Sopenharmony_ci * cores both use c0_config2's bit 12 as "L2 Bypass" bit, that is the
1158c2ecf20Sopenharmony_ci * cache being disabled.  However there is no guarantee for this to be
1168c2ecf20Sopenharmony_ci * true on all platforms.  In an act of stupidity the spec defined bits
1178c2ecf20Sopenharmony_ci * 12..15 as implementation defined so below function will eventually have
1188c2ecf20Sopenharmony_ci * to be replaced by a platform specific probe.
1198c2ecf20Sopenharmony_ci */
1208c2ecf20Sopenharmony_cistatic inline int mips_sc_is_activated(struct cpuinfo_mips *c)
1218c2ecf20Sopenharmony_ci{
1228c2ecf20Sopenharmony_ci	unsigned int config2 = read_c0_config2();
1238c2ecf20Sopenharmony_ci	unsigned int tmp;
1248c2ecf20Sopenharmony_ci
1258c2ecf20Sopenharmony_ci	/* Check the bypass bit (L2B) */
1268c2ecf20Sopenharmony_ci	switch (current_cpu_type()) {
1278c2ecf20Sopenharmony_ci	case CPU_34K:
1288c2ecf20Sopenharmony_ci	case CPU_74K:
1298c2ecf20Sopenharmony_ci	case CPU_1004K:
1308c2ecf20Sopenharmony_ci	case CPU_1074K:
1318c2ecf20Sopenharmony_ci	case CPU_INTERAPTIV:
1328c2ecf20Sopenharmony_ci	case CPU_PROAPTIV:
1338c2ecf20Sopenharmony_ci	case CPU_P5600:
1348c2ecf20Sopenharmony_ci	case CPU_BMIPS5000:
1358c2ecf20Sopenharmony_ci	case CPU_QEMU_GENERIC:
1368c2ecf20Sopenharmony_ci	case CPU_P6600:
1378c2ecf20Sopenharmony_ci		if (config2 & (1 << 12))
1388c2ecf20Sopenharmony_ci			return 0;
1398c2ecf20Sopenharmony_ci	}
1408c2ecf20Sopenharmony_ci
1418c2ecf20Sopenharmony_ci	tmp = (config2 >> 4) & 0x0f;
1428c2ecf20Sopenharmony_ci	if (0 < tmp && tmp <= 7)
1438c2ecf20Sopenharmony_ci		c->scache.linesz = 2 << tmp;
1448c2ecf20Sopenharmony_ci	else
1458c2ecf20Sopenharmony_ci		return 0;
1468c2ecf20Sopenharmony_ci	return 1;
1478c2ecf20Sopenharmony_ci}
1488c2ecf20Sopenharmony_ci
1498c2ecf20Sopenharmony_cistatic int mips_sc_probe_cm3(void)
1508c2ecf20Sopenharmony_ci{
1518c2ecf20Sopenharmony_ci	struct cpuinfo_mips *c = &current_cpu_data;
1528c2ecf20Sopenharmony_ci	unsigned long cfg = read_gcr_l2_config();
1538c2ecf20Sopenharmony_ci	unsigned long sets, line_sz, assoc;
1548c2ecf20Sopenharmony_ci
1558c2ecf20Sopenharmony_ci	if (cfg & CM_GCR_L2_CONFIG_BYPASS)
1568c2ecf20Sopenharmony_ci		return 0;
1578c2ecf20Sopenharmony_ci
1588c2ecf20Sopenharmony_ci	sets = cfg & CM_GCR_L2_CONFIG_SET_SIZE;
1598c2ecf20Sopenharmony_ci	sets >>= __ffs(CM_GCR_L2_CONFIG_SET_SIZE);
1608c2ecf20Sopenharmony_ci	if (sets)
1618c2ecf20Sopenharmony_ci		c->scache.sets = 64 << sets;
1628c2ecf20Sopenharmony_ci
1638c2ecf20Sopenharmony_ci	line_sz = cfg & CM_GCR_L2_CONFIG_LINE_SIZE;
1648c2ecf20Sopenharmony_ci	line_sz >>= __ffs(CM_GCR_L2_CONFIG_LINE_SIZE);
1658c2ecf20Sopenharmony_ci	if (line_sz)
1668c2ecf20Sopenharmony_ci		c->scache.linesz = 2 << line_sz;
1678c2ecf20Sopenharmony_ci
1688c2ecf20Sopenharmony_ci	assoc = cfg & CM_GCR_L2_CONFIG_ASSOC;
1698c2ecf20Sopenharmony_ci	assoc >>= __ffs(CM_GCR_L2_CONFIG_ASSOC);
1708c2ecf20Sopenharmony_ci	c->scache.ways = assoc + 1;
1718c2ecf20Sopenharmony_ci	c->scache.waysize = c->scache.sets * c->scache.linesz;
1728c2ecf20Sopenharmony_ci	c->scache.waybit = __ffs(c->scache.waysize);
1738c2ecf20Sopenharmony_ci
1748c2ecf20Sopenharmony_ci	if (c->scache.linesz) {
1758c2ecf20Sopenharmony_ci		c->scache.flags &= ~MIPS_CACHE_NOT_PRESENT;
1768c2ecf20Sopenharmony_ci		c->options |= MIPS_CPU_INCLUSIVE_CACHES;
1778c2ecf20Sopenharmony_ci		return 1;
1788c2ecf20Sopenharmony_ci	}
1798c2ecf20Sopenharmony_ci
1808c2ecf20Sopenharmony_ci	return 0;
1818c2ecf20Sopenharmony_ci}
1828c2ecf20Sopenharmony_ci
1838c2ecf20Sopenharmony_cistatic inline int mips_sc_probe(void)
1848c2ecf20Sopenharmony_ci{
1858c2ecf20Sopenharmony_ci	struct cpuinfo_mips *c = &current_cpu_data;
1868c2ecf20Sopenharmony_ci	unsigned int config1, config2;
1878c2ecf20Sopenharmony_ci	unsigned int tmp;
1888c2ecf20Sopenharmony_ci
1898c2ecf20Sopenharmony_ci	/* Mark as not present until probe completed */
1908c2ecf20Sopenharmony_ci	c->scache.flags |= MIPS_CACHE_NOT_PRESENT;
1918c2ecf20Sopenharmony_ci
1928c2ecf20Sopenharmony_ci	if (mips_cm_revision() >= CM_REV_CM3)
1938c2ecf20Sopenharmony_ci		return mips_sc_probe_cm3();
1948c2ecf20Sopenharmony_ci
1958c2ecf20Sopenharmony_ci	/* Ignore anything but MIPSxx processors */
1968c2ecf20Sopenharmony_ci	if (!(c->isa_level & (MIPS_CPU_ISA_M32R1 | MIPS_CPU_ISA_M64R1 |
1978c2ecf20Sopenharmony_ci			      MIPS_CPU_ISA_M32R2 | MIPS_CPU_ISA_M64R2 |
1988c2ecf20Sopenharmony_ci			      MIPS_CPU_ISA_M32R5 | MIPS_CPU_ISA_M64R5 |
1998c2ecf20Sopenharmony_ci			      MIPS_CPU_ISA_M32R6 | MIPS_CPU_ISA_M64R6)))
2008c2ecf20Sopenharmony_ci		return 0;
2018c2ecf20Sopenharmony_ci
2028c2ecf20Sopenharmony_ci	/* Does this MIPS32/MIPS64 CPU have a config2 register? */
2038c2ecf20Sopenharmony_ci	config1 = read_c0_config1();
2048c2ecf20Sopenharmony_ci	if (!(config1 & MIPS_CONF_M))
2058c2ecf20Sopenharmony_ci		return 0;
2068c2ecf20Sopenharmony_ci
2078c2ecf20Sopenharmony_ci	config2 = read_c0_config2();
2088c2ecf20Sopenharmony_ci
2098c2ecf20Sopenharmony_ci	if (!mips_sc_is_activated(c))
2108c2ecf20Sopenharmony_ci		return 0;
2118c2ecf20Sopenharmony_ci
2128c2ecf20Sopenharmony_ci	tmp = (config2 >> 8) & 0x0f;
2138c2ecf20Sopenharmony_ci	if (tmp <= 7)
2148c2ecf20Sopenharmony_ci		c->scache.sets = 64 << tmp;
2158c2ecf20Sopenharmony_ci	else
2168c2ecf20Sopenharmony_ci		return 0;
2178c2ecf20Sopenharmony_ci
2188c2ecf20Sopenharmony_ci	tmp = (config2 >> 0) & 0x0f;
2198c2ecf20Sopenharmony_ci	if (tmp <= 7)
2208c2ecf20Sopenharmony_ci		c->scache.ways = tmp + 1;
2218c2ecf20Sopenharmony_ci	else
2228c2ecf20Sopenharmony_ci		return 0;
2238c2ecf20Sopenharmony_ci
2248c2ecf20Sopenharmony_ci	if (current_cpu_type() == CPU_XBURST) {
2258c2ecf20Sopenharmony_ci		switch (mips_machtype) {
2268c2ecf20Sopenharmony_ci		/*
2278c2ecf20Sopenharmony_ci		 * According to config2 it would be 5-ways, but that is
2288c2ecf20Sopenharmony_ci		 * contradicted by all documentation.
2298c2ecf20Sopenharmony_ci		 */
2308c2ecf20Sopenharmony_ci		case MACH_INGENIC_JZ4770:
2318c2ecf20Sopenharmony_ci		case MACH_INGENIC_JZ4775:
2328c2ecf20Sopenharmony_ci			c->scache.ways = 4;
2338c2ecf20Sopenharmony_ci			break;
2348c2ecf20Sopenharmony_ci
2358c2ecf20Sopenharmony_ci		/*
2368c2ecf20Sopenharmony_ci		 * According to config2 it would be 5-ways and 512-sets,
2378c2ecf20Sopenharmony_ci		 * but that is contradicted by all documentation.
2388c2ecf20Sopenharmony_ci		 */
2398c2ecf20Sopenharmony_ci		case MACH_INGENIC_X1000:
2408c2ecf20Sopenharmony_ci		case MACH_INGENIC_X1000E:
2418c2ecf20Sopenharmony_ci			c->scache.sets = 256;
2428c2ecf20Sopenharmony_ci			c->scache.ways = 4;
2438c2ecf20Sopenharmony_ci			break;
2448c2ecf20Sopenharmony_ci		}
2458c2ecf20Sopenharmony_ci	}
2468c2ecf20Sopenharmony_ci
2478c2ecf20Sopenharmony_ci	c->scache.waysize = c->scache.sets * c->scache.linesz;
2488c2ecf20Sopenharmony_ci	c->scache.waybit = __ffs(c->scache.waysize);
2498c2ecf20Sopenharmony_ci
2508c2ecf20Sopenharmony_ci	c->scache.flags &= ~MIPS_CACHE_NOT_PRESENT;
2518c2ecf20Sopenharmony_ci
2528c2ecf20Sopenharmony_ci	return 1;
2538c2ecf20Sopenharmony_ci}
2548c2ecf20Sopenharmony_ci
2558c2ecf20Sopenharmony_ciint mips_sc_init(void)
2568c2ecf20Sopenharmony_ci{
2578c2ecf20Sopenharmony_ci	int found = mips_sc_probe();
2588c2ecf20Sopenharmony_ci	if (found) {
2598c2ecf20Sopenharmony_ci		mips_sc_enable();
2608c2ecf20Sopenharmony_ci		mips_sc_prefetch_enable();
2618c2ecf20Sopenharmony_ci		bcops = &mips_sc_ops;
2628c2ecf20Sopenharmony_ci	}
2638c2ecf20Sopenharmony_ci	return found;
2648c2ecf20Sopenharmony_ci}
265