Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
torvalds
GitHub Repository: torvalds/linux
Path: blob/master/arch/arm/mach-meson/platsmp.c
26295 views
1
// SPDX-License-Identifier: GPL-2.0-or-later
2
/*
3
* Copyright (C) 2015 Carlo Caione <[email protected]>
4
* Copyright (C) 2017 Martin Blumenstingl <[email protected]>
5
*/
6
7
#include <linux/delay.h>
8
#include <linux/init.h>
9
#include <linux/io.h>
10
#include <linux/of.h>
11
#include <linux/of_address.h>
12
#include <linux/regmap.h>
13
#include <linux/reset.h>
14
#include <linux/smp.h>
15
#include <linux/mfd/syscon.h>
16
17
#include <asm/cacheflush.h>
18
#include <asm/cp15.h>
19
#include <asm/smp_scu.h>
20
#include <asm/smp_plat.h>
21
22
#define MESON_SMP_SRAM_CPU_CTRL_REG (0x00)
23
#define MESON_SMP_SRAM_CPU_CTRL_ADDR_REG(c) (0x04 + ((c - 1) << 2))
24
25
#define MESON_CPU_AO_RTI_PWR_A9_CNTL0 (0x00)
26
#define MESON_CPU_AO_RTI_PWR_A9_CNTL1 (0x04)
27
#define MESON_CPU_AO_RTI_PWR_A9_MEM_PD0 (0x14)
28
29
#define MESON_CPU_PWR_A9_CNTL0_M(c) (0x03 << ((c * 2) + 16))
30
#define MESON_CPU_PWR_A9_CNTL1_M(c) (0x03 << ((c + 1) << 1))
31
#define MESON_CPU_PWR_A9_MEM_PD0_M(c) (0x0f << (32 - (c * 4)))
32
#define MESON_CPU_PWR_A9_CNTL1_ST(c) (0x01 << (c + 16))
33
34
static void __iomem *sram_base;
35
static void __iomem *scu_base;
36
static struct regmap *pmu;
37
38
static struct reset_control *meson_smp_get_core_reset(int cpu)
39
{
40
struct device_node *np = of_get_cpu_node(cpu, 0);
41
42
return of_reset_control_get_exclusive(np, NULL);
43
}
44
45
static void meson_smp_set_cpu_ctrl(int cpu, bool on_off)
46
{
47
u32 val = readl(sram_base + MESON_SMP_SRAM_CPU_CTRL_REG);
48
49
if (on_off)
50
val |= BIT(cpu);
51
else
52
val &= ~BIT(cpu);
53
54
/* keep bit 0 always enabled */
55
val |= BIT(0);
56
57
writel(val, sram_base + MESON_SMP_SRAM_CPU_CTRL_REG);
58
}
59
60
static void __init meson_smp_prepare_cpus(const char *scu_compatible,
61
const char *pmu_compatible,
62
const char *sram_compatible)
63
{
64
static struct device_node *node;
65
66
/* SMP SRAM */
67
node = of_find_compatible_node(NULL, NULL, sram_compatible);
68
if (!node) {
69
pr_err("Missing SRAM node\n");
70
return;
71
}
72
73
sram_base = of_iomap(node, 0);
74
of_node_put(node);
75
if (!sram_base) {
76
pr_err("Couldn't map SRAM registers\n");
77
return;
78
}
79
80
/* PMU */
81
pmu = syscon_regmap_lookup_by_compatible(pmu_compatible);
82
if (IS_ERR(pmu)) {
83
pr_err("Couldn't map PMU registers\n");
84
return;
85
}
86
87
/* SCU */
88
node = of_find_compatible_node(NULL, NULL, scu_compatible);
89
if (!node) {
90
pr_err("Missing SCU node\n");
91
return;
92
}
93
94
scu_base = of_iomap(node, 0);
95
of_node_put(node);
96
if (!scu_base) {
97
pr_err("Couldn't map SCU registers\n");
98
return;
99
}
100
101
scu_enable(scu_base);
102
}
103
104
static void __init meson8b_smp_prepare_cpus(unsigned int max_cpus)
105
{
106
meson_smp_prepare_cpus("arm,cortex-a5-scu", "amlogic,meson8b-pmu",
107
"amlogic,meson8b-smp-sram");
108
}
109
110
static void __init meson8_smp_prepare_cpus(unsigned int max_cpus)
111
{
112
meson_smp_prepare_cpus("arm,cortex-a9-scu", "amlogic,meson8-pmu",
113
"amlogic,meson8-smp-sram");
114
}
115
116
static void meson_smp_begin_secondary_boot(unsigned int cpu)
117
{
118
/*
119
* Set the entry point before powering on the CPU through the SCU. This
120
* is needed if the CPU is in "warm" state (= after rebooting the
121
* system without power-cycling, or when taking the CPU offline and
122
* then taking it online again.
123
*/
124
writel(__pa_symbol(secondary_startup),
125
sram_base + MESON_SMP_SRAM_CPU_CTRL_ADDR_REG(cpu));
126
127
/*
128
* SCU Power on CPU (needs to be done before starting the CPU,
129
* otherwise the secondary CPU will not start).
130
*/
131
scu_cpu_power_enable(scu_base, cpu);
132
}
133
134
static int meson_smp_finalize_secondary_boot(unsigned int cpu)
135
{
136
unsigned long timeout;
137
138
timeout = jiffies + (10 * HZ);
139
while (readl(sram_base + MESON_SMP_SRAM_CPU_CTRL_ADDR_REG(cpu))) {
140
if (!time_before(jiffies, timeout)) {
141
pr_err("Timeout while waiting for CPU%d status\n",
142
cpu);
143
return -ETIMEDOUT;
144
}
145
}
146
147
writel(__pa_symbol(secondary_startup),
148
sram_base + MESON_SMP_SRAM_CPU_CTRL_ADDR_REG(cpu));
149
150
meson_smp_set_cpu_ctrl(cpu, true);
151
152
return 0;
153
}
154
155
static int meson8_smp_boot_secondary(unsigned int cpu,
156
struct task_struct *idle)
157
{
158
struct reset_control *rstc;
159
int ret;
160
161
rstc = meson_smp_get_core_reset(cpu);
162
if (IS_ERR(rstc)) {
163
pr_err("Couldn't get the reset controller for CPU%d\n", cpu);
164
return PTR_ERR(rstc);
165
}
166
167
meson_smp_begin_secondary_boot(cpu);
168
169
/* Reset enable */
170
ret = reset_control_assert(rstc);
171
if (ret) {
172
pr_err("Failed to assert CPU%d reset\n", cpu);
173
goto out;
174
}
175
176
/* CPU power ON */
177
ret = regmap_update_bits(pmu, MESON_CPU_AO_RTI_PWR_A9_CNTL1,
178
MESON_CPU_PWR_A9_CNTL1_M(cpu), 0);
179
if (ret < 0) {
180
pr_err("Couldn't wake up CPU%d\n", cpu);
181
goto out;
182
}
183
184
udelay(10);
185
186
/* Isolation disable */
187
ret = regmap_update_bits(pmu, MESON_CPU_AO_RTI_PWR_A9_CNTL0, BIT(cpu),
188
0);
189
if (ret < 0) {
190
pr_err("Error when disabling isolation of CPU%d\n", cpu);
191
goto out;
192
}
193
194
/* Reset disable */
195
ret = reset_control_deassert(rstc);
196
if (ret) {
197
pr_err("Failed to de-assert CPU%d reset\n", cpu);
198
goto out;
199
}
200
201
ret = meson_smp_finalize_secondary_boot(cpu);
202
if (ret)
203
goto out;
204
205
out:
206
reset_control_put(rstc);
207
208
return 0;
209
}
210
211
static int meson8b_smp_boot_secondary(unsigned int cpu,
212
struct task_struct *idle)
213
{
214
struct reset_control *rstc;
215
int ret;
216
u32 val;
217
218
rstc = meson_smp_get_core_reset(cpu);
219
if (IS_ERR(rstc)) {
220
pr_err("Couldn't get the reset controller for CPU%d\n", cpu);
221
return PTR_ERR(rstc);
222
}
223
224
meson_smp_begin_secondary_boot(cpu);
225
226
/* CPU power UP */
227
ret = regmap_update_bits(pmu, MESON_CPU_AO_RTI_PWR_A9_CNTL0,
228
MESON_CPU_PWR_A9_CNTL0_M(cpu), 0);
229
if (ret < 0) {
230
pr_err("Couldn't power up CPU%d\n", cpu);
231
goto out;
232
}
233
234
udelay(5);
235
236
/* Reset enable */
237
ret = reset_control_assert(rstc);
238
if (ret) {
239
pr_err("Failed to assert CPU%d reset\n", cpu);
240
goto out;
241
}
242
243
/* Memory power UP */
244
ret = regmap_update_bits(pmu, MESON_CPU_AO_RTI_PWR_A9_MEM_PD0,
245
MESON_CPU_PWR_A9_MEM_PD0_M(cpu), 0);
246
if (ret < 0) {
247
pr_err("Couldn't power up the memory for CPU%d\n", cpu);
248
goto out;
249
}
250
251
/* Wake up CPU */
252
ret = regmap_update_bits(pmu, MESON_CPU_AO_RTI_PWR_A9_CNTL1,
253
MESON_CPU_PWR_A9_CNTL1_M(cpu), 0);
254
if (ret < 0) {
255
pr_err("Couldn't wake up CPU%d\n", cpu);
256
goto out;
257
}
258
259
udelay(10);
260
261
ret = regmap_read_poll_timeout(pmu, MESON_CPU_AO_RTI_PWR_A9_CNTL1, val,
262
val & MESON_CPU_PWR_A9_CNTL1_ST(cpu),
263
10, 10000);
264
if (ret) {
265
pr_err("Timeout while polling PMU for CPU%d status\n", cpu);
266
goto out;
267
}
268
269
/* Isolation disable */
270
ret = regmap_update_bits(pmu, MESON_CPU_AO_RTI_PWR_A9_CNTL0, BIT(cpu),
271
0);
272
if (ret < 0) {
273
pr_err("Error when disabling isolation of CPU%d\n", cpu);
274
goto out;
275
}
276
277
/* Reset disable */
278
ret = reset_control_deassert(rstc);
279
if (ret) {
280
pr_err("Failed to de-assert CPU%d reset\n", cpu);
281
goto out;
282
}
283
284
ret = meson_smp_finalize_secondary_boot(cpu);
285
if (ret)
286
goto out;
287
288
out:
289
reset_control_put(rstc);
290
291
return 0;
292
}
293
294
#ifdef CONFIG_HOTPLUG_CPU
295
static void meson8_smp_cpu_die(unsigned int cpu)
296
{
297
meson_smp_set_cpu_ctrl(cpu, false);
298
299
v7_exit_coherency_flush(louis);
300
301
scu_power_mode(scu_base, SCU_PM_POWEROFF);
302
303
dsb();
304
wfi();
305
306
/* we should never get here */
307
WARN_ON(1);
308
}
309
310
static int meson8_smp_cpu_kill(unsigned int cpu)
311
{
312
int ret, power_mode;
313
unsigned long timeout;
314
315
timeout = jiffies + (50 * HZ);
316
do {
317
power_mode = scu_get_cpu_power_mode(scu_base, cpu);
318
319
if (power_mode == SCU_PM_POWEROFF)
320
break;
321
322
usleep_range(10000, 15000);
323
} while (time_before(jiffies, timeout));
324
325
if (power_mode != SCU_PM_POWEROFF) {
326
pr_err("Error while waiting for SCU power-off on CPU%d\n",
327
cpu);
328
return -ETIMEDOUT;
329
}
330
331
msleep(30);
332
333
/* Isolation enable */
334
ret = regmap_update_bits(pmu, MESON_CPU_AO_RTI_PWR_A9_CNTL0, BIT(cpu),
335
0x3);
336
if (ret < 0) {
337
pr_err("Error when enabling isolation for CPU%d\n", cpu);
338
return ret;
339
}
340
341
udelay(10);
342
343
/* CPU power OFF */
344
ret = regmap_update_bits(pmu, MESON_CPU_AO_RTI_PWR_A9_CNTL1,
345
MESON_CPU_PWR_A9_CNTL1_M(cpu), 0x3);
346
if (ret < 0) {
347
pr_err("Couldn't change sleep status of CPU%d\n", cpu);
348
return ret;
349
}
350
351
return 1;
352
}
353
354
static int meson8b_smp_cpu_kill(unsigned int cpu)
355
{
356
int ret, power_mode, count = 5000;
357
358
do {
359
power_mode = scu_get_cpu_power_mode(scu_base, cpu);
360
361
if (power_mode == SCU_PM_POWEROFF)
362
break;
363
364
udelay(10);
365
} while (++count);
366
367
if (power_mode != SCU_PM_POWEROFF) {
368
pr_err("Error while waiting for SCU power-off on CPU%d\n",
369
cpu);
370
return -ETIMEDOUT;
371
}
372
373
udelay(10);
374
375
/* CPU power DOWN */
376
ret = regmap_update_bits(pmu, MESON_CPU_AO_RTI_PWR_A9_CNTL0,
377
MESON_CPU_PWR_A9_CNTL0_M(cpu), 0x3);
378
if (ret < 0) {
379
pr_err("Couldn't power down CPU%d\n", cpu);
380
return ret;
381
}
382
383
/* Isolation enable */
384
ret = regmap_update_bits(pmu, MESON_CPU_AO_RTI_PWR_A9_CNTL0, BIT(cpu),
385
0x3);
386
if (ret < 0) {
387
pr_err("Error when enabling isolation for CPU%d\n", cpu);
388
return ret;
389
}
390
391
udelay(10);
392
393
/* Sleep status */
394
ret = regmap_update_bits(pmu, MESON_CPU_AO_RTI_PWR_A9_CNTL1,
395
MESON_CPU_PWR_A9_CNTL1_M(cpu), 0x3);
396
if (ret < 0) {
397
pr_err("Couldn't change sleep status of CPU%d\n", cpu);
398
return ret;
399
}
400
401
/* Memory power DOWN */
402
ret = regmap_update_bits(pmu, MESON_CPU_AO_RTI_PWR_A9_MEM_PD0,
403
MESON_CPU_PWR_A9_MEM_PD0_M(cpu), 0xf);
404
if (ret < 0) {
405
pr_err("Couldn't power down the memory of CPU%d\n", cpu);
406
return ret;
407
}
408
409
return 1;
410
}
411
#endif
412
413
static struct smp_operations meson8_smp_ops __initdata = {
414
.smp_prepare_cpus = meson8_smp_prepare_cpus,
415
.smp_boot_secondary = meson8_smp_boot_secondary,
416
#ifdef CONFIG_HOTPLUG_CPU
417
.cpu_die = meson8_smp_cpu_die,
418
.cpu_kill = meson8_smp_cpu_kill,
419
#endif
420
};
421
422
static struct smp_operations meson8b_smp_ops __initdata = {
423
.smp_prepare_cpus = meson8b_smp_prepare_cpus,
424
.smp_boot_secondary = meson8b_smp_boot_secondary,
425
#ifdef CONFIG_HOTPLUG_CPU
426
.cpu_die = meson8_smp_cpu_die,
427
.cpu_kill = meson8b_smp_cpu_kill,
428
#endif
429
};
430
431
CPU_METHOD_OF_DECLARE(meson8_smp, "amlogic,meson8-smp", &meson8_smp_ops);
432
CPU_METHOD_OF_DECLARE(meson8b_smp, "amlogic,meson8b-smp", &meson8b_smp_ops);
433
434