18c2ecf20Sopenharmony_ci/*
28c2ecf20Sopenharmony_ci * Symmetric Multi Processing (SMP) support for Marvell EBU Cortex-A9
38c2ecf20Sopenharmony_ci * based SOCs (Armada 375/38x).
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * Copyright (C) 2014 Marvell
68c2ecf20Sopenharmony_ci *
78c2ecf20Sopenharmony_ci * Gregory CLEMENT <gregory.clement@free-electrons.com>
88c2ecf20Sopenharmony_ci * Thomas Petazzoni <thomas.petazzoni@free-electrons.com>
98c2ecf20Sopenharmony_ci *
108c2ecf20Sopenharmony_ci * This file is licensed under the terms of the GNU General Public
118c2ecf20Sopenharmony_ci * License version 2.  This program is licensed "as is" without any
128c2ecf20Sopenharmony_ci * warranty of any kind, whether express or implied.
138c2ecf20Sopenharmony_ci */
148c2ecf20Sopenharmony_ci
158c2ecf20Sopenharmony_ci#include <linux/init.h>
168c2ecf20Sopenharmony_ci#include <linux/io.h>
178c2ecf20Sopenharmony_ci#include <linux/of.h>
188c2ecf20Sopenharmony_ci#include <linux/smp.h>
198c2ecf20Sopenharmony_ci#include <linux/mbus.h>
208c2ecf20Sopenharmony_ci#include <asm/smp_scu.h>
218c2ecf20Sopenharmony_ci#include <asm/smp_plat.h>
228c2ecf20Sopenharmony_ci#include "common.h"
238c2ecf20Sopenharmony_ci#include "pmsu.h"
248c2ecf20Sopenharmony_ci
258c2ecf20Sopenharmony_ciextern void mvebu_cortex_a9_secondary_startup(void);
268c2ecf20Sopenharmony_ci
278c2ecf20Sopenharmony_cistatic int mvebu_cortex_a9_boot_secondary(unsigned int cpu,
288c2ecf20Sopenharmony_ci						    struct task_struct *idle)
298c2ecf20Sopenharmony_ci{
308c2ecf20Sopenharmony_ci	int ret, hw_cpu;
318c2ecf20Sopenharmony_ci
328c2ecf20Sopenharmony_ci	pr_info("Booting CPU %d\n", cpu);
338c2ecf20Sopenharmony_ci
348c2ecf20Sopenharmony_ci	/*
358c2ecf20Sopenharmony_ci	 * Write the address of secondary startup into the system-wide
368c2ecf20Sopenharmony_ci	 * flags register. The boot monitor waits until it receives a
378c2ecf20Sopenharmony_ci	 * soft interrupt, and then the secondary CPU branches to this
388c2ecf20Sopenharmony_ci	 * address.
398c2ecf20Sopenharmony_ci	 */
408c2ecf20Sopenharmony_ci	hw_cpu = cpu_logical_map(cpu);
418c2ecf20Sopenharmony_ci	if (of_machine_is_compatible("marvell,armada375"))
428c2ecf20Sopenharmony_ci		mvebu_system_controller_set_cpu_boot_addr(mvebu_cortex_a9_secondary_startup);
438c2ecf20Sopenharmony_ci	else
448c2ecf20Sopenharmony_ci		mvebu_pmsu_set_cpu_boot_addr(hw_cpu, mvebu_cortex_a9_secondary_startup);
458c2ecf20Sopenharmony_ci	smp_wmb();
468c2ecf20Sopenharmony_ci
478c2ecf20Sopenharmony_ci	/*
488c2ecf20Sopenharmony_ci	 * Doing this before deasserting the CPUs is needed to wake up CPUs
498c2ecf20Sopenharmony_ci	 * in the offline state after using CPU hotplug.
508c2ecf20Sopenharmony_ci	 */
518c2ecf20Sopenharmony_ci	arch_send_wakeup_ipi_mask(cpumask_of(cpu));
528c2ecf20Sopenharmony_ci
538c2ecf20Sopenharmony_ci	ret = mvebu_cpu_reset_deassert(hw_cpu);
548c2ecf20Sopenharmony_ci	if (ret) {
558c2ecf20Sopenharmony_ci		pr_err("Could not start the secondary CPU: %d\n", ret);
568c2ecf20Sopenharmony_ci		return ret;
578c2ecf20Sopenharmony_ci	}
588c2ecf20Sopenharmony_ci
598c2ecf20Sopenharmony_ci	return 0;
608c2ecf20Sopenharmony_ci}
618c2ecf20Sopenharmony_ci/*
628c2ecf20Sopenharmony_ci * When a CPU is brought back online, either through CPU hotplug, or
638c2ecf20Sopenharmony_ci * because of the boot of a kexec'ed kernel, the PMSU configuration
648c2ecf20Sopenharmony_ci * for this CPU might be in the deep idle state, preventing this CPU
658c2ecf20Sopenharmony_ci * from receiving interrupts. Here, we therefore take out the current
668c2ecf20Sopenharmony_ci * CPU from this state, which was entered by armada_38x_cpu_die()
678c2ecf20Sopenharmony_ci * below.
688c2ecf20Sopenharmony_ci */
698c2ecf20Sopenharmony_cistatic void armada_38x_secondary_init(unsigned int cpu)
708c2ecf20Sopenharmony_ci{
718c2ecf20Sopenharmony_ci	mvebu_v7_pmsu_idle_exit();
728c2ecf20Sopenharmony_ci}
738c2ecf20Sopenharmony_ci
748c2ecf20Sopenharmony_ci#ifdef CONFIG_HOTPLUG_CPU
758c2ecf20Sopenharmony_cistatic void armada_38x_cpu_die(unsigned int cpu)
768c2ecf20Sopenharmony_ci{
778c2ecf20Sopenharmony_ci	/*
788c2ecf20Sopenharmony_ci	 * CPU hotplug is implemented by putting offline CPUs into the
798c2ecf20Sopenharmony_ci	 * deep idle sleep state.
808c2ecf20Sopenharmony_ci	 */
818c2ecf20Sopenharmony_ci	armada_38x_do_cpu_suspend(true);
828c2ecf20Sopenharmony_ci}
838c2ecf20Sopenharmony_ci
848c2ecf20Sopenharmony_ci/*
858c2ecf20Sopenharmony_ci * We need a dummy function, so that platform_can_cpu_hotplug() knows
868c2ecf20Sopenharmony_ci * we support CPU hotplug. However, the function does not need to do
878c2ecf20Sopenharmony_ci * anything, because CPUs going offline can enter the deep idle state
888c2ecf20Sopenharmony_ci * by themselves, without any help from a still alive CPU.
898c2ecf20Sopenharmony_ci */
908c2ecf20Sopenharmony_cistatic int armada_38x_cpu_kill(unsigned int cpu)
918c2ecf20Sopenharmony_ci{
928c2ecf20Sopenharmony_ci	return 1;
938c2ecf20Sopenharmony_ci}
948c2ecf20Sopenharmony_ci#endif
958c2ecf20Sopenharmony_ci
968c2ecf20Sopenharmony_cistatic const struct smp_operations mvebu_cortex_a9_smp_ops __initconst = {
978c2ecf20Sopenharmony_ci	.smp_boot_secondary	= mvebu_cortex_a9_boot_secondary,
988c2ecf20Sopenharmony_ci};
998c2ecf20Sopenharmony_ci
1008c2ecf20Sopenharmony_cistatic const struct smp_operations armada_38x_smp_ops __initconst = {
1018c2ecf20Sopenharmony_ci	.smp_boot_secondary	= mvebu_cortex_a9_boot_secondary,
1028c2ecf20Sopenharmony_ci	.smp_secondary_init     = armada_38x_secondary_init,
1038c2ecf20Sopenharmony_ci#ifdef CONFIG_HOTPLUG_CPU
1048c2ecf20Sopenharmony_ci	.cpu_die		= armada_38x_cpu_die,
1058c2ecf20Sopenharmony_ci	.cpu_kill               = armada_38x_cpu_kill,
1068c2ecf20Sopenharmony_ci#endif
1078c2ecf20Sopenharmony_ci};
1088c2ecf20Sopenharmony_ci
1098c2ecf20Sopenharmony_ciCPU_METHOD_OF_DECLARE(mvebu_armada_375_smp, "marvell,armada-375-smp",
1108c2ecf20Sopenharmony_ci		      &mvebu_cortex_a9_smp_ops);
1118c2ecf20Sopenharmony_ciCPU_METHOD_OF_DECLARE(mvebu_armada_380_smp, "marvell,armada-380-smp",
1128c2ecf20Sopenharmony_ci		      &armada_38x_smp_ops);
1138c2ecf20Sopenharmony_ciCPU_METHOD_OF_DECLARE(mvebu_armada_390_smp, "marvell,armada-390-smp",
1148c2ecf20Sopenharmony_ci		      &armada_38x_smp_ops);
115