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