xref: /openbmc/linux/arch/arm/mach-qcom/platsmp.c (revision 5b628549)
1 /*
2  *  Copyright (C) 2002 ARM Ltd.
3  *  All Rights Reserved
4  *  Copyright (c) 2010, Code Aurora Forum. All rights reserved.
5  *  Copyright (c) 2014 The Linux Foundation. All rights reserved.
6  *
7  * This program is free software; you can redistribute it and/or modify
8  * it under the terms of the GNU General Public License version 2 as
9  * published by the Free Software Foundation.
10  */
11 
12 #include <linux/init.h>
13 #include <linux/errno.h>
14 #include <linux/delay.h>
15 #include <linux/device.h>
16 #include <linux/of.h>
17 #include <linux/of_address.h>
18 #include <linux/smp.h>
19 #include <linux/io.h>
20 #include <linux/qcom_scm.h>
21 
22 #include <asm/smp_plat.h>
23 
24 
25 #define VDD_SC1_ARRAY_CLAMP_GFS_CTL	0x35a0
26 #define SCSS_CPU1CORE_RESET		0x2d80
27 #define SCSS_DBG_STATUS_CORE_PWRDUP	0x2e64
28 
29 #define APCS_CPU_PWR_CTL	0x04
30 #define PLL_CLAMP		BIT(8)
31 #define CORE_PWRD_UP		BIT(7)
32 #define COREPOR_RST		BIT(5)
33 #define CORE_RST		BIT(4)
34 #define L2DT_SLP		BIT(3)
35 #define CLAMP			BIT(0)
36 
37 #define APC_PWR_GATE_CTL	0x14
38 #define BHS_CNT_SHIFT		24
39 #define LDO_PWR_DWN_SHIFT	16
40 #define LDO_BYP_SHIFT		8
41 #define BHS_SEG_SHIFT		1
42 #define BHS_EN			BIT(0)
43 
44 #define APCS_SAW2_VCTL		0x14
45 #define APCS_SAW2_2_VCTL	0x1c
46 
47 extern void secondary_startup_arm(void);
48 
49 #ifdef CONFIG_HOTPLUG_CPU
50 static void qcom_cpu_die(unsigned int cpu)
51 {
52 	wfi();
53 }
54 #endif
55 
56 static int scss_release_secondary(unsigned int cpu)
57 {
58 	struct device_node *node;
59 	void __iomem *base;
60 
61 	node = of_find_compatible_node(NULL, NULL, "qcom,gcc-msm8660");
62 	if (!node) {
63 		pr_err("%s: can't find node\n", __func__);
64 		return -ENXIO;
65 	}
66 
67 	base = of_iomap(node, 0);
68 	of_node_put(node);
69 	if (!base)
70 		return -ENOMEM;
71 
72 	writel_relaxed(0, base + VDD_SC1_ARRAY_CLAMP_GFS_CTL);
73 	writel_relaxed(0, base + SCSS_CPU1CORE_RESET);
74 	writel_relaxed(3, base + SCSS_DBG_STATUS_CORE_PWRDUP);
75 	mb();
76 	iounmap(base);
77 
78 	return 0;
79 }
80 
81 static int kpssv1_release_secondary(unsigned int cpu)
82 {
83 	int ret = 0;
84 	void __iomem *reg, *saw_reg;
85 	struct device_node *cpu_node, *acc_node, *saw_node;
86 	u32 val;
87 
88 	cpu_node = of_get_cpu_node(cpu, NULL);
89 	if (!cpu_node)
90 		return -ENODEV;
91 
92 	acc_node = of_parse_phandle(cpu_node, "qcom,acc", 0);
93 	if (!acc_node) {
94 		ret = -ENODEV;
95 		goto out_acc;
96 	}
97 
98 	saw_node = of_parse_phandle(cpu_node, "qcom,saw", 0);
99 	if (!saw_node) {
100 		ret = -ENODEV;
101 		goto out_saw;
102 	}
103 
104 	reg = of_iomap(acc_node, 0);
105 	if (!reg) {
106 		ret = -ENOMEM;
107 		goto out_acc_map;
108 	}
109 
110 	saw_reg = of_iomap(saw_node, 0);
111 	if (!saw_reg) {
112 		ret = -ENOMEM;
113 		goto out_saw_map;
114 	}
115 
116 	/* Turn on CPU rail */
117 	writel_relaxed(0xA4, saw_reg + APCS_SAW2_VCTL);
118 	mb();
119 	udelay(512);
120 
121 	/* Krait bring-up sequence */
122 	val = PLL_CLAMP | L2DT_SLP | CLAMP;
123 	writel_relaxed(val, reg + APCS_CPU_PWR_CTL);
124 	val &= ~L2DT_SLP;
125 	writel_relaxed(val, reg + APCS_CPU_PWR_CTL);
126 	mb();
127 	ndelay(300);
128 
129 	val |= COREPOR_RST;
130 	writel_relaxed(val, reg + APCS_CPU_PWR_CTL);
131 	mb();
132 	udelay(2);
133 
134 	val &= ~CLAMP;
135 	writel_relaxed(val, reg + APCS_CPU_PWR_CTL);
136 	mb();
137 	udelay(2);
138 
139 	val &= ~COREPOR_RST;
140 	writel_relaxed(val, reg + APCS_CPU_PWR_CTL);
141 	mb();
142 	udelay(100);
143 
144 	val |= CORE_PWRD_UP;
145 	writel_relaxed(val, reg + APCS_CPU_PWR_CTL);
146 	mb();
147 
148 	iounmap(saw_reg);
149 out_saw_map:
150 	iounmap(reg);
151 out_acc_map:
152 	of_node_put(saw_node);
153 out_saw:
154 	of_node_put(acc_node);
155 out_acc:
156 	of_node_put(cpu_node);
157 	return ret;
158 }
159 
160 static int kpssv2_release_secondary(unsigned int cpu)
161 {
162 	void __iomem *reg;
163 	struct device_node *cpu_node, *l2_node, *acc_node, *saw_node;
164 	void __iomem *l2_saw_base;
165 	unsigned reg_val;
166 	int ret;
167 
168 	cpu_node = of_get_cpu_node(cpu, NULL);
169 	if (!cpu_node)
170 		return -ENODEV;
171 
172 	acc_node = of_parse_phandle(cpu_node, "qcom,acc", 0);
173 	if (!acc_node) {
174 		ret = -ENODEV;
175 		goto out_acc;
176 	}
177 
178 	l2_node = of_parse_phandle(cpu_node, "next-level-cache", 0);
179 	if (!l2_node) {
180 		ret = -ENODEV;
181 		goto out_l2;
182 	}
183 
184 	saw_node = of_parse_phandle(l2_node, "qcom,saw", 0);
185 	if (!saw_node) {
186 		ret = -ENODEV;
187 		goto out_saw;
188 	}
189 
190 	reg = of_iomap(acc_node, 0);
191 	if (!reg) {
192 		ret = -ENOMEM;
193 		goto out_map;
194 	}
195 
196 	l2_saw_base = of_iomap(saw_node, 0);
197 	if (!l2_saw_base) {
198 		ret = -ENOMEM;
199 		goto out_saw_map;
200 	}
201 
202 	/* Turn on the BHS, turn off LDO Bypass and power down LDO */
203 	reg_val = (64 << BHS_CNT_SHIFT) | (0x3f << LDO_PWR_DWN_SHIFT) | BHS_EN;
204 	writel_relaxed(reg_val, reg + APC_PWR_GATE_CTL);
205 	mb();
206 	/* wait for the BHS to settle */
207 	udelay(1);
208 
209 	/* Turn on BHS segments */
210 	reg_val |= 0x3f << BHS_SEG_SHIFT;
211 	writel_relaxed(reg_val, reg + APC_PWR_GATE_CTL);
212 	mb();
213 	 /* wait for the BHS to settle */
214 	udelay(1);
215 
216 	/* Finally turn on the bypass so that BHS supplies power */
217 	reg_val |= 0x3f << LDO_BYP_SHIFT;
218 	writel_relaxed(reg_val, reg + APC_PWR_GATE_CTL);
219 
220 	/* enable max phases */
221 	writel_relaxed(0x10003, l2_saw_base + APCS_SAW2_2_VCTL);
222 	mb();
223 	udelay(50);
224 
225 	reg_val = COREPOR_RST | CLAMP;
226 	writel_relaxed(reg_val, reg + APCS_CPU_PWR_CTL);
227 	mb();
228 	udelay(2);
229 
230 	reg_val &= ~CLAMP;
231 	writel_relaxed(reg_val, reg + APCS_CPU_PWR_CTL);
232 	mb();
233 	udelay(2);
234 
235 	reg_val &= ~COREPOR_RST;
236 	writel_relaxed(reg_val, reg + APCS_CPU_PWR_CTL);
237 	mb();
238 
239 	reg_val |= CORE_PWRD_UP;
240 	writel_relaxed(reg_val, reg + APCS_CPU_PWR_CTL);
241 	mb();
242 
243 	ret = 0;
244 
245 	iounmap(l2_saw_base);
246 out_saw_map:
247 	iounmap(reg);
248 out_map:
249 	of_node_put(saw_node);
250 out_saw:
251 	of_node_put(l2_node);
252 out_l2:
253 	of_node_put(acc_node);
254 out_acc:
255 	of_node_put(cpu_node);
256 
257 	return ret;
258 }
259 
260 static DEFINE_PER_CPU(int, cold_boot_done);
261 
262 static int qcom_boot_secondary(unsigned int cpu, int (*func)(unsigned int))
263 {
264 	int ret = 0;
265 
266 	if (!per_cpu(cold_boot_done, cpu)) {
267 		ret = func(cpu);
268 		if (!ret)
269 			per_cpu(cold_boot_done, cpu) = true;
270 	}
271 
272 	/*
273 	 * Send the secondary CPU a soft interrupt, thereby causing
274 	 * the boot monitor to read the system wide flags register,
275 	 * and branch to the address found there.
276 	 */
277 	arch_send_wakeup_ipi_mask(cpumask_of(cpu));
278 
279 	return ret;
280 }
281 
282 static int msm8660_boot_secondary(unsigned int cpu, struct task_struct *idle)
283 {
284 	return qcom_boot_secondary(cpu, scss_release_secondary);
285 }
286 
287 static int kpssv1_boot_secondary(unsigned int cpu, struct task_struct *idle)
288 {
289 	return qcom_boot_secondary(cpu, kpssv1_release_secondary);
290 }
291 
292 static int kpssv2_boot_secondary(unsigned int cpu, struct task_struct *idle)
293 {
294 	return qcom_boot_secondary(cpu, kpssv2_release_secondary);
295 }
296 
297 static void __init qcom_smp_prepare_cpus(unsigned int max_cpus)
298 {
299 	int cpu;
300 
301 	if (qcom_scm_set_cold_boot_addr(secondary_startup_arm,
302 					cpu_present_mask)) {
303 		for_each_present_cpu(cpu) {
304 			if (cpu == smp_processor_id())
305 				continue;
306 			set_cpu_present(cpu, false);
307 		}
308 		pr_warn("Failed to set CPU boot address, disabling SMP\n");
309 	}
310 }
311 
312 static const struct smp_operations smp_msm8660_ops __initconst = {
313 	.smp_prepare_cpus	= qcom_smp_prepare_cpus,
314 	.smp_boot_secondary	= msm8660_boot_secondary,
315 #ifdef CONFIG_HOTPLUG_CPU
316 	.cpu_die		= qcom_cpu_die,
317 #endif
318 };
319 CPU_METHOD_OF_DECLARE(qcom_smp, "qcom,gcc-msm8660", &smp_msm8660_ops);
320 
321 static const struct smp_operations qcom_smp_kpssv1_ops __initconst = {
322 	.smp_prepare_cpus	= qcom_smp_prepare_cpus,
323 	.smp_boot_secondary	= kpssv1_boot_secondary,
324 #ifdef CONFIG_HOTPLUG_CPU
325 	.cpu_die		= qcom_cpu_die,
326 #endif
327 };
328 CPU_METHOD_OF_DECLARE(qcom_smp_kpssv1, "qcom,kpss-acc-v1", &qcom_smp_kpssv1_ops);
329 
330 static const struct smp_operations qcom_smp_kpssv2_ops __initconst = {
331 	.smp_prepare_cpus	= qcom_smp_prepare_cpus,
332 	.smp_boot_secondary	= kpssv2_boot_secondary,
333 #ifdef CONFIG_HOTPLUG_CPU
334 	.cpu_die		= qcom_cpu_die,
335 #endif
336 };
337 CPU_METHOD_OF_DECLARE(qcom_smp_kpssv2, "qcom,kpss-acc-v2", &qcom_smp_kpssv2_ops);
338