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 <config.h> 11 #include <sysmem.h> 12 #include <asm/gic.h> 13 #include <asm/io.h> 14 #include <asm/arch/rockchip_smccc.h> 15 16 DECLARE_GLOBAL_DATA_PTR; 17 18 /* 19 * [Design Principles] 20 * 21 * [amp.img] 22 * The amp image with FIT format which consists of non-linux firmwares. 23 * Please refer to: driver/cpu/amp.its. 24 * 25 * amp.img generation: ./tools/mkimage -f drivers/cpu/amp.its -E -p 0xe00 amp.img 26 * 27 * [linux] 28 * We still use the traditional solution for a better compatibility: 29 * boot.img/recovery.img with FIT format or Android format. 30 * 31 * The developer need add "/configurations/conf/linux" node to configure: 32 * description, arch, cpu, thumb, hyp, udelay(optional) properties. 33 * The addresses depend on U-Boot: kernel_addr_r, fdt_addr_r and 34 * ramdisk_addr_r. Please refer to: driver/cpu/amp.its. 35 * 36 * [memory management] 37 * U-Boot is not responsible for memory distribution/fixup any more, please 38 * handle this on kernel dts "/memory". 39 * 40 * [trust] 41 * The AMP feature requires trust support. 42 */ 43 44 #define AMP_PART "amp" 45 #define FIT_HEADER_SIZE SZ_4K 46 47 #define gicd_readl(offset) readl((void *)GICD_BASE + (offset)) 48 #define gicd_writel(v, offset) writel(v, (void *)GICD_BASE + (offset)) 49 #define LINXU_AMP_NODES "/rockchip-amp/amp-cpus" 50 51 typedef struct boot_args { 52 ulong arg0; 53 ulong arg1; 54 ulong arg2; 55 ulong arg3; 56 } boot_args_t; 57 58 typedef struct boot_cpu { 59 u32 arch; 60 u32 state; 61 u32 entry; 62 u32 linux_os; 63 } boot_cpu_t; 64 65 static boot_cpu_t g_bootcpu; 66 static u32 g_cpus_boot_by_linux[8]; 67 68 static u32 fit_get_u32_default(const void *fit, int noffset, 69 const char *prop, u32 def) 70 { 71 const fdt32_t *val; 72 73 val = fdt_getprop(fit, noffset, prop, NULL); 74 if (!val) 75 return def; 76 77 return fdt32_to_cpu(*val); 78 } 79 80 static int parse_cpus_boot_by_linux(void) 81 { 82 const void *fdt = gd->fdt_blob; 83 int noffset, cpu, i = 0; 84 u64 mpidr; 85 86 memset(g_cpus_boot_by_linux, 0xff, sizeof(g_cpus_boot_by_linux)); 87 noffset = fdt_path_offset(fdt, LINXU_AMP_NODES); 88 if (noffset < 0) 89 return 0; 90 91 fdt_for_each_subnode(cpu, fdt, noffset) { 92 mpidr = fdtdec_get_uint64(fdt, cpu, "id", 0xffffffff); 93 if (mpidr == 0xffffffff) 94 continue; 95 g_cpus_boot_by_linux[i++] = mpidr; 96 printf("CPU[0x%llx] is required boot by Linux\n", mpidr); 97 } 98 99 return 0; 100 } 101 102 static int load_linux_for_nonboot_cpu(u32 cpu, u32 aarch64, u32 load, 103 u32 *entry, boot_args_t *args) 104 { 105 static const char *boot_cmd[] = { 106 "boot_fit", "boot_android ${devtype} ${devnum}" }; 107 int i, ret; 108 109 env_set_hex("bootm_states_unmask", BOOTM_STATE_OS_GO); 110 for (i = 0; i < ARRAY_SIZE(boot_cmd); i++) { 111 ret = run_command(boot_cmd[i], 0); 112 if (!ret) 113 break; 114 } 115 env_set("bootm_states_unmask", NULL); 116 if (ret) { 117 AMP_E("Load linux failed, ret=%d\n", ret); 118 return ret; 119 } 120 121 /* linux boot args */ 122 if (aarch64) { 123 args->arg0 = (ulong)gd->fdt_blob; 124 args->arg1 = 0; 125 args->arg2 = 0; 126 } else { 127 args->arg0 = 0; 128 args->arg1 = 0; 129 args->arg2 = (ulong)gd->fdt_blob; 130 } 131 132 /* don't need call cleanup_before_linux() as this nonboot cpu is clean */ 133 board_quiesce_devices(&images); 134 flush_dcache_all(); 135 136 /* fixup: ramdisk/fdt/entry depend on U-Boot */ 137 *entry = (u32)images.ep; 138 139 return 0; 140 } 141 142 static int is_default_pe_state(u32 pe_state) 143 { 144 #ifdef CONFIG_ARM64 145 return (pe_state == PE_STATE(1, 1, 0, 0)); 146 #else 147 return (pe_state == PE_STATE(0, 0, 0, 0)); 148 #endif 149 } 150 151 static void setup_sync_bits_for_linux(void) 152 { 153 u32 val, num_irq, offset; 154 155 val = gicd_readl(GICD_CTLR); 156 val &= ~0x3; 157 gicd_writel(val, GICD_CTLR); 158 159 num_irq = 32 * ((gicd_readl(GICD_TYPER) & 0x1F) + 1); 160 offset = ((num_irq - 1) / 4) * 4; 161 gicd_writel(0x0, GICD_IPRIORITYRn + offset); 162 } 163 164 static int smc_cpu_on(u32 cpu, u32 pe_state, u32 entry, 165 boot_args_t *args, bool is_linux) 166 { 167 int ret; 168 169 AMP_I("Brought up cpu[%x] with state 0x%x, entry 0x%08x ...", 170 cpu, pe_state, entry); 171 172 /* if target pe state is default arch state, power up cpu directly */ 173 if (is_default_pe_state(pe_state)) 174 goto finish; 175 176 ret = sip_smc_amp_cfg(AMP_PE_STATE, cpu, pe_state, 0); 177 if (ret) { 178 AMP_E("smc pe-state, ret=%d\n", ret); 179 return ret; 180 } 181 182 /* only linux needs boot args */ 183 if (!is_linux) 184 goto finish; 185 186 ret = sip_smc_amp_cfg(AMP_BOOT_ARG01, cpu, args->arg0, args->arg1); 187 if (ret) { 188 AMP_E("smc boot arg01, ret=%d\n", ret); 189 return ret; 190 } 191 192 ret = sip_smc_amp_cfg(AMP_BOOT_ARG23, cpu, args->arg2, args->arg3); 193 if (ret) { 194 AMP_E("smc boot arg23, ret=%d\n", ret); 195 return ret; 196 } 197 198 finish: 199 ret = psci_cpu_on(cpu, entry); 200 if (ret) { 201 printf("cpu up failed, ret=%d\n", ret); 202 return ret; 203 } 204 printf("OK\n"); 205 206 return 0; 207 } 208 209 __weak int fit_standalone_release(char *id, uintptr_t entry_point) 210 { 211 return 0; 212 } 213 214 static int standalone_handler(const char *id, u32 entry_point, int data_size) 215 { 216 int ret; 217 218 if (!sysmem_alloc_base_by_name(id, 219 (phys_addr_t)entry_point, data_size)) 220 return -ENXIO; 221 222 printf("Handle standalone: '%s' at 0x%08x ...", id, entry_point); 223 224 ret = fit_standalone_release((char *)id, entry_point); 225 if (ret) { 226 printf("failed, ret=%d\n", ret); 227 return ret; 228 } 229 printf("OK\n"); 230 231 return 0; 232 } 233 234 static int brought_up_amp(void *fit, int noffset, 235 boot_cpu_t *bootcpu, int is_linux) 236 { 237 const char *desc; 238 boot_args_t args; 239 u32 cpu, aarch64, hyp; 240 u32 load, thumb, us; 241 u32 pe_state, entry; 242 int boot_on; 243 int data_size; 244 int i, ret; 245 u8 type = -ENODATA; 246 u8 arch = -ENODATA; 247 248 desc = fdt_getprop(fit, noffset, "description", NULL); 249 cpu = fit_get_u32_default(fit, noffset, "cpu", -ENODATA); 250 hyp = fit_get_u32_default(fit, noffset, "hyp", 0); 251 thumb = fit_get_u32_default(fit, noffset, "thumb", 0); 252 entry = load = fit_get_u32_default(fit, noffset, "load", -ENODATA); 253 us = fit_get_u32_default(fit, noffset, "udelay", 0); 254 boot_on = fit_get_u32_default(fit, noffset, "boot-on", 1); 255 fit_image_get_arch(fit, noffset, &arch); 256 fit_image_get_type(fit, noffset, &type); 257 fit_image_get_data_size(fit, noffset, &data_size); 258 memset(&args, 0, sizeof(args)); 259 260 /* standalone is simple, just handle it and then exit. Allow failure */ 261 if (type == IH_TYPE_STANDALONE) { 262 if (!desc || load == -ENODATA) { 263 AMP_E("standalone: \"desc\" or \"load\" property missing!\n"); 264 goto exit; 265 } 266 standalone_handler(desc, load, data_size); 267 goto exit; 268 } 269 270 if (!desc || cpu == -ENODATA || arch == -ENODATA || type == -ENODATA || 271 (load == -ENODATA && !is_linux)) { 272 AMP_E("Property missing!\n"); 273 return -EINVAL; 274 } 275 aarch64 = (arch == IH_ARCH_ARM) ? 0 : 1; 276 pe_state = PE_STATE(aarch64, hyp, thumb, 0); 277 278 #ifdef DEBUG 279 AMP_I(" desc: %s\n", desc); 280 AMP_I(" cpu: 0x%x\n", cpu); 281 AMP_I(" aarch64: %d\n", aarch64); 282 AMP_I(" hyp: %d\n", hyp); 283 AMP_I(" thumb: %d\n", thumb); 284 AMP_I(" load: 0x%08x\n", load); 285 AMP_I(" pe_state: 0x%08x\n", pe_state); 286 AMP_I(" linux-os: %d\n\n", is_linux); 287 #endif 288 289 /* this cpu is boot cpu ? */ 290 if ((read_mpidr() & 0x0fff) == cpu) { 291 bootcpu->arch = arch; 292 bootcpu->entry = entry; 293 bootcpu->state = pe_state; 294 bootcpu->linux_os = is_linux; 295 return 0; 296 } 297 298 /* === only nonboot cpu can reach here === */ 299 300 /* load or check */ 301 if (is_linux) { 302 ret = load_linux_for_nonboot_cpu(cpu, 303 aarch64, load, &entry, &args); 304 if (ret) 305 return ret; 306 /* 307 * Must setup before jump to linux. 308 * This is an appointment on RK amp solution to handle 309 * GIC configure competition. 310 */ 311 setup_sync_bits_for_linux(); 312 } else { 313 if (!sysmem_alloc_base_by_name(desc, 314 (phys_addr_t)load, data_size)) 315 return -ENXIO; 316 } 317 318 /* If linux assign the boot-on state, use it */ 319 for (i = 0; i < ARRAY_SIZE(g_cpus_boot_by_linux); i++) { 320 if (cpu == g_cpus_boot_by_linux[i]) { 321 boot_on = 0; 322 break; 323 } 324 } 325 326 if (!boot_on) 327 return 0; 328 329 /* boot now */ 330 ret = smc_cpu_on(cpu, pe_state, entry, &args, is_linux); 331 if (ret) 332 return ret; 333 exit: 334 if (us) 335 udelay(us); 336 337 return 0; 338 } 339 340 static int brought_up_all_amp(void *fit, const char *fit_uname_cfg) 341 { 342 int loadables_index; 343 int linux_noffset; 344 int conf_noffset; 345 int cpu_noffset; 346 int ret; 347 const char *uname; 348 349 conf_noffset = fit_conf_get_node(fit, fit_uname_cfg); 350 if (conf_noffset < 0) 351 return conf_noffset; 352 353 linux_noffset = fdt_subnode_offset(fit, conf_noffset, "linux"); 354 if (linux_noffset > 0) { 355 ret = brought_up_amp(fit, linux_noffset, &g_bootcpu, 1); 356 if (ret) 357 return ret; 358 } 359 360 for (loadables_index = 0; 361 uname = fdt_stringlist_get(fit, conf_noffset, 362 FIT_LOADABLE_PROP, loadables_index, NULL), uname; 363 loadables_index++) { 364 cpu_noffset = fit_image_get_node(fit, uname); 365 if (cpu_noffset < 0) 366 return cpu_noffset; 367 368 ret = brought_up_amp(fit, cpu_noffset, &g_bootcpu, 0); 369 if (ret) 370 return ret; 371 } 372 373 /* === only boot cpu can reach here === */ 374 375 if (!g_bootcpu.linux_os && g_bootcpu.entry) { 376 flush_dcache_all(); 377 AMP_I("Brought up cpu[%x, self] with state 0x%x, entry 0x%08x ...", 378 (u32)read_mpidr() & 0x0fff, g_bootcpu.state, g_bootcpu.entry); 379 cleanup_before_linux(); 380 printf("OK\n"); 381 #ifdef CONFIG_ARM64 382 armv8_switch_to_el2(0, 0, 0, g_bootcpu.state, (u64)g_bootcpu.entry, 383 g_bootcpu.arch == IH_ARCH_ARM ? ES_TO_AARCH32 : ES_TO_AARCH64); 384 #else 385 void (*armv7_entry)(void); 386 387 armv7_entry = (void (*)(void))g_bootcpu.entry; 388 armv7_entry(); 389 #endif 390 } 391 392 /* return: boot cpu continue to boot linux */ 393 return 0; 394 } 395 396 int amp_cpus_on(void) 397 { 398 struct blk_desc *dev_desc; 399 bootm_headers_t images; 400 disk_partition_t part; 401 void *hdr, *fit; 402 int offset, cnt; 403 int totalsize; 404 int ret = 0; 405 406 dev_desc = rockchip_get_bootdev(); 407 if (!dev_desc) 408 return -EIO; 409 410 if (part_get_info_by_name(dev_desc, AMP_PART, &part) < 0) 411 return -ENODEV; 412 413 hdr = memalign(ARCH_DMA_MINALIGN, FIT_HEADER_SIZE); 414 if (!hdr) 415 return -ENOMEM; 416 417 /* get totalsize */ 418 offset = part.start; 419 cnt = DIV_ROUND_UP(FIT_HEADER_SIZE, part.blksz); 420 if (blk_dread(dev_desc, offset, cnt, hdr) != cnt) { 421 ret = -EIO; 422 goto out2; 423 } 424 425 if (fdt_check_header(hdr)) { 426 AMP_E("Not fit\n"); 427 ret = -EINVAL; 428 goto out2; 429 } 430 431 if (fit_get_totalsize(hdr, &totalsize)) { 432 AMP_E("No totalsize\n"); 433 ret = -EINVAL; 434 goto out2; 435 } 436 437 /* load image */ 438 fit = memalign(ARCH_DMA_MINALIGN, ALIGN(totalsize, part.blksz)); 439 if (!fit) { 440 printf("No memory\n"); 441 ret = -ENOMEM; 442 goto out2; 443 } 444 445 memcpy(fit, hdr, FIT_HEADER_SIZE); 446 447 offset += cnt; 448 cnt = DIV_ROUND_UP(totalsize, part.blksz) - cnt; 449 if (blk_dread(dev_desc, offset, cnt, fit + FIT_HEADER_SIZE) != cnt) { 450 ret = -EIO; 451 goto out1; 452 } 453 454 /* prase linux info */ 455 parse_cpus_boot_by_linux(); 456 457 /* Load loadables */ 458 memset(&images, 0, sizeof(images)); 459 images.fit_uname_cfg = "conf"; 460 images.fit_hdr_os = fit; 461 images.verify = 1; 462 ret = boot_get_loadable(0, NULL, &images, IH_ARCH_DEFAULT, NULL, NULL); 463 if (ret) { 464 AMP_E("Load loadables, ret=%d\n", ret); 465 goto out1; 466 } 467 flush_dcache_all(); 468 469 /* Wakeup */ 470 ret = brought_up_all_amp(images.fit_hdr_os, images.fit_uname_cfg); 471 if (ret) 472 AMP_E("Brought up amps, ret=%d\n", ret); 473 out1: 474 free(fit); 475 out2: 476 free(hdr); 477 478 return ret; 479 } 480 481 int arm64_switch_amp_pe(bootm_headers_t *images) 482 { 483 images->os.arch = g_bootcpu.arch; 484 return g_bootcpu.state; 485 } 486 487