18c2ecf20Sopenharmony_ci/*
28c2ecf20Sopenharmony_ci * arch/arm/mm/cache-tauros2.c - Tauros2 L2 cache controller support
38c2ecf20Sopenharmony_ci *
48c2ecf20Sopenharmony_ci * Copyright (C) 2008 Marvell Semiconductor
58c2ecf20Sopenharmony_ci *
68c2ecf20Sopenharmony_ci * This file is licensed under the terms of the GNU General Public
78c2ecf20Sopenharmony_ci * License version 2.  This program is licensed "as is" without any
88c2ecf20Sopenharmony_ci * warranty of any kind, whether express or implied.
98c2ecf20Sopenharmony_ci *
108c2ecf20Sopenharmony_ci * References:
118c2ecf20Sopenharmony_ci * - PJ1 CPU Core Datasheet,
128c2ecf20Sopenharmony_ci *   Document ID MV-S104837-01, Rev 0.7, January 24 2008.
138c2ecf20Sopenharmony_ci * - PJ4 CPU Core Datasheet,
148c2ecf20Sopenharmony_ci *   Document ID MV-S105190-00, Rev 0.7, March 14 2008.
158c2ecf20Sopenharmony_ci */
168c2ecf20Sopenharmony_ci
178c2ecf20Sopenharmony_ci#include <linux/init.h>
188c2ecf20Sopenharmony_ci#include <linux/of.h>
198c2ecf20Sopenharmony_ci#include <linux/of_address.h>
208c2ecf20Sopenharmony_ci#include <asm/cacheflush.h>
218c2ecf20Sopenharmony_ci#include <asm/cp15.h>
228c2ecf20Sopenharmony_ci#include <asm/cputype.h>
238c2ecf20Sopenharmony_ci#include <asm/hardware/cache-tauros2.h>
248c2ecf20Sopenharmony_ci
258c2ecf20Sopenharmony_ci/* CP15 PJ4 Control configuration register */
268c2ecf20Sopenharmony_ci#define CCR_L2C_PREFETCH_DISABLE	BIT(24)
278c2ecf20Sopenharmony_ci#define CCR_L2C_ECC_ENABLE		BIT(23)
288c2ecf20Sopenharmony_ci#define CCR_L2C_WAY7_4_DISABLE		BIT(21)
298c2ecf20Sopenharmony_ci#define CCR_L2C_BURST8_ENABLE		BIT(20)
308c2ecf20Sopenharmony_ci
318c2ecf20Sopenharmony_ci/*
328c2ecf20Sopenharmony_ci * When Tauros2 is used on a CPU that supports the v7 hierarchical
338c2ecf20Sopenharmony_ci * cache operations, the cache handling code in proc-v7.S takes care
348c2ecf20Sopenharmony_ci * of everything, including handling DMA coherency.
358c2ecf20Sopenharmony_ci *
368c2ecf20Sopenharmony_ci * So, we only need to register outer cache operations here if we're
378c2ecf20Sopenharmony_ci * being used on a pre-v7 CPU, and we only need to build support for
388c2ecf20Sopenharmony_ci * outer cache operations into the kernel image if the kernel has been
398c2ecf20Sopenharmony_ci * configured to support a pre-v7 CPU.
408c2ecf20Sopenharmony_ci */
418c2ecf20Sopenharmony_ci#ifdef CONFIG_CPU_32v5
428c2ecf20Sopenharmony_ci/*
438c2ecf20Sopenharmony_ci * Low-level cache maintenance operations.
448c2ecf20Sopenharmony_ci */
458c2ecf20Sopenharmony_cistatic inline void tauros2_clean_pa(unsigned long addr)
468c2ecf20Sopenharmony_ci{
478c2ecf20Sopenharmony_ci	__asm__("mcr p15, 1, %0, c7, c11, 3" : : "r" (addr));
488c2ecf20Sopenharmony_ci}
498c2ecf20Sopenharmony_ci
508c2ecf20Sopenharmony_cistatic inline void tauros2_clean_inv_pa(unsigned long addr)
518c2ecf20Sopenharmony_ci{
528c2ecf20Sopenharmony_ci	__asm__("mcr p15, 1, %0, c7, c15, 3" : : "r" (addr));
538c2ecf20Sopenharmony_ci}
548c2ecf20Sopenharmony_ci
558c2ecf20Sopenharmony_cistatic inline void tauros2_inv_pa(unsigned long addr)
568c2ecf20Sopenharmony_ci{
578c2ecf20Sopenharmony_ci	__asm__("mcr p15, 1, %0, c7, c7, 3" : : "r" (addr));
588c2ecf20Sopenharmony_ci}
598c2ecf20Sopenharmony_ci
608c2ecf20Sopenharmony_ci
618c2ecf20Sopenharmony_ci/*
628c2ecf20Sopenharmony_ci * Linux primitives.
638c2ecf20Sopenharmony_ci *
648c2ecf20Sopenharmony_ci * Note that the end addresses passed to Linux primitives are
658c2ecf20Sopenharmony_ci * noninclusive.
668c2ecf20Sopenharmony_ci */
678c2ecf20Sopenharmony_ci#define CACHE_LINE_SIZE		32
688c2ecf20Sopenharmony_ci
698c2ecf20Sopenharmony_cistatic void tauros2_inv_range(unsigned long start, unsigned long end)
708c2ecf20Sopenharmony_ci{
718c2ecf20Sopenharmony_ci	/*
728c2ecf20Sopenharmony_ci	 * Clean and invalidate partial first cache line.
738c2ecf20Sopenharmony_ci	 */
748c2ecf20Sopenharmony_ci	if (start & (CACHE_LINE_SIZE - 1)) {
758c2ecf20Sopenharmony_ci		tauros2_clean_inv_pa(start & ~(CACHE_LINE_SIZE - 1));
768c2ecf20Sopenharmony_ci		start = (start | (CACHE_LINE_SIZE - 1)) + 1;
778c2ecf20Sopenharmony_ci	}
788c2ecf20Sopenharmony_ci
798c2ecf20Sopenharmony_ci	/*
808c2ecf20Sopenharmony_ci	 * Clean and invalidate partial last cache line.
818c2ecf20Sopenharmony_ci	 */
828c2ecf20Sopenharmony_ci	if (end & (CACHE_LINE_SIZE - 1)) {
838c2ecf20Sopenharmony_ci		tauros2_clean_inv_pa(end & ~(CACHE_LINE_SIZE - 1));
848c2ecf20Sopenharmony_ci		end &= ~(CACHE_LINE_SIZE - 1);
858c2ecf20Sopenharmony_ci	}
868c2ecf20Sopenharmony_ci
878c2ecf20Sopenharmony_ci	/*
888c2ecf20Sopenharmony_ci	 * Invalidate all full cache lines between 'start' and 'end'.
898c2ecf20Sopenharmony_ci	 */
908c2ecf20Sopenharmony_ci	while (start < end) {
918c2ecf20Sopenharmony_ci		tauros2_inv_pa(start);
928c2ecf20Sopenharmony_ci		start += CACHE_LINE_SIZE;
938c2ecf20Sopenharmony_ci	}
948c2ecf20Sopenharmony_ci
958c2ecf20Sopenharmony_ci	dsb();
968c2ecf20Sopenharmony_ci}
978c2ecf20Sopenharmony_ci
988c2ecf20Sopenharmony_cistatic void tauros2_clean_range(unsigned long start, unsigned long end)
998c2ecf20Sopenharmony_ci{
1008c2ecf20Sopenharmony_ci	start &= ~(CACHE_LINE_SIZE - 1);
1018c2ecf20Sopenharmony_ci	while (start < end) {
1028c2ecf20Sopenharmony_ci		tauros2_clean_pa(start);
1038c2ecf20Sopenharmony_ci		start += CACHE_LINE_SIZE;
1048c2ecf20Sopenharmony_ci	}
1058c2ecf20Sopenharmony_ci
1068c2ecf20Sopenharmony_ci	dsb();
1078c2ecf20Sopenharmony_ci}
1088c2ecf20Sopenharmony_ci
1098c2ecf20Sopenharmony_cistatic void tauros2_flush_range(unsigned long start, unsigned long end)
1108c2ecf20Sopenharmony_ci{
1118c2ecf20Sopenharmony_ci	start &= ~(CACHE_LINE_SIZE - 1);
1128c2ecf20Sopenharmony_ci	while (start < end) {
1138c2ecf20Sopenharmony_ci		tauros2_clean_inv_pa(start);
1148c2ecf20Sopenharmony_ci		start += CACHE_LINE_SIZE;
1158c2ecf20Sopenharmony_ci	}
1168c2ecf20Sopenharmony_ci
1178c2ecf20Sopenharmony_ci	dsb();
1188c2ecf20Sopenharmony_ci}
1198c2ecf20Sopenharmony_ci
1208c2ecf20Sopenharmony_cistatic void tauros2_disable(void)
1218c2ecf20Sopenharmony_ci{
1228c2ecf20Sopenharmony_ci	__asm__ __volatile__ (
1238c2ecf20Sopenharmony_ci	"mcr	p15, 1, %0, c7, c11, 0 @L2 Cache Clean All\n\t"
1248c2ecf20Sopenharmony_ci	"mrc	p15, 0, %0, c1, c0, 0\n\t"
1258c2ecf20Sopenharmony_ci	"bic	%0, %0, #(1 << 26)\n\t"
1268c2ecf20Sopenharmony_ci	"mcr	p15, 0, %0, c1, c0, 0  @Disable L2 Cache\n\t"
1278c2ecf20Sopenharmony_ci	: : "r" (0x0));
1288c2ecf20Sopenharmony_ci}
1298c2ecf20Sopenharmony_ci
1308c2ecf20Sopenharmony_cistatic void tauros2_resume(void)
1318c2ecf20Sopenharmony_ci{
1328c2ecf20Sopenharmony_ci	__asm__ __volatile__ (
1338c2ecf20Sopenharmony_ci	"mcr	p15, 1, %0, c7, c7, 0 @L2 Cache Invalidate All\n\t"
1348c2ecf20Sopenharmony_ci	"mrc	p15, 0, %0, c1, c0, 0\n\t"
1358c2ecf20Sopenharmony_ci	"orr	%0, %0, #(1 << 26)\n\t"
1368c2ecf20Sopenharmony_ci	"mcr	p15, 0, %0, c1, c0, 0 @Enable L2 Cache\n\t"
1378c2ecf20Sopenharmony_ci	: : "r" (0x0));
1388c2ecf20Sopenharmony_ci}
1398c2ecf20Sopenharmony_ci#endif
1408c2ecf20Sopenharmony_ci
1418c2ecf20Sopenharmony_cistatic inline u32 __init read_extra_features(void)
1428c2ecf20Sopenharmony_ci{
1438c2ecf20Sopenharmony_ci	u32 u;
1448c2ecf20Sopenharmony_ci
1458c2ecf20Sopenharmony_ci	__asm__("mrc p15, 1, %0, c15, c1, 0" : "=r" (u));
1468c2ecf20Sopenharmony_ci
1478c2ecf20Sopenharmony_ci	return u;
1488c2ecf20Sopenharmony_ci}
1498c2ecf20Sopenharmony_ci
1508c2ecf20Sopenharmony_cistatic inline void __init write_extra_features(u32 u)
1518c2ecf20Sopenharmony_ci{
1528c2ecf20Sopenharmony_ci	__asm__("mcr p15, 1, %0, c15, c1, 0" : : "r" (u));
1538c2ecf20Sopenharmony_ci}
1548c2ecf20Sopenharmony_ci
1558c2ecf20Sopenharmony_cistatic inline int __init cpuid_scheme(void)
1568c2ecf20Sopenharmony_ci{
1578c2ecf20Sopenharmony_ci	return !!((processor_id & 0x000f0000) == 0x000f0000);
1588c2ecf20Sopenharmony_ci}
1598c2ecf20Sopenharmony_ci
1608c2ecf20Sopenharmony_cistatic inline u32 __init read_mmfr3(void)
1618c2ecf20Sopenharmony_ci{
1628c2ecf20Sopenharmony_ci	u32 mmfr3;
1638c2ecf20Sopenharmony_ci
1648c2ecf20Sopenharmony_ci	__asm__("mrc p15, 0, %0, c0, c1, 7\n" : "=r" (mmfr3));
1658c2ecf20Sopenharmony_ci
1668c2ecf20Sopenharmony_ci	return mmfr3;
1678c2ecf20Sopenharmony_ci}
1688c2ecf20Sopenharmony_ci
1698c2ecf20Sopenharmony_cistatic inline u32 __init read_actlr(void)
1708c2ecf20Sopenharmony_ci{
1718c2ecf20Sopenharmony_ci	u32 actlr;
1728c2ecf20Sopenharmony_ci
1738c2ecf20Sopenharmony_ci	__asm__("mrc p15, 0, %0, c1, c0, 1\n" : "=r" (actlr));
1748c2ecf20Sopenharmony_ci
1758c2ecf20Sopenharmony_ci	return actlr;
1768c2ecf20Sopenharmony_ci}
1778c2ecf20Sopenharmony_ci
1788c2ecf20Sopenharmony_cistatic inline void __init write_actlr(u32 actlr)
1798c2ecf20Sopenharmony_ci{
1808c2ecf20Sopenharmony_ci	__asm__("mcr p15, 0, %0, c1, c0, 1\n" : : "r" (actlr));
1818c2ecf20Sopenharmony_ci}
1828c2ecf20Sopenharmony_ci
1838c2ecf20Sopenharmony_cistatic void enable_extra_feature(unsigned int features)
1848c2ecf20Sopenharmony_ci{
1858c2ecf20Sopenharmony_ci	u32 u;
1868c2ecf20Sopenharmony_ci
1878c2ecf20Sopenharmony_ci	u = read_extra_features();
1888c2ecf20Sopenharmony_ci
1898c2ecf20Sopenharmony_ci	if (features & CACHE_TAUROS2_PREFETCH_ON)
1908c2ecf20Sopenharmony_ci		u &= ~CCR_L2C_PREFETCH_DISABLE;
1918c2ecf20Sopenharmony_ci	else
1928c2ecf20Sopenharmony_ci		u |= CCR_L2C_PREFETCH_DISABLE;
1938c2ecf20Sopenharmony_ci	pr_info("Tauros2: %s L2 prefetch.\n",
1948c2ecf20Sopenharmony_ci			(features & CACHE_TAUROS2_PREFETCH_ON)
1958c2ecf20Sopenharmony_ci			? "Enabling" : "Disabling");
1968c2ecf20Sopenharmony_ci
1978c2ecf20Sopenharmony_ci	if (features & CACHE_TAUROS2_LINEFILL_BURST8)
1988c2ecf20Sopenharmony_ci		u |= CCR_L2C_BURST8_ENABLE;
1998c2ecf20Sopenharmony_ci	else
2008c2ecf20Sopenharmony_ci		u &= ~CCR_L2C_BURST8_ENABLE;
2018c2ecf20Sopenharmony_ci	pr_info("Tauros2: %s burst8 line fill.\n",
2028c2ecf20Sopenharmony_ci			(features & CACHE_TAUROS2_LINEFILL_BURST8)
2038c2ecf20Sopenharmony_ci			? "Enabling" : "Disabling");
2048c2ecf20Sopenharmony_ci
2058c2ecf20Sopenharmony_ci	write_extra_features(u);
2068c2ecf20Sopenharmony_ci}
2078c2ecf20Sopenharmony_ci
2088c2ecf20Sopenharmony_cistatic void __init tauros2_internal_init(unsigned int features)
2098c2ecf20Sopenharmony_ci{
2108c2ecf20Sopenharmony_ci	char *mode = NULL;
2118c2ecf20Sopenharmony_ci
2128c2ecf20Sopenharmony_ci	enable_extra_feature(features);
2138c2ecf20Sopenharmony_ci
2148c2ecf20Sopenharmony_ci#ifdef CONFIG_CPU_32v5
2158c2ecf20Sopenharmony_ci	if ((processor_id & 0xff0f0000) == 0x56050000) {
2168c2ecf20Sopenharmony_ci		u32 feat;
2178c2ecf20Sopenharmony_ci
2188c2ecf20Sopenharmony_ci		/*
2198c2ecf20Sopenharmony_ci		 * v5 CPUs with Tauros2 have the L2 cache enable bit
2208c2ecf20Sopenharmony_ci		 * located in the CPU Extra Features register.
2218c2ecf20Sopenharmony_ci		 */
2228c2ecf20Sopenharmony_ci		feat = read_extra_features();
2238c2ecf20Sopenharmony_ci		if (!(feat & 0x00400000)) {
2248c2ecf20Sopenharmony_ci			pr_info("Tauros2: Enabling L2 cache.\n");
2258c2ecf20Sopenharmony_ci			write_extra_features(feat | 0x00400000);
2268c2ecf20Sopenharmony_ci		}
2278c2ecf20Sopenharmony_ci
2288c2ecf20Sopenharmony_ci		mode = "ARMv5";
2298c2ecf20Sopenharmony_ci		outer_cache.inv_range = tauros2_inv_range;
2308c2ecf20Sopenharmony_ci		outer_cache.clean_range = tauros2_clean_range;
2318c2ecf20Sopenharmony_ci		outer_cache.flush_range = tauros2_flush_range;
2328c2ecf20Sopenharmony_ci		outer_cache.disable = tauros2_disable;
2338c2ecf20Sopenharmony_ci		outer_cache.resume = tauros2_resume;
2348c2ecf20Sopenharmony_ci	}
2358c2ecf20Sopenharmony_ci#endif
2368c2ecf20Sopenharmony_ci
2378c2ecf20Sopenharmony_ci#ifdef CONFIG_CPU_32v7
2388c2ecf20Sopenharmony_ci	/*
2398c2ecf20Sopenharmony_ci	 * Check whether this CPU has support for the v7 hierarchical
2408c2ecf20Sopenharmony_ci	 * cache ops.  (PJ4 is in its v7 personality mode if the MMFR3
2418c2ecf20Sopenharmony_ci	 * register indicates support for the v7 hierarchical cache
2428c2ecf20Sopenharmony_ci	 * ops.)
2438c2ecf20Sopenharmony_ci	 *
2448c2ecf20Sopenharmony_ci	 * (Although strictly speaking there may exist CPUs that
2458c2ecf20Sopenharmony_ci	 * implement the v7 cache ops but are only ARMv6 CPUs (due to
2468c2ecf20Sopenharmony_ci	 * not complying with all of the other ARMv7 requirements),
2478c2ecf20Sopenharmony_ci	 * there are no real-life examples of Tauros2 being used on
2488c2ecf20Sopenharmony_ci	 * such CPUs as of yet.)
2498c2ecf20Sopenharmony_ci	 */
2508c2ecf20Sopenharmony_ci	if (cpuid_scheme() && (read_mmfr3() & 0xf) == 1) {
2518c2ecf20Sopenharmony_ci		u32 actlr;
2528c2ecf20Sopenharmony_ci
2538c2ecf20Sopenharmony_ci		/*
2548c2ecf20Sopenharmony_ci		 * When Tauros2 is used in an ARMv7 system, the L2
2558c2ecf20Sopenharmony_ci		 * enable bit is located in the Auxiliary System Control
2568c2ecf20Sopenharmony_ci		 * Register (which is the only register allowed by the
2578c2ecf20Sopenharmony_ci		 * ARMv7 spec to contain fine-grained cache control bits).
2588c2ecf20Sopenharmony_ci		 */
2598c2ecf20Sopenharmony_ci		actlr = read_actlr();
2608c2ecf20Sopenharmony_ci		if (!(actlr & 0x00000002)) {
2618c2ecf20Sopenharmony_ci			pr_info("Tauros2: Enabling L2 cache.\n");
2628c2ecf20Sopenharmony_ci			write_actlr(actlr | 0x00000002);
2638c2ecf20Sopenharmony_ci		}
2648c2ecf20Sopenharmony_ci
2658c2ecf20Sopenharmony_ci		mode = "ARMv7";
2668c2ecf20Sopenharmony_ci	}
2678c2ecf20Sopenharmony_ci#endif
2688c2ecf20Sopenharmony_ci
2698c2ecf20Sopenharmony_ci	if (mode == NULL) {
2708c2ecf20Sopenharmony_ci		pr_crit("Tauros2: Unable to detect CPU mode.\n");
2718c2ecf20Sopenharmony_ci		return;
2728c2ecf20Sopenharmony_ci	}
2738c2ecf20Sopenharmony_ci
2748c2ecf20Sopenharmony_ci	pr_info("Tauros2: L2 cache support initialised "
2758c2ecf20Sopenharmony_ci			 "in %s mode.\n", mode);
2768c2ecf20Sopenharmony_ci}
2778c2ecf20Sopenharmony_ci
2788c2ecf20Sopenharmony_ci#ifdef CONFIG_OF
2798c2ecf20Sopenharmony_cistatic const struct of_device_id tauros2_ids[] __initconst = {
2808c2ecf20Sopenharmony_ci	{ .compatible = "marvell,tauros2-cache"},
2818c2ecf20Sopenharmony_ci	{}
2828c2ecf20Sopenharmony_ci};
2838c2ecf20Sopenharmony_ci#endif
2848c2ecf20Sopenharmony_ci
2858c2ecf20Sopenharmony_civoid __init tauros2_init(unsigned int features)
2868c2ecf20Sopenharmony_ci{
2878c2ecf20Sopenharmony_ci#ifdef CONFIG_OF
2888c2ecf20Sopenharmony_ci	struct device_node *node;
2898c2ecf20Sopenharmony_ci	int ret;
2908c2ecf20Sopenharmony_ci	unsigned int f;
2918c2ecf20Sopenharmony_ci
2928c2ecf20Sopenharmony_ci	node = of_find_matching_node(NULL, tauros2_ids);
2938c2ecf20Sopenharmony_ci	if (!node) {
2948c2ecf20Sopenharmony_ci		pr_info("Not found marvell,tauros2-cache, disable it\n");
2958c2ecf20Sopenharmony_ci	} else {
2968c2ecf20Sopenharmony_ci		ret = of_property_read_u32(node, "marvell,tauros2-cache-features", &f);
2978c2ecf20Sopenharmony_ci		if (ret) {
2988c2ecf20Sopenharmony_ci			pr_info("Not found marvell,tauros-cache-features property, "
2998c2ecf20Sopenharmony_ci				"disable extra features\n");
3008c2ecf20Sopenharmony_ci			features = 0;
3018c2ecf20Sopenharmony_ci		} else
3028c2ecf20Sopenharmony_ci			features = f;
3038c2ecf20Sopenharmony_ci	}
3048c2ecf20Sopenharmony_ci#endif
3058c2ecf20Sopenharmony_ci	tauros2_internal_init(features);
3068c2ecf20Sopenharmony_ci}
307