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