18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * arch/arm/mm/cache-l2x0.c - L210/L220/L310 cache controller support
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * Copyright (C) 2007 ARM Limited
68c2ecf20Sopenharmony_ci */
78c2ecf20Sopenharmony_ci#include <linux/cpu.h>
88c2ecf20Sopenharmony_ci#include <linux/err.h>
98c2ecf20Sopenharmony_ci#include <linux/init.h>
108c2ecf20Sopenharmony_ci#include <linux/smp.h>
118c2ecf20Sopenharmony_ci#include <linux/spinlock.h>
128c2ecf20Sopenharmony_ci#include <linux/log2.h>
138c2ecf20Sopenharmony_ci#include <linux/io.h>
148c2ecf20Sopenharmony_ci#include <linux/of.h>
158c2ecf20Sopenharmony_ci#include <linux/of_address.h>
168c2ecf20Sopenharmony_ci
178c2ecf20Sopenharmony_ci#include <asm/cacheflush.h>
188c2ecf20Sopenharmony_ci#include <asm/cp15.h>
198c2ecf20Sopenharmony_ci#include <asm/cputype.h>
208c2ecf20Sopenharmony_ci#include <asm/hardware/cache-l2x0.h>
218c2ecf20Sopenharmony_ci#include <asm/hardware/cache-aurora-l2.h>
228c2ecf20Sopenharmony_ci#include "cache-tauros3.h"
238c2ecf20Sopenharmony_ci
248c2ecf20Sopenharmony_cistruct l2c_init_data {
258c2ecf20Sopenharmony_ci	const char *type;
268c2ecf20Sopenharmony_ci	unsigned way_size_0;
278c2ecf20Sopenharmony_ci	unsigned num_lock;
288c2ecf20Sopenharmony_ci	void (*of_parse)(const struct device_node *, u32 *, u32 *);
298c2ecf20Sopenharmony_ci	void (*enable)(void __iomem *, unsigned);
308c2ecf20Sopenharmony_ci	void (*fixup)(void __iomem *, u32, struct outer_cache_fns *);
318c2ecf20Sopenharmony_ci	void (*save)(void __iomem *);
328c2ecf20Sopenharmony_ci	void (*configure)(void __iomem *);
338c2ecf20Sopenharmony_ci	void (*unlock)(void __iomem *, unsigned);
348c2ecf20Sopenharmony_ci	struct outer_cache_fns outer_cache;
358c2ecf20Sopenharmony_ci};
368c2ecf20Sopenharmony_ci
378c2ecf20Sopenharmony_ci#define CACHE_LINE_SIZE		32
388c2ecf20Sopenharmony_ci
398c2ecf20Sopenharmony_cistatic void __iomem *l2x0_base;
408c2ecf20Sopenharmony_cistatic const struct l2c_init_data *l2x0_data;
418c2ecf20Sopenharmony_cistatic DEFINE_RAW_SPINLOCK(l2x0_lock);
428c2ecf20Sopenharmony_cistatic u32 l2x0_way_mask;	/* Bitmask of active ways */
438c2ecf20Sopenharmony_cistatic u32 l2x0_size;
448c2ecf20Sopenharmony_cistatic unsigned long sync_reg_offset = L2X0_CACHE_SYNC;
458c2ecf20Sopenharmony_ci
468c2ecf20Sopenharmony_cistruct l2x0_regs l2x0_saved_regs;
478c2ecf20Sopenharmony_ci
488c2ecf20Sopenharmony_cistatic bool l2x0_bresp_disable;
498c2ecf20Sopenharmony_cistatic bool l2x0_flz_disable;
508c2ecf20Sopenharmony_ci
518c2ecf20Sopenharmony_ci/*
528c2ecf20Sopenharmony_ci * Common code for all cache controllers.
538c2ecf20Sopenharmony_ci */
548c2ecf20Sopenharmony_cistatic inline void l2c_wait_mask(void __iomem *reg, unsigned long mask)
558c2ecf20Sopenharmony_ci{
568c2ecf20Sopenharmony_ci	/* wait for cache operation by line or way to complete */
578c2ecf20Sopenharmony_ci	while (readl_relaxed(reg) & mask)
588c2ecf20Sopenharmony_ci		cpu_relax();
598c2ecf20Sopenharmony_ci}
608c2ecf20Sopenharmony_ci
618c2ecf20Sopenharmony_ci/*
628c2ecf20Sopenharmony_ci * By default, we write directly to secure registers.  Platforms must
638c2ecf20Sopenharmony_ci * override this if they are running non-secure.
648c2ecf20Sopenharmony_ci */
658c2ecf20Sopenharmony_cistatic void l2c_write_sec(unsigned long val, void __iomem *base, unsigned reg)
668c2ecf20Sopenharmony_ci{
678c2ecf20Sopenharmony_ci	if (val == readl_relaxed(base + reg))
688c2ecf20Sopenharmony_ci		return;
698c2ecf20Sopenharmony_ci	if (outer_cache.write_sec)
708c2ecf20Sopenharmony_ci		outer_cache.write_sec(val, reg);
718c2ecf20Sopenharmony_ci	else
728c2ecf20Sopenharmony_ci		writel_relaxed(val, base + reg);
738c2ecf20Sopenharmony_ci}
748c2ecf20Sopenharmony_ci
758c2ecf20Sopenharmony_ci/*
768c2ecf20Sopenharmony_ci * This should only be called when we have a requirement that the
778c2ecf20Sopenharmony_ci * register be written due to a work-around, as platforms running
788c2ecf20Sopenharmony_ci * in non-secure mode may not be able to access this register.
798c2ecf20Sopenharmony_ci */
808c2ecf20Sopenharmony_cistatic inline void l2c_set_debug(void __iomem *base, unsigned long val)
818c2ecf20Sopenharmony_ci{
828c2ecf20Sopenharmony_ci	l2c_write_sec(val, base, L2X0_DEBUG_CTRL);
838c2ecf20Sopenharmony_ci}
848c2ecf20Sopenharmony_ci
858c2ecf20Sopenharmony_cistatic void __l2c_op_way(void __iomem *reg)
868c2ecf20Sopenharmony_ci{
878c2ecf20Sopenharmony_ci	writel_relaxed(l2x0_way_mask, reg);
888c2ecf20Sopenharmony_ci	l2c_wait_mask(reg, l2x0_way_mask);
898c2ecf20Sopenharmony_ci}
908c2ecf20Sopenharmony_ci
918c2ecf20Sopenharmony_cistatic inline void l2c_unlock(void __iomem *base, unsigned num)
928c2ecf20Sopenharmony_ci{
938c2ecf20Sopenharmony_ci	unsigned i;
948c2ecf20Sopenharmony_ci
958c2ecf20Sopenharmony_ci	for (i = 0; i < num; i++) {
968c2ecf20Sopenharmony_ci		writel_relaxed(0, base + L2X0_LOCKDOWN_WAY_D_BASE +
978c2ecf20Sopenharmony_ci			       i * L2X0_LOCKDOWN_STRIDE);
988c2ecf20Sopenharmony_ci		writel_relaxed(0, base + L2X0_LOCKDOWN_WAY_I_BASE +
998c2ecf20Sopenharmony_ci			       i * L2X0_LOCKDOWN_STRIDE);
1008c2ecf20Sopenharmony_ci	}
1018c2ecf20Sopenharmony_ci}
1028c2ecf20Sopenharmony_ci
1038c2ecf20Sopenharmony_cistatic void l2c_configure(void __iomem *base)
1048c2ecf20Sopenharmony_ci{
1058c2ecf20Sopenharmony_ci	l2c_write_sec(l2x0_saved_regs.aux_ctrl, base, L2X0_AUX_CTRL);
1068c2ecf20Sopenharmony_ci}
1078c2ecf20Sopenharmony_ci
1088c2ecf20Sopenharmony_ci/*
1098c2ecf20Sopenharmony_ci * Enable the L2 cache controller.  This function must only be
1108c2ecf20Sopenharmony_ci * called when the cache controller is known to be disabled.
1118c2ecf20Sopenharmony_ci */
1128c2ecf20Sopenharmony_cistatic void l2c_enable(void __iomem *base, unsigned num_lock)
1138c2ecf20Sopenharmony_ci{
1148c2ecf20Sopenharmony_ci	unsigned long flags;
1158c2ecf20Sopenharmony_ci
1168c2ecf20Sopenharmony_ci	if (outer_cache.configure)
1178c2ecf20Sopenharmony_ci		outer_cache.configure(&l2x0_saved_regs);
1188c2ecf20Sopenharmony_ci	else
1198c2ecf20Sopenharmony_ci		l2x0_data->configure(base);
1208c2ecf20Sopenharmony_ci
1218c2ecf20Sopenharmony_ci	l2x0_data->unlock(base, num_lock);
1228c2ecf20Sopenharmony_ci
1238c2ecf20Sopenharmony_ci	local_irq_save(flags);
1248c2ecf20Sopenharmony_ci	__l2c_op_way(base + L2X0_INV_WAY);
1258c2ecf20Sopenharmony_ci	writel_relaxed(0, base + sync_reg_offset);
1268c2ecf20Sopenharmony_ci	l2c_wait_mask(base + sync_reg_offset, 1);
1278c2ecf20Sopenharmony_ci	local_irq_restore(flags);
1288c2ecf20Sopenharmony_ci
1298c2ecf20Sopenharmony_ci	l2c_write_sec(L2X0_CTRL_EN, base, L2X0_CTRL);
1308c2ecf20Sopenharmony_ci}
1318c2ecf20Sopenharmony_ci
1328c2ecf20Sopenharmony_cistatic void l2c_disable(void)
1338c2ecf20Sopenharmony_ci{
1348c2ecf20Sopenharmony_ci	void __iomem *base = l2x0_base;
1358c2ecf20Sopenharmony_ci
1368c2ecf20Sopenharmony_ci	l2x0_pmu_suspend();
1378c2ecf20Sopenharmony_ci
1388c2ecf20Sopenharmony_ci	outer_cache.flush_all();
1398c2ecf20Sopenharmony_ci	l2c_write_sec(0, base, L2X0_CTRL);
1408c2ecf20Sopenharmony_ci	dsb(st);
1418c2ecf20Sopenharmony_ci}
1428c2ecf20Sopenharmony_ci
1438c2ecf20Sopenharmony_cistatic void l2c_save(void __iomem *base)
1448c2ecf20Sopenharmony_ci{
1458c2ecf20Sopenharmony_ci	l2x0_saved_regs.aux_ctrl = readl_relaxed(l2x0_base + L2X0_AUX_CTRL);
1468c2ecf20Sopenharmony_ci}
1478c2ecf20Sopenharmony_ci
1488c2ecf20Sopenharmony_cistatic void l2c_resume(void)
1498c2ecf20Sopenharmony_ci{
1508c2ecf20Sopenharmony_ci	void __iomem *base = l2x0_base;
1518c2ecf20Sopenharmony_ci
1528c2ecf20Sopenharmony_ci	/* Do not touch the controller if already enabled. */
1538c2ecf20Sopenharmony_ci	if (!(readl_relaxed(base + L2X0_CTRL) & L2X0_CTRL_EN))
1548c2ecf20Sopenharmony_ci		l2c_enable(base, l2x0_data->num_lock);
1558c2ecf20Sopenharmony_ci
1568c2ecf20Sopenharmony_ci	l2x0_pmu_resume();
1578c2ecf20Sopenharmony_ci}
1588c2ecf20Sopenharmony_ci
1598c2ecf20Sopenharmony_ci/*
1608c2ecf20Sopenharmony_ci * L2C-210 specific code.
1618c2ecf20Sopenharmony_ci *
1628c2ecf20Sopenharmony_ci * The L2C-2x0 PA, set/way and sync operations are atomic, but we must
1638c2ecf20Sopenharmony_ci * ensure that no background operation is running.  The way operations
1648c2ecf20Sopenharmony_ci * are all background tasks.
1658c2ecf20Sopenharmony_ci *
1668c2ecf20Sopenharmony_ci * While a background operation is in progress, any new operation is
1678c2ecf20Sopenharmony_ci * ignored (unspecified whether this causes an error.)  Thankfully, not
1688c2ecf20Sopenharmony_ci * used on SMP.
1698c2ecf20Sopenharmony_ci *
1708c2ecf20Sopenharmony_ci * Never has a different sync register other than L2X0_CACHE_SYNC, but
1718c2ecf20Sopenharmony_ci * we use sync_reg_offset here so we can share some of this with L2C-310.
1728c2ecf20Sopenharmony_ci */
1738c2ecf20Sopenharmony_cistatic void __l2c210_cache_sync(void __iomem *base)
1748c2ecf20Sopenharmony_ci{
1758c2ecf20Sopenharmony_ci	writel_relaxed(0, base + sync_reg_offset);
1768c2ecf20Sopenharmony_ci}
1778c2ecf20Sopenharmony_ci
1788c2ecf20Sopenharmony_cistatic void __l2c210_op_pa_range(void __iomem *reg, unsigned long start,
1798c2ecf20Sopenharmony_ci	unsigned long end)
1808c2ecf20Sopenharmony_ci{
1818c2ecf20Sopenharmony_ci	while (start < end) {
1828c2ecf20Sopenharmony_ci		writel_relaxed(start, reg);
1838c2ecf20Sopenharmony_ci		start += CACHE_LINE_SIZE;
1848c2ecf20Sopenharmony_ci	}
1858c2ecf20Sopenharmony_ci}
1868c2ecf20Sopenharmony_ci
1878c2ecf20Sopenharmony_cistatic void l2c210_inv_range(unsigned long start, unsigned long end)
1888c2ecf20Sopenharmony_ci{
1898c2ecf20Sopenharmony_ci	void __iomem *base = l2x0_base;
1908c2ecf20Sopenharmony_ci
1918c2ecf20Sopenharmony_ci	if (start & (CACHE_LINE_SIZE - 1)) {
1928c2ecf20Sopenharmony_ci		start &= ~(CACHE_LINE_SIZE - 1);
1938c2ecf20Sopenharmony_ci		writel_relaxed(start, base + L2X0_CLEAN_INV_LINE_PA);
1948c2ecf20Sopenharmony_ci		start += CACHE_LINE_SIZE;
1958c2ecf20Sopenharmony_ci	}
1968c2ecf20Sopenharmony_ci
1978c2ecf20Sopenharmony_ci	if (end & (CACHE_LINE_SIZE - 1)) {
1988c2ecf20Sopenharmony_ci		end &= ~(CACHE_LINE_SIZE - 1);
1998c2ecf20Sopenharmony_ci		writel_relaxed(end, base + L2X0_CLEAN_INV_LINE_PA);
2008c2ecf20Sopenharmony_ci	}
2018c2ecf20Sopenharmony_ci
2028c2ecf20Sopenharmony_ci	__l2c210_op_pa_range(base + L2X0_INV_LINE_PA, start, end);
2038c2ecf20Sopenharmony_ci	__l2c210_cache_sync(base);
2048c2ecf20Sopenharmony_ci}
2058c2ecf20Sopenharmony_ci
2068c2ecf20Sopenharmony_cistatic void l2c210_clean_range(unsigned long start, unsigned long end)
2078c2ecf20Sopenharmony_ci{
2088c2ecf20Sopenharmony_ci	void __iomem *base = l2x0_base;
2098c2ecf20Sopenharmony_ci
2108c2ecf20Sopenharmony_ci	start &= ~(CACHE_LINE_SIZE - 1);
2118c2ecf20Sopenharmony_ci	__l2c210_op_pa_range(base + L2X0_CLEAN_LINE_PA, start, end);
2128c2ecf20Sopenharmony_ci	__l2c210_cache_sync(base);
2138c2ecf20Sopenharmony_ci}
2148c2ecf20Sopenharmony_ci
2158c2ecf20Sopenharmony_cistatic void l2c210_flush_range(unsigned long start, unsigned long end)
2168c2ecf20Sopenharmony_ci{
2178c2ecf20Sopenharmony_ci	void __iomem *base = l2x0_base;
2188c2ecf20Sopenharmony_ci
2198c2ecf20Sopenharmony_ci	start &= ~(CACHE_LINE_SIZE - 1);
2208c2ecf20Sopenharmony_ci	__l2c210_op_pa_range(base + L2X0_CLEAN_INV_LINE_PA, start, end);
2218c2ecf20Sopenharmony_ci	__l2c210_cache_sync(base);
2228c2ecf20Sopenharmony_ci}
2238c2ecf20Sopenharmony_ci
2248c2ecf20Sopenharmony_cistatic void l2c210_flush_all(void)
2258c2ecf20Sopenharmony_ci{
2268c2ecf20Sopenharmony_ci	void __iomem *base = l2x0_base;
2278c2ecf20Sopenharmony_ci
2288c2ecf20Sopenharmony_ci	BUG_ON(!irqs_disabled());
2298c2ecf20Sopenharmony_ci
2308c2ecf20Sopenharmony_ci	__l2c_op_way(base + L2X0_CLEAN_INV_WAY);
2318c2ecf20Sopenharmony_ci	__l2c210_cache_sync(base);
2328c2ecf20Sopenharmony_ci}
2338c2ecf20Sopenharmony_ci
2348c2ecf20Sopenharmony_cistatic void l2c210_sync(void)
2358c2ecf20Sopenharmony_ci{
2368c2ecf20Sopenharmony_ci	__l2c210_cache_sync(l2x0_base);
2378c2ecf20Sopenharmony_ci}
2388c2ecf20Sopenharmony_ci
2398c2ecf20Sopenharmony_cistatic const struct l2c_init_data l2c210_data __initconst = {
2408c2ecf20Sopenharmony_ci	.type = "L2C-210",
2418c2ecf20Sopenharmony_ci	.way_size_0 = SZ_8K,
2428c2ecf20Sopenharmony_ci	.num_lock = 1,
2438c2ecf20Sopenharmony_ci	.enable = l2c_enable,
2448c2ecf20Sopenharmony_ci	.save = l2c_save,
2458c2ecf20Sopenharmony_ci	.configure = l2c_configure,
2468c2ecf20Sopenharmony_ci	.unlock = l2c_unlock,
2478c2ecf20Sopenharmony_ci	.outer_cache = {
2488c2ecf20Sopenharmony_ci		.inv_range = l2c210_inv_range,
2498c2ecf20Sopenharmony_ci		.clean_range = l2c210_clean_range,
2508c2ecf20Sopenharmony_ci		.flush_range = l2c210_flush_range,
2518c2ecf20Sopenharmony_ci		.flush_all = l2c210_flush_all,
2528c2ecf20Sopenharmony_ci		.disable = l2c_disable,
2538c2ecf20Sopenharmony_ci		.sync = l2c210_sync,
2548c2ecf20Sopenharmony_ci		.resume = l2c_resume,
2558c2ecf20Sopenharmony_ci	},
2568c2ecf20Sopenharmony_ci};
2578c2ecf20Sopenharmony_ci
2588c2ecf20Sopenharmony_ci/*
2598c2ecf20Sopenharmony_ci * L2C-220 specific code.
2608c2ecf20Sopenharmony_ci *
2618c2ecf20Sopenharmony_ci * All operations are background operations: they have to be waited for.
2628c2ecf20Sopenharmony_ci * Conflicting requests generate a slave error (which will cause an
2638c2ecf20Sopenharmony_ci * imprecise abort.)  Never uses sync_reg_offset, so we hard-code the
2648c2ecf20Sopenharmony_ci * sync register here.
2658c2ecf20Sopenharmony_ci *
2668c2ecf20Sopenharmony_ci * However, we can re-use the l2c210_resume call.
2678c2ecf20Sopenharmony_ci */
2688c2ecf20Sopenharmony_cistatic inline void __l2c220_cache_sync(void __iomem *base)
2698c2ecf20Sopenharmony_ci{
2708c2ecf20Sopenharmony_ci	writel_relaxed(0, base + L2X0_CACHE_SYNC);
2718c2ecf20Sopenharmony_ci	l2c_wait_mask(base + L2X0_CACHE_SYNC, 1);
2728c2ecf20Sopenharmony_ci}
2738c2ecf20Sopenharmony_ci
2748c2ecf20Sopenharmony_cistatic void l2c220_op_way(void __iomem *base, unsigned reg)
2758c2ecf20Sopenharmony_ci{
2768c2ecf20Sopenharmony_ci	unsigned long flags;
2778c2ecf20Sopenharmony_ci
2788c2ecf20Sopenharmony_ci	raw_spin_lock_irqsave(&l2x0_lock, flags);
2798c2ecf20Sopenharmony_ci	__l2c_op_way(base + reg);
2808c2ecf20Sopenharmony_ci	__l2c220_cache_sync(base);
2818c2ecf20Sopenharmony_ci	raw_spin_unlock_irqrestore(&l2x0_lock, flags);
2828c2ecf20Sopenharmony_ci}
2838c2ecf20Sopenharmony_ci
2848c2ecf20Sopenharmony_cistatic unsigned long l2c220_op_pa_range(void __iomem *reg, unsigned long start,
2858c2ecf20Sopenharmony_ci	unsigned long end, unsigned long flags)
2868c2ecf20Sopenharmony_ci{
2878c2ecf20Sopenharmony_ci	raw_spinlock_t *lock = &l2x0_lock;
2888c2ecf20Sopenharmony_ci
2898c2ecf20Sopenharmony_ci	while (start < end) {
2908c2ecf20Sopenharmony_ci		unsigned long blk_end = start + min(end - start, 4096UL);
2918c2ecf20Sopenharmony_ci
2928c2ecf20Sopenharmony_ci		while (start < blk_end) {
2938c2ecf20Sopenharmony_ci			l2c_wait_mask(reg, 1);
2948c2ecf20Sopenharmony_ci			writel_relaxed(start, reg);
2958c2ecf20Sopenharmony_ci			start += CACHE_LINE_SIZE;
2968c2ecf20Sopenharmony_ci		}
2978c2ecf20Sopenharmony_ci
2988c2ecf20Sopenharmony_ci		if (blk_end < end) {
2998c2ecf20Sopenharmony_ci			raw_spin_unlock_irqrestore(lock, flags);
3008c2ecf20Sopenharmony_ci			raw_spin_lock_irqsave(lock, flags);
3018c2ecf20Sopenharmony_ci		}
3028c2ecf20Sopenharmony_ci	}
3038c2ecf20Sopenharmony_ci
3048c2ecf20Sopenharmony_ci	return flags;
3058c2ecf20Sopenharmony_ci}
3068c2ecf20Sopenharmony_ci
3078c2ecf20Sopenharmony_cistatic void l2c220_inv_range(unsigned long start, unsigned long end)
3088c2ecf20Sopenharmony_ci{
3098c2ecf20Sopenharmony_ci	void __iomem *base = l2x0_base;
3108c2ecf20Sopenharmony_ci	unsigned long flags;
3118c2ecf20Sopenharmony_ci
3128c2ecf20Sopenharmony_ci	raw_spin_lock_irqsave(&l2x0_lock, flags);
3138c2ecf20Sopenharmony_ci	if ((start | end) & (CACHE_LINE_SIZE - 1)) {
3148c2ecf20Sopenharmony_ci		if (start & (CACHE_LINE_SIZE - 1)) {
3158c2ecf20Sopenharmony_ci			start &= ~(CACHE_LINE_SIZE - 1);
3168c2ecf20Sopenharmony_ci			writel_relaxed(start, base + L2X0_CLEAN_INV_LINE_PA);
3178c2ecf20Sopenharmony_ci			start += CACHE_LINE_SIZE;
3188c2ecf20Sopenharmony_ci		}
3198c2ecf20Sopenharmony_ci
3208c2ecf20Sopenharmony_ci		if (end & (CACHE_LINE_SIZE - 1)) {
3218c2ecf20Sopenharmony_ci			end &= ~(CACHE_LINE_SIZE - 1);
3228c2ecf20Sopenharmony_ci			l2c_wait_mask(base + L2X0_CLEAN_INV_LINE_PA, 1);
3238c2ecf20Sopenharmony_ci			writel_relaxed(end, base + L2X0_CLEAN_INV_LINE_PA);
3248c2ecf20Sopenharmony_ci		}
3258c2ecf20Sopenharmony_ci	}
3268c2ecf20Sopenharmony_ci
3278c2ecf20Sopenharmony_ci	flags = l2c220_op_pa_range(base + L2X0_INV_LINE_PA,
3288c2ecf20Sopenharmony_ci				   start, end, flags);
3298c2ecf20Sopenharmony_ci	l2c_wait_mask(base + L2X0_INV_LINE_PA, 1);
3308c2ecf20Sopenharmony_ci	__l2c220_cache_sync(base);
3318c2ecf20Sopenharmony_ci	raw_spin_unlock_irqrestore(&l2x0_lock, flags);
3328c2ecf20Sopenharmony_ci}
3338c2ecf20Sopenharmony_ci
3348c2ecf20Sopenharmony_cistatic void l2c220_clean_range(unsigned long start, unsigned long end)
3358c2ecf20Sopenharmony_ci{
3368c2ecf20Sopenharmony_ci	void __iomem *base = l2x0_base;
3378c2ecf20Sopenharmony_ci	unsigned long flags;
3388c2ecf20Sopenharmony_ci
3398c2ecf20Sopenharmony_ci	start &= ~(CACHE_LINE_SIZE - 1);
3408c2ecf20Sopenharmony_ci	if ((end - start) >= l2x0_size) {
3418c2ecf20Sopenharmony_ci		l2c220_op_way(base, L2X0_CLEAN_WAY);
3428c2ecf20Sopenharmony_ci		return;
3438c2ecf20Sopenharmony_ci	}
3448c2ecf20Sopenharmony_ci
3458c2ecf20Sopenharmony_ci	raw_spin_lock_irqsave(&l2x0_lock, flags);
3468c2ecf20Sopenharmony_ci	flags = l2c220_op_pa_range(base + L2X0_CLEAN_LINE_PA,
3478c2ecf20Sopenharmony_ci				   start, end, flags);
3488c2ecf20Sopenharmony_ci	l2c_wait_mask(base + L2X0_CLEAN_INV_LINE_PA, 1);
3498c2ecf20Sopenharmony_ci	__l2c220_cache_sync(base);
3508c2ecf20Sopenharmony_ci	raw_spin_unlock_irqrestore(&l2x0_lock, flags);
3518c2ecf20Sopenharmony_ci}
3528c2ecf20Sopenharmony_ci
3538c2ecf20Sopenharmony_cistatic void l2c220_flush_range(unsigned long start, unsigned long end)
3548c2ecf20Sopenharmony_ci{
3558c2ecf20Sopenharmony_ci	void __iomem *base = l2x0_base;
3568c2ecf20Sopenharmony_ci	unsigned long flags;
3578c2ecf20Sopenharmony_ci
3588c2ecf20Sopenharmony_ci	start &= ~(CACHE_LINE_SIZE - 1);
3598c2ecf20Sopenharmony_ci	if ((end - start) >= l2x0_size) {
3608c2ecf20Sopenharmony_ci		l2c220_op_way(base, L2X0_CLEAN_INV_WAY);
3618c2ecf20Sopenharmony_ci		return;
3628c2ecf20Sopenharmony_ci	}
3638c2ecf20Sopenharmony_ci
3648c2ecf20Sopenharmony_ci	raw_spin_lock_irqsave(&l2x0_lock, flags);
3658c2ecf20Sopenharmony_ci	flags = l2c220_op_pa_range(base + L2X0_CLEAN_INV_LINE_PA,
3668c2ecf20Sopenharmony_ci				   start, end, flags);
3678c2ecf20Sopenharmony_ci	l2c_wait_mask(base + L2X0_CLEAN_INV_LINE_PA, 1);
3688c2ecf20Sopenharmony_ci	__l2c220_cache_sync(base);
3698c2ecf20Sopenharmony_ci	raw_spin_unlock_irqrestore(&l2x0_lock, flags);
3708c2ecf20Sopenharmony_ci}
3718c2ecf20Sopenharmony_ci
3728c2ecf20Sopenharmony_cistatic void l2c220_flush_all(void)
3738c2ecf20Sopenharmony_ci{
3748c2ecf20Sopenharmony_ci	l2c220_op_way(l2x0_base, L2X0_CLEAN_INV_WAY);
3758c2ecf20Sopenharmony_ci}
3768c2ecf20Sopenharmony_ci
3778c2ecf20Sopenharmony_cistatic void l2c220_sync(void)
3788c2ecf20Sopenharmony_ci{
3798c2ecf20Sopenharmony_ci	unsigned long flags;
3808c2ecf20Sopenharmony_ci
3818c2ecf20Sopenharmony_ci	raw_spin_lock_irqsave(&l2x0_lock, flags);
3828c2ecf20Sopenharmony_ci	__l2c220_cache_sync(l2x0_base);
3838c2ecf20Sopenharmony_ci	raw_spin_unlock_irqrestore(&l2x0_lock, flags);
3848c2ecf20Sopenharmony_ci}
3858c2ecf20Sopenharmony_ci
3868c2ecf20Sopenharmony_cistatic void l2c220_enable(void __iomem *base, unsigned num_lock)
3878c2ecf20Sopenharmony_ci{
3888c2ecf20Sopenharmony_ci	/*
3898c2ecf20Sopenharmony_ci	 * Always enable non-secure access to the lockdown registers -
3908c2ecf20Sopenharmony_ci	 * we write to them as part of the L2C enable sequence so they
3918c2ecf20Sopenharmony_ci	 * need to be accessible.
3928c2ecf20Sopenharmony_ci	 */
3938c2ecf20Sopenharmony_ci	l2x0_saved_regs.aux_ctrl |= L220_AUX_CTRL_NS_LOCKDOWN;
3948c2ecf20Sopenharmony_ci
3958c2ecf20Sopenharmony_ci	l2c_enable(base, num_lock);
3968c2ecf20Sopenharmony_ci}
3978c2ecf20Sopenharmony_ci
3988c2ecf20Sopenharmony_cistatic void l2c220_unlock(void __iomem *base, unsigned num_lock)
3998c2ecf20Sopenharmony_ci{
4008c2ecf20Sopenharmony_ci	if (readl_relaxed(base + L2X0_AUX_CTRL) & L220_AUX_CTRL_NS_LOCKDOWN)
4018c2ecf20Sopenharmony_ci		l2c_unlock(base, num_lock);
4028c2ecf20Sopenharmony_ci}
4038c2ecf20Sopenharmony_ci
4048c2ecf20Sopenharmony_cistatic const struct l2c_init_data l2c220_data = {
4058c2ecf20Sopenharmony_ci	.type = "L2C-220",
4068c2ecf20Sopenharmony_ci	.way_size_0 = SZ_8K,
4078c2ecf20Sopenharmony_ci	.num_lock = 1,
4088c2ecf20Sopenharmony_ci	.enable = l2c220_enable,
4098c2ecf20Sopenharmony_ci	.save = l2c_save,
4108c2ecf20Sopenharmony_ci	.configure = l2c_configure,
4118c2ecf20Sopenharmony_ci	.unlock = l2c220_unlock,
4128c2ecf20Sopenharmony_ci	.outer_cache = {
4138c2ecf20Sopenharmony_ci		.inv_range = l2c220_inv_range,
4148c2ecf20Sopenharmony_ci		.clean_range = l2c220_clean_range,
4158c2ecf20Sopenharmony_ci		.flush_range = l2c220_flush_range,
4168c2ecf20Sopenharmony_ci		.flush_all = l2c220_flush_all,
4178c2ecf20Sopenharmony_ci		.disable = l2c_disable,
4188c2ecf20Sopenharmony_ci		.sync = l2c220_sync,
4198c2ecf20Sopenharmony_ci		.resume = l2c_resume,
4208c2ecf20Sopenharmony_ci	},
4218c2ecf20Sopenharmony_ci};
4228c2ecf20Sopenharmony_ci
4238c2ecf20Sopenharmony_ci/*
4248c2ecf20Sopenharmony_ci * L2C-310 specific code.
4258c2ecf20Sopenharmony_ci *
4268c2ecf20Sopenharmony_ci * Very similar to L2C-210, the PA, set/way and sync operations are atomic,
4278c2ecf20Sopenharmony_ci * and the way operations are all background tasks.  However, issuing an
4288c2ecf20Sopenharmony_ci * operation while a background operation is in progress results in a
4298c2ecf20Sopenharmony_ci * SLVERR response.  We can reuse:
4308c2ecf20Sopenharmony_ci *
4318c2ecf20Sopenharmony_ci *  __l2c210_cache_sync (using sync_reg_offset)
4328c2ecf20Sopenharmony_ci *  l2c210_sync
4338c2ecf20Sopenharmony_ci *  l2c210_inv_range (if 588369 is not applicable)
4348c2ecf20Sopenharmony_ci *  l2c210_clean_range
4358c2ecf20Sopenharmony_ci *  l2c210_flush_range (if 588369 is not applicable)
4368c2ecf20Sopenharmony_ci *  l2c210_flush_all (if 727915 is not applicable)
4378c2ecf20Sopenharmony_ci *
4388c2ecf20Sopenharmony_ci * Errata:
4398c2ecf20Sopenharmony_ci * 588369: PL310 R0P0->R1P0, fixed R2P0.
4408c2ecf20Sopenharmony_ci *	Affects: all clean+invalidate operations
4418c2ecf20Sopenharmony_ci *	clean and invalidate skips the invalidate step, so we need to issue
4428c2ecf20Sopenharmony_ci *	separate operations.  We also require the above debug workaround
4438c2ecf20Sopenharmony_ci *	enclosing this code fragment on affected parts.  On unaffected parts,
4448c2ecf20Sopenharmony_ci *	we must not use this workaround without the debug register writes
4458c2ecf20Sopenharmony_ci *	to avoid exposing a problem similar to 727915.
4468c2ecf20Sopenharmony_ci *
4478c2ecf20Sopenharmony_ci * 727915: PL310 R2P0->R3P0, fixed R3P1.
4488c2ecf20Sopenharmony_ci *	Affects: clean+invalidate by way
4498c2ecf20Sopenharmony_ci *	clean and invalidate by way runs in the background, and a store can
4508c2ecf20Sopenharmony_ci *	hit the line between the clean operation and invalidate operation,
4518c2ecf20Sopenharmony_ci *	resulting in the store being lost.
4528c2ecf20Sopenharmony_ci *
4538c2ecf20Sopenharmony_ci * 752271: PL310 R3P0->R3P1-50REL0, fixed R3P2.
4548c2ecf20Sopenharmony_ci *	Affects: 8x64-bit (double fill) line fetches
4558c2ecf20Sopenharmony_ci *	double fill line fetches can fail to cause dirty data to be evicted
4568c2ecf20Sopenharmony_ci *	from the cache before the new data overwrites the second line.
4578c2ecf20Sopenharmony_ci *
4588c2ecf20Sopenharmony_ci * 753970: PL310 R3P0, fixed R3P1.
4598c2ecf20Sopenharmony_ci *	Affects: sync
4608c2ecf20Sopenharmony_ci *	prevents merging writes after the sync operation, until another L2C
4618c2ecf20Sopenharmony_ci *	operation is performed (or a number of other conditions.)
4628c2ecf20Sopenharmony_ci *
4638c2ecf20Sopenharmony_ci * 769419: PL310 R0P0->R3P1, fixed R3P2.
4648c2ecf20Sopenharmony_ci *	Affects: store buffer
4658c2ecf20Sopenharmony_ci *	store buffer is not automatically drained.
4668c2ecf20Sopenharmony_ci */
4678c2ecf20Sopenharmony_cistatic void l2c310_inv_range_erratum(unsigned long start, unsigned long end)
4688c2ecf20Sopenharmony_ci{
4698c2ecf20Sopenharmony_ci	void __iomem *base = l2x0_base;
4708c2ecf20Sopenharmony_ci
4718c2ecf20Sopenharmony_ci	if ((start | end) & (CACHE_LINE_SIZE - 1)) {
4728c2ecf20Sopenharmony_ci		unsigned long flags;
4738c2ecf20Sopenharmony_ci
4748c2ecf20Sopenharmony_ci		/* Erratum 588369 for both clean+invalidate operations */
4758c2ecf20Sopenharmony_ci		raw_spin_lock_irqsave(&l2x0_lock, flags);
4768c2ecf20Sopenharmony_ci		l2c_set_debug(base, 0x03);
4778c2ecf20Sopenharmony_ci
4788c2ecf20Sopenharmony_ci		if (start & (CACHE_LINE_SIZE - 1)) {
4798c2ecf20Sopenharmony_ci			start &= ~(CACHE_LINE_SIZE - 1);
4808c2ecf20Sopenharmony_ci			writel_relaxed(start, base + L2X0_CLEAN_LINE_PA);
4818c2ecf20Sopenharmony_ci			writel_relaxed(start, base + L2X0_INV_LINE_PA);
4828c2ecf20Sopenharmony_ci			start += CACHE_LINE_SIZE;
4838c2ecf20Sopenharmony_ci		}
4848c2ecf20Sopenharmony_ci
4858c2ecf20Sopenharmony_ci		if (end & (CACHE_LINE_SIZE - 1)) {
4868c2ecf20Sopenharmony_ci			end &= ~(CACHE_LINE_SIZE - 1);
4878c2ecf20Sopenharmony_ci			writel_relaxed(end, base + L2X0_CLEAN_LINE_PA);
4888c2ecf20Sopenharmony_ci			writel_relaxed(end, base + L2X0_INV_LINE_PA);
4898c2ecf20Sopenharmony_ci		}
4908c2ecf20Sopenharmony_ci
4918c2ecf20Sopenharmony_ci		l2c_set_debug(base, 0x00);
4928c2ecf20Sopenharmony_ci		raw_spin_unlock_irqrestore(&l2x0_lock, flags);
4938c2ecf20Sopenharmony_ci	}
4948c2ecf20Sopenharmony_ci
4958c2ecf20Sopenharmony_ci	__l2c210_op_pa_range(base + L2X0_INV_LINE_PA, start, end);
4968c2ecf20Sopenharmony_ci	__l2c210_cache_sync(base);
4978c2ecf20Sopenharmony_ci}
4988c2ecf20Sopenharmony_ci
4998c2ecf20Sopenharmony_cistatic void l2c310_flush_range_erratum(unsigned long start, unsigned long end)
5008c2ecf20Sopenharmony_ci{
5018c2ecf20Sopenharmony_ci	raw_spinlock_t *lock = &l2x0_lock;
5028c2ecf20Sopenharmony_ci	unsigned long flags;
5038c2ecf20Sopenharmony_ci	void __iomem *base = l2x0_base;
5048c2ecf20Sopenharmony_ci
5058c2ecf20Sopenharmony_ci	raw_spin_lock_irqsave(lock, flags);
5068c2ecf20Sopenharmony_ci	while (start < end) {
5078c2ecf20Sopenharmony_ci		unsigned long blk_end = start + min(end - start, 4096UL);
5088c2ecf20Sopenharmony_ci
5098c2ecf20Sopenharmony_ci		l2c_set_debug(base, 0x03);
5108c2ecf20Sopenharmony_ci		while (start < blk_end) {
5118c2ecf20Sopenharmony_ci			writel_relaxed(start, base + L2X0_CLEAN_LINE_PA);
5128c2ecf20Sopenharmony_ci			writel_relaxed(start, base + L2X0_INV_LINE_PA);
5138c2ecf20Sopenharmony_ci			start += CACHE_LINE_SIZE;
5148c2ecf20Sopenharmony_ci		}
5158c2ecf20Sopenharmony_ci		l2c_set_debug(base, 0x00);
5168c2ecf20Sopenharmony_ci
5178c2ecf20Sopenharmony_ci		if (blk_end < end) {
5188c2ecf20Sopenharmony_ci			raw_spin_unlock_irqrestore(lock, flags);
5198c2ecf20Sopenharmony_ci			raw_spin_lock_irqsave(lock, flags);
5208c2ecf20Sopenharmony_ci		}
5218c2ecf20Sopenharmony_ci	}
5228c2ecf20Sopenharmony_ci	raw_spin_unlock_irqrestore(lock, flags);
5238c2ecf20Sopenharmony_ci	__l2c210_cache_sync(base);
5248c2ecf20Sopenharmony_ci}
5258c2ecf20Sopenharmony_ci
5268c2ecf20Sopenharmony_cistatic void l2c310_flush_all_erratum(void)
5278c2ecf20Sopenharmony_ci{
5288c2ecf20Sopenharmony_ci	void __iomem *base = l2x0_base;
5298c2ecf20Sopenharmony_ci	unsigned long flags;
5308c2ecf20Sopenharmony_ci
5318c2ecf20Sopenharmony_ci	raw_spin_lock_irqsave(&l2x0_lock, flags);
5328c2ecf20Sopenharmony_ci	l2c_set_debug(base, 0x03);
5338c2ecf20Sopenharmony_ci	__l2c_op_way(base + L2X0_CLEAN_INV_WAY);
5348c2ecf20Sopenharmony_ci	l2c_set_debug(base, 0x00);
5358c2ecf20Sopenharmony_ci	__l2c210_cache_sync(base);
5368c2ecf20Sopenharmony_ci	raw_spin_unlock_irqrestore(&l2x0_lock, flags);
5378c2ecf20Sopenharmony_ci}
5388c2ecf20Sopenharmony_ci
5398c2ecf20Sopenharmony_cistatic void __init l2c310_save(void __iomem *base)
5408c2ecf20Sopenharmony_ci{
5418c2ecf20Sopenharmony_ci	unsigned revision;
5428c2ecf20Sopenharmony_ci
5438c2ecf20Sopenharmony_ci	l2c_save(base);
5448c2ecf20Sopenharmony_ci
5458c2ecf20Sopenharmony_ci	l2x0_saved_regs.tag_latency = readl_relaxed(base +
5468c2ecf20Sopenharmony_ci		L310_TAG_LATENCY_CTRL);
5478c2ecf20Sopenharmony_ci	l2x0_saved_regs.data_latency = readl_relaxed(base +
5488c2ecf20Sopenharmony_ci		L310_DATA_LATENCY_CTRL);
5498c2ecf20Sopenharmony_ci	l2x0_saved_regs.filter_end = readl_relaxed(base +
5508c2ecf20Sopenharmony_ci		L310_ADDR_FILTER_END);
5518c2ecf20Sopenharmony_ci	l2x0_saved_regs.filter_start = readl_relaxed(base +
5528c2ecf20Sopenharmony_ci		L310_ADDR_FILTER_START);
5538c2ecf20Sopenharmony_ci
5548c2ecf20Sopenharmony_ci	revision = readl_relaxed(base + L2X0_CACHE_ID) &
5558c2ecf20Sopenharmony_ci			L2X0_CACHE_ID_RTL_MASK;
5568c2ecf20Sopenharmony_ci
5578c2ecf20Sopenharmony_ci	/* From r2p0, there is Prefetch offset/control register */
5588c2ecf20Sopenharmony_ci	if (revision >= L310_CACHE_ID_RTL_R2P0)
5598c2ecf20Sopenharmony_ci		l2x0_saved_regs.prefetch_ctrl = readl_relaxed(base +
5608c2ecf20Sopenharmony_ci							L310_PREFETCH_CTRL);
5618c2ecf20Sopenharmony_ci
5628c2ecf20Sopenharmony_ci	/* From r3p0, there is Power control register */
5638c2ecf20Sopenharmony_ci	if (revision >= L310_CACHE_ID_RTL_R3P0)
5648c2ecf20Sopenharmony_ci		l2x0_saved_regs.pwr_ctrl = readl_relaxed(base +
5658c2ecf20Sopenharmony_ci							L310_POWER_CTRL);
5668c2ecf20Sopenharmony_ci}
5678c2ecf20Sopenharmony_ci
5688c2ecf20Sopenharmony_cistatic void l2c310_configure(void __iomem *base)
5698c2ecf20Sopenharmony_ci{
5708c2ecf20Sopenharmony_ci	unsigned revision;
5718c2ecf20Sopenharmony_ci
5728c2ecf20Sopenharmony_ci	l2c_configure(base);
5738c2ecf20Sopenharmony_ci
5748c2ecf20Sopenharmony_ci	/* restore pl310 setup */
5758c2ecf20Sopenharmony_ci	l2c_write_sec(l2x0_saved_regs.tag_latency, base,
5768c2ecf20Sopenharmony_ci		      L310_TAG_LATENCY_CTRL);
5778c2ecf20Sopenharmony_ci	l2c_write_sec(l2x0_saved_regs.data_latency, base,
5788c2ecf20Sopenharmony_ci		      L310_DATA_LATENCY_CTRL);
5798c2ecf20Sopenharmony_ci	l2c_write_sec(l2x0_saved_regs.filter_end, base,
5808c2ecf20Sopenharmony_ci		      L310_ADDR_FILTER_END);
5818c2ecf20Sopenharmony_ci	l2c_write_sec(l2x0_saved_regs.filter_start, base,
5828c2ecf20Sopenharmony_ci		      L310_ADDR_FILTER_START);
5838c2ecf20Sopenharmony_ci
5848c2ecf20Sopenharmony_ci	revision = readl_relaxed(base + L2X0_CACHE_ID) &
5858c2ecf20Sopenharmony_ci				 L2X0_CACHE_ID_RTL_MASK;
5868c2ecf20Sopenharmony_ci
5878c2ecf20Sopenharmony_ci	if (revision >= L310_CACHE_ID_RTL_R2P0)
5888c2ecf20Sopenharmony_ci		l2c_write_sec(l2x0_saved_regs.prefetch_ctrl, base,
5898c2ecf20Sopenharmony_ci			      L310_PREFETCH_CTRL);
5908c2ecf20Sopenharmony_ci	if (revision >= L310_CACHE_ID_RTL_R3P0)
5918c2ecf20Sopenharmony_ci		l2c_write_sec(l2x0_saved_regs.pwr_ctrl, base,
5928c2ecf20Sopenharmony_ci			      L310_POWER_CTRL);
5938c2ecf20Sopenharmony_ci}
5948c2ecf20Sopenharmony_ci
5958c2ecf20Sopenharmony_cistatic int l2c310_starting_cpu(unsigned int cpu)
5968c2ecf20Sopenharmony_ci{
5978c2ecf20Sopenharmony_ci	set_auxcr(get_auxcr() | BIT(3) | BIT(2) | BIT(1));
5988c2ecf20Sopenharmony_ci	return 0;
5998c2ecf20Sopenharmony_ci}
6008c2ecf20Sopenharmony_ci
6018c2ecf20Sopenharmony_cistatic int l2c310_dying_cpu(unsigned int cpu)
6028c2ecf20Sopenharmony_ci{
6038c2ecf20Sopenharmony_ci	set_auxcr(get_auxcr() & ~(BIT(3) | BIT(2) | BIT(1)));
6048c2ecf20Sopenharmony_ci	return 0;
6058c2ecf20Sopenharmony_ci}
6068c2ecf20Sopenharmony_ci
6078c2ecf20Sopenharmony_cistatic void __init l2c310_enable(void __iomem *base, unsigned num_lock)
6088c2ecf20Sopenharmony_ci{
6098c2ecf20Sopenharmony_ci	unsigned rev = readl_relaxed(base + L2X0_CACHE_ID) & L2X0_CACHE_ID_RTL_MASK;
6108c2ecf20Sopenharmony_ci	bool cortex_a9 = read_cpuid_part() == ARM_CPU_PART_CORTEX_A9;
6118c2ecf20Sopenharmony_ci	u32 aux = l2x0_saved_regs.aux_ctrl;
6128c2ecf20Sopenharmony_ci
6138c2ecf20Sopenharmony_ci	if (rev >= L310_CACHE_ID_RTL_R2P0) {
6148c2ecf20Sopenharmony_ci		if (cortex_a9 && !l2x0_bresp_disable) {
6158c2ecf20Sopenharmony_ci			aux |= L310_AUX_CTRL_EARLY_BRESP;
6168c2ecf20Sopenharmony_ci			pr_info("L2C-310 enabling early BRESP for Cortex-A9\n");
6178c2ecf20Sopenharmony_ci		} else if (aux & L310_AUX_CTRL_EARLY_BRESP) {
6188c2ecf20Sopenharmony_ci			pr_warn("L2C-310 early BRESP only supported with Cortex-A9\n");
6198c2ecf20Sopenharmony_ci			aux &= ~L310_AUX_CTRL_EARLY_BRESP;
6208c2ecf20Sopenharmony_ci		}
6218c2ecf20Sopenharmony_ci	}
6228c2ecf20Sopenharmony_ci
6238c2ecf20Sopenharmony_ci	if (cortex_a9 && !l2x0_flz_disable) {
6248c2ecf20Sopenharmony_ci		u32 aux_cur = readl_relaxed(base + L2X0_AUX_CTRL);
6258c2ecf20Sopenharmony_ci		u32 acr = get_auxcr();
6268c2ecf20Sopenharmony_ci
6278c2ecf20Sopenharmony_ci		pr_debug("Cortex-A9 ACR=0x%08x\n", acr);
6288c2ecf20Sopenharmony_ci
6298c2ecf20Sopenharmony_ci		if (acr & BIT(3) && !(aux_cur & L310_AUX_CTRL_FULL_LINE_ZERO))
6308c2ecf20Sopenharmony_ci			pr_err("L2C-310: full line of zeros enabled in Cortex-A9 but not L2C-310 - invalid\n");
6318c2ecf20Sopenharmony_ci
6328c2ecf20Sopenharmony_ci		if (aux & L310_AUX_CTRL_FULL_LINE_ZERO && !(acr & BIT(3)))
6338c2ecf20Sopenharmony_ci			pr_err("L2C-310: enabling full line of zeros but not enabled in Cortex-A9\n");
6348c2ecf20Sopenharmony_ci
6358c2ecf20Sopenharmony_ci		if (!(aux & L310_AUX_CTRL_FULL_LINE_ZERO) && !outer_cache.write_sec) {
6368c2ecf20Sopenharmony_ci			aux |= L310_AUX_CTRL_FULL_LINE_ZERO;
6378c2ecf20Sopenharmony_ci			pr_info("L2C-310 full line of zeros enabled for Cortex-A9\n");
6388c2ecf20Sopenharmony_ci		}
6398c2ecf20Sopenharmony_ci	} else if (aux & (L310_AUX_CTRL_FULL_LINE_ZERO | L310_AUX_CTRL_EARLY_BRESP)) {
6408c2ecf20Sopenharmony_ci		pr_err("L2C-310: disabling Cortex-A9 specific feature bits\n");
6418c2ecf20Sopenharmony_ci		aux &= ~(L310_AUX_CTRL_FULL_LINE_ZERO | L310_AUX_CTRL_EARLY_BRESP);
6428c2ecf20Sopenharmony_ci	}
6438c2ecf20Sopenharmony_ci
6448c2ecf20Sopenharmony_ci	/*
6458c2ecf20Sopenharmony_ci	 * Always enable non-secure access to the lockdown registers -
6468c2ecf20Sopenharmony_ci	 * we write to them as part of the L2C enable sequence so they
6478c2ecf20Sopenharmony_ci	 * need to be accessible.
6488c2ecf20Sopenharmony_ci	 */
6498c2ecf20Sopenharmony_ci	l2x0_saved_regs.aux_ctrl = aux | L310_AUX_CTRL_NS_LOCKDOWN;
6508c2ecf20Sopenharmony_ci
6518c2ecf20Sopenharmony_ci	l2c_enable(base, num_lock);
6528c2ecf20Sopenharmony_ci
6538c2ecf20Sopenharmony_ci	/* Read back resulting AUX_CTRL value as it could have been altered. */
6548c2ecf20Sopenharmony_ci	aux = readl_relaxed(base + L2X0_AUX_CTRL);
6558c2ecf20Sopenharmony_ci
6568c2ecf20Sopenharmony_ci	if (aux & (L310_AUX_CTRL_DATA_PREFETCH | L310_AUX_CTRL_INSTR_PREFETCH)) {
6578c2ecf20Sopenharmony_ci		u32 prefetch = readl_relaxed(base + L310_PREFETCH_CTRL);
6588c2ecf20Sopenharmony_ci
6598c2ecf20Sopenharmony_ci		pr_info("L2C-310 %s%s prefetch enabled, offset %u lines\n",
6608c2ecf20Sopenharmony_ci			aux & L310_AUX_CTRL_INSTR_PREFETCH ? "I" : "",
6618c2ecf20Sopenharmony_ci			aux & L310_AUX_CTRL_DATA_PREFETCH ? "D" : "",
6628c2ecf20Sopenharmony_ci			1 + (prefetch & L310_PREFETCH_CTRL_OFFSET_MASK));
6638c2ecf20Sopenharmony_ci	}
6648c2ecf20Sopenharmony_ci
6658c2ecf20Sopenharmony_ci	/* r3p0 or later has power control register */
6668c2ecf20Sopenharmony_ci	if (rev >= L310_CACHE_ID_RTL_R3P0) {
6678c2ecf20Sopenharmony_ci		u32 power_ctrl;
6688c2ecf20Sopenharmony_ci
6698c2ecf20Sopenharmony_ci		power_ctrl = readl_relaxed(base + L310_POWER_CTRL);
6708c2ecf20Sopenharmony_ci		pr_info("L2C-310 dynamic clock gating %sabled, standby mode %sabled\n",
6718c2ecf20Sopenharmony_ci			power_ctrl & L310_DYNAMIC_CLK_GATING_EN ? "en" : "dis",
6728c2ecf20Sopenharmony_ci			power_ctrl & L310_STNDBY_MODE_EN ? "en" : "dis");
6738c2ecf20Sopenharmony_ci	}
6748c2ecf20Sopenharmony_ci
6758c2ecf20Sopenharmony_ci	if (aux & L310_AUX_CTRL_FULL_LINE_ZERO)
6768c2ecf20Sopenharmony_ci		cpuhp_setup_state(CPUHP_AP_ARM_L2X0_STARTING,
6778c2ecf20Sopenharmony_ci				  "arm/l2x0:starting", l2c310_starting_cpu,
6788c2ecf20Sopenharmony_ci				  l2c310_dying_cpu);
6798c2ecf20Sopenharmony_ci}
6808c2ecf20Sopenharmony_ci
6818c2ecf20Sopenharmony_cistatic void __init l2c310_fixup(void __iomem *base, u32 cache_id,
6828c2ecf20Sopenharmony_ci	struct outer_cache_fns *fns)
6838c2ecf20Sopenharmony_ci{
6848c2ecf20Sopenharmony_ci	unsigned revision = cache_id & L2X0_CACHE_ID_RTL_MASK;
6858c2ecf20Sopenharmony_ci	const char *errata[8];
6868c2ecf20Sopenharmony_ci	unsigned n = 0;
6878c2ecf20Sopenharmony_ci
6888c2ecf20Sopenharmony_ci	if (IS_ENABLED(CONFIG_PL310_ERRATA_588369) &&
6898c2ecf20Sopenharmony_ci	    revision < L310_CACHE_ID_RTL_R2P0 &&
6908c2ecf20Sopenharmony_ci	    /* For bcm compatibility */
6918c2ecf20Sopenharmony_ci	    fns->inv_range == l2c210_inv_range) {
6928c2ecf20Sopenharmony_ci		fns->inv_range = l2c310_inv_range_erratum;
6938c2ecf20Sopenharmony_ci		fns->flush_range = l2c310_flush_range_erratum;
6948c2ecf20Sopenharmony_ci		errata[n++] = "588369";
6958c2ecf20Sopenharmony_ci	}
6968c2ecf20Sopenharmony_ci
6978c2ecf20Sopenharmony_ci	if (IS_ENABLED(CONFIG_PL310_ERRATA_727915) &&
6988c2ecf20Sopenharmony_ci	    revision >= L310_CACHE_ID_RTL_R2P0 &&
6998c2ecf20Sopenharmony_ci	    revision < L310_CACHE_ID_RTL_R3P1) {
7008c2ecf20Sopenharmony_ci		fns->flush_all = l2c310_flush_all_erratum;
7018c2ecf20Sopenharmony_ci		errata[n++] = "727915";
7028c2ecf20Sopenharmony_ci	}
7038c2ecf20Sopenharmony_ci
7048c2ecf20Sopenharmony_ci	if (revision >= L310_CACHE_ID_RTL_R3P0 &&
7058c2ecf20Sopenharmony_ci	    revision < L310_CACHE_ID_RTL_R3P2) {
7068c2ecf20Sopenharmony_ci		u32 val = l2x0_saved_regs.prefetch_ctrl;
7078c2ecf20Sopenharmony_ci		if (val & L310_PREFETCH_CTRL_DBL_LINEFILL) {
7088c2ecf20Sopenharmony_ci			val &= ~L310_PREFETCH_CTRL_DBL_LINEFILL;
7098c2ecf20Sopenharmony_ci			l2x0_saved_regs.prefetch_ctrl = val;
7108c2ecf20Sopenharmony_ci			errata[n++] = "752271";
7118c2ecf20Sopenharmony_ci		}
7128c2ecf20Sopenharmony_ci	}
7138c2ecf20Sopenharmony_ci
7148c2ecf20Sopenharmony_ci	if (IS_ENABLED(CONFIG_PL310_ERRATA_753970) &&
7158c2ecf20Sopenharmony_ci	    revision == L310_CACHE_ID_RTL_R3P0) {
7168c2ecf20Sopenharmony_ci		sync_reg_offset = L2X0_DUMMY_REG;
7178c2ecf20Sopenharmony_ci		errata[n++] = "753970";
7188c2ecf20Sopenharmony_ci	}
7198c2ecf20Sopenharmony_ci
7208c2ecf20Sopenharmony_ci	if (IS_ENABLED(CONFIG_PL310_ERRATA_769419))
7218c2ecf20Sopenharmony_ci		errata[n++] = "769419";
7228c2ecf20Sopenharmony_ci
7238c2ecf20Sopenharmony_ci	if (n) {
7248c2ecf20Sopenharmony_ci		unsigned i;
7258c2ecf20Sopenharmony_ci
7268c2ecf20Sopenharmony_ci		pr_info("L2C-310 errat%s", n > 1 ? "a" : "um");
7278c2ecf20Sopenharmony_ci		for (i = 0; i < n; i++)
7288c2ecf20Sopenharmony_ci			pr_cont(" %s", errata[i]);
7298c2ecf20Sopenharmony_ci		pr_cont(" enabled\n");
7308c2ecf20Sopenharmony_ci	}
7318c2ecf20Sopenharmony_ci}
7328c2ecf20Sopenharmony_ci
7338c2ecf20Sopenharmony_cistatic void l2c310_disable(void)
7348c2ecf20Sopenharmony_ci{
7358c2ecf20Sopenharmony_ci	/*
7368c2ecf20Sopenharmony_ci	 * If full-line-of-zeros is enabled, we must first disable it in the
7378c2ecf20Sopenharmony_ci	 * Cortex-A9 auxiliary control register before disabling the L2 cache.
7388c2ecf20Sopenharmony_ci	 */
7398c2ecf20Sopenharmony_ci	if (l2x0_saved_regs.aux_ctrl & L310_AUX_CTRL_FULL_LINE_ZERO)
7408c2ecf20Sopenharmony_ci		set_auxcr(get_auxcr() & ~(BIT(3) | BIT(2) | BIT(1)));
7418c2ecf20Sopenharmony_ci
7428c2ecf20Sopenharmony_ci	l2c_disable();
7438c2ecf20Sopenharmony_ci}
7448c2ecf20Sopenharmony_ci
7458c2ecf20Sopenharmony_cistatic void l2c310_resume(void)
7468c2ecf20Sopenharmony_ci{
7478c2ecf20Sopenharmony_ci	l2c_resume();
7488c2ecf20Sopenharmony_ci
7498c2ecf20Sopenharmony_ci	/* Re-enable full-line-of-zeros for Cortex-A9 */
7508c2ecf20Sopenharmony_ci	if (l2x0_saved_regs.aux_ctrl & L310_AUX_CTRL_FULL_LINE_ZERO)
7518c2ecf20Sopenharmony_ci		set_auxcr(get_auxcr() | BIT(3) | BIT(2) | BIT(1));
7528c2ecf20Sopenharmony_ci}
7538c2ecf20Sopenharmony_ci
7548c2ecf20Sopenharmony_cistatic void l2c310_unlock(void __iomem *base, unsigned num_lock)
7558c2ecf20Sopenharmony_ci{
7568c2ecf20Sopenharmony_ci	if (readl_relaxed(base + L2X0_AUX_CTRL) & L310_AUX_CTRL_NS_LOCKDOWN)
7578c2ecf20Sopenharmony_ci		l2c_unlock(base, num_lock);
7588c2ecf20Sopenharmony_ci}
7598c2ecf20Sopenharmony_ci
7608c2ecf20Sopenharmony_cistatic const struct l2c_init_data l2c310_init_fns __initconst = {
7618c2ecf20Sopenharmony_ci	.type = "L2C-310",
7628c2ecf20Sopenharmony_ci	.way_size_0 = SZ_8K,
7638c2ecf20Sopenharmony_ci	.num_lock = 8,
7648c2ecf20Sopenharmony_ci	.enable = l2c310_enable,
7658c2ecf20Sopenharmony_ci	.fixup = l2c310_fixup,
7668c2ecf20Sopenharmony_ci	.save = l2c310_save,
7678c2ecf20Sopenharmony_ci	.configure = l2c310_configure,
7688c2ecf20Sopenharmony_ci	.unlock = l2c310_unlock,
7698c2ecf20Sopenharmony_ci	.outer_cache = {
7708c2ecf20Sopenharmony_ci		.inv_range = l2c210_inv_range,
7718c2ecf20Sopenharmony_ci		.clean_range = l2c210_clean_range,
7728c2ecf20Sopenharmony_ci		.flush_range = l2c210_flush_range,
7738c2ecf20Sopenharmony_ci		.flush_all = l2c210_flush_all,
7748c2ecf20Sopenharmony_ci		.disable = l2c310_disable,
7758c2ecf20Sopenharmony_ci		.sync = l2c210_sync,
7768c2ecf20Sopenharmony_ci		.resume = l2c310_resume,
7778c2ecf20Sopenharmony_ci	},
7788c2ecf20Sopenharmony_ci};
7798c2ecf20Sopenharmony_ci
7808c2ecf20Sopenharmony_cistatic int __init __l2c_init(const struct l2c_init_data *data,
7818c2ecf20Sopenharmony_ci			     u32 aux_val, u32 aux_mask, u32 cache_id, bool nosync)
7828c2ecf20Sopenharmony_ci{
7838c2ecf20Sopenharmony_ci	struct outer_cache_fns fns;
7848c2ecf20Sopenharmony_ci	unsigned way_size_bits, ways;
7858c2ecf20Sopenharmony_ci	u32 aux, old_aux;
7868c2ecf20Sopenharmony_ci
7878c2ecf20Sopenharmony_ci	/*
7888c2ecf20Sopenharmony_ci	 * Save the pointer globally so that callbacks which do not receive
7898c2ecf20Sopenharmony_ci	 * context from callers can access the structure.
7908c2ecf20Sopenharmony_ci	 */
7918c2ecf20Sopenharmony_ci	l2x0_data = kmemdup(data, sizeof(*data), GFP_KERNEL);
7928c2ecf20Sopenharmony_ci	if (!l2x0_data)
7938c2ecf20Sopenharmony_ci		return -ENOMEM;
7948c2ecf20Sopenharmony_ci
7958c2ecf20Sopenharmony_ci	/*
7968c2ecf20Sopenharmony_ci	 * Sanity check the aux values.  aux_mask is the bits we preserve
7978c2ecf20Sopenharmony_ci	 * from reading the hardware register, and aux_val is the bits we
7988c2ecf20Sopenharmony_ci	 * set.
7998c2ecf20Sopenharmony_ci	 */
8008c2ecf20Sopenharmony_ci	if (aux_val & aux_mask)
8018c2ecf20Sopenharmony_ci		pr_alert("L2C: platform provided aux values permit register corruption.\n");
8028c2ecf20Sopenharmony_ci
8038c2ecf20Sopenharmony_ci	old_aux = aux = readl_relaxed(l2x0_base + L2X0_AUX_CTRL);
8048c2ecf20Sopenharmony_ci	aux &= aux_mask;
8058c2ecf20Sopenharmony_ci	aux |= aux_val;
8068c2ecf20Sopenharmony_ci
8078c2ecf20Sopenharmony_ci	if (old_aux != aux)
8088c2ecf20Sopenharmony_ci		pr_warn("L2C: DT/platform modifies aux control register: 0x%08x -> 0x%08x\n",
8098c2ecf20Sopenharmony_ci		        old_aux, aux);
8108c2ecf20Sopenharmony_ci
8118c2ecf20Sopenharmony_ci	/* Determine the number of ways */
8128c2ecf20Sopenharmony_ci	switch (cache_id & L2X0_CACHE_ID_PART_MASK) {
8138c2ecf20Sopenharmony_ci	case L2X0_CACHE_ID_PART_L310:
8148c2ecf20Sopenharmony_ci		if ((aux_val | ~aux_mask) & (L2C_AUX_CTRL_WAY_SIZE_MASK | L310_AUX_CTRL_ASSOCIATIVITY_16))
8158c2ecf20Sopenharmony_ci			pr_warn("L2C: DT/platform tries to modify or specify cache size\n");
8168c2ecf20Sopenharmony_ci		if (aux & (1 << 16))
8178c2ecf20Sopenharmony_ci			ways = 16;
8188c2ecf20Sopenharmony_ci		else
8198c2ecf20Sopenharmony_ci			ways = 8;
8208c2ecf20Sopenharmony_ci		break;
8218c2ecf20Sopenharmony_ci
8228c2ecf20Sopenharmony_ci	case L2X0_CACHE_ID_PART_L210:
8238c2ecf20Sopenharmony_ci	case L2X0_CACHE_ID_PART_L220:
8248c2ecf20Sopenharmony_ci		ways = (aux >> 13) & 0xf;
8258c2ecf20Sopenharmony_ci		break;
8268c2ecf20Sopenharmony_ci
8278c2ecf20Sopenharmony_ci	case AURORA_CACHE_ID:
8288c2ecf20Sopenharmony_ci		ways = (aux >> 13) & 0xf;
8298c2ecf20Sopenharmony_ci		ways = 2 << ((ways + 1) >> 2);
8308c2ecf20Sopenharmony_ci		break;
8318c2ecf20Sopenharmony_ci
8328c2ecf20Sopenharmony_ci	default:
8338c2ecf20Sopenharmony_ci		/* Assume unknown chips have 8 ways */
8348c2ecf20Sopenharmony_ci		ways = 8;
8358c2ecf20Sopenharmony_ci		break;
8368c2ecf20Sopenharmony_ci	}
8378c2ecf20Sopenharmony_ci
8388c2ecf20Sopenharmony_ci	l2x0_way_mask = (1 << ways) - 1;
8398c2ecf20Sopenharmony_ci
8408c2ecf20Sopenharmony_ci	/*
8418c2ecf20Sopenharmony_ci	 * way_size_0 is the size that a way_size value of zero would be
8428c2ecf20Sopenharmony_ci	 * given the calculation: way_size = way_size_0 << way_size_bits.
8438c2ecf20Sopenharmony_ci	 * So, if way_size_bits=0 is reserved, but way_size_bits=1 is 16k,
8448c2ecf20Sopenharmony_ci	 * then way_size_0 would be 8k.
8458c2ecf20Sopenharmony_ci	 *
8468c2ecf20Sopenharmony_ci	 * L2 cache size = number of ways * way size.
8478c2ecf20Sopenharmony_ci	 */
8488c2ecf20Sopenharmony_ci	way_size_bits = (aux & L2C_AUX_CTRL_WAY_SIZE_MASK) >>
8498c2ecf20Sopenharmony_ci			L2C_AUX_CTRL_WAY_SIZE_SHIFT;
8508c2ecf20Sopenharmony_ci	l2x0_size = ways * (data->way_size_0 << way_size_bits);
8518c2ecf20Sopenharmony_ci
8528c2ecf20Sopenharmony_ci	fns = data->outer_cache;
8538c2ecf20Sopenharmony_ci	fns.write_sec = outer_cache.write_sec;
8548c2ecf20Sopenharmony_ci	fns.configure = outer_cache.configure;
8558c2ecf20Sopenharmony_ci	if (data->fixup)
8568c2ecf20Sopenharmony_ci		data->fixup(l2x0_base, cache_id, &fns);
8578c2ecf20Sopenharmony_ci	if (nosync) {
8588c2ecf20Sopenharmony_ci		pr_info("L2C: disabling outer sync\n");
8598c2ecf20Sopenharmony_ci		fns.sync = NULL;
8608c2ecf20Sopenharmony_ci	}
8618c2ecf20Sopenharmony_ci
8628c2ecf20Sopenharmony_ci	/*
8638c2ecf20Sopenharmony_ci	 * Check if l2x0 controller is already enabled.  If we are booting
8648c2ecf20Sopenharmony_ci	 * in non-secure mode accessing the below registers will fault.
8658c2ecf20Sopenharmony_ci	 */
8668c2ecf20Sopenharmony_ci	if (!(readl_relaxed(l2x0_base + L2X0_CTRL) & L2X0_CTRL_EN)) {
8678c2ecf20Sopenharmony_ci		l2x0_saved_regs.aux_ctrl = aux;
8688c2ecf20Sopenharmony_ci
8698c2ecf20Sopenharmony_ci		data->enable(l2x0_base, data->num_lock);
8708c2ecf20Sopenharmony_ci	}
8718c2ecf20Sopenharmony_ci
8728c2ecf20Sopenharmony_ci	outer_cache = fns;
8738c2ecf20Sopenharmony_ci
8748c2ecf20Sopenharmony_ci	/*
8758c2ecf20Sopenharmony_ci	 * It is strange to save the register state before initialisation,
8768c2ecf20Sopenharmony_ci	 * but hey, this is what the DT implementations decided to do.
8778c2ecf20Sopenharmony_ci	 */
8788c2ecf20Sopenharmony_ci	if (data->save)
8798c2ecf20Sopenharmony_ci		data->save(l2x0_base);
8808c2ecf20Sopenharmony_ci
8818c2ecf20Sopenharmony_ci	/* Re-read it in case some bits are reserved. */
8828c2ecf20Sopenharmony_ci	aux = readl_relaxed(l2x0_base + L2X0_AUX_CTRL);
8838c2ecf20Sopenharmony_ci
8848c2ecf20Sopenharmony_ci	pr_info("%s cache controller enabled, %d ways, %d kB\n",
8858c2ecf20Sopenharmony_ci		data->type, ways, l2x0_size >> 10);
8868c2ecf20Sopenharmony_ci	pr_info("%s: CACHE_ID 0x%08x, AUX_CTRL 0x%08x\n",
8878c2ecf20Sopenharmony_ci		data->type, cache_id, aux);
8888c2ecf20Sopenharmony_ci
8898c2ecf20Sopenharmony_ci	l2x0_pmu_register(l2x0_base, cache_id);
8908c2ecf20Sopenharmony_ci
8918c2ecf20Sopenharmony_ci	return 0;
8928c2ecf20Sopenharmony_ci}
8938c2ecf20Sopenharmony_ci
8948c2ecf20Sopenharmony_civoid __init l2x0_init(void __iomem *base, u32 aux_val, u32 aux_mask)
8958c2ecf20Sopenharmony_ci{
8968c2ecf20Sopenharmony_ci	const struct l2c_init_data *data;
8978c2ecf20Sopenharmony_ci	u32 cache_id;
8988c2ecf20Sopenharmony_ci
8998c2ecf20Sopenharmony_ci	l2x0_base = base;
9008c2ecf20Sopenharmony_ci
9018c2ecf20Sopenharmony_ci	cache_id = readl_relaxed(base + L2X0_CACHE_ID);
9028c2ecf20Sopenharmony_ci
9038c2ecf20Sopenharmony_ci	switch (cache_id & L2X0_CACHE_ID_PART_MASK) {
9048c2ecf20Sopenharmony_ci	default:
9058c2ecf20Sopenharmony_ci	case L2X0_CACHE_ID_PART_L210:
9068c2ecf20Sopenharmony_ci		data = &l2c210_data;
9078c2ecf20Sopenharmony_ci		break;
9088c2ecf20Sopenharmony_ci
9098c2ecf20Sopenharmony_ci	case L2X0_CACHE_ID_PART_L220:
9108c2ecf20Sopenharmony_ci		data = &l2c220_data;
9118c2ecf20Sopenharmony_ci		break;
9128c2ecf20Sopenharmony_ci
9138c2ecf20Sopenharmony_ci	case L2X0_CACHE_ID_PART_L310:
9148c2ecf20Sopenharmony_ci		data = &l2c310_init_fns;
9158c2ecf20Sopenharmony_ci		break;
9168c2ecf20Sopenharmony_ci	}
9178c2ecf20Sopenharmony_ci
9188c2ecf20Sopenharmony_ci	/* Read back current (default) hardware configuration */
9198c2ecf20Sopenharmony_ci	if (data->save)
9208c2ecf20Sopenharmony_ci		data->save(l2x0_base);
9218c2ecf20Sopenharmony_ci
9228c2ecf20Sopenharmony_ci	__l2c_init(data, aux_val, aux_mask, cache_id, false);
9238c2ecf20Sopenharmony_ci}
9248c2ecf20Sopenharmony_ci
9258c2ecf20Sopenharmony_ci#ifdef CONFIG_OF
9268c2ecf20Sopenharmony_cistatic int l2_wt_override;
9278c2ecf20Sopenharmony_ci
9288c2ecf20Sopenharmony_ci/* Aurora don't have the cache ID register available, so we have to
9298c2ecf20Sopenharmony_ci * pass it though the device tree */
9308c2ecf20Sopenharmony_cistatic u32 cache_id_part_number_from_dt;
9318c2ecf20Sopenharmony_ci
9328c2ecf20Sopenharmony_ci/**
9338c2ecf20Sopenharmony_ci * l2x0_cache_size_of_parse() - read cache size parameters from DT
9348c2ecf20Sopenharmony_ci * @np: the device tree node for the l2 cache
9358c2ecf20Sopenharmony_ci * @aux_val: pointer to machine-supplied auxilary register value, to
9368c2ecf20Sopenharmony_ci * be augmented by the call (bits to be set to 1)
9378c2ecf20Sopenharmony_ci * @aux_mask: pointer to machine-supplied auxilary register mask, to
9388c2ecf20Sopenharmony_ci * be augmented by the call (bits to be set to 0)
9398c2ecf20Sopenharmony_ci * @associativity: variable to return the calculated associativity in
9408c2ecf20Sopenharmony_ci * @max_way_size: the maximum size in bytes for the cache ways
9418c2ecf20Sopenharmony_ci */
9428c2ecf20Sopenharmony_cistatic int __init l2x0_cache_size_of_parse(const struct device_node *np,
9438c2ecf20Sopenharmony_ci					    u32 *aux_val, u32 *aux_mask,
9448c2ecf20Sopenharmony_ci					    u32 *associativity,
9458c2ecf20Sopenharmony_ci					    u32 max_way_size)
9468c2ecf20Sopenharmony_ci{
9478c2ecf20Sopenharmony_ci	u32 mask = 0, val = 0;
9488c2ecf20Sopenharmony_ci	u32 cache_size = 0, sets = 0;
9498c2ecf20Sopenharmony_ci	u32 way_size_bits = 1;
9508c2ecf20Sopenharmony_ci	u32 way_size = 0;
9518c2ecf20Sopenharmony_ci	u32 block_size = 0;
9528c2ecf20Sopenharmony_ci	u32 line_size = 0;
9538c2ecf20Sopenharmony_ci
9548c2ecf20Sopenharmony_ci	of_property_read_u32(np, "cache-size", &cache_size);
9558c2ecf20Sopenharmony_ci	of_property_read_u32(np, "cache-sets", &sets);
9568c2ecf20Sopenharmony_ci	of_property_read_u32(np, "cache-block-size", &block_size);
9578c2ecf20Sopenharmony_ci	of_property_read_u32(np, "cache-line-size", &line_size);
9588c2ecf20Sopenharmony_ci
9598c2ecf20Sopenharmony_ci	if (!cache_size || !sets)
9608c2ecf20Sopenharmony_ci		return -ENODEV;
9618c2ecf20Sopenharmony_ci
9628c2ecf20Sopenharmony_ci	/* All these l2 caches have the same line = block size actually */
9638c2ecf20Sopenharmony_ci	if (!line_size) {
9648c2ecf20Sopenharmony_ci		if (block_size) {
9658c2ecf20Sopenharmony_ci			/* If linesize is not given, it is equal to blocksize */
9668c2ecf20Sopenharmony_ci			line_size = block_size;
9678c2ecf20Sopenharmony_ci		} else {
9688c2ecf20Sopenharmony_ci			/* Fall back to known size */
9698c2ecf20Sopenharmony_ci			pr_warn("L2C OF: no cache block/line size given: "
9708c2ecf20Sopenharmony_ci				"falling back to default size %d bytes\n",
9718c2ecf20Sopenharmony_ci				CACHE_LINE_SIZE);
9728c2ecf20Sopenharmony_ci			line_size = CACHE_LINE_SIZE;
9738c2ecf20Sopenharmony_ci		}
9748c2ecf20Sopenharmony_ci	}
9758c2ecf20Sopenharmony_ci
9768c2ecf20Sopenharmony_ci	if (line_size != CACHE_LINE_SIZE)
9778c2ecf20Sopenharmony_ci		pr_warn("L2C OF: DT supplied line size %d bytes does "
9788c2ecf20Sopenharmony_ci			"not match hardware line size of %d bytes\n",
9798c2ecf20Sopenharmony_ci			line_size,
9808c2ecf20Sopenharmony_ci			CACHE_LINE_SIZE);
9818c2ecf20Sopenharmony_ci
9828c2ecf20Sopenharmony_ci	/*
9838c2ecf20Sopenharmony_ci	 * Since:
9848c2ecf20Sopenharmony_ci	 * set size = cache size / sets
9858c2ecf20Sopenharmony_ci	 * ways = cache size / (sets * line size)
9868c2ecf20Sopenharmony_ci	 * way size = cache size / (cache size / (sets * line size))
9878c2ecf20Sopenharmony_ci	 * way size = sets * line size
9888c2ecf20Sopenharmony_ci	 * associativity = ways = cache size / way size
9898c2ecf20Sopenharmony_ci	 */
9908c2ecf20Sopenharmony_ci	way_size = sets * line_size;
9918c2ecf20Sopenharmony_ci	*associativity = cache_size / way_size;
9928c2ecf20Sopenharmony_ci
9938c2ecf20Sopenharmony_ci	if (way_size > max_way_size) {
9948c2ecf20Sopenharmony_ci		pr_err("L2C OF: set size %dKB is too large\n", way_size);
9958c2ecf20Sopenharmony_ci		return -EINVAL;
9968c2ecf20Sopenharmony_ci	}
9978c2ecf20Sopenharmony_ci
9988c2ecf20Sopenharmony_ci	pr_info("L2C OF: override cache size: %d bytes (%dKB)\n",
9998c2ecf20Sopenharmony_ci		cache_size, cache_size >> 10);
10008c2ecf20Sopenharmony_ci	pr_info("L2C OF: override line size: %d bytes\n", line_size);
10018c2ecf20Sopenharmony_ci	pr_info("L2C OF: override way size: %d bytes (%dKB)\n",
10028c2ecf20Sopenharmony_ci		way_size, way_size >> 10);
10038c2ecf20Sopenharmony_ci	pr_info("L2C OF: override associativity: %d\n", *associativity);
10048c2ecf20Sopenharmony_ci
10058c2ecf20Sopenharmony_ci	/*
10068c2ecf20Sopenharmony_ci	 * Calculates the bits 17:19 to set for way size:
10078c2ecf20Sopenharmony_ci	 * 512KB -> 6, 256KB -> 5, ... 16KB -> 1
10088c2ecf20Sopenharmony_ci	 */
10098c2ecf20Sopenharmony_ci	way_size_bits = ilog2(way_size >> 10) - 3;
10108c2ecf20Sopenharmony_ci	if (way_size_bits < 1 || way_size_bits > 6) {
10118c2ecf20Sopenharmony_ci		pr_err("L2C OF: cache way size illegal: %dKB is not mapped\n",
10128c2ecf20Sopenharmony_ci		       way_size);
10138c2ecf20Sopenharmony_ci		return -EINVAL;
10148c2ecf20Sopenharmony_ci	}
10158c2ecf20Sopenharmony_ci
10168c2ecf20Sopenharmony_ci	mask |= L2C_AUX_CTRL_WAY_SIZE_MASK;
10178c2ecf20Sopenharmony_ci	val |= (way_size_bits << L2C_AUX_CTRL_WAY_SIZE_SHIFT);
10188c2ecf20Sopenharmony_ci
10198c2ecf20Sopenharmony_ci	*aux_val &= ~mask;
10208c2ecf20Sopenharmony_ci	*aux_val |= val;
10218c2ecf20Sopenharmony_ci	*aux_mask &= ~mask;
10228c2ecf20Sopenharmony_ci
10238c2ecf20Sopenharmony_ci	return 0;
10248c2ecf20Sopenharmony_ci}
10258c2ecf20Sopenharmony_ci
10268c2ecf20Sopenharmony_cistatic void __init l2x0_of_parse(const struct device_node *np,
10278c2ecf20Sopenharmony_ci				 u32 *aux_val, u32 *aux_mask)
10288c2ecf20Sopenharmony_ci{
10298c2ecf20Sopenharmony_ci	u32 data[2] = { 0, 0 };
10308c2ecf20Sopenharmony_ci	u32 tag = 0;
10318c2ecf20Sopenharmony_ci	u32 dirty = 0;
10328c2ecf20Sopenharmony_ci	u32 val = 0, mask = 0;
10338c2ecf20Sopenharmony_ci	u32 assoc;
10348c2ecf20Sopenharmony_ci	int ret;
10358c2ecf20Sopenharmony_ci
10368c2ecf20Sopenharmony_ci	of_property_read_u32(np, "arm,tag-latency", &tag);
10378c2ecf20Sopenharmony_ci	if (tag) {
10388c2ecf20Sopenharmony_ci		mask |= L2X0_AUX_CTRL_TAG_LATENCY_MASK;
10398c2ecf20Sopenharmony_ci		val |= (tag - 1) << L2X0_AUX_CTRL_TAG_LATENCY_SHIFT;
10408c2ecf20Sopenharmony_ci	}
10418c2ecf20Sopenharmony_ci
10428c2ecf20Sopenharmony_ci	of_property_read_u32_array(np, "arm,data-latency",
10438c2ecf20Sopenharmony_ci				   data, ARRAY_SIZE(data));
10448c2ecf20Sopenharmony_ci	if (data[0] && data[1]) {
10458c2ecf20Sopenharmony_ci		mask |= L2X0_AUX_CTRL_DATA_RD_LATENCY_MASK |
10468c2ecf20Sopenharmony_ci			L2X0_AUX_CTRL_DATA_WR_LATENCY_MASK;
10478c2ecf20Sopenharmony_ci		val |= ((data[0] - 1) << L2X0_AUX_CTRL_DATA_RD_LATENCY_SHIFT) |
10488c2ecf20Sopenharmony_ci		       ((data[1] - 1) << L2X0_AUX_CTRL_DATA_WR_LATENCY_SHIFT);
10498c2ecf20Sopenharmony_ci	}
10508c2ecf20Sopenharmony_ci
10518c2ecf20Sopenharmony_ci	of_property_read_u32(np, "arm,dirty-latency", &dirty);
10528c2ecf20Sopenharmony_ci	if (dirty) {
10538c2ecf20Sopenharmony_ci		mask |= L2X0_AUX_CTRL_DIRTY_LATENCY_MASK;
10548c2ecf20Sopenharmony_ci		val |= (dirty - 1) << L2X0_AUX_CTRL_DIRTY_LATENCY_SHIFT;
10558c2ecf20Sopenharmony_ci	}
10568c2ecf20Sopenharmony_ci
10578c2ecf20Sopenharmony_ci	if (of_property_read_bool(np, "arm,parity-enable")) {
10588c2ecf20Sopenharmony_ci		mask &= ~L2C_AUX_CTRL_PARITY_ENABLE;
10598c2ecf20Sopenharmony_ci		val |= L2C_AUX_CTRL_PARITY_ENABLE;
10608c2ecf20Sopenharmony_ci	} else if (of_property_read_bool(np, "arm,parity-disable")) {
10618c2ecf20Sopenharmony_ci		mask &= ~L2C_AUX_CTRL_PARITY_ENABLE;
10628c2ecf20Sopenharmony_ci	}
10638c2ecf20Sopenharmony_ci
10648c2ecf20Sopenharmony_ci	if (of_property_read_bool(np, "arm,shared-override")) {
10658c2ecf20Sopenharmony_ci		mask &= ~L2C_AUX_CTRL_SHARED_OVERRIDE;
10668c2ecf20Sopenharmony_ci		val |= L2C_AUX_CTRL_SHARED_OVERRIDE;
10678c2ecf20Sopenharmony_ci	}
10688c2ecf20Sopenharmony_ci
10698c2ecf20Sopenharmony_ci	ret = l2x0_cache_size_of_parse(np, aux_val, aux_mask, &assoc, SZ_256K);
10708c2ecf20Sopenharmony_ci	if (ret)
10718c2ecf20Sopenharmony_ci		return;
10728c2ecf20Sopenharmony_ci
10738c2ecf20Sopenharmony_ci	if (assoc > 8) {
10748c2ecf20Sopenharmony_ci		pr_err("l2x0 of: cache setting yield too high associativity\n");
10758c2ecf20Sopenharmony_ci		pr_err("l2x0 of: %d calculated, max 8\n", assoc);
10768c2ecf20Sopenharmony_ci	} else {
10778c2ecf20Sopenharmony_ci		mask |= L2X0_AUX_CTRL_ASSOC_MASK;
10788c2ecf20Sopenharmony_ci		val |= (assoc << L2X0_AUX_CTRL_ASSOC_SHIFT);
10798c2ecf20Sopenharmony_ci	}
10808c2ecf20Sopenharmony_ci
10818c2ecf20Sopenharmony_ci	*aux_val &= ~mask;
10828c2ecf20Sopenharmony_ci	*aux_val |= val;
10838c2ecf20Sopenharmony_ci	*aux_mask &= ~mask;
10848c2ecf20Sopenharmony_ci}
10858c2ecf20Sopenharmony_ci
10868c2ecf20Sopenharmony_cistatic const struct l2c_init_data of_l2c210_data __initconst = {
10878c2ecf20Sopenharmony_ci	.type = "L2C-210",
10888c2ecf20Sopenharmony_ci	.way_size_0 = SZ_8K,
10898c2ecf20Sopenharmony_ci	.num_lock = 1,
10908c2ecf20Sopenharmony_ci	.of_parse = l2x0_of_parse,
10918c2ecf20Sopenharmony_ci	.enable = l2c_enable,
10928c2ecf20Sopenharmony_ci	.save = l2c_save,
10938c2ecf20Sopenharmony_ci	.configure = l2c_configure,
10948c2ecf20Sopenharmony_ci	.unlock = l2c_unlock,
10958c2ecf20Sopenharmony_ci	.outer_cache = {
10968c2ecf20Sopenharmony_ci		.inv_range   = l2c210_inv_range,
10978c2ecf20Sopenharmony_ci		.clean_range = l2c210_clean_range,
10988c2ecf20Sopenharmony_ci		.flush_range = l2c210_flush_range,
10998c2ecf20Sopenharmony_ci		.flush_all   = l2c210_flush_all,
11008c2ecf20Sopenharmony_ci		.disable     = l2c_disable,
11018c2ecf20Sopenharmony_ci		.sync        = l2c210_sync,
11028c2ecf20Sopenharmony_ci		.resume      = l2c_resume,
11038c2ecf20Sopenharmony_ci	},
11048c2ecf20Sopenharmony_ci};
11058c2ecf20Sopenharmony_ci
11068c2ecf20Sopenharmony_cistatic const struct l2c_init_data of_l2c220_data __initconst = {
11078c2ecf20Sopenharmony_ci	.type = "L2C-220",
11088c2ecf20Sopenharmony_ci	.way_size_0 = SZ_8K,
11098c2ecf20Sopenharmony_ci	.num_lock = 1,
11108c2ecf20Sopenharmony_ci	.of_parse = l2x0_of_parse,
11118c2ecf20Sopenharmony_ci	.enable = l2c220_enable,
11128c2ecf20Sopenharmony_ci	.save = l2c_save,
11138c2ecf20Sopenharmony_ci	.configure = l2c_configure,
11148c2ecf20Sopenharmony_ci	.unlock = l2c220_unlock,
11158c2ecf20Sopenharmony_ci	.outer_cache = {
11168c2ecf20Sopenharmony_ci		.inv_range   = l2c220_inv_range,
11178c2ecf20Sopenharmony_ci		.clean_range = l2c220_clean_range,
11188c2ecf20Sopenharmony_ci		.flush_range = l2c220_flush_range,
11198c2ecf20Sopenharmony_ci		.flush_all   = l2c220_flush_all,
11208c2ecf20Sopenharmony_ci		.disable     = l2c_disable,
11218c2ecf20Sopenharmony_ci		.sync        = l2c220_sync,
11228c2ecf20Sopenharmony_ci		.resume      = l2c_resume,
11238c2ecf20Sopenharmony_ci	},
11248c2ecf20Sopenharmony_ci};
11258c2ecf20Sopenharmony_ci
11268c2ecf20Sopenharmony_cistatic void __init l2c310_of_parse(const struct device_node *np,
11278c2ecf20Sopenharmony_ci	u32 *aux_val, u32 *aux_mask)
11288c2ecf20Sopenharmony_ci{
11298c2ecf20Sopenharmony_ci	u32 data[3] = { 0, 0, 0 };
11308c2ecf20Sopenharmony_ci	u32 tag[3] = { 0, 0, 0 };
11318c2ecf20Sopenharmony_ci	u32 filter[2] = { 0, 0 };
11328c2ecf20Sopenharmony_ci	u32 assoc;
11338c2ecf20Sopenharmony_ci	u32 prefetch;
11348c2ecf20Sopenharmony_ci	u32 power;
11358c2ecf20Sopenharmony_ci	u32 val;
11368c2ecf20Sopenharmony_ci	int ret;
11378c2ecf20Sopenharmony_ci
11388c2ecf20Sopenharmony_ci	of_property_read_u32_array(np, "arm,tag-latency", tag, ARRAY_SIZE(tag));
11398c2ecf20Sopenharmony_ci	if (tag[0] && tag[1] && tag[2])
11408c2ecf20Sopenharmony_ci		l2x0_saved_regs.tag_latency =
11418c2ecf20Sopenharmony_ci			L310_LATENCY_CTRL_RD(tag[0] - 1) |
11428c2ecf20Sopenharmony_ci			L310_LATENCY_CTRL_WR(tag[1] - 1) |
11438c2ecf20Sopenharmony_ci			L310_LATENCY_CTRL_SETUP(tag[2] - 1);
11448c2ecf20Sopenharmony_ci
11458c2ecf20Sopenharmony_ci	of_property_read_u32_array(np, "arm,data-latency",
11468c2ecf20Sopenharmony_ci				   data, ARRAY_SIZE(data));
11478c2ecf20Sopenharmony_ci	if (data[0] && data[1] && data[2])
11488c2ecf20Sopenharmony_ci		l2x0_saved_regs.data_latency =
11498c2ecf20Sopenharmony_ci			L310_LATENCY_CTRL_RD(data[0] - 1) |
11508c2ecf20Sopenharmony_ci			L310_LATENCY_CTRL_WR(data[1] - 1) |
11518c2ecf20Sopenharmony_ci			L310_LATENCY_CTRL_SETUP(data[2] - 1);
11528c2ecf20Sopenharmony_ci
11538c2ecf20Sopenharmony_ci	of_property_read_u32_array(np, "arm,filter-ranges",
11548c2ecf20Sopenharmony_ci				   filter, ARRAY_SIZE(filter));
11558c2ecf20Sopenharmony_ci	if (filter[1]) {
11568c2ecf20Sopenharmony_ci		l2x0_saved_regs.filter_end =
11578c2ecf20Sopenharmony_ci					ALIGN(filter[0] + filter[1], SZ_1M);
11588c2ecf20Sopenharmony_ci		l2x0_saved_regs.filter_start = (filter[0] & ~(SZ_1M - 1))
11598c2ecf20Sopenharmony_ci					| L310_ADDR_FILTER_EN;
11608c2ecf20Sopenharmony_ci	}
11618c2ecf20Sopenharmony_ci
11628c2ecf20Sopenharmony_ci	ret = l2x0_cache_size_of_parse(np, aux_val, aux_mask, &assoc, SZ_512K);
11638c2ecf20Sopenharmony_ci	if (!ret) {
11648c2ecf20Sopenharmony_ci		switch (assoc) {
11658c2ecf20Sopenharmony_ci		case 16:
11668c2ecf20Sopenharmony_ci			*aux_val &= ~L2X0_AUX_CTRL_ASSOC_MASK;
11678c2ecf20Sopenharmony_ci			*aux_val |= L310_AUX_CTRL_ASSOCIATIVITY_16;
11688c2ecf20Sopenharmony_ci			*aux_mask &= ~L2X0_AUX_CTRL_ASSOC_MASK;
11698c2ecf20Sopenharmony_ci			break;
11708c2ecf20Sopenharmony_ci		case 8:
11718c2ecf20Sopenharmony_ci			*aux_val &= ~L2X0_AUX_CTRL_ASSOC_MASK;
11728c2ecf20Sopenharmony_ci			*aux_mask &= ~L2X0_AUX_CTRL_ASSOC_MASK;
11738c2ecf20Sopenharmony_ci			break;
11748c2ecf20Sopenharmony_ci		default:
11758c2ecf20Sopenharmony_ci			pr_err("L2C-310 OF cache associativity %d invalid, only 8 or 16 permitted\n",
11768c2ecf20Sopenharmony_ci			       assoc);
11778c2ecf20Sopenharmony_ci			break;
11788c2ecf20Sopenharmony_ci		}
11798c2ecf20Sopenharmony_ci	}
11808c2ecf20Sopenharmony_ci
11818c2ecf20Sopenharmony_ci	if (of_property_read_bool(np, "arm,shared-override")) {
11828c2ecf20Sopenharmony_ci		*aux_val |= L2C_AUX_CTRL_SHARED_OVERRIDE;
11838c2ecf20Sopenharmony_ci		*aux_mask &= ~L2C_AUX_CTRL_SHARED_OVERRIDE;
11848c2ecf20Sopenharmony_ci	}
11858c2ecf20Sopenharmony_ci
11868c2ecf20Sopenharmony_ci	if (of_property_read_bool(np, "arm,parity-enable")) {
11878c2ecf20Sopenharmony_ci		*aux_val |= L2C_AUX_CTRL_PARITY_ENABLE;
11888c2ecf20Sopenharmony_ci		*aux_mask &= ~L2C_AUX_CTRL_PARITY_ENABLE;
11898c2ecf20Sopenharmony_ci	} else if (of_property_read_bool(np, "arm,parity-disable")) {
11908c2ecf20Sopenharmony_ci		*aux_val &= ~L2C_AUX_CTRL_PARITY_ENABLE;
11918c2ecf20Sopenharmony_ci		*aux_mask &= ~L2C_AUX_CTRL_PARITY_ENABLE;
11928c2ecf20Sopenharmony_ci	}
11938c2ecf20Sopenharmony_ci
11948c2ecf20Sopenharmony_ci	if (of_property_read_bool(np, "arm,early-bresp-disable"))
11958c2ecf20Sopenharmony_ci		l2x0_bresp_disable = true;
11968c2ecf20Sopenharmony_ci
11978c2ecf20Sopenharmony_ci	if (of_property_read_bool(np, "arm,full-line-zero-disable"))
11988c2ecf20Sopenharmony_ci		l2x0_flz_disable = true;
11998c2ecf20Sopenharmony_ci
12008c2ecf20Sopenharmony_ci	prefetch = l2x0_saved_regs.prefetch_ctrl;
12018c2ecf20Sopenharmony_ci
12028c2ecf20Sopenharmony_ci	ret = of_property_read_u32(np, "arm,double-linefill", &val);
12038c2ecf20Sopenharmony_ci	if (ret == 0) {
12048c2ecf20Sopenharmony_ci		if (val)
12058c2ecf20Sopenharmony_ci			prefetch |= L310_PREFETCH_CTRL_DBL_LINEFILL;
12068c2ecf20Sopenharmony_ci		else
12078c2ecf20Sopenharmony_ci			prefetch &= ~L310_PREFETCH_CTRL_DBL_LINEFILL;
12088c2ecf20Sopenharmony_ci	} else if (ret != -EINVAL) {
12098c2ecf20Sopenharmony_ci		pr_err("L2C-310 OF arm,double-linefill property value is missing\n");
12108c2ecf20Sopenharmony_ci	}
12118c2ecf20Sopenharmony_ci
12128c2ecf20Sopenharmony_ci	ret = of_property_read_u32(np, "arm,double-linefill-incr", &val);
12138c2ecf20Sopenharmony_ci	if (ret == 0) {
12148c2ecf20Sopenharmony_ci		if (val)
12158c2ecf20Sopenharmony_ci			prefetch |= L310_PREFETCH_CTRL_DBL_LINEFILL_INCR;
12168c2ecf20Sopenharmony_ci		else
12178c2ecf20Sopenharmony_ci			prefetch &= ~L310_PREFETCH_CTRL_DBL_LINEFILL_INCR;
12188c2ecf20Sopenharmony_ci	} else if (ret != -EINVAL) {
12198c2ecf20Sopenharmony_ci		pr_err("L2C-310 OF arm,double-linefill-incr property value is missing\n");
12208c2ecf20Sopenharmony_ci	}
12218c2ecf20Sopenharmony_ci
12228c2ecf20Sopenharmony_ci	ret = of_property_read_u32(np, "arm,double-linefill-wrap", &val);
12238c2ecf20Sopenharmony_ci	if (ret == 0) {
12248c2ecf20Sopenharmony_ci		if (!val)
12258c2ecf20Sopenharmony_ci			prefetch |= L310_PREFETCH_CTRL_DBL_LINEFILL_WRAP;
12268c2ecf20Sopenharmony_ci		else
12278c2ecf20Sopenharmony_ci			prefetch &= ~L310_PREFETCH_CTRL_DBL_LINEFILL_WRAP;
12288c2ecf20Sopenharmony_ci	} else if (ret != -EINVAL) {
12298c2ecf20Sopenharmony_ci		pr_err("L2C-310 OF arm,double-linefill-wrap property value is missing\n");
12308c2ecf20Sopenharmony_ci	}
12318c2ecf20Sopenharmony_ci
12328c2ecf20Sopenharmony_ci	ret = of_property_read_u32(np, "arm,prefetch-drop", &val);
12338c2ecf20Sopenharmony_ci	if (ret == 0) {
12348c2ecf20Sopenharmony_ci		if (val)
12358c2ecf20Sopenharmony_ci			prefetch |= L310_PREFETCH_CTRL_PREFETCH_DROP;
12368c2ecf20Sopenharmony_ci		else
12378c2ecf20Sopenharmony_ci			prefetch &= ~L310_PREFETCH_CTRL_PREFETCH_DROP;
12388c2ecf20Sopenharmony_ci	} else if (ret != -EINVAL) {
12398c2ecf20Sopenharmony_ci		pr_err("L2C-310 OF arm,prefetch-drop property value is missing\n");
12408c2ecf20Sopenharmony_ci	}
12418c2ecf20Sopenharmony_ci
12428c2ecf20Sopenharmony_ci	ret = of_property_read_u32(np, "arm,prefetch-offset", &val);
12438c2ecf20Sopenharmony_ci	if (ret == 0) {
12448c2ecf20Sopenharmony_ci		prefetch &= ~L310_PREFETCH_CTRL_OFFSET_MASK;
12458c2ecf20Sopenharmony_ci		prefetch |= val & L310_PREFETCH_CTRL_OFFSET_MASK;
12468c2ecf20Sopenharmony_ci	} else if (ret != -EINVAL) {
12478c2ecf20Sopenharmony_ci		pr_err("L2C-310 OF arm,prefetch-offset property value is missing\n");
12488c2ecf20Sopenharmony_ci	}
12498c2ecf20Sopenharmony_ci
12508c2ecf20Sopenharmony_ci	ret = of_property_read_u32(np, "prefetch-data", &val);
12518c2ecf20Sopenharmony_ci	if (ret == 0) {
12528c2ecf20Sopenharmony_ci		if (val) {
12538c2ecf20Sopenharmony_ci			prefetch |= L310_PREFETCH_CTRL_DATA_PREFETCH;
12548c2ecf20Sopenharmony_ci			*aux_val |= L310_PREFETCH_CTRL_DATA_PREFETCH;
12558c2ecf20Sopenharmony_ci		} else {
12568c2ecf20Sopenharmony_ci			prefetch &= ~L310_PREFETCH_CTRL_DATA_PREFETCH;
12578c2ecf20Sopenharmony_ci			*aux_val &= ~L310_PREFETCH_CTRL_DATA_PREFETCH;
12588c2ecf20Sopenharmony_ci		}
12598c2ecf20Sopenharmony_ci		*aux_mask &= ~L310_PREFETCH_CTRL_DATA_PREFETCH;
12608c2ecf20Sopenharmony_ci	} else if (ret != -EINVAL) {
12618c2ecf20Sopenharmony_ci		pr_err("L2C-310 OF prefetch-data property value is missing\n");
12628c2ecf20Sopenharmony_ci	}
12638c2ecf20Sopenharmony_ci
12648c2ecf20Sopenharmony_ci	ret = of_property_read_u32(np, "prefetch-instr", &val);
12658c2ecf20Sopenharmony_ci	if (ret == 0) {
12668c2ecf20Sopenharmony_ci		if (val) {
12678c2ecf20Sopenharmony_ci			prefetch |= L310_PREFETCH_CTRL_INSTR_PREFETCH;
12688c2ecf20Sopenharmony_ci			*aux_val |= L310_PREFETCH_CTRL_INSTR_PREFETCH;
12698c2ecf20Sopenharmony_ci		} else {
12708c2ecf20Sopenharmony_ci			prefetch &= ~L310_PREFETCH_CTRL_INSTR_PREFETCH;
12718c2ecf20Sopenharmony_ci			*aux_val &= ~L310_PREFETCH_CTRL_INSTR_PREFETCH;
12728c2ecf20Sopenharmony_ci		}
12738c2ecf20Sopenharmony_ci		*aux_mask &= ~L310_PREFETCH_CTRL_INSTR_PREFETCH;
12748c2ecf20Sopenharmony_ci	} else if (ret != -EINVAL) {
12758c2ecf20Sopenharmony_ci		pr_err("L2C-310 OF prefetch-instr property value is missing\n");
12768c2ecf20Sopenharmony_ci	}
12778c2ecf20Sopenharmony_ci
12788c2ecf20Sopenharmony_ci	l2x0_saved_regs.prefetch_ctrl = prefetch;
12798c2ecf20Sopenharmony_ci
12808c2ecf20Sopenharmony_ci	power = l2x0_saved_regs.pwr_ctrl |
12818c2ecf20Sopenharmony_ci		L310_DYNAMIC_CLK_GATING_EN | L310_STNDBY_MODE_EN;
12828c2ecf20Sopenharmony_ci
12838c2ecf20Sopenharmony_ci	ret = of_property_read_u32(np, "arm,dynamic-clock-gating", &val);
12848c2ecf20Sopenharmony_ci	if (!ret) {
12858c2ecf20Sopenharmony_ci		if (!val)
12868c2ecf20Sopenharmony_ci			power &= ~L310_DYNAMIC_CLK_GATING_EN;
12878c2ecf20Sopenharmony_ci	} else if (ret != -EINVAL) {
12888c2ecf20Sopenharmony_ci		pr_err("L2C-310 OF dynamic-clock-gating property value is missing or invalid\n");
12898c2ecf20Sopenharmony_ci	}
12908c2ecf20Sopenharmony_ci	ret = of_property_read_u32(np, "arm,standby-mode", &val);
12918c2ecf20Sopenharmony_ci	if (!ret) {
12928c2ecf20Sopenharmony_ci		if (!val)
12938c2ecf20Sopenharmony_ci			power &= ~L310_STNDBY_MODE_EN;
12948c2ecf20Sopenharmony_ci	} else if (ret != -EINVAL) {
12958c2ecf20Sopenharmony_ci		pr_err("L2C-310 OF standby-mode property value is missing or invalid\n");
12968c2ecf20Sopenharmony_ci	}
12978c2ecf20Sopenharmony_ci
12988c2ecf20Sopenharmony_ci	l2x0_saved_regs.pwr_ctrl = power;
12998c2ecf20Sopenharmony_ci}
13008c2ecf20Sopenharmony_ci
13018c2ecf20Sopenharmony_cistatic const struct l2c_init_data of_l2c310_data __initconst = {
13028c2ecf20Sopenharmony_ci	.type = "L2C-310",
13038c2ecf20Sopenharmony_ci	.way_size_0 = SZ_8K,
13048c2ecf20Sopenharmony_ci	.num_lock = 8,
13058c2ecf20Sopenharmony_ci	.of_parse = l2c310_of_parse,
13068c2ecf20Sopenharmony_ci	.enable = l2c310_enable,
13078c2ecf20Sopenharmony_ci	.fixup = l2c310_fixup,
13088c2ecf20Sopenharmony_ci	.save  = l2c310_save,
13098c2ecf20Sopenharmony_ci	.configure = l2c310_configure,
13108c2ecf20Sopenharmony_ci	.unlock = l2c310_unlock,
13118c2ecf20Sopenharmony_ci	.outer_cache = {
13128c2ecf20Sopenharmony_ci		.inv_range   = l2c210_inv_range,
13138c2ecf20Sopenharmony_ci		.clean_range = l2c210_clean_range,
13148c2ecf20Sopenharmony_ci		.flush_range = l2c210_flush_range,
13158c2ecf20Sopenharmony_ci		.flush_all   = l2c210_flush_all,
13168c2ecf20Sopenharmony_ci		.disable     = l2c310_disable,
13178c2ecf20Sopenharmony_ci		.sync        = l2c210_sync,
13188c2ecf20Sopenharmony_ci		.resume      = l2c310_resume,
13198c2ecf20Sopenharmony_ci	},
13208c2ecf20Sopenharmony_ci};
13218c2ecf20Sopenharmony_ci
13228c2ecf20Sopenharmony_ci/*
13238c2ecf20Sopenharmony_ci * This is a variant of the of_l2c310_data with .sync set to
13248c2ecf20Sopenharmony_ci * NULL. Outer sync operations are not needed when the system is I/O
13258c2ecf20Sopenharmony_ci * coherent, and potentially harmful in certain situations (PCIe/PL310
13268c2ecf20Sopenharmony_ci * deadlock on Armada 375/38x due to hardware I/O coherency). The
13278c2ecf20Sopenharmony_ci * other operations are kept because they are infrequent (therefore do
13288c2ecf20Sopenharmony_ci * not cause the deadlock in practice) and needed for secondary CPU
13298c2ecf20Sopenharmony_ci * boot and other power management activities.
13308c2ecf20Sopenharmony_ci */
13318c2ecf20Sopenharmony_cistatic const struct l2c_init_data of_l2c310_coherent_data __initconst = {
13328c2ecf20Sopenharmony_ci	.type = "L2C-310 Coherent",
13338c2ecf20Sopenharmony_ci	.way_size_0 = SZ_8K,
13348c2ecf20Sopenharmony_ci	.num_lock = 8,
13358c2ecf20Sopenharmony_ci	.of_parse = l2c310_of_parse,
13368c2ecf20Sopenharmony_ci	.enable = l2c310_enable,
13378c2ecf20Sopenharmony_ci	.fixup = l2c310_fixup,
13388c2ecf20Sopenharmony_ci	.save  = l2c310_save,
13398c2ecf20Sopenharmony_ci	.configure = l2c310_configure,
13408c2ecf20Sopenharmony_ci	.unlock = l2c310_unlock,
13418c2ecf20Sopenharmony_ci	.outer_cache = {
13428c2ecf20Sopenharmony_ci		.inv_range   = l2c210_inv_range,
13438c2ecf20Sopenharmony_ci		.clean_range = l2c210_clean_range,
13448c2ecf20Sopenharmony_ci		.flush_range = l2c210_flush_range,
13458c2ecf20Sopenharmony_ci		.flush_all   = l2c210_flush_all,
13468c2ecf20Sopenharmony_ci		.disable     = l2c310_disable,
13478c2ecf20Sopenharmony_ci		.resume      = l2c310_resume,
13488c2ecf20Sopenharmony_ci	},
13498c2ecf20Sopenharmony_ci};
13508c2ecf20Sopenharmony_ci
13518c2ecf20Sopenharmony_ci/*
13528c2ecf20Sopenharmony_ci * Note that the end addresses passed to Linux primitives are
13538c2ecf20Sopenharmony_ci * noninclusive, while the hardware cache range operations use
13548c2ecf20Sopenharmony_ci * inclusive start and end addresses.
13558c2ecf20Sopenharmony_ci */
13568c2ecf20Sopenharmony_cistatic unsigned long aurora_range_end(unsigned long start, unsigned long end)
13578c2ecf20Sopenharmony_ci{
13588c2ecf20Sopenharmony_ci	/*
13598c2ecf20Sopenharmony_ci	 * Limit the number of cache lines processed at once,
13608c2ecf20Sopenharmony_ci	 * since cache range operations stall the CPU pipeline
13618c2ecf20Sopenharmony_ci	 * until completion.
13628c2ecf20Sopenharmony_ci	 */
13638c2ecf20Sopenharmony_ci	if (end > start + AURORA_MAX_RANGE_SIZE)
13648c2ecf20Sopenharmony_ci		end = start + AURORA_MAX_RANGE_SIZE;
13658c2ecf20Sopenharmony_ci
13668c2ecf20Sopenharmony_ci	/*
13678c2ecf20Sopenharmony_ci	 * Cache range operations can't straddle a page boundary.
13688c2ecf20Sopenharmony_ci	 */
13698c2ecf20Sopenharmony_ci	if (end > PAGE_ALIGN(start+1))
13708c2ecf20Sopenharmony_ci		end = PAGE_ALIGN(start+1);
13718c2ecf20Sopenharmony_ci
13728c2ecf20Sopenharmony_ci	return end;
13738c2ecf20Sopenharmony_ci}
13748c2ecf20Sopenharmony_ci
13758c2ecf20Sopenharmony_cistatic void aurora_pa_range(unsigned long start, unsigned long end,
13768c2ecf20Sopenharmony_ci			    unsigned long offset)
13778c2ecf20Sopenharmony_ci{
13788c2ecf20Sopenharmony_ci	void __iomem *base = l2x0_base;
13798c2ecf20Sopenharmony_ci	unsigned long range_end;
13808c2ecf20Sopenharmony_ci	unsigned long flags;
13818c2ecf20Sopenharmony_ci
13828c2ecf20Sopenharmony_ci	/*
13838c2ecf20Sopenharmony_ci	 * round start and end adresses up to cache line size
13848c2ecf20Sopenharmony_ci	 */
13858c2ecf20Sopenharmony_ci	start &= ~(CACHE_LINE_SIZE - 1);
13868c2ecf20Sopenharmony_ci	end = ALIGN(end, CACHE_LINE_SIZE);
13878c2ecf20Sopenharmony_ci
13888c2ecf20Sopenharmony_ci	/*
13898c2ecf20Sopenharmony_ci	 * perform operation on all full cache lines between 'start' and 'end'
13908c2ecf20Sopenharmony_ci	 */
13918c2ecf20Sopenharmony_ci	while (start < end) {
13928c2ecf20Sopenharmony_ci		range_end = aurora_range_end(start, end);
13938c2ecf20Sopenharmony_ci
13948c2ecf20Sopenharmony_ci		raw_spin_lock_irqsave(&l2x0_lock, flags);
13958c2ecf20Sopenharmony_ci		writel_relaxed(start, base + AURORA_RANGE_BASE_ADDR_REG);
13968c2ecf20Sopenharmony_ci		writel_relaxed(range_end - CACHE_LINE_SIZE, base + offset);
13978c2ecf20Sopenharmony_ci		raw_spin_unlock_irqrestore(&l2x0_lock, flags);
13988c2ecf20Sopenharmony_ci
13998c2ecf20Sopenharmony_ci		writel_relaxed(0, base + AURORA_SYNC_REG);
14008c2ecf20Sopenharmony_ci		start = range_end;
14018c2ecf20Sopenharmony_ci	}
14028c2ecf20Sopenharmony_ci}
14038c2ecf20Sopenharmony_cistatic void aurora_inv_range(unsigned long start, unsigned long end)
14048c2ecf20Sopenharmony_ci{
14058c2ecf20Sopenharmony_ci	aurora_pa_range(start, end, AURORA_INVAL_RANGE_REG);
14068c2ecf20Sopenharmony_ci}
14078c2ecf20Sopenharmony_ci
14088c2ecf20Sopenharmony_cistatic void aurora_clean_range(unsigned long start, unsigned long end)
14098c2ecf20Sopenharmony_ci{
14108c2ecf20Sopenharmony_ci	/*
14118c2ecf20Sopenharmony_ci	 * If L2 is forced to WT, the L2 will always be clean and we
14128c2ecf20Sopenharmony_ci	 * don't need to do anything here.
14138c2ecf20Sopenharmony_ci	 */
14148c2ecf20Sopenharmony_ci	if (!l2_wt_override)
14158c2ecf20Sopenharmony_ci		aurora_pa_range(start, end, AURORA_CLEAN_RANGE_REG);
14168c2ecf20Sopenharmony_ci}
14178c2ecf20Sopenharmony_ci
14188c2ecf20Sopenharmony_cistatic void aurora_flush_range(unsigned long start, unsigned long end)
14198c2ecf20Sopenharmony_ci{
14208c2ecf20Sopenharmony_ci	if (l2_wt_override)
14218c2ecf20Sopenharmony_ci		aurora_pa_range(start, end, AURORA_INVAL_RANGE_REG);
14228c2ecf20Sopenharmony_ci	else
14238c2ecf20Sopenharmony_ci		aurora_pa_range(start, end, AURORA_FLUSH_RANGE_REG);
14248c2ecf20Sopenharmony_ci}
14258c2ecf20Sopenharmony_ci
14268c2ecf20Sopenharmony_cistatic void aurora_flush_all(void)
14278c2ecf20Sopenharmony_ci{
14288c2ecf20Sopenharmony_ci	void __iomem *base = l2x0_base;
14298c2ecf20Sopenharmony_ci	unsigned long flags;
14308c2ecf20Sopenharmony_ci
14318c2ecf20Sopenharmony_ci	/* clean all ways */
14328c2ecf20Sopenharmony_ci	raw_spin_lock_irqsave(&l2x0_lock, flags);
14338c2ecf20Sopenharmony_ci	__l2c_op_way(base + L2X0_CLEAN_INV_WAY);
14348c2ecf20Sopenharmony_ci	raw_spin_unlock_irqrestore(&l2x0_lock, flags);
14358c2ecf20Sopenharmony_ci
14368c2ecf20Sopenharmony_ci	writel_relaxed(0, base + AURORA_SYNC_REG);
14378c2ecf20Sopenharmony_ci}
14388c2ecf20Sopenharmony_ci
14398c2ecf20Sopenharmony_cistatic void aurora_cache_sync(void)
14408c2ecf20Sopenharmony_ci{
14418c2ecf20Sopenharmony_ci	writel_relaxed(0, l2x0_base + AURORA_SYNC_REG);
14428c2ecf20Sopenharmony_ci}
14438c2ecf20Sopenharmony_ci
14448c2ecf20Sopenharmony_cistatic void aurora_disable(void)
14458c2ecf20Sopenharmony_ci{
14468c2ecf20Sopenharmony_ci	void __iomem *base = l2x0_base;
14478c2ecf20Sopenharmony_ci	unsigned long flags;
14488c2ecf20Sopenharmony_ci
14498c2ecf20Sopenharmony_ci	raw_spin_lock_irqsave(&l2x0_lock, flags);
14508c2ecf20Sopenharmony_ci	__l2c_op_way(base + L2X0_CLEAN_INV_WAY);
14518c2ecf20Sopenharmony_ci	writel_relaxed(0, base + AURORA_SYNC_REG);
14528c2ecf20Sopenharmony_ci	l2c_write_sec(0, base, L2X0_CTRL);
14538c2ecf20Sopenharmony_ci	dsb(st);
14548c2ecf20Sopenharmony_ci	raw_spin_unlock_irqrestore(&l2x0_lock, flags);
14558c2ecf20Sopenharmony_ci}
14568c2ecf20Sopenharmony_ci
14578c2ecf20Sopenharmony_cistatic void aurora_save(void __iomem *base)
14588c2ecf20Sopenharmony_ci{
14598c2ecf20Sopenharmony_ci	l2x0_saved_regs.ctrl = readl_relaxed(base + L2X0_CTRL);
14608c2ecf20Sopenharmony_ci	l2x0_saved_regs.aux_ctrl = readl_relaxed(base + L2X0_AUX_CTRL);
14618c2ecf20Sopenharmony_ci}
14628c2ecf20Sopenharmony_ci
14638c2ecf20Sopenharmony_ci/*
14648c2ecf20Sopenharmony_ci * For Aurora cache in no outer mode, enable via the CP15 coprocessor
14658c2ecf20Sopenharmony_ci * broadcasting of cache commands to L2.
14668c2ecf20Sopenharmony_ci */
14678c2ecf20Sopenharmony_cistatic void __init aurora_enable_no_outer(void __iomem *base,
14688c2ecf20Sopenharmony_ci	unsigned num_lock)
14698c2ecf20Sopenharmony_ci{
14708c2ecf20Sopenharmony_ci	u32 u;
14718c2ecf20Sopenharmony_ci
14728c2ecf20Sopenharmony_ci	asm volatile("mrc p15, 1, %0, c15, c2, 0" : "=r" (u));
14738c2ecf20Sopenharmony_ci	u |= AURORA_CTRL_FW;		/* Set the FW bit */
14748c2ecf20Sopenharmony_ci	asm volatile("mcr p15, 1, %0, c15, c2, 0" : : "r" (u));
14758c2ecf20Sopenharmony_ci
14768c2ecf20Sopenharmony_ci	isb();
14778c2ecf20Sopenharmony_ci
14788c2ecf20Sopenharmony_ci	l2c_enable(base, num_lock);
14798c2ecf20Sopenharmony_ci}
14808c2ecf20Sopenharmony_ci
14818c2ecf20Sopenharmony_cistatic void __init aurora_fixup(void __iomem *base, u32 cache_id,
14828c2ecf20Sopenharmony_ci	struct outer_cache_fns *fns)
14838c2ecf20Sopenharmony_ci{
14848c2ecf20Sopenharmony_ci	sync_reg_offset = AURORA_SYNC_REG;
14858c2ecf20Sopenharmony_ci}
14868c2ecf20Sopenharmony_ci
14878c2ecf20Sopenharmony_cistatic void __init aurora_of_parse(const struct device_node *np,
14888c2ecf20Sopenharmony_ci				u32 *aux_val, u32 *aux_mask)
14898c2ecf20Sopenharmony_ci{
14908c2ecf20Sopenharmony_ci	u32 val = AURORA_ACR_REPLACEMENT_TYPE_SEMIPLRU;
14918c2ecf20Sopenharmony_ci	u32 mask =  AURORA_ACR_REPLACEMENT_MASK;
14928c2ecf20Sopenharmony_ci
14938c2ecf20Sopenharmony_ci	of_property_read_u32(np, "cache-id-part",
14948c2ecf20Sopenharmony_ci			&cache_id_part_number_from_dt);
14958c2ecf20Sopenharmony_ci
14968c2ecf20Sopenharmony_ci	/* Determine and save the write policy */
14978c2ecf20Sopenharmony_ci	l2_wt_override = of_property_read_bool(np, "wt-override");
14988c2ecf20Sopenharmony_ci
14998c2ecf20Sopenharmony_ci	if (l2_wt_override) {
15008c2ecf20Sopenharmony_ci		val |= AURORA_ACR_FORCE_WRITE_THRO_POLICY;
15018c2ecf20Sopenharmony_ci		mask |= AURORA_ACR_FORCE_WRITE_POLICY_MASK;
15028c2ecf20Sopenharmony_ci	}
15038c2ecf20Sopenharmony_ci
15048c2ecf20Sopenharmony_ci	if (of_property_read_bool(np, "marvell,ecc-enable")) {
15058c2ecf20Sopenharmony_ci		mask |= AURORA_ACR_ECC_EN;
15068c2ecf20Sopenharmony_ci		val |= AURORA_ACR_ECC_EN;
15078c2ecf20Sopenharmony_ci	}
15088c2ecf20Sopenharmony_ci
15098c2ecf20Sopenharmony_ci	if (of_property_read_bool(np, "arm,parity-enable")) {
15108c2ecf20Sopenharmony_ci		mask |= AURORA_ACR_PARITY_EN;
15118c2ecf20Sopenharmony_ci		val |= AURORA_ACR_PARITY_EN;
15128c2ecf20Sopenharmony_ci	} else if (of_property_read_bool(np, "arm,parity-disable")) {
15138c2ecf20Sopenharmony_ci		mask |= AURORA_ACR_PARITY_EN;
15148c2ecf20Sopenharmony_ci	}
15158c2ecf20Sopenharmony_ci
15168c2ecf20Sopenharmony_ci	*aux_val &= ~mask;
15178c2ecf20Sopenharmony_ci	*aux_val |= val;
15188c2ecf20Sopenharmony_ci	*aux_mask &= ~mask;
15198c2ecf20Sopenharmony_ci}
15208c2ecf20Sopenharmony_ci
15218c2ecf20Sopenharmony_cistatic const struct l2c_init_data of_aurora_with_outer_data __initconst = {
15228c2ecf20Sopenharmony_ci	.type = "Aurora",
15238c2ecf20Sopenharmony_ci	.way_size_0 = SZ_4K,
15248c2ecf20Sopenharmony_ci	.num_lock = 4,
15258c2ecf20Sopenharmony_ci	.of_parse = aurora_of_parse,
15268c2ecf20Sopenharmony_ci	.enable = l2c_enable,
15278c2ecf20Sopenharmony_ci	.fixup = aurora_fixup,
15288c2ecf20Sopenharmony_ci	.save  = aurora_save,
15298c2ecf20Sopenharmony_ci	.configure = l2c_configure,
15308c2ecf20Sopenharmony_ci	.unlock = l2c_unlock,
15318c2ecf20Sopenharmony_ci	.outer_cache = {
15328c2ecf20Sopenharmony_ci		.inv_range   = aurora_inv_range,
15338c2ecf20Sopenharmony_ci		.clean_range = aurora_clean_range,
15348c2ecf20Sopenharmony_ci		.flush_range = aurora_flush_range,
15358c2ecf20Sopenharmony_ci		.flush_all   = aurora_flush_all,
15368c2ecf20Sopenharmony_ci		.disable     = aurora_disable,
15378c2ecf20Sopenharmony_ci		.sync	     = aurora_cache_sync,
15388c2ecf20Sopenharmony_ci		.resume      = l2c_resume,
15398c2ecf20Sopenharmony_ci	},
15408c2ecf20Sopenharmony_ci};
15418c2ecf20Sopenharmony_ci
15428c2ecf20Sopenharmony_cistatic const struct l2c_init_data of_aurora_no_outer_data __initconst = {
15438c2ecf20Sopenharmony_ci	.type = "Aurora",
15448c2ecf20Sopenharmony_ci	.way_size_0 = SZ_4K,
15458c2ecf20Sopenharmony_ci	.num_lock = 4,
15468c2ecf20Sopenharmony_ci	.of_parse = aurora_of_parse,
15478c2ecf20Sopenharmony_ci	.enable = aurora_enable_no_outer,
15488c2ecf20Sopenharmony_ci	.fixup = aurora_fixup,
15498c2ecf20Sopenharmony_ci	.save  = aurora_save,
15508c2ecf20Sopenharmony_ci	.configure = l2c_configure,
15518c2ecf20Sopenharmony_ci	.unlock = l2c_unlock,
15528c2ecf20Sopenharmony_ci	.outer_cache = {
15538c2ecf20Sopenharmony_ci		.resume      = l2c_resume,
15548c2ecf20Sopenharmony_ci	},
15558c2ecf20Sopenharmony_ci};
15568c2ecf20Sopenharmony_ci
15578c2ecf20Sopenharmony_ci/*
15588c2ecf20Sopenharmony_ci * For certain Broadcom SoCs, depending on the address range, different offsets
15598c2ecf20Sopenharmony_ci * need to be added to the address before passing it to L2 for
15608c2ecf20Sopenharmony_ci * invalidation/clean/flush
15618c2ecf20Sopenharmony_ci *
15628c2ecf20Sopenharmony_ci * Section Address Range              Offset        EMI
15638c2ecf20Sopenharmony_ci *   1     0x00000000 - 0x3FFFFFFF    0x80000000    VC
15648c2ecf20Sopenharmony_ci *   2     0x40000000 - 0xBFFFFFFF    0x40000000    SYS
15658c2ecf20Sopenharmony_ci *   3     0xC0000000 - 0xFFFFFFFF    0x80000000    VC
15668c2ecf20Sopenharmony_ci *
15678c2ecf20Sopenharmony_ci * When the start and end addresses have crossed two different sections, we
15688c2ecf20Sopenharmony_ci * need to break the L2 operation into two, each within its own section.
15698c2ecf20Sopenharmony_ci * For example, if we need to invalidate addresses starts at 0xBFFF0000 and
15708c2ecf20Sopenharmony_ci * ends at 0xC0001000, we need do invalidate 1) 0xBFFF0000 - 0xBFFFFFFF and 2)
15718c2ecf20Sopenharmony_ci * 0xC0000000 - 0xC0001000
15728c2ecf20Sopenharmony_ci *
15738c2ecf20Sopenharmony_ci * Note 1:
15748c2ecf20Sopenharmony_ci * By breaking a single L2 operation into two, we may potentially suffer some
15758c2ecf20Sopenharmony_ci * performance hit, but keep in mind the cross section case is very rare
15768c2ecf20Sopenharmony_ci *
15778c2ecf20Sopenharmony_ci * Note 2:
15788c2ecf20Sopenharmony_ci * We do not need to handle the case when the start address is in
15798c2ecf20Sopenharmony_ci * Section 1 and the end address is in Section 3, since it is not a valid use
15808c2ecf20Sopenharmony_ci * case
15818c2ecf20Sopenharmony_ci *
15828c2ecf20Sopenharmony_ci * Note 3:
15838c2ecf20Sopenharmony_ci * Section 1 in practical terms can no longer be used on rev A2. Because of
15848c2ecf20Sopenharmony_ci * that the code does not need to handle section 1 at all.
15858c2ecf20Sopenharmony_ci *
15868c2ecf20Sopenharmony_ci */
15878c2ecf20Sopenharmony_ci#define BCM_SYS_EMI_START_ADDR        0x40000000UL
15888c2ecf20Sopenharmony_ci#define BCM_VC_EMI_SEC3_START_ADDR    0xC0000000UL
15898c2ecf20Sopenharmony_ci
15908c2ecf20Sopenharmony_ci#define BCM_SYS_EMI_OFFSET            0x40000000UL
15918c2ecf20Sopenharmony_ci#define BCM_VC_EMI_OFFSET             0x80000000UL
15928c2ecf20Sopenharmony_ci
15938c2ecf20Sopenharmony_cistatic inline int bcm_addr_is_sys_emi(unsigned long addr)
15948c2ecf20Sopenharmony_ci{
15958c2ecf20Sopenharmony_ci	return (addr >= BCM_SYS_EMI_START_ADDR) &&
15968c2ecf20Sopenharmony_ci		(addr < BCM_VC_EMI_SEC3_START_ADDR);
15978c2ecf20Sopenharmony_ci}
15988c2ecf20Sopenharmony_ci
15998c2ecf20Sopenharmony_cistatic inline unsigned long bcm_l2_phys_addr(unsigned long addr)
16008c2ecf20Sopenharmony_ci{
16018c2ecf20Sopenharmony_ci	if (bcm_addr_is_sys_emi(addr))
16028c2ecf20Sopenharmony_ci		return addr + BCM_SYS_EMI_OFFSET;
16038c2ecf20Sopenharmony_ci	else
16048c2ecf20Sopenharmony_ci		return addr + BCM_VC_EMI_OFFSET;
16058c2ecf20Sopenharmony_ci}
16068c2ecf20Sopenharmony_ci
16078c2ecf20Sopenharmony_cistatic void bcm_inv_range(unsigned long start, unsigned long end)
16088c2ecf20Sopenharmony_ci{
16098c2ecf20Sopenharmony_ci	unsigned long new_start, new_end;
16108c2ecf20Sopenharmony_ci
16118c2ecf20Sopenharmony_ci	BUG_ON(start < BCM_SYS_EMI_START_ADDR);
16128c2ecf20Sopenharmony_ci
16138c2ecf20Sopenharmony_ci	if (unlikely(end <= start))
16148c2ecf20Sopenharmony_ci		return;
16158c2ecf20Sopenharmony_ci
16168c2ecf20Sopenharmony_ci	new_start = bcm_l2_phys_addr(start);
16178c2ecf20Sopenharmony_ci	new_end = bcm_l2_phys_addr(end);
16188c2ecf20Sopenharmony_ci
16198c2ecf20Sopenharmony_ci	/* normal case, no cross section between start and end */
16208c2ecf20Sopenharmony_ci	if (likely(bcm_addr_is_sys_emi(end) || !bcm_addr_is_sys_emi(start))) {
16218c2ecf20Sopenharmony_ci		l2c210_inv_range(new_start, new_end);
16228c2ecf20Sopenharmony_ci		return;
16238c2ecf20Sopenharmony_ci	}
16248c2ecf20Sopenharmony_ci
16258c2ecf20Sopenharmony_ci	/* They cross sections, so it can only be a cross from section
16268c2ecf20Sopenharmony_ci	 * 2 to section 3
16278c2ecf20Sopenharmony_ci	 */
16288c2ecf20Sopenharmony_ci	l2c210_inv_range(new_start,
16298c2ecf20Sopenharmony_ci		bcm_l2_phys_addr(BCM_VC_EMI_SEC3_START_ADDR-1));
16308c2ecf20Sopenharmony_ci	l2c210_inv_range(bcm_l2_phys_addr(BCM_VC_EMI_SEC3_START_ADDR),
16318c2ecf20Sopenharmony_ci		new_end);
16328c2ecf20Sopenharmony_ci}
16338c2ecf20Sopenharmony_ci
16348c2ecf20Sopenharmony_cistatic void bcm_clean_range(unsigned long start, unsigned long end)
16358c2ecf20Sopenharmony_ci{
16368c2ecf20Sopenharmony_ci	unsigned long new_start, new_end;
16378c2ecf20Sopenharmony_ci
16388c2ecf20Sopenharmony_ci	BUG_ON(start < BCM_SYS_EMI_START_ADDR);
16398c2ecf20Sopenharmony_ci
16408c2ecf20Sopenharmony_ci	if (unlikely(end <= start))
16418c2ecf20Sopenharmony_ci		return;
16428c2ecf20Sopenharmony_ci
16438c2ecf20Sopenharmony_ci	new_start = bcm_l2_phys_addr(start);
16448c2ecf20Sopenharmony_ci	new_end = bcm_l2_phys_addr(end);
16458c2ecf20Sopenharmony_ci
16468c2ecf20Sopenharmony_ci	/* normal case, no cross section between start and end */
16478c2ecf20Sopenharmony_ci	if (likely(bcm_addr_is_sys_emi(end) || !bcm_addr_is_sys_emi(start))) {
16488c2ecf20Sopenharmony_ci		l2c210_clean_range(new_start, new_end);
16498c2ecf20Sopenharmony_ci		return;
16508c2ecf20Sopenharmony_ci	}
16518c2ecf20Sopenharmony_ci
16528c2ecf20Sopenharmony_ci	/* They cross sections, so it can only be a cross from section
16538c2ecf20Sopenharmony_ci	 * 2 to section 3
16548c2ecf20Sopenharmony_ci	 */
16558c2ecf20Sopenharmony_ci	l2c210_clean_range(new_start,
16568c2ecf20Sopenharmony_ci		bcm_l2_phys_addr(BCM_VC_EMI_SEC3_START_ADDR-1));
16578c2ecf20Sopenharmony_ci	l2c210_clean_range(bcm_l2_phys_addr(BCM_VC_EMI_SEC3_START_ADDR),
16588c2ecf20Sopenharmony_ci		new_end);
16598c2ecf20Sopenharmony_ci}
16608c2ecf20Sopenharmony_ci
16618c2ecf20Sopenharmony_cistatic void bcm_flush_range(unsigned long start, unsigned long end)
16628c2ecf20Sopenharmony_ci{
16638c2ecf20Sopenharmony_ci	unsigned long new_start, new_end;
16648c2ecf20Sopenharmony_ci
16658c2ecf20Sopenharmony_ci	BUG_ON(start < BCM_SYS_EMI_START_ADDR);
16668c2ecf20Sopenharmony_ci
16678c2ecf20Sopenharmony_ci	if (unlikely(end <= start))
16688c2ecf20Sopenharmony_ci		return;
16698c2ecf20Sopenharmony_ci
16708c2ecf20Sopenharmony_ci	if ((end - start) >= l2x0_size) {
16718c2ecf20Sopenharmony_ci		outer_cache.flush_all();
16728c2ecf20Sopenharmony_ci		return;
16738c2ecf20Sopenharmony_ci	}
16748c2ecf20Sopenharmony_ci
16758c2ecf20Sopenharmony_ci	new_start = bcm_l2_phys_addr(start);
16768c2ecf20Sopenharmony_ci	new_end = bcm_l2_phys_addr(end);
16778c2ecf20Sopenharmony_ci
16788c2ecf20Sopenharmony_ci	/* normal case, no cross section between start and end */
16798c2ecf20Sopenharmony_ci	if (likely(bcm_addr_is_sys_emi(end) || !bcm_addr_is_sys_emi(start))) {
16808c2ecf20Sopenharmony_ci		l2c210_flush_range(new_start, new_end);
16818c2ecf20Sopenharmony_ci		return;
16828c2ecf20Sopenharmony_ci	}
16838c2ecf20Sopenharmony_ci
16848c2ecf20Sopenharmony_ci	/* They cross sections, so it can only be a cross from section
16858c2ecf20Sopenharmony_ci	 * 2 to section 3
16868c2ecf20Sopenharmony_ci	 */
16878c2ecf20Sopenharmony_ci	l2c210_flush_range(new_start,
16888c2ecf20Sopenharmony_ci		bcm_l2_phys_addr(BCM_VC_EMI_SEC3_START_ADDR-1));
16898c2ecf20Sopenharmony_ci	l2c210_flush_range(bcm_l2_phys_addr(BCM_VC_EMI_SEC3_START_ADDR),
16908c2ecf20Sopenharmony_ci		new_end);
16918c2ecf20Sopenharmony_ci}
16928c2ecf20Sopenharmony_ci
16938c2ecf20Sopenharmony_ci/* Broadcom L2C-310 start from ARMs R3P2 or later, and require no fixups */
16948c2ecf20Sopenharmony_cistatic const struct l2c_init_data of_bcm_l2x0_data __initconst = {
16958c2ecf20Sopenharmony_ci	.type = "BCM-L2C-310",
16968c2ecf20Sopenharmony_ci	.way_size_0 = SZ_8K,
16978c2ecf20Sopenharmony_ci	.num_lock = 8,
16988c2ecf20Sopenharmony_ci	.of_parse = l2c310_of_parse,
16998c2ecf20Sopenharmony_ci	.enable = l2c310_enable,
17008c2ecf20Sopenharmony_ci	.save  = l2c310_save,
17018c2ecf20Sopenharmony_ci	.configure = l2c310_configure,
17028c2ecf20Sopenharmony_ci	.unlock = l2c310_unlock,
17038c2ecf20Sopenharmony_ci	.outer_cache = {
17048c2ecf20Sopenharmony_ci		.inv_range   = bcm_inv_range,
17058c2ecf20Sopenharmony_ci		.clean_range = bcm_clean_range,
17068c2ecf20Sopenharmony_ci		.flush_range = bcm_flush_range,
17078c2ecf20Sopenharmony_ci		.flush_all   = l2c210_flush_all,
17088c2ecf20Sopenharmony_ci		.disable     = l2c310_disable,
17098c2ecf20Sopenharmony_ci		.sync        = l2c210_sync,
17108c2ecf20Sopenharmony_ci		.resume      = l2c310_resume,
17118c2ecf20Sopenharmony_ci	},
17128c2ecf20Sopenharmony_ci};
17138c2ecf20Sopenharmony_ci
17148c2ecf20Sopenharmony_cistatic void __init tauros3_save(void __iomem *base)
17158c2ecf20Sopenharmony_ci{
17168c2ecf20Sopenharmony_ci	l2c_save(base);
17178c2ecf20Sopenharmony_ci
17188c2ecf20Sopenharmony_ci	l2x0_saved_regs.aux2_ctrl =
17198c2ecf20Sopenharmony_ci		readl_relaxed(base + TAUROS3_AUX2_CTRL);
17208c2ecf20Sopenharmony_ci	l2x0_saved_regs.prefetch_ctrl =
17218c2ecf20Sopenharmony_ci		readl_relaxed(base + L310_PREFETCH_CTRL);
17228c2ecf20Sopenharmony_ci}
17238c2ecf20Sopenharmony_ci
17248c2ecf20Sopenharmony_cistatic void tauros3_configure(void __iomem *base)
17258c2ecf20Sopenharmony_ci{
17268c2ecf20Sopenharmony_ci	l2c_configure(base);
17278c2ecf20Sopenharmony_ci	writel_relaxed(l2x0_saved_regs.aux2_ctrl,
17288c2ecf20Sopenharmony_ci		       base + TAUROS3_AUX2_CTRL);
17298c2ecf20Sopenharmony_ci	writel_relaxed(l2x0_saved_regs.prefetch_ctrl,
17308c2ecf20Sopenharmony_ci		       base + L310_PREFETCH_CTRL);
17318c2ecf20Sopenharmony_ci}
17328c2ecf20Sopenharmony_ci
17338c2ecf20Sopenharmony_cistatic const struct l2c_init_data of_tauros3_data __initconst = {
17348c2ecf20Sopenharmony_ci	.type = "Tauros3",
17358c2ecf20Sopenharmony_ci	.way_size_0 = SZ_8K,
17368c2ecf20Sopenharmony_ci	.num_lock = 8,
17378c2ecf20Sopenharmony_ci	.enable = l2c_enable,
17388c2ecf20Sopenharmony_ci	.save  = tauros3_save,
17398c2ecf20Sopenharmony_ci	.configure = tauros3_configure,
17408c2ecf20Sopenharmony_ci	.unlock = l2c_unlock,
17418c2ecf20Sopenharmony_ci	/* Tauros3 broadcasts L1 cache operations to L2 */
17428c2ecf20Sopenharmony_ci	.outer_cache = {
17438c2ecf20Sopenharmony_ci		.resume      = l2c_resume,
17448c2ecf20Sopenharmony_ci	},
17458c2ecf20Sopenharmony_ci};
17468c2ecf20Sopenharmony_ci
17478c2ecf20Sopenharmony_ci#define L2C_ID(name, fns) { .compatible = name, .data = (void *)&fns }
17488c2ecf20Sopenharmony_cistatic const struct of_device_id l2x0_ids[] __initconst = {
17498c2ecf20Sopenharmony_ci	L2C_ID("arm,l210-cache", of_l2c210_data),
17508c2ecf20Sopenharmony_ci	L2C_ID("arm,l220-cache", of_l2c220_data),
17518c2ecf20Sopenharmony_ci	L2C_ID("arm,pl310-cache", of_l2c310_data),
17528c2ecf20Sopenharmony_ci	L2C_ID("brcm,bcm11351-a2-pl310-cache", of_bcm_l2x0_data),
17538c2ecf20Sopenharmony_ci	L2C_ID("marvell,aurora-outer-cache", of_aurora_with_outer_data),
17548c2ecf20Sopenharmony_ci	L2C_ID("marvell,aurora-system-cache", of_aurora_no_outer_data),
17558c2ecf20Sopenharmony_ci	L2C_ID("marvell,tauros3-cache", of_tauros3_data),
17568c2ecf20Sopenharmony_ci	/* Deprecated IDs */
17578c2ecf20Sopenharmony_ci	L2C_ID("bcm,bcm11351-a2-pl310-cache", of_bcm_l2x0_data),
17588c2ecf20Sopenharmony_ci	{}
17598c2ecf20Sopenharmony_ci};
17608c2ecf20Sopenharmony_ci
17618c2ecf20Sopenharmony_ciint __init l2x0_of_init(u32 aux_val, u32 aux_mask)
17628c2ecf20Sopenharmony_ci{
17638c2ecf20Sopenharmony_ci	const struct l2c_init_data *data;
17648c2ecf20Sopenharmony_ci	struct device_node *np;
17658c2ecf20Sopenharmony_ci	struct resource res;
17668c2ecf20Sopenharmony_ci	u32 cache_id, old_aux;
17678c2ecf20Sopenharmony_ci	u32 cache_level = 2;
17688c2ecf20Sopenharmony_ci	bool nosync = false;
17698c2ecf20Sopenharmony_ci
17708c2ecf20Sopenharmony_ci	np = of_find_matching_node(NULL, l2x0_ids);
17718c2ecf20Sopenharmony_ci	if (!np)
17728c2ecf20Sopenharmony_ci		return -ENODEV;
17738c2ecf20Sopenharmony_ci
17748c2ecf20Sopenharmony_ci	if (of_address_to_resource(np, 0, &res))
17758c2ecf20Sopenharmony_ci		return -ENODEV;
17768c2ecf20Sopenharmony_ci
17778c2ecf20Sopenharmony_ci	l2x0_base = ioremap(res.start, resource_size(&res));
17788c2ecf20Sopenharmony_ci	if (!l2x0_base)
17798c2ecf20Sopenharmony_ci		return -ENOMEM;
17808c2ecf20Sopenharmony_ci
17818c2ecf20Sopenharmony_ci	l2x0_saved_regs.phy_base = res.start;
17828c2ecf20Sopenharmony_ci
17838c2ecf20Sopenharmony_ci	data = of_match_node(l2x0_ids, np)->data;
17848c2ecf20Sopenharmony_ci
17858c2ecf20Sopenharmony_ci	if (of_device_is_compatible(np, "arm,pl310-cache") &&
17868c2ecf20Sopenharmony_ci	    of_property_read_bool(np, "arm,io-coherent"))
17878c2ecf20Sopenharmony_ci		data = &of_l2c310_coherent_data;
17888c2ecf20Sopenharmony_ci
17898c2ecf20Sopenharmony_ci	old_aux = readl_relaxed(l2x0_base + L2X0_AUX_CTRL);
17908c2ecf20Sopenharmony_ci	if (old_aux != ((old_aux & aux_mask) | aux_val)) {
17918c2ecf20Sopenharmony_ci		pr_warn("L2C: platform modifies aux control register: 0x%08x -> 0x%08x\n",
17928c2ecf20Sopenharmony_ci		        old_aux, (old_aux & aux_mask) | aux_val);
17938c2ecf20Sopenharmony_ci	} else if (aux_mask != ~0U && aux_val != 0) {
17948c2ecf20Sopenharmony_ci		pr_alert("L2C: platform provided aux values match the hardware, so have no effect.  Please remove them.\n");
17958c2ecf20Sopenharmony_ci	}
17968c2ecf20Sopenharmony_ci
17978c2ecf20Sopenharmony_ci	/* All L2 caches are unified, so this property should be specified */
17988c2ecf20Sopenharmony_ci	if (!of_property_read_bool(np, "cache-unified"))
17998c2ecf20Sopenharmony_ci		pr_err("L2C: device tree omits to specify unified cache\n");
18008c2ecf20Sopenharmony_ci
18018c2ecf20Sopenharmony_ci	if (of_property_read_u32(np, "cache-level", &cache_level))
18028c2ecf20Sopenharmony_ci		pr_err("L2C: device tree omits to specify cache-level\n");
18038c2ecf20Sopenharmony_ci
18048c2ecf20Sopenharmony_ci	if (cache_level != 2)
18058c2ecf20Sopenharmony_ci		pr_err("L2C: device tree specifies invalid cache level\n");
18068c2ecf20Sopenharmony_ci
18078c2ecf20Sopenharmony_ci	nosync = of_property_read_bool(np, "arm,outer-sync-disable");
18088c2ecf20Sopenharmony_ci
18098c2ecf20Sopenharmony_ci	/* Read back current (default) hardware configuration */
18108c2ecf20Sopenharmony_ci	if (data->save)
18118c2ecf20Sopenharmony_ci		data->save(l2x0_base);
18128c2ecf20Sopenharmony_ci
18138c2ecf20Sopenharmony_ci	/* L2 configuration can only be changed if the cache is disabled */
18148c2ecf20Sopenharmony_ci	if (!(readl_relaxed(l2x0_base + L2X0_CTRL) & L2X0_CTRL_EN))
18158c2ecf20Sopenharmony_ci		if (data->of_parse)
18168c2ecf20Sopenharmony_ci			data->of_parse(np, &aux_val, &aux_mask);
18178c2ecf20Sopenharmony_ci
18188c2ecf20Sopenharmony_ci	if (cache_id_part_number_from_dt)
18198c2ecf20Sopenharmony_ci		cache_id = cache_id_part_number_from_dt;
18208c2ecf20Sopenharmony_ci	else
18218c2ecf20Sopenharmony_ci		cache_id = readl_relaxed(l2x0_base + L2X0_CACHE_ID);
18228c2ecf20Sopenharmony_ci
18238c2ecf20Sopenharmony_ci	return __l2c_init(data, aux_val, aux_mask, cache_id, nosync);
18248c2ecf20Sopenharmony_ci}
18258c2ecf20Sopenharmony_ci#endif
1826