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