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 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 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 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 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 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 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 220b8fb8531SJoseph Chen __weak int fit_standalone_release(char *id, uintptr_t entry_point) 221b8fb8531SJoseph Chen { 222b8fb8531SJoseph Chen return 0; 223b8fb8531SJoseph Chen } 224b8fb8531SJoseph Chen 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 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; 251*1dc5141fSJoseph 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); 264*1dc5141fSJoseph 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 } 287*1dc5141fSJoseph Chen 288*1dc5141fSJoseph Chen if (is_linux) { 289*1dc5141fSJoseph Chen if (load != -ENODATA) 290*1dc5141fSJoseph Chen env_set_hex("kernel_addr_r", load); 291*1dc5141fSJoseph Chen if (load_c != -ENODATA) 292*1dc5141fSJoseph Chen env_set_hex("kernel_addr_c", load_c); 293*1dc5141fSJoseph Chen } 294*1dc5141fSJoseph 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); 305*1dc5141fSJoseph 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 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 426bb4a1f43SJoseph Chen armv8_switch_to_el2(0, 0, 0, g_bootcpu.state, (u64)g_bootcpu.entry, 427bb4a1f43SJoseph Chen g_bootcpu.arch == IH_ARCH_ARM ? ES_TO_AARCH32 : ES_TO_AARCH64); 428187bcfa0SJoseph Chen #else 429187bcfa0SJoseph Chen void (*armv7_entry)(void); 430187bcfa0SJoseph Chen 431187bcfa0SJoseph Chen armv7_entry = (void (*)(void))g_bootcpu.entry; 432187bcfa0SJoseph Chen armv7_entry(); 433187bcfa0SJoseph Chen #endif 4344388decaSJoseph Chen } 4354388decaSJoseph Chen 436bb4a1f43SJoseph Chen /* return: boot cpu continue to boot linux */ 437bb4a1f43SJoseph Chen return 0; 43831f8f6ebSJoseph Chen } 4394388decaSJoseph Chen 44031f8f6ebSJoseph Chen int amp_cpus_on(void) 4414388decaSJoseph Chen { 44231f8f6ebSJoseph Chen struct blk_desc *dev_desc; 44331f8f6ebSJoseph Chen bootm_headers_t images; 44431f8f6ebSJoseph Chen disk_partition_t part; 445043d91f2SJoseph Chen void *hdr, *fit; 446043d91f2SJoseph Chen int offset, cnt; 447043d91f2SJoseph Chen int totalsize; 44831f8f6ebSJoseph Chen int ret = 0; 44931f8f6ebSJoseph Chen 45031f8f6ebSJoseph Chen dev_desc = rockchip_get_bootdev(); 45131f8f6ebSJoseph Chen if (!dev_desc) 45231f8f6ebSJoseph Chen return -EIO; 45331f8f6ebSJoseph Chen 45431f8f6ebSJoseph Chen if (part_get_info_by_name(dev_desc, AMP_PART, &part) < 0) 45531f8f6ebSJoseph Chen return -ENODEV; 45631f8f6ebSJoseph Chen 457043d91f2SJoseph Chen hdr = memalign(ARCH_DMA_MINALIGN, FIT_HEADER_SIZE); 458043d91f2SJoseph Chen if (!hdr) 45931f8f6ebSJoseph Chen return -ENOMEM; 46031f8f6ebSJoseph Chen 461043d91f2SJoseph Chen /* get totalsize */ 462043d91f2SJoseph Chen offset = part.start; 463043d91f2SJoseph Chen cnt = DIV_ROUND_UP(FIT_HEADER_SIZE, part.blksz); 464043d91f2SJoseph Chen if (blk_dread(dev_desc, offset, cnt, hdr) != cnt) { 46531f8f6ebSJoseph Chen ret = -EIO; 466043d91f2SJoseph Chen goto out2; 4674388decaSJoseph Chen } 4684388decaSJoseph Chen 469043d91f2SJoseph Chen if (fdt_check_header(hdr)) { 470bb4a1f43SJoseph Chen AMP_E("Not fit\n"); 47131f8f6ebSJoseph Chen ret = -EINVAL; 472043d91f2SJoseph Chen goto out2; 473043d91f2SJoseph Chen } 474043d91f2SJoseph Chen 475043d91f2SJoseph Chen if (fit_get_totalsize(hdr, &totalsize)) { 476043d91f2SJoseph Chen AMP_E("No totalsize\n"); 477043d91f2SJoseph Chen ret = -EINVAL; 478043d91f2SJoseph Chen goto out2; 479043d91f2SJoseph Chen } 480043d91f2SJoseph Chen 481043d91f2SJoseph Chen /* load image */ 482043d91f2SJoseph Chen fit = memalign(ARCH_DMA_MINALIGN, ALIGN(totalsize, part.blksz)); 483043d91f2SJoseph Chen if (!fit) { 484043d91f2SJoseph Chen printf("No memory\n"); 485043d91f2SJoseph Chen ret = -ENOMEM; 486043d91f2SJoseph Chen goto out2; 487043d91f2SJoseph Chen } 488043d91f2SJoseph Chen 489043d91f2SJoseph Chen memcpy(fit, hdr, FIT_HEADER_SIZE); 490043d91f2SJoseph Chen 491043d91f2SJoseph Chen offset += cnt; 492043d91f2SJoseph Chen cnt = DIV_ROUND_UP(totalsize, part.blksz) - cnt; 493043d91f2SJoseph Chen if (blk_dread(dev_desc, offset, cnt, fit + FIT_HEADER_SIZE) != cnt) { 494043d91f2SJoseph Chen ret = -EIO; 495043d91f2SJoseph Chen goto out1; 49631f8f6ebSJoseph Chen } 4974388decaSJoseph Chen 4984a758d95SJoseph Chen ret = parse_os_amp_dispatcher(); 4994a758d95SJoseph Chen if (ret < 0) { 5004a758d95SJoseph Chen ret = -EINVAL; 5014a758d95SJoseph Chen goto out1; 5024a758d95SJoseph Chen } 503d184e484SJoseph Chen 504bb4a1f43SJoseph Chen /* Load loadables */ 50531f8f6ebSJoseph Chen memset(&images, 0, sizeof(images)); 50631f8f6ebSJoseph Chen images.fit_uname_cfg = "conf"; 50731f8f6ebSJoseph Chen images.fit_hdr_os = fit; 50831f8f6ebSJoseph Chen images.verify = 1; 509bb4a1f43SJoseph Chen ret = boot_get_loadable(0, NULL, &images, IH_ARCH_DEFAULT, NULL, NULL); 51031f8f6ebSJoseph Chen if (ret) { 511bb4a1f43SJoseph Chen AMP_E("Load loadables, ret=%d\n", ret); 512043d91f2SJoseph Chen goto out1; 51331f8f6ebSJoseph Chen } 51431f8f6ebSJoseph Chen flush_dcache_all(); 51531f8f6ebSJoseph Chen 516bb4a1f43SJoseph Chen /* Wakeup */ 51731f8f6ebSJoseph Chen ret = brought_up_all_amp(images.fit_hdr_os, images.fit_uname_cfg); 518bb4a1f43SJoseph Chen if (ret) 519bb4a1f43SJoseph Chen AMP_E("Brought up amps, ret=%d\n", ret); 520043d91f2SJoseph Chen out1: 521bb4a1f43SJoseph Chen free(fit); 522043d91f2SJoseph Chen out2: 523043d91f2SJoseph Chen free(hdr); 52431f8f6ebSJoseph Chen 52531f8f6ebSJoseph Chen return ret; 52631f8f6ebSJoseph Chen } 52731f8f6ebSJoseph Chen 5288223aa47SJoseph Chen int arm64_switch_amp_pe(bootm_headers_t *images) 52931f8f6ebSJoseph Chen { 530bb4a1f43SJoseph Chen images->os.arch = g_bootcpu.arch; 531bb4a1f43SJoseph Chen return g_bootcpu.state; 53231f8f6ebSJoseph Chen } 53331f8f6ebSJoseph Chen 534