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