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