xref: /rk3399_rockchip-uboot/drivers/cpu/rockchip_amp.c (revision a3d2307ae14bfc117fbc3e39041ee708aae249d6)
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 <sysmem.h>
11 #include <asm/arch/rockchip_smccc.h>
12 
13 /*
14  * [Design Principles]
15  *
16  * [amp.img]
17  *	The amp image with FIT format which consists of non-linux firmwares.
18  *	Please refer to: driver/cpu/amp.its.
19  *
20  *	amp.img generation: ./tools/mkimage -f drivers/cpu/amp.its -E -p 0xe00 amp.img
21  *
22  * [linux]
23  *	We still use the traditional solution for a better compatibility:
24  *	boot.img/recovery.img with FIT format or Android format.
25  *
26  *	The developer need add "/configurations/conf/linux" node to configure:
27  *	description, arch, cpu, thumb, hyp, udelay(optional) properties.
28  *	The addresses depend on U-Boot: kernel_addr_r, fdt_addr_r and
29  *	ramdisk_addr_r. Please refer to: driver/cpu/amp.its.
30  *
31  * [memory management]
32  *	U-Boot is not responsible for memory distribution/fixup any more, please
33  *	handle this on kernel dts "/memory".
34  *
35  * [trust]
36  *	The AMP feature requires trust support.
37  */
38 
39 #define AMP_PART	"amp"
40 
41 typedef struct boot_args {
42 	ulong arg0;
43 	ulong arg1;
44 	ulong arg2;
45 	ulong arg3;
46 } boot_args_t;
47 
48 typedef struct boot_cpu {
49 	u32 arch;
50 	u32 state;
51 	u32 entry;
52 	u32 linux_os;
53 } boot_cpu_t;
54 
55 static boot_cpu_t g_bootcpu;
56 
57 static u32 fit_get_u32_default(const void *fit, int noffset,
58 			       const char *prop, u32 def)
59 {
60 	const fdt32_t *val;
61 
62 	val = fdt_getprop(fit, noffset, prop, NULL);
63 	if (!val)
64 		return def;
65 
66 	return fdt32_to_cpu(*val);
67 }
68 
69 static int load_linux_for_nonboot_cpu(u32 cpu, u32 aarch64, u32 load,
70 				      u32 *entry, boot_args_t *args)
71 {
72 	static const char *boot_cmd[] = {
73 		"boot_fit", "boot_android ${devtype} ${devnum}" };
74 	int i, ret;
75 
76 	env_set_hex("bootm_states_unmask", BOOTM_STATE_OS_GO);
77 	for (i = 0; i < ARRAY_SIZE(boot_cmd); i++) {
78 		ret = run_command(boot_cmd[i], 0);
79 		if (!ret)
80 			break;
81 	}
82 	env_set("bootm_states_unmask", NULL);
83 	if (ret) {
84 		AMP_E("Load linux failed, ret=%d\n", ret);
85 		return ret;
86 	}
87 
88 	/* linux boot args */
89 	if (aarch64) {
90 		args->arg0 = (ulong)gd->fdt_blob;
91 		args->arg1 = 0;
92 		args->arg2 = 0;
93 	} else {
94 		args->arg0 = 0;
95 		args->arg1 = 0;
96 		args->arg2 = (ulong)gd->fdt_blob;
97 	}
98 
99 	/* don't need call cleanup_before_linux() as this nonboot cpu is clean */
100 	board_quiesce_devices(&images);
101 	flush_dcache_all();
102 
103 	/* fixup: ramdisk/fdt/entry depend on U-Boot */
104 	*entry = env_get_ulong("kernel_addr_r", 16, 0);
105 
106 	return 0;
107 }
108 
109 static int is_default_pe_state(u32 pe_state)
110 {
111 #ifdef CONFIG_ARM64
112 	return (pe_state == PE_STATE(1, 1, 0, 0));
113 #else
114 	return (pe_state == PE_STATE(0, 0, 0, 0));
115 #endif
116 }
117 
118 static int smc_cpu_on(u32 cpu, u32 pe_state, u32 entry, boot_args_t *args)
119 {
120 	int ret;
121 
122 	AMP_I("Brought up cpu[%x] with state 0x%x, entry 0x%08x ...",
123 	      cpu, pe_state, entry);
124 
125 	if (is_default_pe_state(pe_state))
126 		goto finish;
127 
128 	ret = sip_smc_amp_cfg(AMP_PE_STATE, cpu, pe_state, 0);
129 	if (ret) {
130 		AMP_E("smc pe-state, ret=%d\n", ret);
131 		return ret;
132 	}
133 
134 	ret = sip_smc_amp_cfg(AMP_BOOT_ARG01, cpu, args->arg0, args->arg1);
135 	if (ret) {
136 		AMP_E("smc boot arg01, ret=%d\n", ret);
137 		return ret;
138 	}
139 
140 	ret = sip_smc_amp_cfg(AMP_BOOT_ARG23, cpu, args->arg2, args->arg3);
141 	if (ret) {
142 		AMP_E("smc boot arg23, ret=%d\n", ret);
143 		return ret;
144 	}
145 
146 finish:
147 	ret = psci_cpu_on(cpu, entry);
148 	if (ret) {
149 		printf("cpu up failed, ret=%d\n", ret);
150 		return ret;
151 	}
152 	printf("OK\n");
153 
154 	return 0;
155 }
156 
157 static int brought_up_amp(void *fit, int noffset,
158 			  boot_cpu_t *bootcpu, int is_linux)
159 {
160 	const char *desc;
161 	boot_args_t args;
162 	u32 cpu, aarch64, hyp;
163 	u32 load, thumb, us;
164 	u32 pe_state, entry;
165 	int data_size;
166 	int ret;
167 	u8  arch = -ENODATA;
168 
169 	desc = fdt_getprop(fit, noffset, "description", NULL);
170 	cpu = fit_get_u32_default(fit, noffset, "cpu", -ENODATA);
171 	hyp = fit_get_u32_default(fit, noffset, "hyp", 0);
172 	thumb = fit_get_u32_default(fit, noffset, "thumb", 0);
173 	load = fit_get_u32_default(fit, noffset, "load", -ENODATA);
174 	us = fit_get_u32_default(fit, noffset, "udelay", 0);
175 	fit_image_get_arch(fit, noffset, &arch);
176 	fit_image_get_data_size(fit, noffset, &data_size);
177 	memset(&args, 0, sizeof(args));
178 
179 	if (!desc || cpu == -ENODATA || arch == -ENODATA ||
180 	    (load == -ENODATA && !is_linux)) {
181 		AMP_E("Property missing!\n");
182 		return -EINVAL;
183 	}
184 	aarch64 = (arch == IH_ARCH_ARM) ? 0 : 1;
185 	pe_state = PE_STATE(aarch64, hyp, thumb, 0);
186 	entry = load;
187 
188 #ifdef DEBUG
189 	AMP_I("       desc: %s\n", desc);
190 	AMP_I("        cpu: 0x%x\n", cpu);
191 	AMP_I("    aarch64: %d\n", aarch64);
192 	AMP_I("        hyp: %d\n", hyp);
193 	AMP_I("      thumb: %d\n", thumb);
194 	AMP_I("      entry: 0x%08x\n", entry);
195 	AMP_I("   pe_state: 0x%08x\n", pe_state);
196 	AMP_I("   linux-os: %d\n\n", is_linux);
197 #endif
198 
199 	if ((read_mpidr() & 0x0fff) == cpu) {
200 		bootcpu->arch = arch;
201 		bootcpu->entry = entry;
202 		bootcpu->state = pe_state;
203 		bootcpu->linux_os = is_linux;
204 		return 0;
205 	}
206 
207 	/* === only nonboot cpu can reach here === */
208 
209 	/* load or check */
210 	if (is_linux) {
211 		ret = load_linux_for_nonboot_cpu(cpu,
212 				aarch64, load, &entry, &args);
213 		if (ret)
214 			return ret;
215 	} else {
216 		if (!sysmem_alloc_base_by_name(desc,
217 				(phys_addr_t)load, data_size))
218 			return -ENXIO;
219 	}
220 
221 	/* wakeup */
222 	ret = smc_cpu_on(cpu, pe_state, entry, &args);
223 	if (ret)
224 		return ret;
225 
226 	if (us)
227 		udelay(us);
228 
229 	return 0;
230 }
231 
232 static int brought_up_all_amp(void *fit, const char *fit_uname_cfg)
233 {
234 	int loadables_index;
235 	int linux_noffset;
236 	int conf_noffset;
237 	int cpu_noffset;
238 	int ret;
239 	const char *uname;
240 
241 	conf_noffset = fit_conf_get_node(fit, fit_uname_cfg);
242 	if (conf_noffset < 0)
243 		return conf_noffset;
244 
245 	linux_noffset = fdt_subnode_offset(fit, conf_noffset, "linux");
246 	if (linux_noffset > 0) {
247 		ret = brought_up_amp(fit, linux_noffset, &g_bootcpu, 1);
248 		if (ret)
249 			return ret;
250 	}
251 
252 	for (loadables_index = 0;
253 	     uname = fdt_stringlist_get(fit, conf_noffset,
254 			FIT_LOADABLE_PROP, loadables_index, NULL), uname;
255 	     loadables_index++) {
256 		cpu_noffset = fit_image_get_node(fit, uname);
257 		if (cpu_noffset < 0)
258 			return cpu_noffset;
259 
260 		ret = brought_up_amp(fit, cpu_noffset, &g_bootcpu, 0);
261 		if (ret)
262 			return ret;
263 	}
264 
265 	/* === only boot cpu can reach here === */
266 
267 	if (!g_bootcpu.linux_os) {
268 		flush_dcache_all();
269 		AMP_I("Brought up cpu[%x, self] with state 0x%x, entry 0x%08x ...",
270 		      (u32)read_mpidr() & 0x0fff, g_bootcpu.state, g_bootcpu.entry);
271 		cleanup_before_linux();
272 		printf("OK\n");
273 		armv8_switch_to_el2(0, 0, 0, g_bootcpu.state, (u64)g_bootcpu.entry,
274 		     g_bootcpu.arch == IH_ARCH_ARM ? ES_TO_AARCH32 : ES_TO_AARCH64);
275 	}
276 
277 	/* return: boot cpu continue to boot linux */
278 	return 0;
279 }
280 
281 int amp_cpus_on(void)
282 {
283 	struct blk_desc *dev_desc;
284 	bootm_headers_t images;
285 	disk_partition_t part;
286 	void *fit;
287 	int ret = 0;
288 
289 	dev_desc = rockchip_get_bootdev();
290 	if (!dev_desc)
291 		return -EIO;
292 
293 	if (part_get_info_by_name(dev_desc, AMP_PART, &part) < 0)
294 		return -ENODEV;
295 
296 	fit = malloc(part.size * part.blksz);
297 	if (!fit) {
298 		AMP_E("No memory, please increase CONFIG_SYS_MALLOC_LEN\n");
299 		return -ENOMEM;
300 	}
301 
302 	if (blk_dread(dev_desc, part.start, part.size, fit) != part.size) {
303 		ret = -EIO;
304 		goto out;
305 	}
306 
307 	if (fdt_check_header(fit)) {
308 		AMP_E("Not fit\n");
309 		ret = -EINVAL;
310 		goto out;
311 	}
312 
313 	/* Load loadables */
314 	memset(&images, 0, sizeof(images));
315 	images.fit_uname_cfg = "conf";
316 	images.fit_hdr_os = fit;
317 	images.verify = 1;
318 	ret = boot_get_loadable(0, NULL, &images, IH_ARCH_DEFAULT, NULL, NULL);
319 	if (ret) {
320 		AMP_E("Load loadables, ret=%d\n", ret);
321 		goto out;
322 	}
323 	flush_dcache_all();
324 
325 	/* Wakeup */
326 	ret = brought_up_all_amp(images.fit_hdr_os, images.fit_uname_cfg);
327 	if (ret)
328 		AMP_E("Brought up amps, ret=%d\n", ret);
329 out:
330 	free(fit);
331 
332 	return ret;
333 }
334 
335 int arm64_switch_amp_pe(bootm_headers_t *images)
336 {
337 	images->os.arch = g_bootcpu.arch;
338 	return g_bootcpu.state;
339 }
340 
341