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