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