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> 10*49cfbee5SJoseph Chen #include <config.h> 114388decaSJoseph Chen #include <sysmem.h> 12*49cfbee5SJoseph Chen #include <asm/gic.h> 13*49cfbee5SJoseph Chen #include <asm/io.h> 144388decaSJoseph Chen #include <asm/arch/rockchip_smccc.h> 154388decaSJoseph Chen 16bb4a1f43SJoseph Chen /* 17bb4a1f43SJoseph Chen * [Design Principles] 18bb4a1f43SJoseph Chen * 19bb4a1f43SJoseph Chen * [amp.img] 20bb4a1f43SJoseph Chen * The amp image with FIT format which consists of non-linux firmwares. 21bb4a1f43SJoseph Chen * Please refer to: driver/cpu/amp.its. 22bb4a1f43SJoseph Chen * 23bb4a1f43SJoseph Chen * amp.img generation: ./tools/mkimage -f drivers/cpu/amp.its -E -p 0xe00 amp.img 24bb4a1f43SJoseph Chen * 25bb4a1f43SJoseph Chen * [linux] 26bb4a1f43SJoseph Chen * We still use the traditional solution for a better compatibility: 27bb4a1f43SJoseph Chen * boot.img/recovery.img with FIT format or Android format. 28bb4a1f43SJoseph Chen * 29bb4a1f43SJoseph Chen * The developer need add "/configurations/conf/linux" node to configure: 30bb4a1f43SJoseph Chen * description, arch, cpu, thumb, hyp, udelay(optional) properties. 31bb4a1f43SJoseph Chen * The addresses depend on U-Boot: kernel_addr_r, fdt_addr_r and 32bb4a1f43SJoseph Chen * ramdisk_addr_r. Please refer to: driver/cpu/amp.its. 33bb4a1f43SJoseph Chen * 34bb4a1f43SJoseph Chen * [memory management] 35bb4a1f43SJoseph Chen * U-Boot is not responsible for memory distribution/fixup any more, please 36bb4a1f43SJoseph Chen * handle this on kernel dts "/memory". 37bb4a1f43SJoseph Chen * 38bb4a1f43SJoseph Chen * [trust] 39bb4a1f43SJoseph Chen * The AMP feature requires trust support. 40bb4a1f43SJoseph Chen */ 41bb4a1f43SJoseph Chen 4231f8f6ebSJoseph Chen #define AMP_PART "amp" 43*49cfbee5SJoseph Chen #define gicd_readl(offset) readl((void *)GICD_BASE + (offset)) 44*49cfbee5SJoseph Chen #define gicd_writel(v, offset) writel(v, (void *)GICD_BASE + (offset)) 454388decaSJoseph Chen 46bb4a1f43SJoseph Chen typedef struct boot_args { 47bb4a1f43SJoseph Chen ulong arg0; 48bb4a1f43SJoseph Chen ulong arg1; 49bb4a1f43SJoseph Chen ulong arg2; 50bb4a1f43SJoseph Chen ulong arg3; 51bb4a1f43SJoseph Chen } boot_args_t; 52bb4a1f43SJoseph Chen 53bb4a1f43SJoseph Chen typedef struct boot_cpu { 54bb4a1f43SJoseph Chen u32 arch; 55bb4a1f43SJoseph Chen u32 state; 56bb4a1f43SJoseph Chen u32 entry; 57bb4a1f43SJoseph Chen u32 linux_os; 58bb4a1f43SJoseph Chen } boot_cpu_t; 59bb4a1f43SJoseph Chen 60bb4a1f43SJoseph Chen static boot_cpu_t g_bootcpu; 6131f8f6ebSJoseph Chen 6231f8f6ebSJoseph Chen static u32 fit_get_u32_default(const void *fit, int noffset, 6331f8f6ebSJoseph Chen const char *prop, u32 def) 640df2c3dfSJoseph Chen { 6531f8f6ebSJoseph Chen const fdt32_t *val; 660df2c3dfSJoseph Chen 6731f8f6ebSJoseph Chen val = fdt_getprop(fit, noffset, prop, NULL); 6831f8f6ebSJoseph Chen if (!val) 6931f8f6ebSJoseph Chen return def; 700df2c3dfSJoseph Chen 7131f8f6ebSJoseph Chen return fdt32_to_cpu(*val); 720df2c3dfSJoseph Chen } 730df2c3dfSJoseph Chen 74bb4a1f43SJoseph Chen static int load_linux_for_nonboot_cpu(u32 cpu, u32 aarch64, u32 load, 75bb4a1f43SJoseph Chen u32 *entry, boot_args_t *args) 764388decaSJoseph Chen { 77bb4a1f43SJoseph Chen static const char *boot_cmd[] = { 78bb4a1f43SJoseph Chen "boot_fit", "boot_android ${devtype} ${devnum}" }; 79bb4a1f43SJoseph Chen int i, ret; 80bb4a1f43SJoseph Chen 81bb4a1f43SJoseph Chen env_set_hex("bootm_states_unmask", BOOTM_STATE_OS_GO); 82bb4a1f43SJoseph Chen for (i = 0; i < ARRAY_SIZE(boot_cmd); i++) { 83bb4a1f43SJoseph Chen ret = run_command(boot_cmd[i], 0); 84bb4a1f43SJoseph Chen if (!ret) 85bb4a1f43SJoseph Chen break; 86bb4a1f43SJoseph Chen } 87bb4a1f43SJoseph Chen env_set("bootm_states_unmask", NULL); 88bb4a1f43SJoseph Chen if (ret) { 89bb4a1f43SJoseph Chen AMP_E("Load linux failed, ret=%d\n", ret); 90bb4a1f43SJoseph Chen return ret; 91bb4a1f43SJoseph Chen } 92bb4a1f43SJoseph Chen 93bb4a1f43SJoseph Chen /* linux boot args */ 94bb4a1f43SJoseph Chen if (aarch64) { 95bb4a1f43SJoseph Chen args->arg0 = (ulong)gd->fdt_blob; 96bb4a1f43SJoseph Chen args->arg1 = 0; 97bb4a1f43SJoseph Chen args->arg2 = 0; 98bb4a1f43SJoseph Chen } else { 99bb4a1f43SJoseph Chen args->arg0 = 0; 100bb4a1f43SJoseph Chen args->arg1 = 0; 101bb4a1f43SJoseph Chen args->arg2 = (ulong)gd->fdt_blob; 102bb4a1f43SJoseph Chen } 103bb4a1f43SJoseph Chen 104bb4a1f43SJoseph Chen /* don't need call cleanup_before_linux() as this nonboot cpu is clean */ 105bb4a1f43SJoseph Chen board_quiesce_devices(&images); 106bb4a1f43SJoseph Chen flush_dcache_all(); 107bb4a1f43SJoseph Chen 108bb4a1f43SJoseph Chen /* fixup: ramdisk/fdt/entry depend on U-Boot */ 109bb4a1f43SJoseph Chen *entry = env_get_ulong("kernel_addr_r", 16, 0); 110bb4a1f43SJoseph Chen 111bb4a1f43SJoseph Chen return 0; 112bb4a1f43SJoseph Chen } 113bb4a1f43SJoseph Chen 114bb4a1f43SJoseph Chen static int is_default_pe_state(u32 pe_state) 115bb4a1f43SJoseph Chen { 116bb4a1f43SJoseph Chen #ifdef CONFIG_ARM64 117bb4a1f43SJoseph Chen return (pe_state == PE_STATE(1, 1, 0, 0)); 118bb4a1f43SJoseph Chen #else 119bb4a1f43SJoseph Chen return (pe_state == PE_STATE(0, 0, 0, 0)); 120bb4a1f43SJoseph Chen #endif 121bb4a1f43SJoseph Chen } 122bb4a1f43SJoseph Chen 123*49cfbee5SJoseph Chen static void setup_sync_bits_for_linux(void) 124*49cfbee5SJoseph Chen { 125*49cfbee5SJoseph Chen u32 val, num_irq, offset; 126*49cfbee5SJoseph Chen 127*49cfbee5SJoseph Chen val = gicd_readl(GICD_CTLR); 128*49cfbee5SJoseph Chen val &= ~0x3; 129*49cfbee5SJoseph Chen gicd_writel(val, GICD_CTLR); 130*49cfbee5SJoseph Chen 131*49cfbee5SJoseph Chen num_irq = 32 * ((gicd_readl(GICD_TYPER) & 0x1F) + 1); 132*49cfbee5SJoseph Chen offset = ((num_irq - 1) / 4) * 4; 133*49cfbee5SJoseph Chen gicd_writel(0x0, GICD_IPRIORITYRn + offset); 134*49cfbee5SJoseph Chen } 135*49cfbee5SJoseph Chen 136bb4a1f43SJoseph Chen static int smc_cpu_on(u32 cpu, u32 pe_state, u32 entry, boot_args_t *args) 137bb4a1f43SJoseph Chen { 13831f8f6ebSJoseph Chen int ret; 1394388decaSJoseph Chen 140bb4a1f43SJoseph Chen AMP_I("Brought up cpu[%x] with state 0x%x, entry 0x%08x ...", 141bb4a1f43SJoseph Chen cpu, pe_state, entry); 142bb4a1f43SJoseph Chen 143bb4a1f43SJoseph Chen if (is_default_pe_state(pe_state)) 144bb4a1f43SJoseph Chen goto finish; 145bb4a1f43SJoseph Chen 146bb4a1f43SJoseph Chen ret = sip_smc_amp_cfg(AMP_PE_STATE, cpu, pe_state, 0); 147bb4a1f43SJoseph Chen if (ret) { 148bb4a1f43SJoseph Chen AMP_E("smc pe-state, ret=%d\n", ret); 149bb4a1f43SJoseph Chen return ret; 150bb4a1f43SJoseph Chen } 151bb4a1f43SJoseph Chen 152bb4a1f43SJoseph Chen ret = sip_smc_amp_cfg(AMP_BOOT_ARG01, cpu, args->arg0, args->arg1); 153bb4a1f43SJoseph Chen if (ret) { 154bb4a1f43SJoseph Chen AMP_E("smc boot arg01, ret=%d\n", ret); 155bb4a1f43SJoseph Chen return ret; 156bb4a1f43SJoseph Chen } 157bb4a1f43SJoseph Chen 158bb4a1f43SJoseph Chen ret = sip_smc_amp_cfg(AMP_BOOT_ARG23, cpu, args->arg2, args->arg3); 159bb4a1f43SJoseph Chen if (ret) { 160bb4a1f43SJoseph Chen AMP_E("smc boot arg23, ret=%d\n", ret); 161bb4a1f43SJoseph Chen return ret; 162bb4a1f43SJoseph Chen } 163bb4a1f43SJoseph Chen 164bb4a1f43SJoseph Chen finish: 165bb4a1f43SJoseph Chen ret = psci_cpu_on(cpu, entry); 166bb4a1f43SJoseph Chen if (ret) { 167bb4a1f43SJoseph Chen printf("cpu up failed, ret=%d\n", ret); 168bb4a1f43SJoseph Chen return ret; 169bb4a1f43SJoseph Chen } 170bb4a1f43SJoseph Chen printf("OK\n"); 171bb4a1f43SJoseph Chen 172bb4a1f43SJoseph Chen return 0; 173bb4a1f43SJoseph Chen } 174bb4a1f43SJoseph Chen 175bb4a1f43SJoseph Chen static int brought_up_amp(void *fit, int noffset, 176bb4a1f43SJoseph Chen boot_cpu_t *bootcpu, int is_linux) 177bb4a1f43SJoseph Chen { 178bb4a1f43SJoseph Chen const char *desc; 179bb4a1f43SJoseph Chen boot_args_t args; 180bb4a1f43SJoseph Chen u32 cpu, aarch64, hyp; 181bb4a1f43SJoseph Chen u32 load, thumb, us; 182bb4a1f43SJoseph Chen u32 pe_state, entry; 183bb4a1f43SJoseph Chen int data_size; 184bb4a1f43SJoseph Chen int ret; 185bb4a1f43SJoseph Chen u8 arch = -ENODATA; 1864388decaSJoseph Chen 18731f8f6ebSJoseph Chen desc = fdt_getprop(fit, noffset, "description", NULL); 18831f8f6ebSJoseph Chen cpu = fit_get_u32_default(fit, noffset, "cpu", -ENODATA); 18931f8f6ebSJoseph Chen hyp = fit_get_u32_default(fit, noffset, "hyp", 0); 19031f8f6ebSJoseph Chen thumb = fit_get_u32_default(fit, noffset, "thumb", 0); 19131f8f6ebSJoseph Chen load = fit_get_u32_default(fit, noffset, "load", -ENODATA); 19231f8f6ebSJoseph Chen us = fit_get_u32_default(fit, noffset, "udelay", 0); 19331f8f6ebSJoseph Chen fit_image_get_arch(fit, noffset, &arch); 19431f8f6ebSJoseph Chen fit_image_get_data_size(fit, noffset, &data_size); 195bb4a1f43SJoseph Chen memset(&args, 0, sizeof(args)); 1964388decaSJoseph Chen 197bb4a1f43SJoseph Chen if (!desc || cpu == -ENODATA || arch == -ENODATA || 198bb4a1f43SJoseph Chen (load == -ENODATA && !is_linux)) { 199bb4a1f43SJoseph Chen AMP_E("Property missing!\n"); 20031f8f6ebSJoseph Chen return -EINVAL; 2014388decaSJoseph Chen } 20231f8f6ebSJoseph Chen aarch64 = (arch == IH_ARCH_ARM) ? 0 : 1; 203bb4a1f43SJoseph Chen pe_state = PE_STATE(aarch64, hyp, thumb, 0); 204bb4a1f43SJoseph Chen entry = load; 20531f8f6ebSJoseph Chen 20631f8f6ebSJoseph Chen #ifdef DEBUG 207bb4a1f43SJoseph Chen AMP_I(" desc: %s\n", desc); 20831f8f6ebSJoseph Chen AMP_I(" cpu: 0x%x\n", cpu); 20931f8f6ebSJoseph Chen AMP_I(" aarch64: %d\n", aarch64); 21031f8f6ebSJoseph Chen AMP_I(" hyp: %d\n", hyp); 21131f8f6ebSJoseph Chen AMP_I(" thumb: %d\n", thumb); 21231f8f6ebSJoseph Chen AMP_I(" entry: 0x%08x\n", entry); 213bb4a1f43SJoseph Chen AMP_I(" pe_state: 0x%08x\n", pe_state); 214bb4a1f43SJoseph Chen AMP_I(" linux-os: %d\n\n", is_linux); 21531f8f6ebSJoseph Chen #endif 21631f8f6ebSJoseph Chen 2176aef53bdSJoseph Chen if ((read_mpidr() & 0x0fff) == cpu) { 218bb4a1f43SJoseph Chen bootcpu->arch = arch; 219bb4a1f43SJoseph Chen bootcpu->entry = entry; 220bb4a1f43SJoseph Chen bootcpu->state = pe_state; 221bb4a1f43SJoseph Chen bootcpu->linux_os = is_linux; 222bb4a1f43SJoseph Chen return 0; 2236aef53bdSJoseph Chen } 2246aef53bdSJoseph Chen 225bb4a1f43SJoseph Chen /* === only nonboot cpu can reach here === */ 226bb4a1f43SJoseph Chen 227bb4a1f43SJoseph Chen /* load or check */ 228bb4a1f43SJoseph Chen if (is_linux) { 229bb4a1f43SJoseph Chen ret = load_linux_for_nonboot_cpu(cpu, 230bb4a1f43SJoseph Chen aarch64, load, &entry, &args); 231bb4a1f43SJoseph Chen if (ret) 232bb4a1f43SJoseph Chen return ret; 233*49cfbee5SJoseph Chen /* 234*49cfbee5SJoseph Chen * Must setup before jump to linux. 235*49cfbee5SJoseph Chen * This is an appointment on RK amp solution to handle 236*49cfbee5SJoseph Chen * GIC configure competition. 237*49cfbee5SJoseph Chen */ 238*49cfbee5SJoseph Chen setup_sync_bits_for_linux(); 239bb4a1f43SJoseph Chen } else { 240bb4a1f43SJoseph Chen if (!sysmem_alloc_base_by_name(desc, 241bb4a1f43SJoseph Chen (phys_addr_t)load, data_size)) 24231f8f6ebSJoseph Chen return -ENXIO; 243f06413e4SJoseph Chen } 2444388decaSJoseph Chen 245bb4a1f43SJoseph Chen /* wakeup */ 246bb4a1f43SJoseph Chen ret = smc_cpu_on(cpu, pe_state, entry, &args); 247bb4a1f43SJoseph Chen if (ret) 24839be8a08SJoseph Chen return ret; 24931f8f6ebSJoseph Chen 25031f8f6ebSJoseph Chen if (us) 25131f8f6ebSJoseph Chen udelay(us); 252bb4a1f43SJoseph Chen 253bb4a1f43SJoseph Chen return 0; 25439be8a08SJoseph Chen } 2554388decaSJoseph Chen 256bb4a1f43SJoseph Chen static int brought_up_all_amp(void *fit, const char *fit_uname_cfg) 257bb4a1f43SJoseph Chen { 258bb4a1f43SJoseph Chen int loadables_index; 259bb4a1f43SJoseph Chen int linux_noffset; 260bb4a1f43SJoseph Chen int conf_noffset; 261bb4a1f43SJoseph Chen int cpu_noffset; 262bb4a1f43SJoseph Chen int ret; 263bb4a1f43SJoseph Chen const char *uname; 264bb4a1f43SJoseph Chen 265bb4a1f43SJoseph Chen conf_noffset = fit_conf_get_node(fit, fit_uname_cfg); 266bb4a1f43SJoseph Chen if (conf_noffset < 0) 267bb4a1f43SJoseph Chen return conf_noffset; 268bb4a1f43SJoseph Chen 269bb4a1f43SJoseph Chen linux_noffset = fdt_subnode_offset(fit, conf_noffset, "linux"); 270bb4a1f43SJoseph Chen if (linux_noffset > 0) { 271bb4a1f43SJoseph Chen ret = brought_up_amp(fit, linux_noffset, &g_bootcpu, 1); 272bb4a1f43SJoseph Chen if (ret) 273bb4a1f43SJoseph Chen return ret; 274bb4a1f43SJoseph Chen } 275bb4a1f43SJoseph Chen 276bb4a1f43SJoseph Chen for (loadables_index = 0; 277bb4a1f43SJoseph Chen uname = fdt_stringlist_get(fit, conf_noffset, 278bb4a1f43SJoseph Chen FIT_LOADABLE_PROP, loadables_index, NULL), uname; 279bb4a1f43SJoseph Chen loadables_index++) { 280bb4a1f43SJoseph Chen cpu_noffset = fit_image_get_node(fit, uname); 281bb4a1f43SJoseph Chen if (cpu_noffset < 0) 282bb4a1f43SJoseph Chen return cpu_noffset; 283bb4a1f43SJoseph Chen 284bb4a1f43SJoseph Chen ret = brought_up_amp(fit, cpu_noffset, &g_bootcpu, 0); 285bb4a1f43SJoseph Chen if (ret) 286bb4a1f43SJoseph Chen return ret; 287bb4a1f43SJoseph Chen } 288bb4a1f43SJoseph Chen 289bb4a1f43SJoseph Chen /* === only boot cpu can reach here === */ 290bb4a1f43SJoseph Chen 291bb4a1f43SJoseph Chen if (!g_bootcpu.linux_os) { 29231f8f6ebSJoseph Chen flush_dcache_all(); 29331f8f6ebSJoseph Chen AMP_I("Brought up cpu[%x, self] with state 0x%x, entry 0x%08x ...", 294bb4a1f43SJoseph Chen (u32)read_mpidr() & 0x0fff, g_bootcpu.state, g_bootcpu.entry); 29531f8f6ebSJoseph Chen cleanup_before_linux(); 29631f8f6ebSJoseph Chen printf("OK\n"); 297bb4a1f43SJoseph Chen armv8_switch_to_el2(0, 0, 0, g_bootcpu.state, (u64)g_bootcpu.entry, 298bb4a1f43SJoseph Chen g_bootcpu.arch == IH_ARCH_ARM ? ES_TO_AARCH32 : ES_TO_AARCH64); 2994388decaSJoseph Chen } 3004388decaSJoseph Chen 301bb4a1f43SJoseph Chen /* return: boot cpu continue to boot linux */ 302bb4a1f43SJoseph Chen return 0; 30331f8f6ebSJoseph Chen } 3044388decaSJoseph Chen 30531f8f6ebSJoseph Chen int amp_cpus_on(void) 3064388decaSJoseph Chen { 30731f8f6ebSJoseph Chen struct blk_desc *dev_desc; 30831f8f6ebSJoseph Chen bootm_headers_t images; 30931f8f6ebSJoseph Chen disk_partition_t part; 31031f8f6ebSJoseph Chen void *fit; 31131f8f6ebSJoseph Chen int ret = 0; 31231f8f6ebSJoseph Chen 31331f8f6ebSJoseph Chen dev_desc = rockchip_get_bootdev(); 31431f8f6ebSJoseph Chen if (!dev_desc) 31531f8f6ebSJoseph Chen return -EIO; 31631f8f6ebSJoseph Chen 31731f8f6ebSJoseph Chen if (part_get_info_by_name(dev_desc, AMP_PART, &part) < 0) 31831f8f6ebSJoseph Chen return -ENODEV; 31931f8f6ebSJoseph Chen 320bb4a1f43SJoseph Chen fit = malloc(part.size * part.blksz); 321bb4a1f43SJoseph Chen if (!fit) { 322bb4a1f43SJoseph Chen AMP_E("No memory, please increase CONFIG_SYS_MALLOC_LEN\n"); 32331f8f6ebSJoseph Chen return -ENOMEM; 324bb4a1f43SJoseph Chen } 32531f8f6ebSJoseph Chen 32631f8f6ebSJoseph Chen if (blk_dread(dev_desc, part.start, part.size, fit) != part.size) { 32731f8f6ebSJoseph Chen ret = -EIO; 32831f8f6ebSJoseph Chen goto out; 3294388decaSJoseph Chen } 3304388decaSJoseph Chen 33131f8f6ebSJoseph Chen if (fdt_check_header(fit)) { 332bb4a1f43SJoseph Chen AMP_E("Not fit\n"); 33331f8f6ebSJoseph Chen ret = -EINVAL; 33431f8f6ebSJoseph Chen goto out; 33531f8f6ebSJoseph Chen } 3364388decaSJoseph Chen 337bb4a1f43SJoseph Chen /* Load loadables */ 33831f8f6ebSJoseph Chen memset(&images, 0, sizeof(images)); 33931f8f6ebSJoseph Chen images.fit_uname_cfg = "conf"; 34031f8f6ebSJoseph Chen images.fit_hdr_os = fit; 34131f8f6ebSJoseph Chen images.verify = 1; 342bb4a1f43SJoseph Chen ret = boot_get_loadable(0, NULL, &images, IH_ARCH_DEFAULT, NULL, NULL); 34331f8f6ebSJoseph Chen if (ret) { 344bb4a1f43SJoseph Chen AMP_E("Load loadables, ret=%d\n", ret); 345bb4a1f43SJoseph Chen goto out; 34631f8f6ebSJoseph Chen } 34731f8f6ebSJoseph Chen flush_dcache_all(); 34831f8f6ebSJoseph Chen 349bb4a1f43SJoseph Chen /* Wakeup */ 35031f8f6ebSJoseph Chen ret = brought_up_all_amp(images.fit_hdr_os, images.fit_uname_cfg); 351bb4a1f43SJoseph Chen if (ret) 352bb4a1f43SJoseph Chen AMP_E("Brought up amps, ret=%d\n", ret); 35331f8f6ebSJoseph Chen out: 354bb4a1f43SJoseph Chen free(fit); 35531f8f6ebSJoseph Chen 35631f8f6ebSJoseph Chen return ret; 35731f8f6ebSJoseph Chen } 35831f8f6ebSJoseph Chen 3598223aa47SJoseph Chen int arm64_switch_amp_pe(bootm_headers_t *images) 36031f8f6ebSJoseph Chen { 361bb4a1f43SJoseph Chen images->os.arch = g_bootcpu.arch; 362bb4a1f43SJoseph Chen return g_bootcpu.state; 36331f8f6ebSJoseph Chen } 36431f8f6ebSJoseph Chen 365