xref: /OK3568_Linux_fs/kernel/arch/arm/mach-mvebu/platsmp.c (revision 4882a59341e53eb6f0b4789bf948001014eff981)
1*4882a593Smuzhiyun /*
2*4882a593Smuzhiyun  * Symmetric Multi Processing (SMP) support for Armada XP
3*4882a593Smuzhiyun  *
4*4882a593Smuzhiyun  * Copyright (C) 2012 Marvell
5*4882a593Smuzhiyun  *
6*4882a593Smuzhiyun  * Lior Amsalem <alior@marvell.com>
7*4882a593Smuzhiyun  * Yehuda Yitschak <yehuday@marvell.com>
8*4882a593Smuzhiyun  * Gregory CLEMENT <gregory.clement@free-electrons.com>
9*4882a593Smuzhiyun  * Thomas Petazzoni <thomas.petazzoni@free-electrons.com>
10*4882a593Smuzhiyun  *
11*4882a593Smuzhiyun  * This file is licensed under the terms of the GNU General Public
12*4882a593Smuzhiyun  * License version 2.  This program is licensed "as is" without any
13*4882a593Smuzhiyun  * warranty of any kind, whether express or implied.
14*4882a593Smuzhiyun  *
15*4882a593Smuzhiyun  * The Armada XP SoC has 4 ARMv7 PJ4B CPUs running in full HW coherency
16*4882a593Smuzhiyun  * This file implements the routines for preparing the SMP infrastructure
17*4882a593Smuzhiyun  * and waking up the secondary CPUs
18*4882a593Smuzhiyun  */
19*4882a593Smuzhiyun 
20*4882a593Smuzhiyun #include <linux/init.h>
21*4882a593Smuzhiyun #include <linux/smp.h>
22*4882a593Smuzhiyun #include <linux/clk.h>
23*4882a593Smuzhiyun #include <linux/of.h>
24*4882a593Smuzhiyun #include <linux/of_address.h>
25*4882a593Smuzhiyun #include <linux/mbus.h>
26*4882a593Smuzhiyun #include <asm/cacheflush.h>
27*4882a593Smuzhiyun #include <asm/smp_plat.h>
28*4882a593Smuzhiyun #include "common.h"
29*4882a593Smuzhiyun #include "armada-370-xp.h"
30*4882a593Smuzhiyun #include "pmsu.h"
31*4882a593Smuzhiyun #include "coherency.h"
32*4882a593Smuzhiyun 
33*4882a593Smuzhiyun #define ARMADA_XP_MAX_CPUS 4
34*4882a593Smuzhiyun 
35*4882a593Smuzhiyun #define AXP_BOOTROM_BASE 0xfff00000
36*4882a593Smuzhiyun #define AXP_BOOTROM_SIZE 0x100000
37*4882a593Smuzhiyun 
38*4882a593Smuzhiyun static struct clk *boot_cpu_clk;
39*4882a593Smuzhiyun 
get_cpu_clk(int cpu)40*4882a593Smuzhiyun static struct clk *get_cpu_clk(int cpu)
41*4882a593Smuzhiyun {
42*4882a593Smuzhiyun 	struct clk *cpu_clk;
43*4882a593Smuzhiyun 	struct device_node *np = of_get_cpu_node(cpu, NULL);
44*4882a593Smuzhiyun 
45*4882a593Smuzhiyun 	if (WARN(!np, "missing cpu node\n"))
46*4882a593Smuzhiyun 		return NULL;
47*4882a593Smuzhiyun 	cpu_clk = of_clk_get(np, 0);
48*4882a593Smuzhiyun 	if (WARN_ON(IS_ERR(cpu_clk)))
49*4882a593Smuzhiyun 		return NULL;
50*4882a593Smuzhiyun 	return cpu_clk;
51*4882a593Smuzhiyun }
52*4882a593Smuzhiyun 
armada_xp_boot_secondary(unsigned int cpu,struct task_struct * idle)53*4882a593Smuzhiyun static int armada_xp_boot_secondary(unsigned int cpu, struct task_struct *idle)
54*4882a593Smuzhiyun {
55*4882a593Smuzhiyun 	int ret, hw_cpu;
56*4882a593Smuzhiyun 
57*4882a593Smuzhiyun 	pr_info("Booting CPU %d\n", cpu);
58*4882a593Smuzhiyun 
59*4882a593Smuzhiyun 	hw_cpu = cpu_logical_map(cpu);
60*4882a593Smuzhiyun 	mvebu_pmsu_set_cpu_boot_addr(hw_cpu, armada_xp_secondary_startup);
61*4882a593Smuzhiyun 
62*4882a593Smuzhiyun 	/*
63*4882a593Smuzhiyun 	 * This is needed to wake up CPUs in the offline state after
64*4882a593Smuzhiyun 	 * using CPU hotplug.
65*4882a593Smuzhiyun 	 */
66*4882a593Smuzhiyun 	arch_send_wakeup_ipi_mask(cpumask_of(cpu));
67*4882a593Smuzhiyun 
68*4882a593Smuzhiyun 	/*
69*4882a593Smuzhiyun 	 * This is needed to take secondary CPUs out of reset on the
70*4882a593Smuzhiyun 	 * initial boot.
71*4882a593Smuzhiyun 	 */
72*4882a593Smuzhiyun 	ret = mvebu_cpu_reset_deassert(hw_cpu);
73*4882a593Smuzhiyun 	if (ret) {
74*4882a593Smuzhiyun 		pr_warn("unable to boot CPU: %d\n", ret);
75*4882a593Smuzhiyun 		return ret;
76*4882a593Smuzhiyun 	}
77*4882a593Smuzhiyun 
78*4882a593Smuzhiyun 	return 0;
79*4882a593Smuzhiyun }
80*4882a593Smuzhiyun 
81*4882a593Smuzhiyun /*
82*4882a593Smuzhiyun  * When a CPU is brought back online, either through CPU hotplug, or
83*4882a593Smuzhiyun  * because of the boot of a kexec'ed kernel, the PMSU configuration
84*4882a593Smuzhiyun  * for this CPU might be in the deep idle state, preventing this CPU
85*4882a593Smuzhiyun  * from receiving interrupts. Here, we therefore take out the current
86*4882a593Smuzhiyun  * CPU from this state, which was entered by armada_xp_cpu_die()
87*4882a593Smuzhiyun  * below.
88*4882a593Smuzhiyun  */
armada_xp_secondary_init(unsigned int cpu)89*4882a593Smuzhiyun static void armada_xp_secondary_init(unsigned int cpu)
90*4882a593Smuzhiyun {
91*4882a593Smuzhiyun 	mvebu_v7_pmsu_idle_exit();
92*4882a593Smuzhiyun }
93*4882a593Smuzhiyun 
armada_xp_smp_init_cpus(void)94*4882a593Smuzhiyun static void __init armada_xp_smp_init_cpus(void)
95*4882a593Smuzhiyun {
96*4882a593Smuzhiyun 	unsigned int ncores = num_possible_cpus();
97*4882a593Smuzhiyun 
98*4882a593Smuzhiyun 	if (ncores == 0 || ncores > ARMADA_XP_MAX_CPUS)
99*4882a593Smuzhiyun 		panic("Invalid number of CPUs in DT\n");
100*4882a593Smuzhiyun }
101*4882a593Smuzhiyun 
armada_xp_sync_secondary_clk(unsigned int cpu)102*4882a593Smuzhiyun static int armada_xp_sync_secondary_clk(unsigned int cpu)
103*4882a593Smuzhiyun {
104*4882a593Smuzhiyun 	struct clk *cpu_clk = get_cpu_clk(cpu);
105*4882a593Smuzhiyun 
106*4882a593Smuzhiyun 	if (!cpu_clk || !boot_cpu_clk)
107*4882a593Smuzhiyun 		return 0;
108*4882a593Smuzhiyun 
109*4882a593Smuzhiyun 	clk_prepare_enable(cpu_clk);
110*4882a593Smuzhiyun 	clk_set_rate(cpu_clk, clk_get_rate(boot_cpu_clk));
111*4882a593Smuzhiyun 
112*4882a593Smuzhiyun 	return 0;
113*4882a593Smuzhiyun }
114*4882a593Smuzhiyun 
armada_xp_smp_prepare_cpus(unsigned int max_cpus)115*4882a593Smuzhiyun static void __init armada_xp_smp_prepare_cpus(unsigned int max_cpus)
116*4882a593Smuzhiyun {
117*4882a593Smuzhiyun 	struct device_node *node;
118*4882a593Smuzhiyun 	struct resource res;
119*4882a593Smuzhiyun 	int err;
120*4882a593Smuzhiyun 
121*4882a593Smuzhiyun 	flush_cache_all();
122*4882a593Smuzhiyun 	set_cpu_coherent();
123*4882a593Smuzhiyun 
124*4882a593Smuzhiyun 	boot_cpu_clk = get_cpu_clk(smp_processor_id());
125*4882a593Smuzhiyun 	if (boot_cpu_clk) {
126*4882a593Smuzhiyun 		clk_prepare_enable(boot_cpu_clk);
127*4882a593Smuzhiyun 		cpuhp_setup_state_nocalls(CPUHP_AP_ARM_MVEBU_SYNC_CLOCKS,
128*4882a593Smuzhiyun 					  "arm/mvebu/sync_clocks:online",
129*4882a593Smuzhiyun 					  armada_xp_sync_secondary_clk, NULL);
130*4882a593Smuzhiyun 	}
131*4882a593Smuzhiyun 
132*4882a593Smuzhiyun 	/*
133*4882a593Smuzhiyun 	 * In order to boot the secondary CPUs we need to ensure
134*4882a593Smuzhiyun 	 * the bootROM is mapped at the correct address.
135*4882a593Smuzhiyun 	 */
136*4882a593Smuzhiyun 	node = of_find_compatible_node(NULL, NULL, "marvell,bootrom");
137*4882a593Smuzhiyun 	if (!node)
138*4882a593Smuzhiyun 		panic("Cannot find 'marvell,bootrom' compatible node");
139*4882a593Smuzhiyun 
140*4882a593Smuzhiyun 	err = of_address_to_resource(node, 0, &res);
141*4882a593Smuzhiyun 	of_node_put(node);
142*4882a593Smuzhiyun 	if (err < 0)
143*4882a593Smuzhiyun 		panic("Cannot get 'bootrom' node address");
144*4882a593Smuzhiyun 
145*4882a593Smuzhiyun 	if (res.start != AXP_BOOTROM_BASE ||
146*4882a593Smuzhiyun 	    resource_size(&res) != AXP_BOOTROM_SIZE)
147*4882a593Smuzhiyun 		panic("The address for the BootROM is incorrect");
148*4882a593Smuzhiyun }
149*4882a593Smuzhiyun 
150*4882a593Smuzhiyun #ifdef CONFIG_HOTPLUG_CPU
armada_xp_cpu_die(unsigned int cpu)151*4882a593Smuzhiyun static void armada_xp_cpu_die(unsigned int cpu)
152*4882a593Smuzhiyun {
153*4882a593Smuzhiyun 	/*
154*4882a593Smuzhiyun 	 * CPU hotplug is implemented by putting offline CPUs into the
155*4882a593Smuzhiyun 	 * deep idle sleep state.
156*4882a593Smuzhiyun 	 */
157*4882a593Smuzhiyun 	armada_370_xp_pmsu_idle_enter(true);
158*4882a593Smuzhiyun }
159*4882a593Smuzhiyun 
160*4882a593Smuzhiyun /*
161*4882a593Smuzhiyun  * We need a dummy function, so that platform_can_cpu_hotplug() knows
162*4882a593Smuzhiyun  * we support CPU hotplug. However, the function does not need to do
163*4882a593Smuzhiyun  * anything, because CPUs going offline can enter the deep idle state
164*4882a593Smuzhiyun  * by themselves, without any help from a still alive CPU.
165*4882a593Smuzhiyun  */
armada_xp_cpu_kill(unsigned int cpu)166*4882a593Smuzhiyun static int armada_xp_cpu_kill(unsigned int cpu)
167*4882a593Smuzhiyun {
168*4882a593Smuzhiyun 	return 1;
169*4882a593Smuzhiyun }
170*4882a593Smuzhiyun #endif
171*4882a593Smuzhiyun 
172*4882a593Smuzhiyun const struct smp_operations armada_xp_smp_ops __initconst = {
173*4882a593Smuzhiyun 	.smp_init_cpus		= armada_xp_smp_init_cpus,
174*4882a593Smuzhiyun 	.smp_prepare_cpus	= armada_xp_smp_prepare_cpus,
175*4882a593Smuzhiyun 	.smp_boot_secondary	= armada_xp_boot_secondary,
176*4882a593Smuzhiyun 	.smp_secondary_init     = armada_xp_secondary_init,
177*4882a593Smuzhiyun #ifdef CONFIG_HOTPLUG_CPU
178*4882a593Smuzhiyun 	.cpu_die		= armada_xp_cpu_die,
179*4882a593Smuzhiyun 	.cpu_kill               = armada_xp_cpu_kill,
180*4882a593Smuzhiyun #endif
181*4882a593Smuzhiyun };
182*4882a593Smuzhiyun 
183*4882a593Smuzhiyun CPU_METHOD_OF_DECLARE(armada_xp_smp, "marvell,armada-xp-smp",
184*4882a593Smuzhiyun 		      &armada_xp_smp_ops);
185*4882a593Smuzhiyun 
186*4882a593Smuzhiyun #define MV98DX3236_CPU_RESUME_CTRL_REG 0x08
187*4882a593Smuzhiyun #define MV98DX3236_CPU_RESUME_ADDR_REG 0x04
188*4882a593Smuzhiyun 
189*4882a593Smuzhiyun static const struct of_device_id of_mv98dx3236_resume_table[] = {
190*4882a593Smuzhiyun 	{
191*4882a593Smuzhiyun 		.compatible = "marvell,98dx3336-resume-ctrl",
192*4882a593Smuzhiyun 	},
193*4882a593Smuzhiyun 	{ /* end of list */ },
194*4882a593Smuzhiyun };
195*4882a593Smuzhiyun 
mv98dx3236_resume_set_cpu_boot_addr(int hw_cpu,void * boot_addr)196*4882a593Smuzhiyun static int mv98dx3236_resume_set_cpu_boot_addr(int hw_cpu, void *boot_addr)
197*4882a593Smuzhiyun {
198*4882a593Smuzhiyun 	struct device_node *np;
199*4882a593Smuzhiyun 	void __iomem *base;
200*4882a593Smuzhiyun 	WARN_ON(hw_cpu != 1);
201*4882a593Smuzhiyun 
202*4882a593Smuzhiyun 	np = of_find_matching_node(NULL, of_mv98dx3236_resume_table);
203*4882a593Smuzhiyun 	if (!np)
204*4882a593Smuzhiyun 		return -ENODEV;
205*4882a593Smuzhiyun 
206*4882a593Smuzhiyun 	base = of_io_request_and_map(np, 0, of_node_full_name(np));
207*4882a593Smuzhiyun 	of_node_put(np);
208*4882a593Smuzhiyun 	if (IS_ERR(base))
209*4882a593Smuzhiyun 		return PTR_ERR(base);
210*4882a593Smuzhiyun 
211*4882a593Smuzhiyun 	writel(0, base + MV98DX3236_CPU_RESUME_CTRL_REG);
212*4882a593Smuzhiyun 	writel(__pa_symbol(boot_addr), base + MV98DX3236_CPU_RESUME_ADDR_REG);
213*4882a593Smuzhiyun 
214*4882a593Smuzhiyun 	iounmap(base);
215*4882a593Smuzhiyun 
216*4882a593Smuzhiyun 	return 0;
217*4882a593Smuzhiyun }
218*4882a593Smuzhiyun 
mv98dx3236_boot_secondary(unsigned int cpu,struct task_struct * idle)219*4882a593Smuzhiyun static int mv98dx3236_boot_secondary(unsigned int cpu, struct task_struct *idle)
220*4882a593Smuzhiyun {
221*4882a593Smuzhiyun 	int ret, hw_cpu;
222*4882a593Smuzhiyun 
223*4882a593Smuzhiyun 	hw_cpu = cpu_logical_map(cpu);
224*4882a593Smuzhiyun 	mv98dx3236_resume_set_cpu_boot_addr(hw_cpu,
225*4882a593Smuzhiyun 					    armada_xp_secondary_startup);
226*4882a593Smuzhiyun 
227*4882a593Smuzhiyun 	/*
228*4882a593Smuzhiyun 	 * This is needed to wake up CPUs in the offline state after
229*4882a593Smuzhiyun 	 * using CPU hotplug.
230*4882a593Smuzhiyun 	 */
231*4882a593Smuzhiyun 	arch_send_wakeup_ipi_mask(cpumask_of(cpu));
232*4882a593Smuzhiyun 
233*4882a593Smuzhiyun 	/*
234*4882a593Smuzhiyun 	 * This is needed to take secondary CPUs out of reset on the
235*4882a593Smuzhiyun 	 * initial boot.
236*4882a593Smuzhiyun 	 */
237*4882a593Smuzhiyun 	ret = mvebu_cpu_reset_deassert(hw_cpu);
238*4882a593Smuzhiyun 	if (ret) {
239*4882a593Smuzhiyun 		pr_warn("unable to boot CPU: %d\n", ret);
240*4882a593Smuzhiyun 		return ret;
241*4882a593Smuzhiyun 	}
242*4882a593Smuzhiyun 
243*4882a593Smuzhiyun 	return 0;
244*4882a593Smuzhiyun }
245*4882a593Smuzhiyun 
246*4882a593Smuzhiyun static const struct smp_operations mv98dx3236_smp_ops __initconst = {
247*4882a593Smuzhiyun 	.smp_init_cpus		= armada_xp_smp_init_cpus,
248*4882a593Smuzhiyun 	.smp_prepare_cpus	= armada_xp_smp_prepare_cpus,
249*4882a593Smuzhiyun 	.smp_boot_secondary	= mv98dx3236_boot_secondary,
250*4882a593Smuzhiyun 	.smp_secondary_init     = armada_xp_secondary_init,
251*4882a593Smuzhiyun #ifdef CONFIG_HOTPLUG_CPU
252*4882a593Smuzhiyun 	.cpu_die		= armada_xp_cpu_die,
253*4882a593Smuzhiyun 	.cpu_kill               = armada_xp_cpu_kill,
254*4882a593Smuzhiyun #endif
255*4882a593Smuzhiyun };
256*4882a593Smuzhiyun 
257*4882a593Smuzhiyun CPU_METHOD_OF_DECLARE(mv98dx3236_smp, "marvell,98dx3236-smp",
258*4882a593Smuzhiyun 		      &mv98dx3236_smp_ops);
259