18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * sc-rm7k.c: RM7000 cache management functions.
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * Copyright (C) 1997, 2001, 2003, 2004 Ralf Baechle (ralf@linux-mips.org)
68c2ecf20Sopenharmony_ci */
78c2ecf20Sopenharmony_ci
88c2ecf20Sopenharmony_ci#undef DEBUG
98c2ecf20Sopenharmony_ci
108c2ecf20Sopenharmony_ci#include <linux/kernel.h>
118c2ecf20Sopenharmony_ci#include <linux/mm.h>
128c2ecf20Sopenharmony_ci#include <linux/bitops.h>
138c2ecf20Sopenharmony_ci
148c2ecf20Sopenharmony_ci#include <asm/addrspace.h>
158c2ecf20Sopenharmony_ci#include <asm/bcache.h>
168c2ecf20Sopenharmony_ci#include <asm/cacheops.h>
178c2ecf20Sopenharmony_ci#include <asm/mipsregs.h>
188c2ecf20Sopenharmony_ci#include <asm/processor.h>
198c2ecf20Sopenharmony_ci#include <asm/sections.h>
208c2ecf20Sopenharmony_ci#include <asm/cacheflush.h> /* for run_uncached() */
218c2ecf20Sopenharmony_ci
228c2ecf20Sopenharmony_ci/* Primary cache parameters. */
238c2ecf20Sopenharmony_ci#define sc_lsize	32
248c2ecf20Sopenharmony_ci#define tc_pagesize	(32*128)
258c2ecf20Sopenharmony_ci
268c2ecf20Sopenharmony_ci/* Secondary cache parameters. */
278c2ecf20Sopenharmony_ci#define scache_size	(256*1024)	/* Fixed to 256KiB on RM7000 */
288c2ecf20Sopenharmony_ci
298c2ecf20Sopenharmony_ci/* Tertiary cache parameters */
308c2ecf20Sopenharmony_ci#define tc_lsize	32
318c2ecf20Sopenharmony_ci
328c2ecf20Sopenharmony_ciextern unsigned long icache_way_size, dcache_way_size;
338c2ecf20Sopenharmony_cistatic unsigned long tcache_size;
348c2ecf20Sopenharmony_ci
358c2ecf20Sopenharmony_ci#include <asm/r4kcache.h>
368c2ecf20Sopenharmony_ci
378c2ecf20Sopenharmony_cistatic int rm7k_tcache_init;
388c2ecf20Sopenharmony_ci
398c2ecf20Sopenharmony_ci/*
408c2ecf20Sopenharmony_ci * Writeback and invalidate the primary cache dcache before DMA.
418c2ecf20Sopenharmony_ci * (XXX These need to be fixed ...)
428c2ecf20Sopenharmony_ci */
438c2ecf20Sopenharmony_cistatic void rm7k_sc_wback_inv(unsigned long addr, unsigned long size)
448c2ecf20Sopenharmony_ci{
458c2ecf20Sopenharmony_ci	unsigned long end, a;
468c2ecf20Sopenharmony_ci
478c2ecf20Sopenharmony_ci	pr_debug("rm7k_sc_wback_inv[%08lx,%08lx]", addr, size);
488c2ecf20Sopenharmony_ci
498c2ecf20Sopenharmony_ci	/* Catch bad driver code */
508c2ecf20Sopenharmony_ci	BUG_ON(size == 0);
518c2ecf20Sopenharmony_ci
528c2ecf20Sopenharmony_ci	blast_scache_range(addr, addr + size);
538c2ecf20Sopenharmony_ci
548c2ecf20Sopenharmony_ci	if (!rm7k_tcache_init)
558c2ecf20Sopenharmony_ci		return;
568c2ecf20Sopenharmony_ci
578c2ecf20Sopenharmony_ci	a = addr & ~(tc_pagesize - 1);
588c2ecf20Sopenharmony_ci	end = (addr + size - 1) & ~(tc_pagesize - 1);
598c2ecf20Sopenharmony_ci	while(1) {
608c2ecf20Sopenharmony_ci		invalidate_tcache_page(a);	/* Page_Invalidate_T */
618c2ecf20Sopenharmony_ci		if (a == end)
628c2ecf20Sopenharmony_ci			break;
638c2ecf20Sopenharmony_ci		a += tc_pagesize;
648c2ecf20Sopenharmony_ci	}
658c2ecf20Sopenharmony_ci}
668c2ecf20Sopenharmony_ci
678c2ecf20Sopenharmony_cistatic void rm7k_sc_inv(unsigned long addr, unsigned long size)
688c2ecf20Sopenharmony_ci{
698c2ecf20Sopenharmony_ci	unsigned long end, a;
708c2ecf20Sopenharmony_ci
718c2ecf20Sopenharmony_ci	pr_debug("rm7k_sc_inv[%08lx,%08lx]", addr, size);
728c2ecf20Sopenharmony_ci
738c2ecf20Sopenharmony_ci	/* Catch bad driver code */
748c2ecf20Sopenharmony_ci	BUG_ON(size == 0);
758c2ecf20Sopenharmony_ci
768c2ecf20Sopenharmony_ci	blast_inv_scache_range(addr, addr + size);
778c2ecf20Sopenharmony_ci
788c2ecf20Sopenharmony_ci	if (!rm7k_tcache_init)
798c2ecf20Sopenharmony_ci		return;
808c2ecf20Sopenharmony_ci
818c2ecf20Sopenharmony_ci	a = addr & ~(tc_pagesize - 1);
828c2ecf20Sopenharmony_ci	end = (addr + size - 1) & ~(tc_pagesize - 1);
838c2ecf20Sopenharmony_ci	while(1) {
848c2ecf20Sopenharmony_ci		invalidate_tcache_page(a);	/* Page_Invalidate_T */
858c2ecf20Sopenharmony_ci		if (a == end)
868c2ecf20Sopenharmony_ci			break;
878c2ecf20Sopenharmony_ci		a += tc_pagesize;
888c2ecf20Sopenharmony_ci	}
898c2ecf20Sopenharmony_ci}
908c2ecf20Sopenharmony_ci
918c2ecf20Sopenharmony_cistatic void blast_rm7k_tcache(void)
928c2ecf20Sopenharmony_ci{
938c2ecf20Sopenharmony_ci	unsigned long start = CKSEG0ADDR(0);
948c2ecf20Sopenharmony_ci	unsigned long end = start + tcache_size;
958c2ecf20Sopenharmony_ci
968c2ecf20Sopenharmony_ci	write_c0_taglo(0);
978c2ecf20Sopenharmony_ci
988c2ecf20Sopenharmony_ci	while (start < end) {
998c2ecf20Sopenharmony_ci		cache_op(Page_Invalidate_T, start);
1008c2ecf20Sopenharmony_ci		start += tc_pagesize;
1018c2ecf20Sopenharmony_ci	}
1028c2ecf20Sopenharmony_ci}
1038c2ecf20Sopenharmony_ci
1048c2ecf20Sopenharmony_ci/*
1058c2ecf20Sopenharmony_ci * This function is executed in uncached address space.
1068c2ecf20Sopenharmony_ci */
1078c2ecf20Sopenharmony_cistatic void __rm7k_tc_enable(void)
1088c2ecf20Sopenharmony_ci{
1098c2ecf20Sopenharmony_ci	int i;
1108c2ecf20Sopenharmony_ci
1118c2ecf20Sopenharmony_ci	set_c0_config(RM7K_CONF_TE);
1128c2ecf20Sopenharmony_ci
1138c2ecf20Sopenharmony_ci	write_c0_taglo(0);
1148c2ecf20Sopenharmony_ci	write_c0_taghi(0);
1158c2ecf20Sopenharmony_ci
1168c2ecf20Sopenharmony_ci	for (i = 0; i < tcache_size; i += tc_lsize)
1178c2ecf20Sopenharmony_ci		cache_op(Index_Store_Tag_T, CKSEG0ADDR(i));
1188c2ecf20Sopenharmony_ci}
1198c2ecf20Sopenharmony_ci
1208c2ecf20Sopenharmony_cistatic void rm7k_tc_enable(void)
1218c2ecf20Sopenharmony_ci{
1228c2ecf20Sopenharmony_ci	if (read_c0_config() & RM7K_CONF_TE)
1238c2ecf20Sopenharmony_ci		return;
1248c2ecf20Sopenharmony_ci
1258c2ecf20Sopenharmony_ci	BUG_ON(tcache_size == 0);
1268c2ecf20Sopenharmony_ci
1278c2ecf20Sopenharmony_ci	run_uncached(__rm7k_tc_enable);
1288c2ecf20Sopenharmony_ci}
1298c2ecf20Sopenharmony_ci
1308c2ecf20Sopenharmony_ci/*
1318c2ecf20Sopenharmony_ci * This function is executed in uncached address space.
1328c2ecf20Sopenharmony_ci */
1338c2ecf20Sopenharmony_cistatic void __rm7k_sc_enable(void)
1348c2ecf20Sopenharmony_ci{
1358c2ecf20Sopenharmony_ci	int i;
1368c2ecf20Sopenharmony_ci
1378c2ecf20Sopenharmony_ci	set_c0_config(RM7K_CONF_SE);
1388c2ecf20Sopenharmony_ci
1398c2ecf20Sopenharmony_ci	write_c0_taglo(0);
1408c2ecf20Sopenharmony_ci	write_c0_taghi(0);
1418c2ecf20Sopenharmony_ci
1428c2ecf20Sopenharmony_ci	for (i = 0; i < scache_size; i += sc_lsize)
1438c2ecf20Sopenharmony_ci		cache_op(Index_Store_Tag_SD, CKSEG0ADDR(i));
1448c2ecf20Sopenharmony_ci}
1458c2ecf20Sopenharmony_ci
1468c2ecf20Sopenharmony_cistatic void rm7k_sc_enable(void)
1478c2ecf20Sopenharmony_ci{
1488c2ecf20Sopenharmony_ci	if (read_c0_config() & RM7K_CONF_SE)
1498c2ecf20Sopenharmony_ci		return;
1508c2ecf20Sopenharmony_ci
1518c2ecf20Sopenharmony_ci	pr_info("Enabling secondary cache...\n");
1528c2ecf20Sopenharmony_ci	run_uncached(__rm7k_sc_enable);
1538c2ecf20Sopenharmony_ci
1548c2ecf20Sopenharmony_ci	if (rm7k_tcache_init)
1558c2ecf20Sopenharmony_ci		rm7k_tc_enable();
1568c2ecf20Sopenharmony_ci}
1578c2ecf20Sopenharmony_ci
1588c2ecf20Sopenharmony_cistatic void rm7k_tc_disable(void)
1598c2ecf20Sopenharmony_ci{
1608c2ecf20Sopenharmony_ci	unsigned long flags;
1618c2ecf20Sopenharmony_ci
1628c2ecf20Sopenharmony_ci	local_irq_save(flags);
1638c2ecf20Sopenharmony_ci	blast_rm7k_tcache();
1648c2ecf20Sopenharmony_ci	clear_c0_config(RM7K_CONF_TE);
1658c2ecf20Sopenharmony_ci	local_irq_restore(flags);
1668c2ecf20Sopenharmony_ci}
1678c2ecf20Sopenharmony_ci
1688c2ecf20Sopenharmony_cistatic void rm7k_sc_disable(void)
1698c2ecf20Sopenharmony_ci{
1708c2ecf20Sopenharmony_ci	clear_c0_config(RM7K_CONF_SE);
1718c2ecf20Sopenharmony_ci
1728c2ecf20Sopenharmony_ci	if (rm7k_tcache_init)
1738c2ecf20Sopenharmony_ci		rm7k_tc_disable();
1748c2ecf20Sopenharmony_ci}
1758c2ecf20Sopenharmony_ci
1768c2ecf20Sopenharmony_cistatic struct bcache_ops rm7k_sc_ops = {
1778c2ecf20Sopenharmony_ci	.bc_enable = rm7k_sc_enable,
1788c2ecf20Sopenharmony_ci	.bc_disable = rm7k_sc_disable,
1798c2ecf20Sopenharmony_ci	.bc_wback_inv = rm7k_sc_wback_inv,
1808c2ecf20Sopenharmony_ci	.bc_inv = rm7k_sc_inv
1818c2ecf20Sopenharmony_ci};
1828c2ecf20Sopenharmony_ci
1838c2ecf20Sopenharmony_ci/*
1848c2ecf20Sopenharmony_ci * This is a probing function like the one found in c-r4k.c, we look for the
1858c2ecf20Sopenharmony_ci * wrap around point with different addresses.
1868c2ecf20Sopenharmony_ci */
1878c2ecf20Sopenharmony_cistatic void __probe_tcache(void)
1888c2ecf20Sopenharmony_ci{
1898c2ecf20Sopenharmony_ci	unsigned long flags, addr, begin, end, pow2;
1908c2ecf20Sopenharmony_ci
1918c2ecf20Sopenharmony_ci	begin = (unsigned long) &_stext;
1928c2ecf20Sopenharmony_ci	begin  &= ~((8 * 1024 * 1024) - 1);
1938c2ecf20Sopenharmony_ci	end = begin + (8 * 1024 * 1024);
1948c2ecf20Sopenharmony_ci
1958c2ecf20Sopenharmony_ci	local_irq_save(flags);
1968c2ecf20Sopenharmony_ci
1978c2ecf20Sopenharmony_ci	set_c0_config(RM7K_CONF_TE);
1988c2ecf20Sopenharmony_ci
1998c2ecf20Sopenharmony_ci	/* Fill size-multiple lines with a valid tag */
2008c2ecf20Sopenharmony_ci	pow2 = (256 * 1024);
2018c2ecf20Sopenharmony_ci	for (addr = begin; addr <= end; addr = (begin + pow2)) {
2028c2ecf20Sopenharmony_ci		unsigned long *p = (unsigned long *) addr;
2038c2ecf20Sopenharmony_ci		__asm__ __volatile__("nop" : : "r" (*p));
2048c2ecf20Sopenharmony_ci		pow2 <<= 1;
2058c2ecf20Sopenharmony_ci	}
2068c2ecf20Sopenharmony_ci
2078c2ecf20Sopenharmony_ci	/* Load first line with a 0 tag, to check after */
2088c2ecf20Sopenharmony_ci	write_c0_taglo(0);
2098c2ecf20Sopenharmony_ci	write_c0_taghi(0);
2108c2ecf20Sopenharmony_ci	cache_op(Index_Store_Tag_T, begin);
2118c2ecf20Sopenharmony_ci
2128c2ecf20Sopenharmony_ci	/* Look for the wrap-around */
2138c2ecf20Sopenharmony_ci	pow2 = (512 * 1024);
2148c2ecf20Sopenharmony_ci	for (addr = begin + (512 * 1024); addr <= end; addr = begin + pow2) {
2158c2ecf20Sopenharmony_ci		cache_op(Index_Load_Tag_T, addr);
2168c2ecf20Sopenharmony_ci		if (!read_c0_taglo())
2178c2ecf20Sopenharmony_ci			break;
2188c2ecf20Sopenharmony_ci		pow2 <<= 1;
2198c2ecf20Sopenharmony_ci	}
2208c2ecf20Sopenharmony_ci
2218c2ecf20Sopenharmony_ci	addr -= begin;
2228c2ecf20Sopenharmony_ci	tcache_size = addr;
2238c2ecf20Sopenharmony_ci
2248c2ecf20Sopenharmony_ci	clear_c0_config(RM7K_CONF_TE);
2258c2ecf20Sopenharmony_ci
2268c2ecf20Sopenharmony_ci	local_irq_restore(flags);
2278c2ecf20Sopenharmony_ci}
2288c2ecf20Sopenharmony_ci
2298c2ecf20Sopenharmony_civoid rm7k_sc_init(void)
2308c2ecf20Sopenharmony_ci{
2318c2ecf20Sopenharmony_ci	struct cpuinfo_mips *c = &current_cpu_data;
2328c2ecf20Sopenharmony_ci	unsigned int config = read_c0_config();
2338c2ecf20Sopenharmony_ci
2348c2ecf20Sopenharmony_ci	if ((config & RM7K_CONF_SC))
2358c2ecf20Sopenharmony_ci		return;
2368c2ecf20Sopenharmony_ci
2378c2ecf20Sopenharmony_ci	c->scache.linesz = sc_lsize;
2388c2ecf20Sopenharmony_ci	c->scache.ways = 4;
2398c2ecf20Sopenharmony_ci	c->scache.waybit= __ffs(scache_size / c->scache.ways);
2408c2ecf20Sopenharmony_ci	c->scache.waysize = scache_size / c->scache.ways;
2418c2ecf20Sopenharmony_ci	c->scache.sets = scache_size / (c->scache.linesz * c->scache.ways);
2428c2ecf20Sopenharmony_ci	printk(KERN_INFO "Secondary cache size %dK, linesize %d bytes.\n",
2438c2ecf20Sopenharmony_ci	       (scache_size >> 10), sc_lsize);
2448c2ecf20Sopenharmony_ci
2458c2ecf20Sopenharmony_ci	if (!(config & RM7K_CONF_SE))
2468c2ecf20Sopenharmony_ci		rm7k_sc_enable();
2478c2ecf20Sopenharmony_ci
2488c2ecf20Sopenharmony_ci	bcops = &rm7k_sc_ops;
2498c2ecf20Sopenharmony_ci
2508c2ecf20Sopenharmony_ci	/*
2518c2ecf20Sopenharmony_ci	 * While we're at it let's deal with the tertiary cache.
2528c2ecf20Sopenharmony_ci	 */
2538c2ecf20Sopenharmony_ci
2548c2ecf20Sopenharmony_ci	rm7k_tcache_init = 0;
2558c2ecf20Sopenharmony_ci	tcache_size = 0;
2568c2ecf20Sopenharmony_ci
2578c2ecf20Sopenharmony_ci	if (config & RM7K_CONF_TC)
2588c2ecf20Sopenharmony_ci		return;
2598c2ecf20Sopenharmony_ci
2608c2ecf20Sopenharmony_ci	/*
2618c2ecf20Sopenharmony_ci	 * No efficient way to ask the hardware for the size of the tcache,
2628c2ecf20Sopenharmony_ci	 * so must probe for it.
2638c2ecf20Sopenharmony_ci	 */
2648c2ecf20Sopenharmony_ci	run_uncached(__probe_tcache);
2658c2ecf20Sopenharmony_ci	rm7k_tc_enable();
2668c2ecf20Sopenharmony_ci	rm7k_tcache_init = 1;
2678c2ecf20Sopenharmony_ci	c->tcache.linesz = tc_lsize;
2688c2ecf20Sopenharmony_ci	c->tcache.ways = 1;
2698c2ecf20Sopenharmony_ci	pr_info("Tertiary cache size %ldK.\n", (tcache_size >> 10));
2708c2ecf20Sopenharmony_ci}
271