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