xref: /rk3399_rockchip-uboot/drivers/cpu/rockchip_amp.c (revision 70ad550d6f37befbc82a8e5e7c7753be83ff08b1)
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 
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 
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 
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 
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 
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 
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 
209 static int brought_up_amp(void *fit, int noffset,
210 			  boot_cpu_t *bootcpu, int is_linux)
211 {
212 	const char *desc;
213 	boot_args_t args;
214 	u32 cpu, aarch64, hyp;
215 	u32 load, thumb, us;
216 	u32 pe_state, entry;
217 	int boot_on;
218 	int data_size;
219 	int i, ret;
220 	u8  arch = -ENODATA;
221 
222 	desc = fdt_getprop(fit, noffset, "description", NULL);
223 	cpu = fit_get_u32_default(fit, noffset, "cpu", -ENODATA);
224 	hyp = fit_get_u32_default(fit, noffset, "hyp", 0);
225 	thumb = fit_get_u32_default(fit, noffset, "thumb", 0);
226 	load = fit_get_u32_default(fit, noffset, "load", -ENODATA);
227 	us = fit_get_u32_default(fit, noffset, "udelay", 0);
228 	boot_on = fit_get_u32_default(fit, noffset, "boot-on", 1);
229 	fit_image_get_arch(fit, noffset, &arch);
230 	fit_image_get_data_size(fit, noffset, &data_size);
231 	memset(&args, 0, sizeof(args));
232 
233 	if (!desc || cpu == -ENODATA || arch == -ENODATA ||
234 	    (load == -ENODATA && !is_linux)) {
235 		AMP_E("Property missing!\n");
236 		return -EINVAL;
237 	}
238 	aarch64 = (arch == IH_ARCH_ARM) ? 0 : 1;
239 	pe_state = PE_STATE(aarch64, hyp, thumb, 0);
240 	entry = load;
241 
242 #ifdef DEBUG
243 	AMP_I("       desc: %s\n", desc);
244 	AMP_I("        cpu: 0x%x\n", cpu);
245 	AMP_I("    aarch64: %d\n", aarch64);
246 	AMP_I("        hyp: %d\n", hyp);
247 	AMP_I("      thumb: %d\n", thumb);
248 	AMP_I("      entry: 0x%08x\n", entry);
249 	AMP_I("   pe_state: 0x%08x\n", pe_state);
250 	AMP_I("   linux-os: %d\n\n", is_linux);
251 #endif
252 
253 	/* this cpu is boot cpu ? */
254 	if ((read_mpidr() & 0x0fff) == cpu) {
255 		bootcpu->arch = arch;
256 		bootcpu->entry = entry;
257 		bootcpu->state = pe_state;
258 		bootcpu->linux_os = is_linux;
259 		return 0;
260 	}
261 
262 	/* === only nonboot cpu can reach here === */
263 
264 	/* load or check */
265 	if (is_linux) {
266 		ret = load_linux_for_nonboot_cpu(cpu,
267 				aarch64, load, &entry, &args);
268 		if (ret)
269 			return ret;
270 		/*
271 		 * Must setup before jump to linux.
272 		 * This is an appointment on RK amp solution to handle
273 		 * GIC configure competition.
274 		 */
275 		setup_sync_bits_for_linux();
276 	} else {
277 		if (!sysmem_alloc_base_by_name(desc,
278 				(phys_addr_t)load, data_size))
279 			return -ENXIO;
280 	}
281 
282 	/* If linux assign the boot-on state, use it */
283 	for (i = 0; i < ARRAY_SIZE(g_cpus_boot_by_linux); i++) {
284 		if (cpu == g_cpus_boot_by_linux[i]) {
285 			boot_on = 0;
286 			break;
287 		}
288 	}
289 
290 	if (!boot_on)
291 		return 0;
292 
293 	/* boot now */
294 	ret = smc_cpu_on(cpu, pe_state, entry, &args, is_linux);
295 	if (ret)
296 		return ret;
297 
298 	if (us)
299 		udelay(us);
300 
301 	return 0;
302 }
303 
304 static int brought_up_all_amp(void *fit, const char *fit_uname_cfg)
305 {
306 	int loadables_index;
307 	int linux_noffset;
308 	int conf_noffset;
309 	int cpu_noffset;
310 	int ret;
311 	const char *uname;
312 
313 	conf_noffset = fit_conf_get_node(fit, fit_uname_cfg);
314 	if (conf_noffset < 0)
315 		return conf_noffset;
316 
317 	linux_noffset = fdt_subnode_offset(fit, conf_noffset, "linux");
318 	if (linux_noffset > 0) {
319 		ret = brought_up_amp(fit, linux_noffset, &g_bootcpu, 1);
320 		if (ret)
321 			return ret;
322 	}
323 
324 	for (loadables_index = 0;
325 	     uname = fdt_stringlist_get(fit, conf_noffset,
326 			FIT_LOADABLE_PROP, loadables_index, NULL), uname;
327 	     loadables_index++) {
328 		cpu_noffset = fit_image_get_node(fit, uname);
329 		if (cpu_noffset < 0)
330 			return cpu_noffset;
331 
332 		ret = brought_up_amp(fit, cpu_noffset, &g_bootcpu, 0);
333 		if (ret)
334 			return ret;
335 	}
336 
337 	/* === only boot cpu can reach here === */
338 
339 	if (!g_bootcpu.linux_os) {
340 		flush_dcache_all();
341 		AMP_I("Brought up cpu[%x, self] with state 0x%x, entry 0x%08x ...",
342 		      (u32)read_mpidr() & 0x0fff, g_bootcpu.state, g_bootcpu.entry);
343 		cleanup_before_linux();
344 		printf("OK\n");
345 #ifdef CONFIG_ARM64
346 		armv8_switch_to_el2(0, 0, 0, g_bootcpu.state, (u64)g_bootcpu.entry,
347 		     g_bootcpu.arch == IH_ARCH_ARM ? ES_TO_AARCH32 : ES_TO_AARCH64);
348 #else
349 		void (*armv7_entry)(void);
350 
351 		armv7_entry = (void (*)(void))g_bootcpu.entry;
352 		armv7_entry();
353 #endif
354 	}
355 
356 	/* return: boot cpu continue to boot linux */
357 	return 0;
358 }
359 
360 int amp_cpus_on(void)
361 {
362 	struct blk_desc *dev_desc;
363 	bootm_headers_t images;
364 	disk_partition_t part;
365 	void *hdr, *fit;
366 	int offset, cnt;
367 	int totalsize;
368 	int ret = 0;
369 
370 	dev_desc = rockchip_get_bootdev();
371 	if (!dev_desc)
372 		return -EIO;
373 
374 	if (part_get_info_by_name(dev_desc, AMP_PART, &part) < 0)
375 		return -ENODEV;
376 
377 	hdr = memalign(ARCH_DMA_MINALIGN, FIT_HEADER_SIZE);
378 	if (!hdr)
379 		return -ENOMEM;
380 
381 	/* get totalsize */
382 	offset = part.start;
383 	cnt = DIV_ROUND_UP(FIT_HEADER_SIZE, part.blksz);
384 	if (blk_dread(dev_desc, offset, cnt, hdr) != cnt) {
385 		ret = -EIO;
386 		goto out2;
387 	}
388 
389 	if (fdt_check_header(hdr)) {
390 		AMP_E("Not fit\n");
391 		ret = -EINVAL;
392 		goto out2;
393 	}
394 
395 	if (fit_get_totalsize(hdr, &totalsize)) {
396 		AMP_E("No totalsize\n");
397 		ret = -EINVAL;
398 		goto out2;
399 	}
400 
401 	/* load image */
402 	fit = memalign(ARCH_DMA_MINALIGN, ALIGN(totalsize, part.blksz));
403 	if (!fit) {
404 		printf("No memory\n");
405 		ret = -ENOMEM;
406 		goto out2;
407 	}
408 
409 	memcpy(fit, hdr, FIT_HEADER_SIZE);
410 
411 	offset += cnt;
412 	cnt = DIV_ROUND_UP(totalsize, part.blksz) - cnt;
413 	if (blk_dread(dev_desc, offset, cnt, fit + FIT_HEADER_SIZE) != cnt) {
414 		ret = -EIO;
415 		goto out1;
416 	}
417 
418 	/* prase linux info */
419 	parse_cpus_boot_by_linux();
420 
421 	/* Load loadables */
422 	memset(&images, 0, sizeof(images));
423 	images.fit_uname_cfg = "conf";
424 	images.fit_hdr_os = fit;
425 	images.verify = 1;
426 	ret = boot_get_loadable(0, NULL, &images, IH_ARCH_DEFAULT, NULL, NULL);
427 	if (ret) {
428 		AMP_E("Load loadables, ret=%d\n", ret);
429 		goto out1;
430 	}
431 	flush_dcache_all();
432 
433 	/* Wakeup */
434 	ret = brought_up_all_amp(images.fit_hdr_os, images.fit_uname_cfg);
435 	if (ret)
436 		AMP_E("Brought up amps, ret=%d\n", ret);
437 out1:
438 	free(fit);
439 out2:
440 	free(hdr);
441 
442 	return ret;
443 }
444 
445 int arm64_switch_amp_pe(bootm_headers_t *images)
446 {
447 	images->os.arch = g_bootcpu.arch;
448 	return g_bootcpu.state;
449 }
450 
451