18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * Copyright (C) 2014 Marvell Technology Group Ltd.
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * Antoine Ténart <antoine.tenart@free-electrons.com>
68c2ecf20Sopenharmony_ci */
78c2ecf20Sopenharmony_ci
88c2ecf20Sopenharmony_ci#include <linux/io.h>
98c2ecf20Sopenharmony_ci#include <linux/delay.h>
108c2ecf20Sopenharmony_ci#include <linux/of.h>
118c2ecf20Sopenharmony_ci#include <linux/of_address.h>
128c2ecf20Sopenharmony_ci
138c2ecf20Sopenharmony_ci#include <asm/cacheflush.h>
148c2ecf20Sopenharmony_ci#include <asm/cp15.h>
158c2ecf20Sopenharmony_ci#include <asm/memory.h>
168c2ecf20Sopenharmony_ci#include <asm/smp_plat.h>
178c2ecf20Sopenharmony_ci#include <asm/smp_scu.h>
188c2ecf20Sopenharmony_ci
198c2ecf20Sopenharmony_ci/*
208c2ecf20Sopenharmony_ci * There are two reset registers, one with self-clearing (SC)
218c2ecf20Sopenharmony_ci * reset and one with non-self-clearing reset (NON_SC).
228c2ecf20Sopenharmony_ci */
238c2ecf20Sopenharmony_ci#define CPU_RESET_SC		0x00
248c2ecf20Sopenharmony_ci#define CPU_RESET_NON_SC	0x20
258c2ecf20Sopenharmony_ci
268c2ecf20Sopenharmony_ci#define RESET_VECT		0x00
278c2ecf20Sopenharmony_ci#define SW_RESET_ADDR		0x94
288c2ecf20Sopenharmony_ci
298c2ecf20Sopenharmony_ciextern u32 boot_inst;
308c2ecf20Sopenharmony_ci
318c2ecf20Sopenharmony_cistatic void __iomem *cpu_ctrl;
328c2ecf20Sopenharmony_ci
338c2ecf20Sopenharmony_cistatic inline void berlin_perform_reset_cpu(unsigned int cpu)
348c2ecf20Sopenharmony_ci{
358c2ecf20Sopenharmony_ci	u32 val;
368c2ecf20Sopenharmony_ci
378c2ecf20Sopenharmony_ci	val = readl(cpu_ctrl + CPU_RESET_NON_SC);
388c2ecf20Sopenharmony_ci	val &= ~BIT(cpu_logical_map(cpu));
398c2ecf20Sopenharmony_ci	writel(val, cpu_ctrl + CPU_RESET_NON_SC);
408c2ecf20Sopenharmony_ci	val |= BIT(cpu_logical_map(cpu));
418c2ecf20Sopenharmony_ci	writel(val, cpu_ctrl + CPU_RESET_NON_SC);
428c2ecf20Sopenharmony_ci}
438c2ecf20Sopenharmony_ci
448c2ecf20Sopenharmony_cistatic int berlin_boot_secondary(unsigned int cpu, struct task_struct *idle)
458c2ecf20Sopenharmony_ci{
468c2ecf20Sopenharmony_ci	if (!cpu_ctrl)
478c2ecf20Sopenharmony_ci		return -EFAULT;
488c2ecf20Sopenharmony_ci
498c2ecf20Sopenharmony_ci	/*
508c2ecf20Sopenharmony_ci	 * Reset the CPU, making it to execute the instruction in the reset
518c2ecf20Sopenharmony_ci	 * exception vector.
528c2ecf20Sopenharmony_ci	 */
538c2ecf20Sopenharmony_ci	berlin_perform_reset_cpu(cpu);
548c2ecf20Sopenharmony_ci
558c2ecf20Sopenharmony_ci	return 0;
568c2ecf20Sopenharmony_ci}
578c2ecf20Sopenharmony_ci
588c2ecf20Sopenharmony_cistatic void __init berlin_smp_prepare_cpus(unsigned int max_cpus)
598c2ecf20Sopenharmony_ci{
608c2ecf20Sopenharmony_ci	struct device_node *np;
618c2ecf20Sopenharmony_ci	void __iomem *scu_base;
628c2ecf20Sopenharmony_ci	void __iomem *vectors_base;
638c2ecf20Sopenharmony_ci
648c2ecf20Sopenharmony_ci	np = of_find_compatible_node(NULL, NULL, "arm,cortex-a9-scu");
658c2ecf20Sopenharmony_ci	scu_base = of_iomap(np, 0);
668c2ecf20Sopenharmony_ci	of_node_put(np);
678c2ecf20Sopenharmony_ci	if (!scu_base)
688c2ecf20Sopenharmony_ci		return;
698c2ecf20Sopenharmony_ci
708c2ecf20Sopenharmony_ci	np = of_find_compatible_node(NULL, NULL, "marvell,berlin-cpu-ctrl");
718c2ecf20Sopenharmony_ci	cpu_ctrl = of_iomap(np, 0);
728c2ecf20Sopenharmony_ci	of_node_put(np);
738c2ecf20Sopenharmony_ci	if (!cpu_ctrl)
748c2ecf20Sopenharmony_ci		goto unmap_scu;
758c2ecf20Sopenharmony_ci
768c2ecf20Sopenharmony_ci	vectors_base = ioremap(VECTORS_BASE, SZ_32K);
778c2ecf20Sopenharmony_ci	if (!vectors_base)
788c2ecf20Sopenharmony_ci		goto unmap_scu;
798c2ecf20Sopenharmony_ci
808c2ecf20Sopenharmony_ci	scu_enable(scu_base);
818c2ecf20Sopenharmony_ci
828c2ecf20Sopenharmony_ci	/*
838c2ecf20Sopenharmony_ci	 * Write the first instruction the CPU will execute after being reset
848c2ecf20Sopenharmony_ci	 * in the reset exception vector.
858c2ecf20Sopenharmony_ci	 */
868c2ecf20Sopenharmony_ci	writel(boot_inst, vectors_base + RESET_VECT);
878c2ecf20Sopenharmony_ci
888c2ecf20Sopenharmony_ci	/*
898c2ecf20Sopenharmony_ci	 * Write the secondary startup address into the SW reset address
908c2ecf20Sopenharmony_ci	 * vector. This is used by boot_inst.
918c2ecf20Sopenharmony_ci	 */
928c2ecf20Sopenharmony_ci	writel(__pa_symbol(secondary_startup), vectors_base + SW_RESET_ADDR);
938c2ecf20Sopenharmony_ci
948c2ecf20Sopenharmony_ci	iounmap(vectors_base);
958c2ecf20Sopenharmony_ciunmap_scu:
968c2ecf20Sopenharmony_ci	iounmap(scu_base);
978c2ecf20Sopenharmony_ci}
988c2ecf20Sopenharmony_ci
998c2ecf20Sopenharmony_ci#ifdef CONFIG_HOTPLUG_CPU
1008c2ecf20Sopenharmony_cistatic void berlin_cpu_die(unsigned int cpu)
1018c2ecf20Sopenharmony_ci{
1028c2ecf20Sopenharmony_ci	v7_exit_coherency_flush(louis);
1038c2ecf20Sopenharmony_ci	while (1)
1048c2ecf20Sopenharmony_ci		cpu_do_idle();
1058c2ecf20Sopenharmony_ci}
1068c2ecf20Sopenharmony_ci
1078c2ecf20Sopenharmony_cistatic int berlin_cpu_kill(unsigned int cpu)
1088c2ecf20Sopenharmony_ci{
1098c2ecf20Sopenharmony_ci	u32 val;
1108c2ecf20Sopenharmony_ci
1118c2ecf20Sopenharmony_ci	val = readl(cpu_ctrl + CPU_RESET_NON_SC);
1128c2ecf20Sopenharmony_ci	val &= ~BIT(cpu_logical_map(cpu));
1138c2ecf20Sopenharmony_ci	writel(val, cpu_ctrl + CPU_RESET_NON_SC);
1148c2ecf20Sopenharmony_ci
1158c2ecf20Sopenharmony_ci	return 1;
1168c2ecf20Sopenharmony_ci}
1178c2ecf20Sopenharmony_ci#endif
1188c2ecf20Sopenharmony_ci
1198c2ecf20Sopenharmony_cistatic const struct smp_operations berlin_smp_ops __initconst = {
1208c2ecf20Sopenharmony_ci	.smp_prepare_cpus	= berlin_smp_prepare_cpus,
1218c2ecf20Sopenharmony_ci	.smp_boot_secondary	= berlin_boot_secondary,
1228c2ecf20Sopenharmony_ci#ifdef CONFIG_HOTPLUG_CPU
1238c2ecf20Sopenharmony_ci	.cpu_die		= berlin_cpu_die,
1248c2ecf20Sopenharmony_ci	.cpu_kill		= berlin_cpu_kill,
1258c2ecf20Sopenharmony_ci#endif
1268c2ecf20Sopenharmony_ci};
1278c2ecf20Sopenharmony_ciCPU_METHOD_OF_DECLARE(berlin_smp, "marvell,berlin-smp", &berlin_smp_ops);
128