xref: /rk3399_rockchip-uboot/drivers/cpu/rockchip_amp.c (revision 3792164f84564c891a54c231fd2a0e4b47d4f4f9)
14388decaSJoseph Chen // SPDX-License-Identifier: GPL-2.0
24388decaSJoseph Chen /*
331f8f6ebSJoseph Chen  * Copyright (c) 2021 Rockchip Electronics Co., Ltd
44388decaSJoseph Chen  */
531f8f6ebSJoseph Chen 
64388decaSJoseph Chen #include <common.h>
74388decaSJoseph Chen #include <amp.h>
84388decaSJoseph Chen #include <bidram.h>
931f8f6ebSJoseph Chen #include <boot_rkimg.h>
1049cfbee5SJoseph Chen #include <config.h>
114388decaSJoseph Chen #include <sysmem.h>
1249cfbee5SJoseph Chen #include <asm/gic.h>
1349cfbee5SJoseph Chen #include <asm/io.h>
144388decaSJoseph Chen #include <asm/arch/rockchip_smccc.h>
154388decaSJoseph Chen 
16d184e484SJoseph Chen DECLARE_GLOBAL_DATA_PTR;
17d184e484SJoseph Chen 
18bb4a1f43SJoseph Chen /*
19bb4a1f43SJoseph Chen  * [Design Principles]
20bb4a1f43SJoseph Chen  *
21bb4a1f43SJoseph Chen  * [amp.img]
22bb4a1f43SJoseph Chen  *	The amp image with FIT format which consists of non-linux firmwares.
23bb4a1f43SJoseph Chen  *	Please refer to: driver/cpu/amp.its.
24bb4a1f43SJoseph Chen  *
25bb4a1f43SJoseph Chen  *	amp.img generation: ./tools/mkimage -f drivers/cpu/amp.its -E -p 0xe00 amp.img
26bb4a1f43SJoseph Chen  *
27bb4a1f43SJoseph Chen  * [linux]
28bb4a1f43SJoseph Chen  *	We still use the traditional solution for a better compatibility:
29bb4a1f43SJoseph Chen  *	boot.img/recovery.img with FIT format or Android format.
30bb4a1f43SJoseph Chen  *
31bb4a1f43SJoseph Chen  *	The developer need add "/configurations/conf/linux" node to configure:
32bb4a1f43SJoseph Chen  *	description, arch, cpu, thumb, hyp, udelay(optional) properties.
33bb4a1f43SJoseph Chen  *	The addresses depend on U-Boot: kernel_addr_r, fdt_addr_r and
34bb4a1f43SJoseph Chen  *	ramdisk_addr_r. Please refer to: driver/cpu/amp.its.
35bb4a1f43SJoseph Chen  *
36bb4a1f43SJoseph Chen  * [memory management]
37bb4a1f43SJoseph Chen  *	U-Boot is not responsible for memory distribution/fixup any more, please
38bb4a1f43SJoseph Chen  *	handle this on kernel dts "/memory".
39bb4a1f43SJoseph Chen  *
40bb4a1f43SJoseph Chen  * [trust]
41bb4a1f43SJoseph Chen  *	The AMP feature requires trust support.
42bb4a1f43SJoseph Chen  */
43bb4a1f43SJoseph Chen 
4431f8f6ebSJoseph Chen #define AMP_PART		"amp"
45043d91f2SJoseph Chen #define FIT_HEADER_SIZE		SZ_4K
46043d91f2SJoseph Chen 
4749cfbee5SJoseph Chen #define gicd_readl(offset)	readl((void *)GICD_BASE + (offset))
4849cfbee5SJoseph Chen #define gicd_writel(v, offset)	writel(v, (void *)GICD_BASE + (offset))
494a758d95SJoseph Chen #define ROCKCHIP_AMP_NODE		"/rockchip-amp"
504a758d95SJoseph Chen #define ROCKCHIP_AMP_CPUS_NODE		"/rockchip-amp/amp-cpus"
514388decaSJoseph Chen 
52bb4a1f43SJoseph Chen typedef struct boot_args {
53bb4a1f43SJoseph Chen 	ulong arg0;
54bb4a1f43SJoseph Chen 	ulong arg1;
55bb4a1f43SJoseph Chen 	ulong arg2;
56bb4a1f43SJoseph Chen 	ulong arg3;
57bb4a1f43SJoseph Chen } boot_args_t;
58bb4a1f43SJoseph Chen 
59bb4a1f43SJoseph Chen typedef struct boot_cpu {
60bb4a1f43SJoseph Chen 	u32 arch;
61bb4a1f43SJoseph Chen 	u32 state;
62bb4a1f43SJoseph Chen 	u32 entry;
63bb4a1f43SJoseph Chen 	u32 linux_os;
644a758d95SJoseph Chen 	u32 boot_on;
65bb4a1f43SJoseph Chen } boot_cpu_t;
66bb4a1f43SJoseph Chen 
67bb4a1f43SJoseph Chen static boot_cpu_t g_bootcpu;
684a758d95SJoseph Chen static u32 os_amp_dispatcher_cpu[8];
6931f8f6ebSJoseph Chen 
fit_get_u32_default(const void * fit,int noffset,const char * prop,u32 def)7031f8f6ebSJoseph Chen static u32 fit_get_u32_default(const void *fit, int noffset,
7131f8f6ebSJoseph Chen 			       const char *prop, u32 def)
720df2c3dfSJoseph Chen {
7331f8f6ebSJoseph Chen 	const fdt32_t *val;
740df2c3dfSJoseph Chen 
7531f8f6ebSJoseph Chen 	val = fdt_getprop(fit, noffset, prop, NULL);
7631f8f6ebSJoseph Chen 	if (!val)
7731f8f6ebSJoseph Chen 		return def;
780df2c3dfSJoseph Chen 
7931f8f6ebSJoseph Chen 	return fdt32_to_cpu(*val);
800df2c3dfSJoseph Chen }
810df2c3dfSJoseph Chen 
parse_os_amp_dispatcher(void)824a758d95SJoseph Chen static int parse_os_amp_dispatcher(void)
83d184e484SJoseph Chen {
84d184e484SJoseph Chen 	const void *fdt = gd->fdt_blob;
85cc173a5cSJoseph Chen 	int noffset, cpu, i = 0;
86cc173a5cSJoseph Chen 	u64 mpidr;
87d184e484SJoseph Chen 
884a758d95SJoseph Chen 	memset(os_amp_dispatcher_cpu, 0xff, sizeof(os_amp_dispatcher_cpu));
894a758d95SJoseph Chen 
904a758d95SJoseph Chen 	noffset = fdt_path_offset(fdt, ROCKCHIP_AMP_NODE);
914a758d95SJoseph Chen 	if (noffset < 0)
924a758d95SJoseph Chen 		return 0;
934a758d95SJoseph Chen 
944a758d95SJoseph Chen 	if (!fdtdec_get_is_enabled(fdt, noffset))
954a758d95SJoseph Chen 		return 0;
964a758d95SJoseph Chen 
974a758d95SJoseph Chen 	noffset = fdt_path_offset(fdt, ROCKCHIP_AMP_CPUS_NODE);
98d184e484SJoseph Chen 	if (noffset < 0)
99d184e484SJoseph Chen 		return 0;
100d184e484SJoseph Chen 
101d184e484SJoseph Chen 	fdt_for_each_subnode(cpu, fdt, noffset) {
102cc173a5cSJoseph Chen 		mpidr = fdtdec_get_uint64(fdt, cpu, "id", 0xffffffff);
1034a758d95SJoseph Chen 		if (mpidr == 0xffffffff) /* invalid */
104d184e484SJoseph Chen 			continue;
1054a758d95SJoseph Chen 
1064a758d95SJoseph Chen 		os_amp_dispatcher_cpu[i++] = mpidr;
1074a758d95SJoseph Chen 		AMP_I("cpu[%llx] belong to os amp-dispatcher\n", mpidr);
108d184e484SJoseph Chen 	}
109d184e484SJoseph Chen 
110d184e484SJoseph Chen 	return 0;
111d184e484SJoseph Chen }
112d184e484SJoseph Chen 
load_linux_for_nonboot_cpu(u32 cpu,u32 aarch64,u32 load,u32 * entry,boot_args_t * args)113bb4a1f43SJoseph Chen static int load_linux_for_nonboot_cpu(u32 cpu, u32 aarch64, u32 load,
114bb4a1f43SJoseph Chen 				      u32 *entry, boot_args_t *args)
1154388decaSJoseph Chen {
116bb4a1f43SJoseph Chen 	static const char *boot_cmd[] = {
117bb4a1f43SJoseph Chen 		"boot_fit", "boot_android ${devtype} ${devnum}" };
118bb4a1f43SJoseph Chen 	int i, ret;
119bb4a1f43SJoseph Chen 
120bb4a1f43SJoseph Chen 	env_set_hex("bootm_states_unmask", BOOTM_STATE_OS_GO);
121bb4a1f43SJoseph Chen 	for (i = 0; i < ARRAY_SIZE(boot_cmd); i++) {
122bb4a1f43SJoseph Chen 		ret = run_command(boot_cmd[i], 0);
123bb4a1f43SJoseph Chen 		if (!ret)
124bb4a1f43SJoseph Chen 			break;
125bb4a1f43SJoseph Chen 	}
126bb4a1f43SJoseph Chen 	env_set("bootm_states_unmask", NULL);
127bb4a1f43SJoseph Chen 	if (ret) {
128bb4a1f43SJoseph Chen 		AMP_E("Load linux failed, ret=%d\n", ret);
129bb4a1f43SJoseph Chen 		return ret;
130bb4a1f43SJoseph Chen 	}
131bb4a1f43SJoseph Chen 
132bb4a1f43SJoseph Chen 	/* linux boot args */
133bb4a1f43SJoseph Chen 	if (aarch64) {
134bb4a1f43SJoseph Chen 		args->arg0 = (ulong)gd->fdt_blob;
135bb4a1f43SJoseph Chen 		args->arg1 = 0;
136bb4a1f43SJoseph Chen 		args->arg2 = 0;
137bb4a1f43SJoseph Chen 	} else {
138bb4a1f43SJoseph Chen 		args->arg0 = 0;
139bb4a1f43SJoseph Chen 		args->arg1 = 0;
140bb4a1f43SJoseph Chen 		args->arg2 = (ulong)gd->fdt_blob;
141bb4a1f43SJoseph Chen 	}
142bb4a1f43SJoseph Chen 
143bb4a1f43SJoseph Chen 	/* don't need call cleanup_before_linux() as this nonboot cpu is clean */
144bb4a1f43SJoseph Chen 	board_quiesce_devices(&images);
145bb4a1f43SJoseph Chen 	flush_dcache_all();
146bb4a1f43SJoseph Chen 
147bb4a1f43SJoseph Chen 	/* fixup: ramdisk/fdt/entry depend on U-Boot */
14882b7aaedSJoseph Chen 	*entry = (u32)images.ep;
149bb4a1f43SJoseph Chen 
150bb4a1f43SJoseph Chen 	return 0;
151bb4a1f43SJoseph Chen }
152bb4a1f43SJoseph Chen 
is_default_pe_state(u32 pe_state)153bb4a1f43SJoseph Chen static int is_default_pe_state(u32 pe_state)
154bb4a1f43SJoseph Chen {
155bb4a1f43SJoseph Chen #ifdef CONFIG_ARM64
156bb4a1f43SJoseph Chen 	return (pe_state == PE_STATE(1, 1, 0, 0));
157bb4a1f43SJoseph Chen #else
158bb4a1f43SJoseph Chen 	return (pe_state == PE_STATE(0, 0, 0, 0));
159bb4a1f43SJoseph Chen #endif
160bb4a1f43SJoseph Chen }
161bb4a1f43SJoseph Chen 
setup_sync_bits_for_linux(void)16249cfbee5SJoseph Chen static void setup_sync_bits_for_linux(void)
16349cfbee5SJoseph Chen {
16449cfbee5SJoseph Chen 	u32 val, num_irq, offset;
16549cfbee5SJoseph Chen 
16649cfbee5SJoseph Chen 	val = gicd_readl(GICD_CTLR);
16749cfbee5SJoseph Chen 	val &= ~0x3;
16849cfbee5SJoseph Chen 	gicd_writel(val, GICD_CTLR);
16949cfbee5SJoseph Chen 
17049cfbee5SJoseph Chen 	num_irq = 32 * ((gicd_readl(GICD_TYPER) & 0x1F) + 1);
17149cfbee5SJoseph Chen 	offset = ((num_irq - 1) / 4) * 4;
17249cfbee5SJoseph Chen 	gicd_writel(0x0, GICD_IPRIORITYRn + offset);
17349cfbee5SJoseph Chen }
17449cfbee5SJoseph Chen 
smc_cpu_on(u32 cpu,u32 pe_state,u32 entry,boot_args_t * args,bool is_linux)175257f4b84SJoseph Chen static int smc_cpu_on(u32 cpu, u32 pe_state, u32 entry,
176257f4b84SJoseph Chen 		      boot_args_t *args, bool is_linux)
177bb4a1f43SJoseph Chen {
17831f8f6ebSJoseph Chen 	int ret;
1794388decaSJoseph Chen 
180bb4a1f43SJoseph Chen 	AMP_I("Brought up cpu[%x] with state 0x%x, entry 0x%08x ...",
181bb4a1f43SJoseph Chen 	      cpu, pe_state, entry);
182bb4a1f43SJoseph Chen 
183257f4b84SJoseph Chen 	/* if target pe state is default arch state, power up cpu directly */
184bb4a1f43SJoseph Chen 	if (is_default_pe_state(pe_state))
185bb4a1f43SJoseph Chen 		goto finish;
186bb4a1f43SJoseph Chen 
187bb4a1f43SJoseph Chen 	ret = sip_smc_amp_cfg(AMP_PE_STATE, cpu, pe_state, 0);
188bb4a1f43SJoseph Chen 	if (ret) {
189bb4a1f43SJoseph Chen 		AMP_E("smc pe-state, ret=%d\n", ret);
190bb4a1f43SJoseph Chen 		return ret;
191bb4a1f43SJoseph Chen 	}
192bb4a1f43SJoseph Chen 
193257f4b84SJoseph Chen 	/* only linux needs boot args */
194257f4b84SJoseph Chen 	if (!is_linux)
195257f4b84SJoseph Chen 		goto finish;
196257f4b84SJoseph Chen 
197bb4a1f43SJoseph Chen 	ret = sip_smc_amp_cfg(AMP_BOOT_ARG01, cpu, args->arg0, args->arg1);
198bb4a1f43SJoseph Chen 	if (ret) {
199bb4a1f43SJoseph Chen 		AMP_E("smc boot arg01, ret=%d\n", ret);
200bb4a1f43SJoseph Chen 		return ret;
201bb4a1f43SJoseph Chen 	}
202bb4a1f43SJoseph Chen 
203bb4a1f43SJoseph Chen 	ret = sip_smc_amp_cfg(AMP_BOOT_ARG23, cpu, args->arg2, args->arg3);
204bb4a1f43SJoseph Chen 	if (ret) {
205bb4a1f43SJoseph Chen 		AMP_E("smc boot arg23, ret=%d\n", ret);
206bb4a1f43SJoseph Chen 		return ret;
207bb4a1f43SJoseph Chen 	}
208bb4a1f43SJoseph Chen 
209bb4a1f43SJoseph Chen finish:
210bb4a1f43SJoseph Chen 	ret = psci_cpu_on(cpu, entry);
211bb4a1f43SJoseph Chen 	if (ret) {
212bb4a1f43SJoseph Chen 		printf("cpu up failed, ret=%d\n", ret);
213bb4a1f43SJoseph Chen 		return ret;
214bb4a1f43SJoseph Chen 	}
215bb4a1f43SJoseph Chen 	printf("OK\n");
216bb4a1f43SJoseph Chen 
217bb4a1f43SJoseph Chen 	return 0;
218bb4a1f43SJoseph Chen }
219bb4a1f43SJoseph Chen 
fit_standalone_release(char * id,uintptr_t entry_point)220b8fb8531SJoseph Chen __weak int fit_standalone_release(char *id, uintptr_t entry_point)
221b8fb8531SJoseph Chen {
222b8fb8531SJoseph Chen 	return 0;
223b8fb8531SJoseph Chen }
224b8fb8531SJoseph Chen 
standalone_handler(const char * id,u32 entry_point,int data_size)225b8fb8531SJoseph Chen static int standalone_handler(const char *id, u32 entry_point, int data_size)
226b8fb8531SJoseph Chen {
227b8fb8531SJoseph Chen 	int ret;
228b8fb8531SJoseph Chen 
229b8fb8531SJoseph Chen 	if (!sysmem_alloc_base_by_name(id,
230b8fb8531SJoseph Chen 			(phys_addr_t)entry_point, data_size))
231b8fb8531SJoseph Chen 		return -ENXIO;
232b8fb8531SJoseph Chen 
233b8fb8531SJoseph Chen 	printf("Handle standalone: '%s' at 0x%08x ...", id, entry_point);
234b8fb8531SJoseph Chen 
235b8fb8531SJoseph Chen 	ret = fit_standalone_release((char *)id, entry_point);
236b8fb8531SJoseph Chen 	if (ret) {
237b8fb8531SJoseph Chen 		printf("failed, ret=%d\n", ret);
238b8fb8531SJoseph Chen 		return ret;
239b8fb8531SJoseph Chen 	}
240b8fb8531SJoseph Chen 	printf("OK\n");
241b8fb8531SJoseph Chen 
242b8fb8531SJoseph Chen 	return 0;
243b8fb8531SJoseph Chen }
244b8fb8531SJoseph Chen 
brought_up_amp(void * fit,int noffset,boot_cpu_t * bootcpu,int is_linux)245bb4a1f43SJoseph Chen static int brought_up_amp(void *fit, int noffset,
246bb4a1f43SJoseph Chen 			  boot_cpu_t *bootcpu, int is_linux)
247bb4a1f43SJoseph Chen {
248bb4a1f43SJoseph Chen 	const char *desc;
249bb4a1f43SJoseph Chen 	boot_args_t args;
250bb4a1f43SJoseph Chen 	u32 cpu, aarch64, hyp;
2511dc5141fSJoseph Chen 	u32 load, load_c, thumb, us;
252bb4a1f43SJoseph Chen 	u32 pe_state, entry;
253d184e484SJoseph Chen 	int boot_on;
254bb4a1f43SJoseph Chen 	int data_size;
255d184e484SJoseph Chen 	int i, ret;
256b8fb8531SJoseph Chen 	u8 type = -ENODATA;
257bb4a1f43SJoseph Chen 	u8 arch = -ENODATA;
2584388decaSJoseph Chen 
25931f8f6ebSJoseph Chen 	desc = fdt_getprop(fit, noffset, "description", NULL);
26031f8f6ebSJoseph Chen 	cpu = fit_get_u32_default(fit, noffset, "cpu", -ENODATA);
26131f8f6ebSJoseph Chen 	hyp = fit_get_u32_default(fit, noffset, "hyp", 0);
26231f8f6ebSJoseph Chen 	thumb = fit_get_u32_default(fit, noffset, "thumb", 0);
263b8fb8531SJoseph Chen 	entry = load = fit_get_u32_default(fit, noffset, "load", -ENODATA);
2641dc5141fSJoseph Chen 	load_c = fit_get_u32_default(fit, noffset, "load_c", -ENODATA);
26531f8f6ebSJoseph Chen 	us = fit_get_u32_default(fit, noffset, "udelay", 0);
266d184e484SJoseph Chen 	boot_on = fit_get_u32_default(fit, noffset, "boot-on", 1);
26731f8f6ebSJoseph Chen 	fit_image_get_arch(fit, noffset, &arch);
268b8fb8531SJoseph Chen 	fit_image_get_type(fit, noffset, &type);
26931f8f6ebSJoseph Chen 	fit_image_get_data_size(fit, noffset, &data_size);
270bb4a1f43SJoseph Chen 	memset(&args, 0, sizeof(args));
2714388decaSJoseph Chen 
272b8fb8531SJoseph Chen 	/* standalone is simple, just handle it and then exit. Allow failure */
273b8fb8531SJoseph Chen 	if (type == IH_TYPE_STANDALONE) {
274b8fb8531SJoseph Chen 		if (!desc || load == -ENODATA) {
275b8fb8531SJoseph Chen 			AMP_E("standalone: \"desc\" or \"load\" property missing!\n");
276b8fb8531SJoseph Chen 			goto exit;
277b8fb8531SJoseph Chen 		}
278b8fb8531SJoseph Chen 		standalone_handler(desc, load, data_size);
279b8fb8531SJoseph Chen 		goto exit;
280b8fb8531SJoseph Chen 	}
281b8fb8531SJoseph Chen 
282b8fb8531SJoseph Chen 	if (!desc || cpu == -ENODATA || arch == -ENODATA || type == -ENODATA ||
283bb4a1f43SJoseph Chen 	    (load == -ENODATA && !is_linux)) {
284bb4a1f43SJoseph Chen 		AMP_E("Property missing!\n");
28531f8f6ebSJoseph Chen 		return -EINVAL;
2864388decaSJoseph Chen 	}
2871dc5141fSJoseph Chen 
2881dc5141fSJoseph Chen 	if (is_linux) {
2891dc5141fSJoseph Chen 		if (load != -ENODATA)
2901dc5141fSJoseph Chen 			env_set_hex("kernel_addr_r", load);
2911dc5141fSJoseph Chen 		if (load_c != -ENODATA)
2921dc5141fSJoseph Chen 			env_set_hex("kernel_addr_c", load_c);
2931dc5141fSJoseph Chen 	}
2941dc5141fSJoseph Chen 
29531f8f6ebSJoseph Chen 	aarch64 = (arch == IH_ARCH_ARM) ? 0 : 1;
296bb4a1f43SJoseph Chen 	pe_state = PE_STATE(aarch64, hyp, thumb, 0);
29731f8f6ebSJoseph Chen 
29831f8f6ebSJoseph Chen #ifdef DEBUG
299bb4a1f43SJoseph Chen 	AMP_I("       desc: %s\n", desc);
30031f8f6ebSJoseph Chen 	AMP_I("        cpu: 0x%x\n", cpu);
30131f8f6ebSJoseph Chen 	AMP_I("    aarch64: %d\n", aarch64);
30231f8f6ebSJoseph Chen 	AMP_I("        hyp: %d\n", hyp);
30331f8f6ebSJoseph Chen 	AMP_I("      thumb: %d\n", thumb);
304b8fb8531SJoseph Chen 	AMP_I("       load: 0x%08x\n", load);
3051dc5141fSJoseph Chen 	AMP_I("     load_c: 0x%08x\n", load_c);
306bb4a1f43SJoseph Chen 	AMP_I("   pe_state: 0x%08x\n", pe_state);
307bb4a1f43SJoseph Chen 	AMP_I("   linux-os: %d\n\n", is_linux);
30831f8f6ebSJoseph Chen #endif
30931f8f6ebSJoseph Chen 
3104a758d95SJoseph Chen 	/* If os amp-dispatcher owns this cpu, don't boot it */
3114a758d95SJoseph Chen 	for (i = 0; i < ARRAY_SIZE(os_amp_dispatcher_cpu); i++) {
3124a758d95SJoseph Chen 		if (cpu == os_amp_dispatcher_cpu[i]) {
3134a758d95SJoseph Chen 			boot_on = 0;
3144a758d95SJoseph Chen 			break;
3154a758d95SJoseph Chen 		}
3164a758d95SJoseph Chen 	}
3174a758d95SJoseph Chen 
318d184e484SJoseph Chen 	/* this cpu is boot cpu ? */
3196aef53bdSJoseph Chen 	if ((read_mpidr() & 0x0fff) == cpu) {
320bb4a1f43SJoseph Chen 		bootcpu->arch = arch;
321bb4a1f43SJoseph Chen 		bootcpu->entry = entry;
322bb4a1f43SJoseph Chen 		bootcpu->state = pe_state;
323bb4a1f43SJoseph Chen 		bootcpu->linux_os = is_linux;
3244a758d95SJoseph Chen 		bootcpu->boot_on = boot_on;
325bb4a1f43SJoseph Chen 		return 0;
3266aef53bdSJoseph Chen 	}
3276aef53bdSJoseph Chen 
328bb4a1f43SJoseph Chen 	/* === only nonboot cpu can reach here === */
329bb4a1f43SJoseph Chen 
330bb4a1f43SJoseph Chen 	/* load or check */
331bb4a1f43SJoseph Chen 	if (is_linux) {
332bb4a1f43SJoseph Chen 		ret = load_linux_for_nonboot_cpu(cpu,
333bb4a1f43SJoseph Chen 				aarch64, load, &entry, &args);
334bb4a1f43SJoseph Chen 		if (ret)
335bb4a1f43SJoseph Chen 			return ret;
33649cfbee5SJoseph Chen 		/*
33749cfbee5SJoseph Chen 		 * Must setup before jump to linux.
33849cfbee5SJoseph Chen 		 * This is an appointment on RK amp solution to handle
33949cfbee5SJoseph Chen 		 * GIC configure competition.
34049cfbee5SJoseph Chen 		 */
34149cfbee5SJoseph Chen 		setup_sync_bits_for_linux();
342bb4a1f43SJoseph Chen 	} else {
343bb4a1f43SJoseph Chen 		if (!sysmem_alloc_base_by_name(desc,
344bb4a1f43SJoseph Chen 				(phys_addr_t)load, data_size))
34531f8f6ebSJoseph Chen 			return -ENXIO;
346f06413e4SJoseph Chen 	}
3474388decaSJoseph Chen 
348d184e484SJoseph Chen 	if (!boot_on)
349d184e484SJoseph Chen 		return 0;
350d184e484SJoseph Chen 
351d184e484SJoseph Chen 	/* boot now */
352257f4b84SJoseph Chen 	ret = smc_cpu_on(cpu, pe_state, entry, &args, is_linux);
353bb4a1f43SJoseph Chen 	if (ret)
35439be8a08SJoseph Chen 		return ret;
355b8fb8531SJoseph Chen exit:
35631f8f6ebSJoseph Chen 	if (us)
35731f8f6ebSJoseph Chen 		udelay(us);
358bb4a1f43SJoseph Chen 
359bb4a1f43SJoseph Chen 	return 0;
36039be8a08SJoseph Chen }
3614388decaSJoseph Chen 
brought_up_all_amp(void * fit,const char * fit_uname_cfg)362bb4a1f43SJoseph Chen static int brought_up_all_amp(void *fit, const char *fit_uname_cfg)
363bb4a1f43SJoseph Chen {
364bb4a1f43SJoseph Chen 	int loadables_index;
365bb4a1f43SJoseph Chen 	int linux_noffset;
366bb4a1f43SJoseph Chen 	int conf_noffset;
367bb4a1f43SJoseph Chen 	int cpu_noffset;
368bb4a1f43SJoseph Chen 	int ret;
369bb4a1f43SJoseph Chen 	const char *uname;
370bb4a1f43SJoseph Chen 
371bb4a1f43SJoseph Chen 	conf_noffset = fit_conf_get_node(fit, fit_uname_cfg);
372bb4a1f43SJoseph Chen 	if (conf_noffset < 0)
373bb4a1f43SJoseph Chen 		return conf_noffset;
374bb4a1f43SJoseph Chen 
375f214eec9SJoseph Chen 	/*
376f214eec9SJoseph Chen 	 * If boot cpu is not assigned in amp.img, the default value 0 makes
377f214eec9SJoseph Chen 	 * boot cpu power down itself in final process, so we must initial it.
378f214eec9SJoseph Chen 	 */
379f214eec9SJoseph Chen 	g_bootcpu.boot_on = 1;
380f214eec9SJoseph Chen 
381bb4a1f43SJoseph Chen 	linux_noffset = fdt_subnode_offset(fit, conf_noffset, "linux");
382bb4a1f43SJoseph Chen 	if (linux_noffset > 0) {
383bb4a1f43SJoseph Chen 		ret = brought_up_amp(fit, linux_noffset, &g_bootcpu, 1);
384bb4a1f43SJoseph Chen 		if (ret)
385bb4a1f43SJoseph Chen 			return ret;
386bb4a1f43SJoseph Chen 	}
387bb4a1f43SJoseph Chen 
388bb4a1f43SJoseph Chen 	for (loadables_index = 0;
389bb4a1f43SJoseph Chen 	     uname = fdt_stringlist_get(fit, conf_noffset,
390bb4a1f43SJoseph Chen 			FIT_LOADABLE_PROP, loadables_index, NULL), uname;
391bb4a1f43SJoseph Chen 	     loadables_index++) {
392bb4a1f43SJoseph Chen 		cpu_noffset = fit_image_get_node(fit, uname);
393bb4a1f43SJoseph Chen 		if (cpu_noffset < 0)
394bb4a1f43SJoseph Chen 			return cpu_noffset;
395bb4a1f43SJoseph Chen 
396bb4a1f43SJoseph Chen 		ret = brought_up_amp(fit, cpu_noffset, &g_bootcpu, 0);
397bb4a1f43SJoseph Chen 		if (ret)
398bb4a1f43SJoseph Chen 			return ret;
399bb4a1f43SJoseph Chen 	}
400bb4a1f43SJoseph Chen 
401bb4a1f43SJoseph Chen 	/* === only boot cpu can reach here === */
402bb4a1f43SJoseph Chen 
4034a758d95SJoseph Chen 	if (!g_bootcpu.boot_on) {
4044a758d95SJoseph Chen 		AMP_I("Primary cpu[%x, self] with state 0x%x, entry 0x%08x ... Power down!\n",
4054a758d95SJoseph Chen 		      (u32)read_mpidr() & 0x0fff, g_bootcpu.state, g_bootcpu.entry);
4064a758d95SJoseph Chen 		dsb();
4074a758d95SJoseph Chen 		isb();
4084a758d95SJoseph Chen 		/* make sure I am off before os amp-dispatcher probe */
4094a758d95SJoseph Chen 		psci_cpu_off(0);
4104a758d95SJoseph Chen 
4114a758d95SJoseph Chen 		/* Never reach here */
4124a758d95SJoseph Chen 		while (1) {
4134a758d95SJoseph Chen 			AMP_E("Primary cpu[%x, self] power down failed\n",
4144a758d95SJoseph Chen 			      (u32)read_mpidr());
4154a758d95SJoseph Chen 			__asm("wfe");
4164a758d95SJoseph Chen 		}
4174a758d95SJoseph Chen 	}
4184a758d95SJoseph Chen 
419b8fb8531SJoseph Chen 	if (!g_bootcpu.linux_os && g_bootcpu.entry) {
42031f8f6ebSJoseph Chen 		flush_dcache_all();
4214a758d95SJoseph Chen 		AMP_I("Brought up primary cpu[%x, self] with state 0x%x, entry 0x%08x ...",
422bb4a1f43SJoseph Chen 		      (u32)read_mpidr() & 0x0fff, g_bootcpu.state, g_bootcpu.entry);
42331f8f6ebSJoseph Chen 		cleanup_before_linux();
42431f8f6ebSJoseph Chen 		printf("OK\n");
425187bcfa0SJoseph Chen #ifdef CONFIG_ARM64
426*3792164fSJoseph Chen 		if (g_bootcpu.state & (1 << MODE_HYP_SHIFT))
427bb4a1f43SJoseph Chen 			armv8_switch_to_el2(0, 0, 0, g_bootcpu.state, (u64)g_bootcpu.entry,
428bb4a1f43SJoseph Chen 			     g_bootcpu.arch == IH_ARCH_ARM ? ES_TO_AARCH32 : ES_TO_AARCH64);
429*3792164fSJoseph Chen 		else
430*3792164fSJoseph Chen 			armv8_switch_to_el1(0, 0, 0, g_bootcpu.state, (u64)g_bootcpu.entry,
431*3792164fSJoseph Chen 			     g_bootcpu.arch == IH_ARCH_ARM ? ES_TO_AARCH32 : ES_TO_AARCH64);
432187bcfa0SJoseph Chen #else
433187bcfa0SJoseph Chen 		void (*armv7_entry)(void);
434187bcfa0SJoseph Chen 
435187bcfa0SJoseph Chen 		armv7_entry = (void (*)(void))g_bootcpu.entry;
436187bcfa0SJoseph Chen 		armv7_entry();
437187bcfa0SJoseph Chen #endif
4384388decaSJoseph Chen 	}
4394388decaSJoseph Chen 
440bb4a1f43SJoseph Chen 	/* return: boot cpu continue to boot linux */
441bb4a1f43SJoseph Chen 	return 0;
44231f8f6ebSJoseph Chen }
4434388decaSJoseph Chen 
amp_cpus_on(void)44431f8f6ebSJoseph Chen int amp_cpus_on(void)
4454388decaSJoseph Chen {
44631f8f6ebSJoseph Chen 	struct blk_desc *dev_desc;
44731f8f6ebSJoseph Chen 	bootm_headers_t images;
44831f8f6ebSJoseph Chen 	disk_partition_t part;
449043d91f2SJoseph Chen 	void *hdr, *fit;
450043d91f2SJoseph Chen 	int offset, cnt;
451043d91f2SJoseph Chen 	int totalsize;
45231f8f6ebSJoseph Chen 	int ret = 0;
45331f8f6ebSJoseph Chen 
45431f8f6ebSJoseph Chen 	dev_desc = rockchip_get_bootdev();
45531f8f6ebSJoseph Chen 	if (!dev_desc)
45631f8f6ebSJoseph Chen 		return -EIO;
45731f8f6ebSJoseph Chen 
45831f8f6ebSJoseph Chen 	if (part_get_info_by_name(dev_desc, AMP_PART, &part) < 0)
45931f8f6ebSJoseph Chen 		return -ENODEV;
46031f8f6ebSJoseph Chen 
461043d91f2SJoseph Chen 	hdr = memalign(ARCH_DMA_MINALIGN, FIT_HEADER_SIZE);
462043d91f2SJoseph Chen 	if (!hdr)
46331f8f6ebSJoseph Chen 		return -ENOMEM;
46431f8f6ebSJoseph Chen 
465043d91f2SJoseph Chen 	/* get totalsize */
466043d91f2SJoseph Chen 	offset = part.start;
467043d91f2SJoseph Chen 	cnt = DIV_ROUND_UP(FIT_HEADER_SIZE, part.blksz);
468043d91f2SJoseph Chen 	if (blk_dread(dev_desc, offset, cnt, hdr) != cnt) {
46931f8f6ebSJoseph Chen 		ret = -EIO;
470043d91f2SJoseph Chen 		goto out2;
4714388decaSJoseph Chen 	}
4724388decaSJoseph Chen 
473043d91f2SJoseph Chen 	if (fdt_check_header(hdr)) {
474bb4a1f43SJoseph Chen 		AMP_E("Not fit\n");
47531f8f6ebSJoseph Chen 		ret = -EINVAL;
476043d91f2SJoseph Chen 		goto out2;
477043d91f2SJoseph Chen 	}
478043d91f2SJoseph Chen 
479043d91f2SJoseph Chen 	if (fit_get_totalsize(hdr, &totalsize)) {
480043d91f2SJoseph Chen 		AMP_E("No totalsize\n");
481043d91f2SJoseph Chen 		ret = -EINVAL;
482043d91f2SJoseph Chen 		goto out2;
483043d91f2SJoseph Chen 	}
484043d91f2SJoseph Chen 
485043d91f2SJoseph Chen 	/* load image */
486043d91f2SJoseph Chen 	fit = memalign(ARCH_DMA_MINALIGN, ALIGN(totalsize, part.blksz));
487043d91f2SJoseph Chen 	if (!fit) {
488043d91f2SJoseph Chen 		printf("No memory\n");
489043d91f2SJoseph Chen 		ret = -ENOMEM;
490043d91f2SJoseph Chen 		goto out2;
491043d91f2SJoseph Chen 	}
492043d91f2SJoseph Chen 
493043d91f2SJoseph Chen 	memcpy(fit, hdr, FIT_HEADER_SIZE);
494043d91f2SJoseph Chen 
495043d91f2SJoseph Chen 	offset += cnt;
496043d91f2SJoseph Chen 	cnt = DIV_ROUND_UP(totalsize, part.blksz) - cnt;
497043d91f2SJoseph Chen 	if (blk_dread(dev_desc, offset, cnt, fit + FIT_HEADER_SIZE) != cnt) {
498043d91f2SJoseph Chen 		ret = -EIO;
499043d91f2SJoseph Chen 		goto out1;
50031f8f6ebSJoseph Chen 	}
5014388decaSJoseph Chen 
5024a758d95SJoseph Chen 	ret = parse_os_amp_dispatcher();
5034a758d95SJoseph Chen 	if (ret < 0) {
5044a758d95SJoseph Chen 		ret = -EINVAL;
5054a758d95SJoseph Chen 		goto out1;
5064a758d95SJoseph Chen 	}
507d184e484SJoseph Chen 
508bb4a1f43SJoseph Chen 	/* Load loadables */
50931f8f6ebSJoseph Chen 	memset(&images, 0, sizeof(images));
51031f8f6ebSJoseph Chen 	images.fit_uname_cfg = "conf";
51131f8f6ebSJoseph Chen 	images.fit_hdr_os = fit;
51231f8f6ebSJoseph Chen 	images.verify = 1;
513bb4a1f43SJoseph Chen 	ret = boot_get_loadable(0, NULL, &images, IH_ARCH_DEFAULT, NULL, NULL);
51431f8f6ebSJoseph Chen 	if (ret) {
515bb4a1f43SJoseph Chen 		AMP_E("Load loadables, ret=%d\n", ret);
516043d91f2SJoseph Chen 		goto out1;
51731f8f6ebSJoseph Chen 	}
51831f8f6ebSJoseph Chen 	flush_dcache_all();
51931f8f6ebSJoseph Chen 
520bb4a1f43SJoseph Chen 	/* Wakeup */
52131f8f6ebSJoseph Chen 	ret = brought_up_all_amp(images.fit_hdr_os, images.fit_uname_cfg);
522bb4a1f43SJoseph Chen 	if (ret)
523bb4a1f43SJoseph Chen 		AMP_E("Brought up amps, ret=%d\n", ret);
524043d91f2SJoseph Chen out1:
525bb4a1f43SJoseph Chen 	free(fit);
526043d91f2SJoseph Chen out2:
527043d91f2SJoseph Chen 	free(hdr);
52831f8f6ebSJoseph Chen 
52931f8f6ebSJoseph Chen 	return ret;
53031f8f6ebSJoseph Chen }
53131f8f6ebSJoseph Chen 
arm64_switch_amp_pe(bootm_headers_t * images)5328223aa47SJoseph Chen int arm64_switch_amp_pe(bootm_headers_t *images)
53331f8f6ebSJoseph Chen {
534bb4a1f43SJoseph Chen 	images->os.arch = g_bootcpu.arch;
535bb4a1f43SJoseph Chen 	return g_bootcpu.state;
53631f8f6ebSJoseph Chen }
53731f8f6ebSJoseph Chen 
538