162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Copyright (C) 2014 Marvell Technology Group Ltd.
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Antoine Ténart <antoine.tenart@free-electrons.com>
662306a36Sopenharmony_ci */
762306a36Sopenharmony_ci
862306a36Sopenharmony_ci#include <linux/io.h>
962306a36Sopenharmony_ci#include <linux/delay.h>
1062306a36Sopenharmony_ci#include <linux/of.h>
1162306a36Sopenharmony_ci#include <linux/of_address.h>
1262306a36Sopenharmony_ci
1362306a36Sopenharmony_ci#include <asm/cacheflush.h>
1462306a36Sopenharmony_ci#include <asm/cp15.h>
1562306a36Sopenharmony_ci#include <asm/page.h>
1662306a36Sopenharmony_ci#include <asm/smp_plat.h>
1762306a36Sopenharmony_ci#include <asm/smp_scu.h>
1862306a36Sopenharmony_ci
1962306a36Sopenharmony_ci/*
2062306a36Sopenharmony_ci * There are two reset registers, one with self-clearing (SC)
2162306a36Sopenharmony_ci * reset and one with non-self-clearing reset (NON_SC).
2262306a36Sopenharmony_ci */
2362306a36Sopenharmony_ci#define CPU_RESET_SC		0x00
2462306a36Sopenharmony_ci#define CPU_RESET_NON_SC	0x20
2562306a36Sopenharmony_ci
2662306a36Sopenharmony_ci#define RESET_VECT		0x00
2762306a36Sopenharmony_ci#define SW_RESET_ADDR		0x94
2862306a36Sopenharmony_ci
2962306a36Sopenharmony_ciextern u32 boot_inst;
3062306a36Sopenharmony_ci
3162306a36Sopenharmony_cistatic void __iomem *cpu_ctrl;
3262306a36Sopenharmony_ci
3362306a36Sopenharmony_cistatic inline void berlin_perform_reset_cpu(unsigned int cpu)
3462306a36Sopenharmony_ci{
3562306a36Sopenharmony_ci	u32 val;
3662306a36Sopenharmony_ci
3762306a36Sopenharmony_ci	val = readl(cpu_ctrl + CPU_RESET_NON_SC);
3862306a36Sopenharmony_ci	val &= ~BIT(cpu_logical_map(cpu));
3962306a36Sopenharmony_ci	writel(val, cpu_ctrl + CPU_RESET_NON_SC);
4062306a36Sopenharmony_ci	val |= BIT(cpu_logical_map(cpu));
4162306a36Sopenharmony_ci	writel(val, cpu_ctrl + CPU_RESET_NON_SC);
4262306a36Sopenharmony_ci}
4362306a36Sopenharmony_ci
4462306a36Sopenharmony_cistatic int berlin_boot_secondary(unsigned int cpu, struct task_struct *idle)
4562306a36Sopenharmony_ci{
4662306a36Sopenharmony_ci	if (!cpu_ctrl)
4762306a36Sopenharmony_ci		return -EFAULT;
4862306a36Sopenharmony_ci
4962306a36Sopenharmony_ci	/*
5062306a36Sopenharmony_ci	 * Reset the CPU, making it to execute the instruction in the reset
5162306a36Sopenharmony_ci	 * exception vector.
5262306a36Sopenharmony_ci	 */
5362306a36Sopenharmony_ci	berlin_perform_reset_cpu(cpu);
5462306a36Sopenharmony_ci
5562306a36Sopenharmony_ci	return 0;
5662306a36Sopenharmony_ci}
5762306a36Sopenharmony_ci
5862306a36Sopenharmony_cistatic void __init berlin_smp_prepare_cpus(unsigned int max_cpus)
5962306a36Sopenharmony_ci{
6062306a36Sopenharmony_ci	struct device_node *np;
6162306a36Sopenharmony_ci	void __iomem *scu_base;
6262306a36Sopenharmony_ci	void __iomem *vectors_base;
6362306a36Sopenharmony_ci
6462306a36Sopenharmony_ci	np = of_find_compatible_node(NULL, NULL, "arm,cortex-a9-scu");
6562306a36Sopenharmony_ci	scu_base = of_iomap(np, 0);
6662306a36Sopenharmony_ci	of_node_put(np);
6762306a36Sopenharmony_ci	if (!scu_base)
6862306a36Sopenharmony_ci		return;
6962306a36Sopenharmony_ci
7062306a36Sopenharmony_ci	np = of_find_compatible_node(NULL, NULL, "marvell,berlin-cpu-ctrl");
7162306a36Sopenharmony_ci	cpu_ctrl = of_iomap(np, 0);
7262306a36Sopenharmony_ci	of_node_put(np);
7362306a36Sopenharmony_ci	if (!cpu_ctrl)
7462306a36Sopenharmony_ci		goto unmap_scu;
7562306a36Sopenharmony_ci
7662306a36Sopenharmony_ci	vectors_base = ioremap(VECTORS_BASE, SZ_32K);
7762306a36Sopenharmony_ci	if (!vectors_base)
7862306a36Sopenharmony_ci		goto unmap_scu;
7962306a36Sopenharmony_ci
8062306a36Sopenharmony_ci	scu_enable(scu_base);
8162306a36Sopenharmony_ci
8262306a36Sopenharmony_ci	/*
8362306a36Sopenharmony_ci	 * Write the first instruction the CPU will execute after being reset
8462306a36Sopenharmony_ci	 * in the reset exception vector.
8562306a36Sopenharmony_ci	 */
8662306a36Sopenharmony_ci	writel(boot_inst, vectors_base + RESET_VECT);
8762306a36Sopenharmony_ci
8862306a36Sopenharmony_ci	/*
8962306a36Sopenharmony_ci	 * Write the secondary startup address into the SW reset address
9062306a36Sopenharmony_ci	 * vector. This is used by boot_inst.
9162306a36Sopenharmony_ci	 */
9262306a36Sopenharmony_ci	writel(__pa_symbol(secondary_startup), vectors_base + SW_RESET_ADDR);
9362306a36Sopenharmony_ci
9462306a36Sopenharmony_ci	iounmap(vectors_base);
9562306a36Sopenharmony_ciunmap_scu:
9662306a36Sopenharmony_ci	iounmap(scu_base);
9762306a36Sopenharmony_ci}
9862306a36Sopenharmony_ci
9962306a36Sopenharmony_ci#ifdef CONFIG_HOTPLUG_CPU
10062306a36Sopenharmony_cistatic void berlin_cpu_die(unsigned int cpu)
10162306a36Sopenharmony_ci{
10262306a36Sopenharmony_ci	v7_exit_coherency_flush(louis);
10362306a36Sopenharmony_ci	while (1)
10462306a36Sopenharmony_ci		cpu_do_idle();
10562306a36Sopenharmony_ci}
10662306a36Sopenharmony_ci
10762306a36Sopenharmony_cistatic int berlin_cpu_kill(unsigned int cpu)
10862306a36Sopenharmony_ci{
10962306a36Sopenharmony_ci	u32 val;
11062306a36Sopenharmony_ci
11162306a36Sopenharmony_ci	val = readl(cpu_ctrl + CPU_RESET_NON_SC);
11262306a36Sopenharmony_ci	val &= ~BIT(cpu_logical_map(cpu));
11362306a36Sopenharmony_ci	writel(val, cpu_ctrl + CPU_RESET_NON_SC);
11462306a36Sopenharmony_ci
11562306a36Sopenharmony_ci	return 1;
11662306a36Sopenharmony_ci}
11762306a36Sopenharmony_ci#endif
11862306a36Sopenharmony_ci
11962306a36Sopenharmony_cistatic const struct smp_operations berlin_smp_ops __initconst = {
12062306a36Sopenharmony_ci	.smp_prepare_cpus	= berlin_smp_prepare_cpus,
12162306a36Sopenharmony_ci	.smp_boot_secondary	= berlin_boot_secondary,
12262306a36Sopenharmony_ci#ifdef CONFIG_HOTPLUG_CPU
12362306a36Sopenharmony_ci	.cpu_die		= berlin_cpu_die,
12462306a36Sopenharmony_ci	.cpu_kill		= berlin_cpu_kill,
12562306a36Sopenharmony_ci#endif
12662306a36Sopenharmony_ci};
12762306a36Sopenharmony_ciCPU_METHOD_OF_DECLARE(berlin_smp, "marvell,berlin-smp", &berlin_smp_ops);
128