xref: /OK3568_Linux_fs/u-boot/drivers/cpu/rockchip_amp.c (revision 4882a59341e53eb6f0b4789bf948001014eff981)
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 LINXU_AMP_NODES		"/rockchip-amp/amp-cpus"
50 
51 typedef struct boot_args {
52 	ulong arg0;
53 	ulong arg1;
54 	ulong arg2;
55 	ulong arg3;
56 } boot_args_t;
57 
58 typedef struct boot_cpu {
59 	u32 arch;
60 	u32 state;
61 	u32 entry;
62 	u32 linux_os;
63 } boot_cpu_t;
64 
65 static boot_cpu_t g_bootcpu;
66 static u32 g_cpus_boot_by_linux[8];
67 
fit_get_u32_default(const void * fit,int noffset,const char * prop,u32 def)68 static u32 fit_get_u32_default(const void *fit, int noffset,
69 			       const char *prop, u32 def)
70 {
71 	const fdt32_t *val;
72 
73 	val = fdt_getprop(fit, noffset, prop, NULL);
74 	if (!val)
75 		return def;
76 
77 	return fdt32_to_cpu(*val);
78 }
79 
parse_cpus_boot_by_linux(void)80 static int parse_cpus_boot_by_linux(void)
81 {
82 	const void *fdt = gd->fdt_blob;
83 	int noffset, cpu, i = 0;
84 	u64 mpidr;
85 
86 	memset(g_cpus_boot_by_linux, 0xff, sizeof(g_cpus_boot_by_linux));
87 	noffset = fdt_path_offset(fdt, LINXU_AMP_NODES);
88 	if (noffset < 0)
89 		return 0;
90 
91 	fdt_for_each_subnode(cpu, fdt, noffset) {
92 		mpidr = fdtdec_get_uint64(fdt, cpu, "id", 0xffffffff);
93 		if (mpidr == 0xffffffff)
94 			continue;
95 		g_cpus_boot_by_linux[i++] = mpidr;
96 		printf("CPU[0x%llx] is required boot by Linux\n", mpidr);
97 	}
98 
99 	return 0;
100 }
101 
load_linux_for_nonboot_cpu(u32 cpu,u32 aarch64,u32 load,u32 * entry,boot_args_t * args)102 static int load_linux_for_nonboot_cpu(u32 cpu, u32 aarch64, u32 load,
103 				      u32 *entry, boot_args_t *args)
104 {
105 	static const char *boot_cmd[] = {
106 		"boot_fit", "boot_android ${devtype} ${devnum}" };
107 	int i, ret;
108 
109 	env_set_hex("bootm_states_unmask", BOOTM_STATE_OS_GO);
110 	for (i = 0; i < ARRAY_SIZE(boot_cmd); i++) {
111 		ret = run_command(boot_cmd[i], 0);
112 		if (!ret)
113 			break;
114 	}
115 	env_set("bootm_states_unmask", NULL);
116 	if (ret) {
117 		AMP_E("Load linux failed, ret=%d\n", ret);
118 		return ret;
119 	}
120 
121 	/* linux boot args */
122 	if (aarch64) {
123 		args->arg0 = (ulong)gd->fdt_blob;
124 		args->arg1 = 0;
125 		args->arg2 = 0;
126 	} else {
127 		args->arg0 = 0;
128 		args->arg1 = 0;
129 		args->arg2 = (ulong)gd->fdt_blob;
130 	}
131 
132 	/* don't need call cleanup_before_linux() as this nonboot cpu is clean */
133 	board_quiesce_devices(&images);
134 	flush_dcache_all();
135 
136 	/* fixup: ramdisk/fdt/entry depend on U-Boot */
137 	*entry = (u32)images.ep;
138 
139 	return 0;
140 }
141 
is_default_pe_state(u32 pe_state)142 static int is_default_pe_state(u32 pe_state)
143 {
144 #ifdef CONFIG_ARM64
145 	return (pe_state == PE_STATE(1, 1, 0, 0));
146 #else
147 	return (pe_state == PE_STATE(0, 0, 0, 0));
148 #endif
149 }
150 
setup_sync_bits_for_linux(void)151 static void setup_sync_bits_for_linux(void)
152 {
153 	u32 val, num_irq, offset;
154 
155 	val = gicd_readl(GICD_CTLR);
156 	val &= ~0x3;
157 	gicd_writel(val, GICD_CTLR);
158 
159 	num_irq = 32 * ((gicd_readl(GICD_TYPER) & 0x1F) + 1);
160 	offset = ((num_irq - 1) / 4) * 4;
161 	gicd_writel(0x0, GICD_IPRIORITYRn + offset);
162 }
163 
smc_cpu_on(u32 cpu,u32 pe_state,u32 entry,boot_args_t * args,bool is_linux)164 static int smc_cpu_on(u32 cpu, u32 pe_state, u32 entry,
165 		      boot_args_t *args, bool is_linux)
166 {
167 	int ret;
168 
169 	AMP_I("Brought up cpu[%x] with state 0x%x, entry 0x%08x ...",
170 	      cpu, pe_state, entry);
171 
172 	/* if target pe state is default arch state, power up cpu directly */
173 	if (is_default_pe_state(pe_state))
174 		goto finish;
175 
176 	ret = sip_smc_amp_cfg(AMP_PE_STATE, cpu, pe_state, 0);
177 	if (ret) {
178 		AMP_E("smc pe-state, ret=%d\n", ret);
179 		return ret;
180 	}
181 
182 	/* only linux needs boot args */
183 	if (!is_linux)
184 		goto finish;
185 
186 	ret = sip_smc_amp_cfg(AMP_BOOT_ARG01, cpu, args->arg0, args->arg1);
187 	if (ret) {
188 		AMP_E("smc boot arg01, ret=%d\n", ret);
189 		return ret;
190 	}
191 
192 	ret = sip_smc_amp_cfg(AMP_BOOT_ARG23, cpu, args->arg2, args->arg3);
193 	if (ret) {
194 		AMP_E("smc boot arg23, ret=%d\n", ret);
195 		return ret;
196 	}
197 
198 finish:
199 	ret = psci_cpu_on(cpu, entry);
200 	if (ret) {
201 		printf("cpu up failed, ret=%d\n", ret);
202 		return ret;
203 	}
204 	printf("OK\n");
205 
206 	return 0;
207 }
208 
fit_standalone_release(char * id,uintptr_t entry_point)209 __weak int fit_standalone_release(char *id, uintptr_t entry_point)
210 {
211 	return 0;
212 }
213 
standalone_handler(const char * id,u32 entry_point,int data_size)214 static int standalone_handler(const char *id, u32 entry_point, int data_size)
215 {
216 	int ret;
217 
218 	if (!sysmem_alloc_base_by_name(id,
219 			(phys_addr_t)entry_point, data_size))
220 		return -ENXIO;
221 
222 	printf("Handle standalone: '%s' at 0x%08x ...", id, entry_point);
223 
224 	ret = fit_standalone_release((char *)id, entry_point);
225 	if (ret) {
226 		printf("failed, ret=%d\n", ret);
227 		return ret;
228 	}
229 	printf("OK\n");
230 
231 	return 0;
232 }
233 
brought_up_amp(void * fit,int noffset,boot_cpu_t * bootcpu,int is_linux)234 static int brought_up_amp(void *fit, int noffset,
235 			  boot_cpu_t *bootcpu, int is_linux)
236 {
237 	const char *desc;
238 	boot_args_t args;
239 	u32 cpu, aarch64, hyp;
240 	u32 load, thumb, us;
241 	u32 pe_state, entry;
242 	int boot_on;
243 	int data_size;
244 	int i, ret;
245 	u8 type = -ENODATA;
246 	u8 arch = -ENODATA;
247 
248 	desc = fdt_getprop(fit, noffset, "description", NULL);
249 	cpu = fit_get_u32_default(fit, noffset, "cpu", -ENODATA);
250 	hyp = fit_get_u32_default(fit, noffset, "hyp", 0);
251 	thumb = fit_get_u32_default(fit, noffset, "thumb", 0);
252 	entry = load = fit_get_u32_default(fit, noffset, "load", -ENODATA);
253 	us = fit_get_u32_default(fit, noffset, "udelay", 0);
254 	boot_on = fit_get_u32_default(fit, noffset, "boot-on", 1);
255 	fit_image_get_arch(fit, noffset, &arch);
256 	fit_image_get_type(fit, noffset, &type);
257 	fit_image_get_data_size(fit, noffset, &data_size);
258 	memset(&args, 0, sizeof(args));
259 
260 	/* standalone is simple, just handle it and then exit. Allow failure */
261 	if (type == IH_TYPE_STANDALONE) {
262 		if (!desc || load == -ENODATA) {
263 			AMP_E("standalone: \"desc\" or \"load\" property missing!\n");
264 			goto exit;
265 		}
266 		standalone_handler(desc, load, data_size);
267 		goto exit;
268 	}
269 
270 	if (!desc || cpu == -ENODATA || arch == -ENODATA || type == -ENODATA ||
271 	    (load == -ENODATA && !is_linux)) {
272 		AMP_E("Property missing!\n");
273 		return -EINVAL;
274 	}
275 	aarch64 = (arch == IH_ARCH_ARM) ? 0 : 1;
276 	pe_state = PE_STATE(aarch64, hyp, thumb, 0);
277 
278 #ifdef DEBUG
279 	AMP_I("       desc: %s\n", desc);
280 	AMP_I("        cpu: 0x%x\n", cpu);
281 	AMP_I("    aarch64: %d\n", aarch64);
282 	AMP_I("        hyp: %d\n", hyp);
283 	AMP_I("      thumb: %d\n", thumb);
284 	AMP_I("       load: 0x%08x\n", load);
285 	AMP_I("   pe_state: 0x%08x\n", pe_state);
286 	AMP_I("   linux-os: %d\n\n", is_linux);
287 #endif
288 
289 	/* this cpu is boot cpu ? */
290 	if ((read_mpidr() & 0x0fff) == cpu) {
291 		bootcpu->arch = arch;
292 		bootcpu->entry = entry;
293 		bootcpu->state = pe_state;
294 		bootcpu->linux_os = is_linux;
295 		return 0;
296 	}
297 
298 	/* === only nonboot cpu can reach here === */
299 
300 	/* load or check */
301 	if (is_linux) {
302 		ret = load_linux_for_nonboot_cpu(cpu,
303 				aarch64, load, &entry, &args);
304 		if (ret)
305 			return ret;
306 		/*
307 		 * Must setup before jump to linux.
308 		 * This is an appointment on RK amp solution to handle
309 		 * GIC configure competition.
310 		 */
311 		setup_sync_bits_for_linux();
312 	} else {
313 		if (!sysmem_alloc_base_by_name(desc,
314 				(phys_addr_t)load, data_size))
315 			return -ENXIO;
316 	}
317 
318 	/* If linux assign the boot-on state, use it */
319 	for (i = 0; i < ARRAY_SIZE(g_cpus_boot_by_linux); i++) {
320 		if (cpu == g_cpus_boot_by_linux[i]) {
321 			boot_on = 0;
322 			break;
323 		}
324 	}
325 
326 	if (!boot_on)
327 		return 0;
328 
329 	/* boot now */
330 	ret = smc_cpu_on(cpu, pe_state, entry, &args, is_linux);
331 	if (ret)
332 		return ret;
333 exit:
334 	if (us)
335 		udelay(us);
336 
337 	return 0;
338 }
339 
brought_up_all_amp(void * fit,const char * fit_uname_cfg)340 static int brought_up_all_amp(void *fit, const char *fit_uname_cfg)
341 {
342 	int loadables_index;
343 	int linux_noffset;
344 	int conf_noffset;
345 	int cpu_noffset;
346 	int ret;
347 	const char *uname;
348 
349 	conf_noffset = fit_conf_get_node(fit, fit_uname_cfg);
350 	if (conf_noffset < 0)
351 		return conf_noffset;
352 
353 	linux_noffset = fdt_subnode_offset(fit, conf_noffset, "linux");
354 	if (linux_noffset > 0) {
355 		ret = brought_up_amp(fit, linux_noffset, &g_bootcpu, 1);
356 		if (ret)
357 			return ret;
358 	}
359 
360 	for (loadables_index = 0;
361 	     uname = fdt_stringlist_get(fit, conf_noffset,
362 			FIT_LOADABLE_PROP, loadables_index, NULL), uname;
363 	     loadables_index++) {
364 		cpu_noffset = fit_image_get_node(fit, uname);
365 		if (cpu_noffset < 0)
366 			return cpu_noffset;
367 
368 		ret = brought_up_amp(fit, cpu_noffset, &g_bootcpu, 0);
369 		if (ret)
370 			return ret;
371 	}
372 
373 	/* === only boot cpu can reach here === */
374 
375 	if (!g_bootcpu.linux_os && g_bootcpu.entry) {
376 		flush_dcache_all();
377 		AMP_I("Brought up cpu[%x, self] with state 0x%x, entry 0x%08x ...",
378 		      (u32)read_mpidr() & 0x0fff, g_bootcpu.state, g_bootcpu.entry);
379 		cleanup_before_linux();
380 		printf("OK\n");
381 #ifdef CONFIG_ARM64
382 		armv8_switch_to_el2(0, 0, 0, g_bootcpu.state, (u64)g_bootcpu.entry,
383 		     g_bootcpu.arch == IH_ARCH_ARM ? ES_TO_AARCH32 : ES_TO_AARCH64);
384 #else
385 		void (*armv7_entry)(void);
386 
387 		armv7_entry = (void (*)(void))g_bootcpu.entry;
388 		armv7_entry();
389 #endif
390 	}
391 
392 	/* return: boot cpu continue to boot linux */
393 	return 0;
394 }
395 
amp_cpus_on(void)396 int amp_cpus_on(void)
397 {
398 	struct blk_desc *dev_desc;
399 	bootm_headers_t images;
400 	disk_partition_t part;
401 	void *hdr, *fit;
402 	int offset, cnt;
403 	int totalsize;
404 	int ret = 0;
405 
406 	dev_desc = rockchip_get_bootdev();
407 	if (!dev_desc)
408 		return -EIO;
409 
410 	if (part_get_info_by_name(dev_desc, AMP_PART, &part) < 0)
411 		return -ENODEV;
412 
413 	hdr = memalign(ARCH_DMA_MINALIGN, FIT_HEADER_SIZE);
414 	if (!hdr)
415 		return -ENOMEM;
416 
417 	/* get totalsize */
418 	offset = part.start;
419 	cnt = DIV_ROUND_UP(FIT_HEADER_SIZE, part.blksz);
420 	if (blk_dread(dev_desc, offset, cnt, hdr) != cnt) {
421 		ret = -EIO;
422 		goto out2;
423 	}
424 
425 	if (fdt_check_header(hdr)) {
426 		AMP_E("Not fit\n");
427 		ret = -EINVAL;
428 		goto out2;
429 	}
430 
431 	if (fit_get_totalsize(hdr, &totalsize)) {
432 		AMP_E("No totalsize\n");
433 		ret = -EINVAL;
434 		goto out2;
435 	}
436 
437 	/* load image */
438 	fit = memalign(ARCH_DMA_MINALIGN, ALIGN(totalsize, part.blksz));
439 	if (!fit) {
440 		printf("No memory\n");
441 		ret = -ENOMEM;
442 		goto out2;
443 	}
444 
445 	memcpy(fit, hdr, FIT_HEADER_SIZE);
446 
447 	offset += cnt;
448 	cnt = DIV_ROUND_UP(totalsize, part.blksz) - cnt;
449 	if (blk_dread(dev_desc, offset, cnt, fit + FIT_HEADER_SIZE) != cnt) {
450 		ret = -EIO;
451 		goto out1;
452 	}
453 
454 	/* prase linux info */
455 	parse_cpus_boot_by_linux();
456 
457 	/* Load loadables */
458 	memset(&images, 0, sizeof(images));
459 	images.fit_uname_cfg = "conf";
460 	images.fit_hdr_os = fit;
461 	images.verify = 1;
462 	ret = boot_get_loadable(0, NULL, &images, IH_ARCH_DEFAULT, NULL, NULL);
463 	if (ret) {
464 		AMP_E("Load loadables, ret=%d\n", ret);
465 		goto out1;
466 	}
467 	flush_dcache_all();
468 
469 	/* Wakeup */
470 	ret = brought_up_all_amp(images.fit_hdr_os, images.fit_uname_cfg);
471 	if (ret)
472 		AMP_E("Brought up amps, ret=%d\n", ret);
473 out1:
474 	free(fit);
475 out2:
476 	free(hdr);
477 
478 	return ret;
479 }
480 
arm64_switch_amp_pe(bootm_headers_t * images)481 int arm64_switch_amp_pe(bootm_headers_t *images)
482 {
483 	images->os.arch = g_bootcpu.arch;
484 	return g_bootcpu.state;
485 }
486 
487