1 // SPDX-License-Identifier: GPL-2.0-only 2 /* 3 * Symmetric Multi Processing (SMP) support for Marvell EBU Cortex-A9 4 * based SOCs (Armada 375/38x). 5 * 6 * Copyright (C) 2014 Marvell 7 * 8 * Gregory CLEMENT <gregory.clement@free-electrons.com> 9 * Thomas Petazzoni <thomas.petazzoni@free-electrons.com> 10 */ 11 12 #include <linux/init.h> 13 #include <linux/io.h> 14 #include <linux/of.h> 15 #include <linux/smp.h> 16 #include <linux/mbus.h> 17 #include <asm/smp_scu.h> 18 #include <asm/smp_plat.h> 19 #include "common.h" 20 #include "pmsu.h" 21 22 extern void mvebu_cortex_a9_secondary_startup(void); 23 24 static int mvebu_cortex_a9_boot_secondary(unsigned int cpu, 25 struct task_struct *idle) 26 { 27 int ret, hw_cpu; 28 29 pr_info("Booting CPU %d\n", cpu); 30 31 /* 32 * Write the address of secondary startup into the system-wide 33 * flags register. The boot monitor waits until it receives a 34 * soft interrupt, and then the secondary CPU branches to this 35 * address. 36 */ 37 hw_cpu = cpu_logical_map(cpu); 38 if (of_machine_is_compatible("marvell,armada375")) 39 mvebu_system_controller_set_cpu_boot_addr(mvebu_cortex_a9_secondary_startup); 40 else 41 mvebu_pmsu_set_cpu_boot_addr(hw_cpu, mvebu_cortex_a9_secondary_startup); 42 smp_wmb(); 43 44 /* 45 * Doing this before deasserting the CPUs is needed to wake up CPUs 46 * in the offline state after using CPU hotplug. 47 */ 48 arch_send_wakeup_ipi_mask(cpumask_of(cpu)); 49 50 ret = mvebu_cpu_reset_deassert(hw_cpu); 51 if (ret) { 52 pr_err("Could not start the secondary CPU: %d\n", ret); 53 return ret; 54 } 55 56 return 0; 57 } 58 /* 59 * When a CPU is brought back online, either through CPU hotplug, or 60 * because of the boot of a kexec'ed kernel, the PMSU configuration 61 * for this CPU might be in the deep idle state, preventing this CPU 62 * from receiving interrupts. Here, we therefore take out the current 63 * CPU from this state, which was entered by armada_38x_cpu_die() 64 * below. 65 */ 66 static void armada_38x_secondary_init(unsigned int cpu) 67 { 68 mvebu_v7_pmsu_idle_exit(); 69 } 70 71 #ifdef CONFIG_HOTPLUG_CPU 72 static void armada_38x_cpu_die(unsigned int cpu) 73 { 74 /* 75 * CPU hotplug is implemented by putting offline CPUs into the 76 * deep idle sleep state. 77 */ 78 armada_38x_do_cpu_suspend(true); 79 } 80 81 /* 82 * We need a dummy function, so that platform_can_cpu_hotplug() knows 83 * we support CPU hotplug. However, the function does not need to do 84 * anything, because CPUs going offline can enter the deep idle state 85 * by themselves, without any help from a still alive CPU. 86 */ 87 static int armada_38x_cpu_kill(unsigned int cpu) 88 { 89 return 1; 90 } 91 #endif 92 93 static const struct smp_operations mvebu_cortex_a9_smp_ops __initconst = { 94 .smp_boot_secondary = mvebu_cortex_a9_boot_secondary, 95 }; 96 97 static const struct smp_operations armada_38x_smp_ops __initconst = { 98 .smp_boot_secondary = mvebu_cortex_a9_boot_secondary, 99 .smp_secondary_init = armada_38x_secondary_init, 100 #ifdef CONFIG_HOTPLUG_CPU 101 .cpu_die = armada_38x_cpu_die, 102 .cpu_kill = armada_38x_cpu_kill, 103 #endif 104 }; 105 106 CPU_METHOD_OF_DECLARE(mvebu_armada_375_smp, "marvell,armada-375-smp", 107 &mvebu_cortex_a9_smp_ops); 108 CPU_METHOD_OF_DECLARE(mvebu_armada_380_smp, "marvell,armada-380-smp", 109 &armada_38x_smp_ops); 110 CPU_METHOD_OF_DECLARE(mvebu_armada_390_smp, "marvell,armada-390-smp", 111 &armada_38x_smp_ops); 112