xref: /rk3399_rockchip-uboot/drivers/cpu/rockchip_amp.c (revision 2c3c84fc7fd0ab439baf1e634c8b5504daa40db4)
1 // SPDX-License-Identifier: GPL-2.0
2 /*
3  * Copyright (c) 2021 Rockchip Electronics Co., Ltd
4  */
5 
6 #include <common.h>
7 #include <amp.h>
8 #include <bidram.h>
9 #include <boot_rkimg.h>
10 #include <config.h>
11 #include <sysmem.h>
12 #include <asm/gic.h>
13 #include <asm/io.h>
14 #include <asm/arch/rockchip_smccc.h>
15 
16 DECLARE_GLOBAL_DATA_PTR;
17 
18 /*
19  * [Design Principles]
20  *
21  * [amp.img]
22  *	The amp image with FIT format which consists of non-linux firmwares.
23  *	Please refer to: driver/cpu/amp.its.
24  *
25  *	amp.img generation: ./tools/mkimage -f drivers/cpu/amp.its -E -p 0xe00 amp.img
26  *
27  * [linux]
28  *	We still use the traditional solution for a better compatibility:
29  *	boot.img/recovery.img with FIT format or Android format.
30  *
31  *	The developer need add "/configurations/conf/linux" node to configure:
32  *	description, arch, cpu, thumb, hyp, udelay(optional) properties.
33  *	The addresses depend on U-Boot: kernel_addr_r, fdt_addr_r and
34  *	ramdisk_addr_r. Please refer to: driver/cpu/amp.its.
35  *
36  * [memory management]
37  *	U-Boot is not responsible for memory distribution/fixup any more, please
38  *	handle this on kernel dts "/memory".
39  *
40  * [trust]
41  *	The AMP feature requires trust support.
42  */
43 
44 #define AMP_PART		"amp"
45 #define FIT_HEADER_SIZE		SZ_4K
46 
47 #define gicd_readl(offset)	readl((void *)GICD_BASE + (offset))
48 #define gicd_writel(v, offset)	writel(v, (void *)GICD_BASE + (offset))
49 #define ROCKCHIP_AMP_NODE		"/rockchip-amp"
50 #define ROCKCHIP_AMP_CPUS_NODE		"/rockchip-amp/amp-cpus"
51 
52 typedef struct boot_args {
53 	ulong arg0;
54 	ulong arg1;
55 	ulong arg2;
56 	ulong arg3;
57 } boot_args_t;
58 
59 typedef struct boot_cpu {
60 	u32 arch;
61 	u32 state;
62 	u32 entry;
63 	u32 linux_os;
64 	u32 boot_on;
65 } boot_cpu_t;
66 
67 static boot_cpu_t g_bootcpu;
68 static u32 os_amp_dispatcher_cpu[8];
69 
70 static u32 fit_get_u32_default(const void *fit, int noffset,
71 			       const char *prop, u32 def)
72 {
73 	const fdt32_t *val;
74 
75 	val = fdt_getprop(fit, noffset, prop, NULL);
76 	if (!val)
77 		return def;
78 
79 	return fdt32_to_cpu(*val);
80 }
81 
82 static int parse_os_amp_dispatcher(void)
83 {
84 	const void *fdt = gd->fdt_blob;
85 	int noffset, cpu, i = 0;
86 	u64 mpidr;
87 
88 	memset(os_amp_dispatcher_cpu, 0xff, sizeof(os_amp_dispatcher_cpu));
89 
90 	noffset = fdt_path_offset(fdt, ROCKCHIP_AMP_NODE);
91 	if (noffset < 0)
92 		return 0;
93 
94 	if (!fdtdec_get_is_enabled(fdt, noffset))
95 		return 0;
96 
97 	noffset = fdt_path_offset(fdt, ROCKCHIP_AMP_CPUS_NODE);
98 	if (noffset < 0)
99 		return 0;
100 
101 	fdt_for_each_subnode(cpu, fdt, noffset) {
102 		mpidr = fdtdec_get_uint64(fdt, cpu, "id", 0xffffffff);
103 		if (mpidr == 0xffffffff) /* invalid */
104 			continue;
105 
106 		os_amp_dispatcher_cpu[i++] = mpidr;
107 		AMP_I("cpu[%llx] belong to os amp-dispatcher\n", mpidr);
108 	}
109 
110 	return 0;
111 }
112 
113 static int load_linux_for_nonboot_cpu(u32 cpu, u32 aarch64, u32 load,
114 				      u32 *entry, boot_args_t *args)
115 {
116 	static const char *boot_cmd[] = {
117 		"boot_fit", "boot_android ${devtype} ${devnum}" };
118 	int i, ret;
119 
120 	env_set_hex("bootm_states_unmask", BOOTM_STATE_OS_GO);
121 	for (i = 0; i < ARRAY_SIZE(boot_cmd); i++) {
122 		ret = run_command(boot_cmd[i], 0);
123 		if (!ret)
124 			break;
125 	}
126 	env_set("bootm_states_unmask", NULL);
127 	if (ret) {
128 		AMP_E("Load linux failed, ret=%d\n", ret);
129 		return ret;
130 	}
131 
132 	/* linux boot args */
133 	if (aarch64) {
134 		args->arg0 = (ulong)gd->fdt_blob;
135 		args->arg1 = 0;
136 		args->arg2 = 0;
137 	} else {
138 		args->arg0 = 0;
139 		args->arg1 = 0;
140 		args->arg2 = (ulong)gd->fdt_blob;
141 	}
142 
143 	/* don't need call cleanup_before_linux() as this nonboot cpu is clean */
144 	board_quiesce_devices(&images);
145 	flush_dcache_all();
146 
147 	/* fixup: ramdisk/fdt/entry depend on U-Boot */
148 	*entry = (u32)images.ep;
149 
150 	return 0;
151 }
152 
153 static int is_default_pe_state(u32 pe_state)
154 {
155 #ifdef CONFIG_ARM64
156 	return (pe_state == PE_STATE(1, 1, 0, 0));
157 #else
158 	return (pe_state == PE_STATE(0, 0, 0, 0));
159 #endif
160 }
161 
162 static void setup_sync_bits_for_linux(void)
163 {
164 	u32 val, num_irq, offset;
165 
166 	val = gicd_readl(GICD_CTLR);
167 	val &= ~0x3;
168 	gicd_writel(val, GICD_CTLR);
169 
170 	num_irq = 32 * ((gicd_readl(GICD_TYPER) & 0x1F) + 1);
171 	offset = ((num_irq - 1) / 4) * 4;
172 	gicd_writel(0x0, GICD_IPRIORITYRn + offset);
173 }
174 
175 static int smc_cpu_on(u32 cpu, u32 pe_state, u32 entry,
176 		      boot_args_t *args, bool is_linux)
177 {
178 	int ret;
179 
180 	AMP_I("Brought up cpu[%x] with state 0x%x, entry 0x%08x ...",
181 	      cpu, pe_state, entry);
182 
183 	/* if target pe state is default arch state, power up cpu directly */
184 	if (is_default_pe_state(pe_state))
185 		goto finish;
186 
187 	ret = sip_smc_amp_cfg(AMP_PE_STATE, cpu, pe_state, 0);
188 	if (ret) {
189 		AMP_E("smc pe-state, ret=%d\n", ret);
190 		return ret;
191 	}
192 
193 	/* only linux needs boot args */
194 	if (!is_linux)
195 		goto finish;
196 
197 	ret = sip_smc_amp_cfg(AMP_BOOT_ARG01, cpu, args->arg0, args->arg1);
198 	if (ret) {
199 		AMP_E("smc boot arg01, ret=%d\n", ret);
200 		return ret;
201 	}
202 
203 	ret = sip_smc_amp_cfg(AMP_BOOT_ARG23, cpu, args->arg2, args->arg3);
204 	if (ret) {
205 		AMP_E("smc boot arg23, ret=%d\n", ret);
206 		return ret;
207 	}
208 
209 finish:
210 	ret = psci_cpu_on(cpu, entry);
211 	if (ret) {
212 		printf("cpu up failed, ret=%d\n", ret);
213 		return ret;
214 	}
215 	printf("OK\n");
216 
217 	return 0;
218 }
219 
220 __weak int fit_standalone_release(char *id, uintptr_t entry_point)
221 {
222 	return 0;
223 }
224 
225 static int standalone_handler(const char *id, u32 entry_point, int data_size)
226 {
227 	int ret;
228 
229 	if (!sysmem_alloc_base_by_name(id,
230 			(phys_addr_t)entry_point, data_size))
231 		return -ENXIO;
232 
233 	printf("Handle standalone: '%s' at 0x%08x ...", id, entry_point);
234 
235 	ret = fit_standalone_release((char *)id, entry_point);
236 	if (ret) {
237 		printf("failed, ret=%d\n", ret);
238 		return ret;
239 	}
240 	printf("OK\n");
241 
242 	return 0;
243 }
244 
245 static int brought_up_amp(void *fit, int noffset,
246 			  boot_cpu_t *bootcpu, int is_linux)
247 {
248 	const char *desc;
249 	boot_args_t args;
250 	u32 cpu, aarch64, hyp;
251 	u32 load, thumb, us;
252 	u32 pe_state, entry;
253 	int boot_on;
254 	int data_size;
255 	int i, ret;
256 	u8 type = -ENODATA;
257 	u8 arch = -ENODATA;
258 
259 	desc = fdt_getprop(fit, noffset, "description", NULL);
260 	cpu = fit_get_u32_default(fit, noffset, "cpu", -ENODATA);
261 	hyp = fit_get_u32_default(fit, noffset, "hyp", 0);
262 	thumb = fit_get_u32_default(fit, noffset, "thumb", 0);
263 	entry = load = fit_get_u32_default(fit, noffset, "load", -ENODATA);
264 	us = fit_get_u32_default(fit, noffset, "udelay", 0);
265 	boot_on = fit_get_u32_default(fit, noffset, "boot-on", 1);
266 	fit_image_get_arch(fit, noffset, &arch);
267 	fit_image_get_type(fit, noffset, &type);
268 	fit_image_get_data_size(fit, noffset, &data_size);
269 	memset(&args, 0, sizeof(args));
270 
271 	/* standalone is simple, just handle it and then exit. Allow failure */
272 	if (type == IH_TYPE_STANDALONE) {
273 		if (!desc || load == -ENODATA) {
274 			AMP_E("standalone: \"desc\" or \"load\" property missing!\n");
275 			goto exit;
276 		}
277 		standalone_handler(desc, load, data_size);
278 		goto exit;
279 	}
280 
281 	if (!desc || cpu == -ENODATA || arch == -ENODATA || type == -ENODATA ||
282 	    (load == -ENODATA && !is_linux)) {
283 		AMP_E("Property missing!\n");
284 		return -EINVAL;
285 	}
286 	aarch64 = (arch == IH_ARCH_ARM) ? 0 : 1;
287 	pe_state = PE_STATE(aarch64, hyp, thumb, 0);
288 
289 #ifdef DEBUG
290 	AMP_I("       desc: %s\n", desc);
291 	AMP_I("        cpu: 0x%x\n", cpu);
292 	AMP_I("    aarch64: %d\n", aarch64);
293 	AMP_I("        hyp: %d\n", hyp);
294 	AMP_I("      thumb: %d\n", thumb);
295 	AMP_I("       load: 0x%08x\n", load);
296 	AMP_I("   pe_state: 0x%08x\n", pe_state);
297 	AMP_I("   linux-os: %d\n\n", is_linux);
298 #endif
299 
300 	/* If os amp-dispatcher owns this cpu, don't boot it */
301 	for (i = 0; i < ARRAY_SIZE(os_amp_dispatcher_cpu); i++) {
302 		if (cpu == os_amp_dispatcher_cpu[i]) {
303 			boot_on = 0;
304 			break;
305 		}
306 	}
307 
308 	/* this cpu is boot cpu ? */
309 	if ((read_mpidr() & 0x0fff) == cpu) {
310 		bootcpu->arch = arch;
311 		bootcpu->entry = entry;
312 		bootcpu->state = pe_state;
313 		bootcpu->linux_os = is_linux;
314 		bootcpu->boot_on = boot_on;
315 		return 0;
316 	}
317 
318 	/* === only nonboot cpu can reach here === */
319 
320 	/* load or check */
321 	if (is_linux) {
322 		ret = load_linux_for_nonboot_cpu(cpu,
323 				aarch64, load, &entry, &args);
324 		if (ret)
325 			return ret;
326 		/*
327 		 * Must setup before jump to linux.
328 		 * This is an appointment on RK amp solution to handle
329 		 * GIC configure competition.
330 		 */
331 		setup_sync_bits_for_linux();
332 	} else {
333 		if (!sysmem_alloc_base_by_name(desc,
334 				(phys_addr_t)load, data_size))
335 			return -ENXIO;
336 	}
337 
338 	if (!boot_on)
339 		return 0;
340 
341 	/* boot now */
342 	ret = smc_cpu_on(cpu, pe_state, entry, &args, is_linux);
343 	if (ret)
344 		return ret;
345 exit:
346 	if (us)
347 		udelay(us);
348 
349 	return 0;
350 }
351 
352 static int brought_up_all_amp(void *fit, const char *fit_uname_cfg)
353 {
354 	int loadables_index;
355 	int linux_noffset;
356 	int conf_noffset;
357 	int cpu_noffset;
358 	int ret;
359 	const char *uname;
360 
361 	conf_noffset = fit_conf_get_node(fit, fit_uname_cfg);
362 	if (conf_noffset < 0)
363 		return conf_noffset;
364 
365 	/*
366 	 * If boot cpu is not assigned in amp.img, the default value 0 makes
367 	 * boot cpu power down itself in final process, so we must initial it.
368 	 */
369 	g_bootcpu.boot_on = 1;
370 
371 	linux_noffset = fdt_subnode_offset(fit, conf_noffset, "linux");
372 	if (linux_noffset > 0) {
373 		ret = brought_up_amp(fit, linux_noffset, &g_bootcpu, 1);
374 		if (ret)
375 			return ret;
376 	}
377 
378 	for (loadables_index = 0;
379 	     uname = fdt_stringlist_get(fit, conf_noffset,
380 			FIT_LOADABLE_PROP, loadables_index, NULL), uname;
381 	     loadables_index++) {
382 		cpu_noffset = fit_image_get_node(fit, uname);
383 		if (cpu_noffset < 0)
384 			return cpu_noffset;
385 
386 		ret = brought_up_amp(fit, cpu_noffset, &g_bootcpu, 0);
387 		if (ret)
388 			return ret;
389 	}
390 
391 	/* === only boot cpu can reach here === */
392 
393 	if (!g_bootcpu.boot_on) {
394 		AMP_I("Primary cpu[%x, self] with state 0x%x, entry 0x%08x ... Power down!\n",
395 		      (u32)read_mpidr() & 0x0fff, g_bootcpu.state, g_bootcpu.entry);
396 		dsb();
397 		isb();
398 		/* make sure I am off before os amp-dispatcher probe */
399 		psci_cpu_off(0);
400 
401 		/* Never reach here */
402 		while (1) {
403 			AMP_E("Primary cpu[%x, self] power down failed\n",
404 			      (u32)read_mpidr());
405 			__asm("wfe");
406 		}
407 	}
408 
409 	if (!g_bootcpu.linux_os && g_bootcpu.entry) {
410 		flush_dcache_all();
411 		AMP_I("Brought up primary cpu[%x, self] with state 0x%x, entry 0x%08x ...",
412 		      (u32)read_mpidr() & 0x0fff, g_bootcpu.state, g_bootcpu.entry);
413 		cleanup_before_linux();
414 		printf("OK\n");
415 #ifdef CONFIG_ARM64
416 		armv8_switch_to_el2(0, 0, 0, g_bootcpu.state, (u64)g_bootcpu.entry,
417 		     g_bootcpu.arch == IH_ARCH_ARM ? ES_TO_AARCH32 : ES_TO_AARCH64);
418 #else
419 		void (*armv7_entry)(void);
420 
421 		armv7_entry = (void (*)(void))g_bootcpu.entry;
422 		armv7_entry();
423 #endif
424 	}
425 
426 	/* return: boot cpu continue to boot linux */
427 	return 0;
428 }
429 
430 int amp_cpus_on(void)
431 {
432 	struct blk_desc *dev_desc;
433 	bootm_headers_t images;
434 	disk_partition_t part;
435 	void *hdr, *fit;
436 	int offset, cnt;
437 	int totalsize;
438 	int ret = 0;
439 
440 	dev_desc = rockchip_get_bootdev();
441 	if (!dev_desc)
442 		return -EIO;
443 
444 	if (part_get_info_by_name(dev_desc, AMP_PART, &part) < 0)
445 		return -ENODEV;
446 
447 	hdr = memalign(ARCH_DMA_MINALIGN, FIT_HEADER_SIZE);
448 	if (!hdr)
449 		return -ENOMEM;
450 
451 	/* get totalsize */
452 	offset = part.start;
453 	cnt = DIV_ROUND_UP(FIT_HEADER_SIZE, part.blksz);
454 	if (blk_dread(dev_desc, offset, cnt, hdr) != cnt) {
455 		ret = -EIO;
456 		goto out2;
457 	}
458 
459 	if (fdt_check_header(hdr)) {
460 		AMP_E("Not fit\n");
461 		ret = -EINVAL;
462 		goto out2;
463 	}
464 
465 	if (fit_get_totalsize(hdr, &totalsize)) {
466 		AMP_E("No totalsize\n");
467 		ret = -EINVAL;
468 		goto out2;
469 	}
470 
471 	/* load image */
472 	fit = memalign(ARCH_DMA_MINALIGN, ALIGN(totalsize, part.blksz));
473 	if (!fit) {
474 		printf("No memory\n");
475 		ret = -ENOMEM;
476 		goto out2;
477 	}
478 
479 	memcpy(fit, hdr, FIT_HEADER_SIZE);
480 
481 	offset += cnt;
482 	cnt = DIV_ROUND_UP(totalsize, part.blksz) - cnt;
483 	if (blk_dread(dev_desc, offset, cnt, fit + FIT_HEADER_SIZE) != cnt) {
484 		ret = -EIO;
485 		goto out1;
486 	}
487 
488 	ret = parse_os_amp_dispatcher();
489 	if (ret < 0) {
490 		ret = -EINVAL;
491 		goto out1;
492 	}
493 
494 	/* Load loadables */
495 	memset(&images, 0, sizeof(images));
496 	images.fit_uname_cfg = "conf";
497 	images.fit_hdr_os = fit;
498 	images.verify = 1;
499 	ret = boot_get_loadable(0, NULL, &images, IH_ARCH_DEFAULT, NULL, NULL);
500 	if (ret) {
501 		AMP_E("Load loadables, ret=%d\n", ret);
502 		goto out1;
503 	}
504 	flush_dcache_all();
505 
506 	/* Wakeup */
507 	ret = brought_up_all_amp(images.fit_hdr_os, images.fit_uname_cfg);
508 	if (ret)
509 		AMP_E("Brought up amps, ret=%d\n", ret);
510 out1:
511 	free(fit);
512 out2:
513 	free(hdr);
514 
515 	return ret;
516 }
517 
518 int arm64_switch_amp_pe(bootm_headers_t *images)
519 {
520 	images->os.arch = g_bootcpu.arch;
521 	return g_bootcpu.state;
522 }
523 
524