Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
torvalds
GitHub Repository: torvalds/linux
Path: blob/master/arch/arm/mach-hisi/hotplug.c
26292 views
1
// SPDX-License-Identifier: GPL-2.0-only
2
/*
3
* Copyright (c) 2013 Linaro Ltd.
4
* Copyright (c) 2013 HiSilicon Limited.
5
*/
6
7
#include <linux/cpu.h>
8
#include <linux/delay.h>
9
#include <linux/io.h>
10
#include <linux/of_address.h>
11
#include <asm/cacheflush.h>
12
#include <asm/smp_plat.h>
13
#include "core.h"
14
15
/* Sysctrl registers in Hi3620 SoC */
16
#define SCISOEN 0xc0
17
#define SCISODIS 0xc4
18
#define SCPERPWREN 0xd0
19
#define SCPERPWRDIS 0xd4
20
#define SCCPUCOREEN 0xf4
21
#define SCCPUCOREDIS 0xf8
22
#define SCPERCTRL0 0x200
23
#define SCCPURSTEN 0x410
24
#define SCCPURSTDIS 0x414
25
26
/*
27
* bit definition in SCISOEN/SCPERPWREN/...
28
*
29
* CPU2_ISO_CTRL (1 << 5)
30
* CPU3_ISO_CTRL (1 << 6)
31
* ...
32
*/
33
#define CPU2_ISO_CTRL (1 << 5)
34
35
/*
36
* bit definition in SCPERCTRL0
37
*
38
* CPU0_WFI_MASK_CFG (1 << 28)
39
* CPU1_WFI_MASK_CFG (1 << 29)
40
* ...
41
*/
42
#define CPU0_WFI_MASK_CFG (1 << 28)
43
44
/*
45
* bit definition in SCCPURSTEN/...
46
*
47
* CPU0_SRST_REQ_EN (1 << 0)
48
* CPU1_SRST_REQ_EN (1 << 1)
49
* ...
50
*/
51
#define CPU0_HPM_SRST_REQ_EN (1 << 22)
52
#define CPU0_DBG_SRST_REQ_EN (1 << 12)
53
#define CPU0_NEON_SRST_REQ_EN (1 << 4)
54
#define CPU0_SRST_REQ_EN (1 << 0)
55
56
#define HIX5HD2_PERI_CRG20 0x50
57
#define CRG20_CPU1_RESET (1 << 17)
58
59
#define HIX5HD2_PERI_PMC0 0x1000
60
#define PMC0_CPU1_WAIT_MTCOMS_ACK (1 << 8)
61
#define PMC0_CPU1_PMC_ENABLE (1 << 7)
62
#define PMC0_CPU1_POWERDOWN (1 << 3)
63
64
#define HIP01_PERI9 0x50
65
#define PERI9_CPU1_RESET (1 << 1)
66
67
enum {
68
HI3620_CTRL,
69
ERROR_CTRL,
70
};
71
72
static void __iomem *ctrl_base;
73
static int id;
74
75
static void set_cpu_hi3620(int cpu, bool enable)
76
{
77
u32 val = 0;
78
79
if (enable) {
80
/* MTCMOS set */
81
if ((cpu == 2) || (cpu == 3))
82
writel_relaxed(CPU2_ISO_CTRL << (cpu - 2),
83
ctrl_base + SCPERPWREN);
84
udelay(100);
85
86
/* Enable core */
87
writel_relaxed(0x01 << cpu, ctrl_base + SCCPUCOREEN);
88
89
/* unreset */
90
val = CPU0_DBG_SRST_REQ_EN | CPU0_NEON_SRST_REQ_EN
91
| CPU0_SRST_REQ_EN;
92
writel_relaxed(val << cpu, ctrl_base + SCCPURSTDIS);
93
/* reset */
94
val |= CPU0_HPM_SRST_REQ_EN;
95
writel_relaxed(val << cpu, ctrl_base + SCCPURSTEN);
96
97
/* ISO disable */
98
if ((cpu == 2) || (cpu == 3))
99
writel_relaxed(CPU2_ISO_CTRL << (cpu - 2),
100
ctrl_base + SCISODIS);
101
udelay(1);
102
103
/* WFI Mask */
104
val = readl_relaxed(ctrl_base + SCPERCTRL0);
105
val &= ~(CPU0_WFI_MASK_CFG << cpu);
106
writel_relaxed(val, ctrl_base + SCPERCTRL0);
107
108
/* Unreset */
109
val = CPU0_DBG_SRST_REQ_EN | CPU0_NEON_SRST_REQ_EN
110
| CPU0_SRST_REQ_EN | CPU0_HPM_SRST_REQ_EN;
111
writel_relaxed(val << cpu, ctrl_base + SCCPURSTDIS);
112
} else {
113
/* wfi mask */
114
val = readl_relaxed(ctrl_base + SCPERCTRL0);
115
val |= (CPU0_WFI_MASK_CFG << cpu);
116
writel_relaxed(val, ctrl_base + SCPERCTRL0);
117
118
/* disable core*/
119
writel_relaxed(0x01 << cpu, ctrl_base + SCCPUCOREDIS);
120
121
if ((cpu == 2) || (cpu == 3)) {
122
/* iso enable */
123
writel_relaxed(CPU2_ISO_CTRL << (cpu - 2),
124
ctrl_base + SCISOEN);
125
udelay(1);
126
}
127
128
/* reset */
129
val = CPU0_DBG_SRST_REQ_EN | CPU0_NEON_SRST_REQ_EN
130
| CPU0_SRST_REQ_EN | CPU0_HPM_SRST_REQ_EN;
131
writel_relaxed(val << cpu, ctrl_base + SCCPURSTEN);
132
133
if ((cpu == 2) || (cpu == 3)) {
134
/* MTCMOS unset */
135
writel_relaxed(CPU2_ISO_CTRL << (cpu - 2),
136
ctrl_base + SCPERPWRDIS);
137
udelay(100);
138
}
139
}
140
}
141
142
static int hi3xxx_hotplug_init(void)
143
{
144
struct device_node *node;
145
146
node = of_find_compatible_node(NULL, NULL, "hisilicon,sysctrl");
147
if (!node) {
148
id = ERROR_CTRL;
149
return -ENOENT;
150
}
151
152
ctrl_base = of_iomap(node, 0);
153
of_node_put(node);
154
if (!ctrl_base) {
155
id = ERROR_CTRL;
156
return -ENOMEM;
157
}
158
159
id = HI3620_CTRL;
160
return 0;
161
}
162
163
void hi3xxx_set_cpu(int cpu, bool enable)
164
{
165
if (!ctrl_base) {
166
if (hi3xxx_hotplug_init() < 0)
167
return;
168
}
169
170
if (id == HI3620_CTRL)
171
set_cpu_hi3620(cpu, enable);
172
}
173
174
static bool hix5hd2_hotplug_init(void)
175
{
176
struct device_node *np;
177
178
np = of_find_compatible_node(NULL, NULL, "hisilicon,cpuctrl");
179
if (!np)
180
return false;
181
182
ctrl_base = of_iomap(np, 0);
183
of_node_put(np);
184
if (!ctrl_base)
185
return false;
186
187
return true;
188
}
189
190
void hix5hd2_set_cpu(int cpu, bool enable)
191
{
192
u32 val = 0;
193
194
if (!ctrl_base)
195
if (!hix5hd2_hotplug_init())
196
BUG();
197
198
if (enable) {
199
/* power on cpu1 */
200
val = readl_relaxed(ctrl_base + HIX5HD2_PERI_PMC0);
201
val &= ~(PMC0_CPU1_WAIT_MTCOMS_ACK | PMC0_CPU1_POWERDOWN);
202
val |= PMC0_CPU1_PMC_ENABLE;
203
writel_relaxed(val, ctrl_base + HIX5HD2_PERI_PMC0);
204
/* unreset */
205
val = readl_relaxed(ctrl_base + HIX5HD2_PERI_CRG20);
206
val &= ~CRG20_CPU1_RESET;
207
writel_relaxed(val, ctrl_base + HIX5HD2_PERI_CRG20);
208
} else {
209
/* power down cpu1 */
210
val = readl_relaxed(ctrl_base + HIX5HD2_PERI_PMC0);
211
val |= PMC0_CPU1_PMC_ENABLE | PMC0_CPU1_POWERDOWN;
212
val &= ~PMC0_CPU1_WAIT_MTCOMS_ACK;
213
writel_relaxed(val, ctrl_base + HIX5HD2_PERI_PMC0);
214
215
/* reset */
216
val = readl_relaxed(ctrl_base + HIX5HD2_PERI_CRG20);
217
val |= CRG20_CPU1_RESET;
218
writel_relaxed(val, ctrl_base + HIX5HD2_PERI_CRG20);
219
}
220
}
221
222
void hip01_set_cpu(int cpu, bool enable)
223
{
224
unsigned int temp;
225
struct device_node *np;
226
227
if (!ctrl_base) {
228
np = of_find_compatible_node(NULL, NULL, "hisilicon,hip01-sysctrl");
229
BUG_ON(!np);
230
ctrl_base = of_iomap(np, 0);
231
of_node_put(np);
232
BUG_ON(!ctrl_base);
233
}
234
235
if (enable) {
236
/* reset on CPU1 */
237
temp = readl_relaxed(ctrl_base + HIP01_PERI9);
238
temp |= PERI9_CPU1_RESET;
239
writel_relaxed(temp, ctrl_base + HIP01_PERI9);
240
241
udelay(50);
242
243
/* unreset on CPU1 */
244
temp = readl_relaxed(ctrl_base + HIP01_PERI9);
245
temp &= ~PERI9_CPU1_RESET;
246
writel_relaxed(temp, ctrl_base + HIP01_PERI9);
247
}
248
}
249
250
static inline void cpu_enter_lowpower(void)
251
{
252
unsigned int v;
253
254
flush_cache_all();
255
256
/*
257
* Turn off coherency and L1 D-cache
258
*/
259
asm volatile(
260
" mrc p15, 0, %0, c1, c0, 1\n"
261
" bic %0, %0, #0x40\n"
262
" mcr p15, 0, %0, c1, c0, 1\n"
263
" mrc p15, 0, %0, c1, c0, 0\n"
264
" bic %0, %0, #0x04\n"
265
" mcr p15, 0, %0, c1, c0, 0\n"
266
: "=&r" (v)
267
: "r" (0)
268
: "cc");
269
}
270
271
#ifdef CONFIG_HOTPLUG_CPU
272
void hi3xxx_cpu_die(unsigned int cpu)
273
{
274
cpu_enter_lowpower();
275
hi3xxx_set_cpu_jump(cpu, phys_to_virt(0));
276
cpu_do_idle();
277
278
/* We should have never returned from idle */
279
panic("cpu %d unexpectedly exit from shutdown\n", cpu);
280
}
281
282
int hi3xxx_cpu_kill(unsigned int cpu)
283
{
284
unsigned long timeout = jiffies + msecs_to_jiffies(50);
285
286
while (hi3xxx_get_cpu_jump(cpu))
287
if (time_after(jiffies, timeout))
288
return 0;
289
hi3xxx_set_cpu(cpu, false);
290
return 1;
291
}
292
293
void hix5hd2_cpu_die(unsigned int cpu)
294
{
295
flush_cache_all();
296
hix5hd2_set_cpu(cpu, false);
297
}
298
#endif
299
300