xref: /linux/arch/arm/mach-zynq/platsmp.c (revision 8dd06ef34b6e2f41b29fbf5fc1663780f2524285)
19c92ab61SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
2aa7eb2bbSMichal Simek /*
3aa7eb2bbSMichal Simek  * This file contains Xilinx specific SMP code, used to start up
4aa7eb2bbSMichal Simek  * the second processor.
5aa7eb2bbSMichal Simek  *
6aa7eb2bbSMichal Simek  * Copyright (C) 2011-2013 Xilinx
7aa7eb2bbSMichal Simek  *
8aa7eb2bbSMichal Simek  * based on linux/arch/arm/mach-realview/platsmp.c
9aa7eb2bbSMichal Simek  *
10aa7eb2bbSMichal Simek  * Copyright (C) 2002 ARM Ltd.
11aa7eb2bbSMichal Simek  */
12aa7eb2bbSMichal Simek 
13aa7eb2bbSMichal Simek #include <linux/export.h>
14aa7eb2bbSMichal Simek #include <linux/jiffies.h>
15aa7eb2bbSMichal Simek #include <linux/init.h>
16aa7eb2bbSMichal Simek #include <linux/io.h>
17aa7eb2bbSMichal Simek #include <asm/cacheflush.h>
18*6c6b3f1fSQuanyang Wang #include <asm/smp_plat.h>
19aa7eb2bbSMichal Simek #include <asm/smp_scu.h>
20aa7eb2bbSMichal Simek #include <linux/irqchip/arm-gic.h>
21aa7eb2bbSMichal Simek #include "common.h"
22aa7eb2bbSMichal Simek 
23aa7eb2bbSMichal Simek /*
24aa7eb2bbSMichal Simek  * Store number of cores in the system
25aa7eb2bbSMichal Simek  * Because of scu_get_core_count() must be in __init section and can't
268bd26e3aSPaul Gortmaker  * be called from zynq_cpun_start() because it is not in __init section.
27aa7eb2bbSMichal Simek  */
28aa7eb2bbSMichal Simek static int ncores;
29aa7eb2bbSMichal Simek 
zynq_cpun_start(u32 address,int cpu)308bd26e3aSPaul Gortmaker int zynq_cpun_start(u32 address, int cpu)
31aa7eb2bbSMichal Simek {
32aa7eb2bbSMichal Simek 	u32 trampoline_code_size = &zynq_secondary_trampoline_end -
33aa7eb2bbSMichal Simek 						&zynq_secondary_trampoline;
34*6c6b3f1fSQuanyang Wang 	u32 phy_cpuid = cpu_logical_map(cpu);
35aa7eb2bbSMichal Simek 
36aa7eb2bbSMichal Simek 	/* MS: Expectation that SLCR are directly map and accessible */
37aa7eb2bbSMichal Simek 	/* Not possible to jump to non aligned address */
38aa7eb2bbSMichal Simek 	if (!(address & 3) && (!address || (address >= trampoline_code_size))) {
39aa7eb2bbSMichal Simek 		/* Store pointer to ioremap area which points to address 0x0 */
40aa7eb2bbSMichal Simek 		static u8 __iomem *zero;
41aa7eb2bbSMichal Simek 		u32 trampoline_size = &zynq_secondary_trampoline_jump -
42aa7eb2bbSMichal Simek 						&zynq_secondary_trampoline;
43aa7eb2bbSMichal Simek 
44*6c6b3f1fSQuanyang Wang 		zynq_slcr_cpu_stop(phy_cpuid);
4588cd4e88SMichal Simek 		if (address) {
46aa7eb2bbSMichal Simek 			if (__pa(PAGE_OFFSET)) {
47aa7eb2bbSMichal Simek 				zero = ioremap(0, trampoline_code_size);
48aa7eb2bbSMichal Simek 				if (!zero) {
49aa7eb2bbSMichal Simek 					pr_warn("BOOTUP jump vectors not accessible\n");
50aa7eb2bbSMichal Simek 					return -1;
51aa7eb2bbSMichal Simek 				}
52aa7eb2bbSMichal Simek 			} else {
53aa7eb2bbSMichal Simek 				zero = (__force u8 __iomem *)PAGE_OFFSET;
54aa7eb2bbSMichal Simek 			}
55aa7eb2bbSMichal Simek 
56aa7eb2bbSMichal Simek 			/*
57aa7eb2bbSMichal Simek 			* This is elegant way how to jump to any address
58aa7eb2bbSMichal Simek 			* 0x0: Load address at 0x8 to r0
59aa7eb2bbSMichal Simek 			* 0x4: Jump by mov instruction
60aa7eb2bbSMichal Simek 			* 0x8: Jumping address
61aa7eb2bbSMichal Simek 			*/
62b7005d4eSLuis Araneda 			memcpy_toio(zero, &zynq_secondary_trampoline,
63aa7eb2bbSMichal Simek 							trampoline_size);
64aa7eb2bbSMichal Simek 			writel(address, zero + trampoline_size);
65aa7eb2bbSMichal Simek 
66aa7eb2bbSMichal Simek 			flush_cache_all();
67aa7eb2bbSMichal Simek 			outer_flush_range(0, trampoline_code_size);
68aa7eb2bbSMichal Simek 			smp_wmb();
69aa7eb2bbSMichal Simek 
70aa7eb2bbSMichal Simek 			if (__pa(PAGE_OFFSET))
71aa7eb2bbSMichal Simek 				iounmap(zero);
7288cd4e88SMichal Simek 		}
73*6c6b3f1fSQuanyang Wang 		zynq_slcr_cpu_start(phy_cpuid);
74aa7eb2bbSMichal Simek 
75aa7eb2bbSMichal Simek 		return 0;
76aa7eb2bbSMichal Simek 	}
77aa7eb2bbSMichal Simek 
78aa7eb2bbSMichal Simek 	pr_warn("Can't start CPU%d: Wrong starting address %x\n", cpu, address);
79aa7eb2bbSMichal Simek 
80aa7eb2bbSMichal Simek 	return -1;
81aa7eb2bbSMichal Simek }
82aa7eb2bbSMichal Simek EXPORT_SYMBOL(zynq_cpun_start);
83aa7eb2bbSMichal Simek 
zynq_boot_secondary(unsigned int cpu,struct task_struct * idle)8402b4e275SRussell King static int zynq_boot_secondary(unsigned int cpu, struct task_struct *idle)
85aa7eb2bbSMichal Simek {
865f595063SLuis Araneda 	return zynq_cpun_start(__pa_symbol(secondary_startup_arm), cpu);
87aa7eb2bbSMichal Simek }
88aa7eb2bbSMichal Simek 
89aa7eb2bbSMichal Simek /*
90aa7eb2bbSMichal Simek  * Initialise the CPU possible map early - this describes the CPUs
91aa7eb2bbSMichal Simek  * which may be present or become present in the system.
92aa7eb2bbSMichal Simek  */
zynq_smp_init_cpus(void)93aa7eb2bbSMichal Simek static void __init zynq_smp_init_cpus(void)
94aa7eb2bbSMichal Simek {
95aa7eb2bbSMichal Simek 	int i;
96aa7eb2bbSMichal Simek 
97aa7eb2bbSMichal Simek 	ncores = scu_get_core_count(zynq_scu_base);
98aa7eb2bbSMichal Simek 
99aa7eb2bbSMichal Simek 	for (i = 0; i < ncores && i < CONFIG_NR_CPUS; i++)
100aa7eb2bbSMichal Simek 		set_cpu_possible(i, true);
101aa7eb2bbSMichal Simek }
102aa7eb2bbSMichal Simek 
zynq_smp_prepare_cpus(unsigned int max_cpus)103aa7eb2bbSMichal Simek static void __init zynq_smp_prepare_cpus(unsigned int max_cpus)
104aa7eb2bbSMichal Simek {
105aa7eb2bbSMichal Simek 	scu_enable(zynq_scu_base);
106aa7eb2bbSMichal Simek }
107aa7eb2bbSMichal Simek 
108ae88b85eSSoren Brinkmann /**
109ae88b85eSSoren Brinkmann  * zynq_secondary_init - Initialize secondary CPU cores
110ae88b85eSSoren Brinkmann  * @cpu:	CPU that is initialized
111ae88b85eSSoren Brinkmann  *
112ae88b85eSSoren Brinkmann  * This function is in the hotplug path. Don't move it into the
113ae88b85eSSoren Brinkmann  * init section!!
114ae88b85eSSoren Brinkmann  */
zynq_secondary_init(unsigned int cpu)115ae88b85eSSoren Brinkmann static void zynq_secondary_init(unsigned int cpu)
116ae88b85eSSoren Brinkmann {
117ae88b85eSSoren Brinkmann 	zynq_core_pm_init();
118ae88b85eSSoren Brinkmann }
119ae88b85eSSoren Brinkmann 
120f1fd2fa6SMichal Simek #ifdef CONFIG_HOTPLUG_CPU
zynq_cpu_kill(unsigned cpu)121f1fd2fa6SMichal Simek static int zynq_cpu_kill(unsigned cpu)
122f1fd2fa6SMichal Simek {
12350c7960aSSoren Brinkmann 	unsigned long timeout = jiffies + msecs_to_jiffies(50);
12450c7960aSSoren Brinkmann 
12550c7960aSSoren Brinkmann 	while (zynq_slcr_cpu_state_read(cpu))
12650c7960aSSoren Brinkmann 		if (time_after(jiffies, timeout))
12750c7960aSSoren Brinkmann 			return 0;
12850c7960aSSoren Brinkmann 
129f1fd2fa6SMichal Simek 	zynq_slcr_cpu_stop(cpu);
130f1fd2fa6SMichal Simek 	return 1;
131f1fd2fa6SMichal Simek }
132caf86a73SSoren Brinkmann 
133ed62e330SSoren Brinkmann /**
134ed62e330SSoren Brinkmann  * zynq_cpu_die - Let a CPU core die
135ed62e330SSoren Brinkmann  * @cpu:	Dying CPU
136caf86a73SSoren Brinkmann  *
137ed62e330SSoren Brinkmann  * Platform-specific code to shutdown a CPU.
138ed62e330SSoren Brinkmann  * Called with IRQs disabled on the dying CPU.
139caf86a73SSoren Brinkmann  */
zynq_cpu_die(unsigned int cpu)140ed62e330SSoren Brinkmann static void zynq_cpu_die(unsigned int cpu)
141caf86a73SSoren Brinkmann {
142caf86a73SSoren Brinkmann 	zynq_slcr_cpu_state_write(cpu, true);
143caf86a73SSoren Brinkmann 
144caf86a73SSoren Brinkmann 	/*
145caf86a73SSoren Brinkmann 	 * there is no power-control hardware on this platform, so all
146caf86a73SSoren Brinkmann 	 * we can do is put the core into WFI; this is safe as the calling
147caf86a73SSoren Brinkmann 	 * code will have already disabled interrupts
148caf86a73SSoren Brinkmann 	 */
149caf86a73SSoren Brinkmann 	for (;;)
150caf86a73SSoren Brinkmann 		cpu_do_idle();
151caf86a73SSoren Brinkmann }
152f1fd2fa6SMichal Simek #endif
153f1fd2fa6SMichal Simek 
15475305275SMasahiro Yamada const struct smp_operations zynq_smp_ops __initconst = {
155aa7eb2bbSMichal Simek 	.smp_init_cpus		= zynq_smp_init_cpus,
156aa7eb2bbSMichal Simek 	.smp_prepare_cpus	= zynq_smp_prepare_cpus,
157aa7eb2bbSMichal Simek 	.smp_boot_secondary	= zynq_boot_secondary,
158ae88b85eSSoren Brinkmann 	.smp_secondary_init	= zynq_secondary_init,
159c7c28b0fSMichal Simek #ifdef CONFIG_HOTPLUG_CPU
160ed62e330SSoren Brinkmann 	.cpu_die		= zynq_cpu_die,
161f1fd2fa6SMichal Simek 	.cpu_kill		= zynq_cpu_kill,
162c7c28b0fSMichal Simek #endif
163aa7eb2bbSMichal Simek };
164