1*0fdebc5eSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only 21ee89e22SGregory CLEMENT /* 31ee89e22SGregory CLEMENT * Symmetric Multi Processing (SMP) support for Marvell EBU Cortex-A9 41ee89e22SGregory CLEMENT * based SOCs (Armada 375/38x). 51ee89e22SGregory CLEMENT * 61ee89e22SGregory CLEMENT * Copyright (C) 2014 Marvell 71ee89e22SGregory CLEMENT * 81ee89e22SGregory CLEMENT * Gregory CLEMENT <gregory.clement@free-electrons.com> 91ee89e22SGregory CLEMENT * Thomas Petazzoni <thomas.petazzoni@free-electrons.com> 101ee89e22SGregory CLEMENT */ 111ee89e22SGregory CLEMENT 121ee89e22SGregory CLEMENT #include <linux/init.h> 131ee89e22SGregory CLEMENT #include <linux/io.h> 141ee89e22SGregory CLEMENT #include <linux/of.h> 151ee89e22SGregory CLEMENT #include <linux/smp.h> 1687384cc0SGregory CLEMENT #include <linux/mbus.h> 171ee89e22SGregory CLEMENT #include <asm/smp_scu.h> 181ee89e22SGregory CLEMENT #include <asm/smp_plat.h> 191ee89e22SGregory CLEMENT #include "common.h" 201ee89e22SGregory CLEMENT #include "pmsu.h" 211ee89e22SGregory CLEMENT 221ee89e22SGregory CLEMENT extern void mvebu_cortex_a9_secondary_startup(void); 231ee89e22SGregory CLEMENT 24ff6138adSPaul Gortmaker static int mvebu_cortex_a9_boot_secondary(unsigned int cpu, 251ee89e22SGregory CLEMENT struct task_struct *idle) 261ee89e22SGregory CLEMENT { 271ee89e22SGregory CLEMENT int ret, hw_cpu; 281ee89e22SGregory CLEMENT 291ee89e22SGregory CLEMENT pr_info("Booting CPU %d\n", cpu); 301ee89e22SGregory CLEMENT 311ee89e22SGregory CLEMENT /* 321ee89e22SGregory CLEMENT * Write the address of secondary startup into the system-wide 331ee89e22SGregory CLEMENT * flags register. The boot monitor waits until it receives a 341ee89e22SGregory CLEMENT * soft interrupt, and then the secondary CPU branches to this 351ee89e22SGregory CLEMENT * address. 361ee89e22SGregory CLEMENT */ 371ee89e22SGregory CLEMENT hw_cpu = cpu_logical_map(cpu); 38305969fbSGregory CLEMENT if (of_machine_is_compatible("marvell,armada375")) 391ee89e22SGregory CLEMENT mvebu_system_controller_set_cpu_boot_addr(mvebu_cortex_a9_secondary_startup); 40305969fbSGregory CLEMENT else 41305969fbSGregory CLEMENT mvebu_pmsu_set_cpu_boot_addr(hw_cpu, mvebu_cortex_a9_secondary_startup); 421ee89e22SGregory CLEMENT smp_wmb(); 43626d6864SGregory CLEMENT 44626d6864SGregory CLEMENT /* 45626d6864SGregory CLEMENT * Doing this before deasserting the CPUs is needed to wake up CPUs 46626d6864SGregory CLEMENT * in the offline state after using CPU hotplug. 47626d6864SGregory CLEMENT */ 48626d6864SGregory CLEMENT arch_send_wakeup_ipi_mask(cpumask_of(cpu)); 49626d6864SGregory CLEMENT 501ee89e22SGregory CLEMENT ret = mvebu_cpu_reset_deassert(hw_cpu); 511ee89e22SGregory CLEMENT if (ret) { 521ee89e22SGregory CLEMENT pr_err("Could not start the secondary CPU: %d\n", ret); 531ee89e22SGregory CLEMENT return ret; 541ee89e22SGregory CLEMENT } 551ee89e22SGregory CLEMENT 561ee89e22SGregory CLEMENT return 0; 571ee89e22SGregory CLEMENT } 58626d6864SGregory CLEMENT /* 59626d6864SGregory CLEMENT * When a CPU is brought back online, either through CPU hotplug, or 60626d6864SGregory CLEMENT * because of the boot of a kexec'ed kernel, the PMSU configuration 61626d6864SGregory CLEMENT * for this CPU might be in the deep idle state, preventing this CPU 62626d6864SGregory CLEMENT * from receiving interrupts. Here, we therefore take out the current 63626d6864SGregory CLEMENT * CPU from this state, which was entered by armada_38x_cpu_die() 64626d6864SGregory CLEMENT * below. 65626d6864SGregory CLEMENT */ 66626d6864SGregory CLEMENT static void armada_38x_secondary_init(unsigned int cpu) 67626d6864SGregory CLEMENT { 68626d6864SGregory CLEMENT mvebu_v7_pmsu_idle_exit(); 69626d6864SGregory CLEMENT } 70626d6864SGregory CLEMENT 71626d6864SGregory CLEMENT #ifdef CONFIG_HOTPLUG_CPU 72626d6864SGregory CLEMENT static void armada_38x_cpu_die(unsigned int cpu) 73626d6864SGregory CLEMENT { 74626d6864SGregory CLEMENT /* 75626d6864SGregory CLEMENT * CPU hotplug is implemented by putting offline CPUs into the 76626d6864SGregory CLEMENT * deep idle sleep state. 77626d6864SGregory CLEMENT */ 78626d6864SGregory CLEMENT armada_38x_do_cpu_suspend(true); 79626d6864SGregory CLEMENT } 80626d6864SGregory CLEMENT 81626d6864SGregory CLEMENT /* 82626d6864SGregory CLEMENT * We need a dummy function, so that platform_can_cpu_hotplug() knows 83626d6864SGregory CLEMENT * we support CPU hotplug. However, the function does not need to do 84626d6864SGregory CLEMENT * anything, because CPUs going offline can enter the deep idle state 85626d6864SGregory CLEMENT * by themselves, without any help from a still alive CPU. 86626d6864SGregory CLEMENT */ 87626d6864SGregory CLEMENT static int armada_38x_cpu_kill(unsigned int cpu) 88626d6864SGregory CLEMENT { 89626d6864SGregory CLEMENT return 1; 90626d6864SGregory CLEMENT } 91626d6864SGregory CLEMENT #endif 921ee89e22SGregory CLEMENT 9375305275SMasahiro Yamada static const struct smp_operations mvebu_cortex_a9_smp_ops __initconst = { 941ee89e22SGregory CLEMENT .smp_boot_secondary = mvebu_cortex_a9_boot_secondary, 951ee89e22SGregory CLEMENT }; 961ee89e22SGregory CLEMENT 9775305275SMasahiro Yamada static const struct smp_operations armada_38x_smp_ops __initconst = { 98626d6864SGregory CLEMENT .smp_boot_secondary = mvebu_cortex_a9_boot_secondary, 99626d6864SGregory CLEMENT .smp_secondary_init = armada_38x_secondary_init, 100626d6864SGregory CLEMENT #ifdef CONFIG_HOTPLUG_CPU 101626d6864SGregory CLEMENT .cpu_die = armada_38x_cpu_die, 102626d6864SGregory CLEMENT .cpu_kill = armada_38x_cpu_kill, 103626d6864SGregory CLEMENT #endif 104626d6864SGregory CLEMENT }; 105626d6864SGregory CLEMENT 1061ee89e22SGregory CLEMENT CPU_METHOD_OF_DECLARE(mvebu_armada_375_smp, "marvell,armada-375-smp", 1071ee89e22SGregory CLEMENT &mvebu_cortex_a9_smp_ops); 1081ee89e22SGregory CLEMENT CPU_METHOD_OF_DECLARE(mvebu_armada_380_smp, "marvell,armada-380-smp", 109626d6864SGregory CLEMENT &armada_38x_smp_ops); 110242ede0bSThomas Petazzoni CPU_METHOD_OF_DECLARE(mvebu_armada_390_smp, "marvell,armada-390-smp", 111242ede0bSThomas Petazzoni &armada_38x_smp_ops); 112