162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Symmetric Multi Processing (SMP) support for Marvell EBU Cortex-A9
462306a36Sopenharmony_ci * based SOCs (Armada 375/38x).
562306a36Sopenharmony_ci *
662306a36Sopenharmony_ci * Copyright (C) 2014 Marvell
762306a36Sopenharmony_ci *
862306a36Sopenharmony_ci * Gregory CLEMENT <gregory.clement@free-electrons.com>
962306a36Sopenharmony_ci * Thomas Petazzoni <thomas.petazzoni@free-electrons.com>
1062306a36Sopenharmony_ci */
1162306a36Sopenharmony_ci
1262306a36Sopenharmony_ci#include <linux/init.h>
1362306a36Sopenharmony_ci#include <linux/io.h>
1462306a36Sopenharmony_ci#include <linux/of.h>
1562306a36Sopenharmony_ci#include <linux/smp.h>
1662306a36Sopenharmony_ci#include <linux/mbus.h>
1762306a36Sopenharmony_ci#include <asm/smp_scu.h>
1862306a36Sopenharmony_ci#include <asm/smp_plat.h>
1962306a36Sopenharmony_ci#include "common.h"
2062306a36Sopenharmony_ci#include "pmsu.h"
2162306a36Sopenharmony_ci
2262306a36Sopenharmony_ciextern void mvebu_cortex_a9_secondary_startup(void);
2362306a36Sopenharmony_ci
2462306a36Sopenharmony_cistatic int mvebu_cortex_a9_boot_secondary(unsigned int cpu,
2562306a36Sopenharmony_ci						    struct task_struct *idle)
2662306a36Sopenharmony_ci{
2762306a36Sopenharmony_ci	int ret, hw_cpu;
2862306a36Sopenharmony_ci
2962306a36Sopenharmony_ci	pr_info("Booting CPU %d\n", cpu);
3062306a36Sopenharmony_ci
3162306a36Sopenharmony_ci	/*
3262306a36Sopenharmony_ci	 * Write the address of secondary startup into the system-wide
3362306a36Sopenharmony_ci	 * flags register. The boot monitor waits until it receives a
3462306a36Sopenharmony_ci	 * soft interrupt, and then the secondary CPU branches to this
3562306a36Sopenharmony_ci	 * address.
3662306a36Sopenharmony_ci	 */
3762306a36Sopenharmony_ci	hw_cpu = cpu_logical_map(cpu);
3862306a36Sopenharmony_ci	if (of_machine_is_compatible("marvell,armada375"))
3962306a36Sopenharmony_ci		mvebu_system_controller_set_cpu_boot_addr(mvebu_cortex_a9_secondary_startup);
4062306a36Sopenharmony_ci	else
4162306a36Sopenharmony_ci		mvebu_pmsu_set_cpu_boot_addr(hw_cpu, mvebu_cortex_a9_secondary_startup);
4262306a36Sopenharmony_ci	smp_wmb();
4362306a36Sopenharmony_ci
4462306a36Sopenharmony_ci	/*
4562306a36Sopenharmony_ci	 * Doing this before deasserting the CPUs is needed to wake up CPUs
4662306a36Sopenharmony_ci	 * in the offline state after using CPU hotplug.
4762306a36Sopenharmony_ci	 */
4862306a36Sopenharmony_ci	arch_send_wakeup_ipi_mask(cpumask_of(cpu));
4962306a36Sopenharmony_ci
5062306a36Sopenharmony_ci	ret = mvebu_cpu_reset_deassert(hw_cpu);
5162306a36Sopenharmony_ci	if (ret) {
5262306a36Sopenharmony_ci		pr_err("Could not start the secondary CPU: %d\n", ret);
5362306a36Sopenharmony_ci		return ret;
5462306a36Sopenharmony_ci	}
5562306a36Sopenharmony_ci
5662306a36Sopenharmony_ci	return 0;
5762306a36Sopenharmony_ci}
5862306a36Sopenharmony_ci/*
5962306a36Sopenharmony_ci * When a CPU is brought back online, either through CPU hotplug, or
6062306a36Sopenharmony_ci * because of the boot of a kexec'ed kernel, the PMSU configuration
6162306a36Sopenharmony_ci * for this CPU might be in the deep idle state, preventing this CPU
6262306a36Sopenharmony_ci * from receiving interrupts. Here, we therefore take out the current
6362306a36Sopenharmony_ci * CPU from this state, which was entered by armada_38x_cpu_die()
6462306a36Sopenharmony_ci * below.
6562306a36Sopenharmony_ci */
6662306a36Sopenharmony_cistatic void armada_38x_secondary_init(unsigned int cpu)
6762306a36Sopenharmony_ci{
6862306a36Sopenharmony_ci	mvebu_v7_pmsu_idle_exit();
6962306a36Sopenharmony_ci}
7062306a36Sopenharmony_ci
7162306a36Sopenharmony_ci#ifdef CONFIG_HOTPLUG_CPU
7262306a36Sopenharmony_cistatic void armada_38x_cpu_die(unsigned int cpu)
7362306a36Sopenharmony_ci{
7462306a36Sopenharmony_ci	/*
7562306a36Sopenharmony_ci	 * CPU hotplug is implemented by putting offline CPUs into the
7662306a36Sopenharmony_ci	 * deep idle sleep state.
7762306a36Sopenharmony_ci	 */
7862306a36Sopenharmony_ci	armada_38x_do_cpu_suspend(true);
7962306a36Sopenharmony_ci}
8062306a36Sopenharmony_ci
8162306a36Sopenharmony_ci/*
8262306a36Sopenharmony_ci * We need a dummy function, so that platform_can_cpu_hotplug() knows
8362306a36Sopenharmony_ci * we support CPU hotplug. However, the function does not need to do
8462306a36Sopenharmony_ci * anything, because CPUs going offline can enter the deep idle state
8562306a36Sopenharmony_ci * by themselves, without any help from a still alive CPU.
8662306a36Sopenharmony_ci */
8762306a36Sopenharmony_cistatic int armada_38x_cpu_kill(unsigned int cpu)
8862306a36Sopenharmony_ci{
8962306a36Sopenharmony_ci	return 1;
9062306a36Sopenharmony_ci}
9162306a36Sopenharmony_ci#endif
9262306a36Sopenharmony_ci
9362306a36Sopenharmony_cistatic const struct smp_operations mvebu_cortex_a9_smp_ops __initconst = {
9462306a36Sopenharmony_ci	.smp_boot_secondary	= mvebu_cortex_a9_boot_secondary,
9562306a36Sopenharmony_ci};
9662306a36Sopenharmony_ci
9762306a36Sopenharmony_cistatic const struct smp_operations armada_38x_smp_ops __initconst = {
9862306a36Sopenharmony_ci	.smp_boot_secondary	= mvebu_cortex_a9_boot_secondary,
9962306a36Sopenharmony_ci	.smp_secondary_init     = armada_38x_secondary_init,
10062306a36Sopenharmony_ci#ifdef CONFIG_HOTPLUG_CPU
10162306a36Sopenharmony_ci	.cpu_die		= armada_38x_cpu_die,
10262306a36Sopenharmony_ci	.cpu_kill               = armada_38x_cpu_kill,
10362306a36Sopenharmony_ci#endif
10462306a36Sopenharmony_ci};
10562306a36Sopenharmony_ci
10662306a36Sopenharmony_ciCPU_METHOD_OF_DECLARE(mvebu_armada_375_smp, "marvell,armada-375-smp",
10762306a36Sopenharmony_ci		      &mvebu_cortex_a9_smp_ops);
10862306a36Sopenharmony_ciCPU_METHOD_OF_DECLARE(mvebu_armada_380_smp, "marvell,armada-380-smp",
10962306a36Sopenharmony_ci		      &armada_38x_smp_ops);
11062306a36Sopenharmony_ciCPU_METHOD_OF_DECLARE(mvebu_armada_390_smp, "marvell,armada-390-smp",
11162306a36Sopenharmony_ci		      &armada_38x_smp_ops);
112