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 gicd_readl(offset) readl((void *)GICD_BASE + (offset)) 46 #define gicd_writel(v, offset) writel(v, (void *)GICD_BASE + (offset)) 47 #define LINXU_AMP_NODES "/rockchip-amp/amp-cpus" 48 49 typedef struct boot_args { 50 ulong arg0; 51 ulong arg1; 52 ulong arg2; 53 ulong arg3; 54 } boot_args_t; 55 56 typedef struct boot_cpu { 57 u32 arch; 58 u32 state; 59 u32 entry; 60 u32 linux_os; 61 } boot_cpu_t; 62 63 static boot_cpu_t g_bootcpu; 64 static u32 g_cpus_boot_by_linux[8]; 65 66 static u32 fit_get_u32_default(const void *fit, int noffset, 67 const char *prop, u32 def) 68 { 69 const fdt32_t *val; 70 71 val = fdt_getprop(fit, noffset, prop, NULL); 72 if (!val) 73 return def; 74 75 return fdt32_to_cpu(*val); 76 } 77 78 static int parse_cpus_boot_by_linux(void) 79 { 80 const void *fdt = gd->fdt_blob; 81 int noffset, cpu; 82 int mpidr, i = 0; 83 84 memset(g_cpus_boot_by_linux, 0xff, sizeof(g_cpus_boot_by_linux)); 85 noffset = fdt_path_offset(fdt, LINXU_AMP_NODES); 86 if (noffset < 0) 87 return 0; 88 89 fdt_for_each_subnode(cpu, fdt, noffset) { 90 mpidr = fdtdec_get_uint(fdt, cpu, "id", 0xffffffff); 91 if (mpidr == 0xffffffff) 92 continue; 93 g_cpus_boot_by_linux[i++] = mpidr; 94 printf("CPU[0x%x] is required boot by Linux\n", mpidr); 95 } 96 97 return 0; 98 } 99 100 static int load_linux_for_nonboot_cpu(u32 cpu, u32 aarch64, u32 load, 101 u32 *entry, boot_args_t *args) 102 { 103 static const char *boot_cmd[] = { 104 "boot_fit", "boot_android ${devtype} ${devnum}" }; 105 int i, ret; 106 107 env_set_hex("bootm_states_unmask", BOOTM_STATE_OS_GO); 108 for (i = 0; i < ARRAY_SIZE(boot_cmd); i++) { 109 ret = run_command(boot_cmd[i], 0); 110 if (!ret) 111 break; 112 } 113 env_set("bootm_states_unmask", NULL); 114 if (ret) { 115 AMP_E("Load linux failed, ret=%d\n", ret); 116 return ret; 117 } 118 119 /* linux boot args */ 120 if (aarch64) { 121 args->arg0 = (ulong)gd->fdt_blob; 122 args->arg1 = 0; 123 args->arg2 = 0; 124 } else { 125 args->arg0 = 0; 126 args->arg1 = 0; 127 args->arg2 = (ulong)gd->fdt_blob; 128 } 129 130 /* don't need call cleanup_before_linux() as this nonboot cpu is clean */ 131 board_quiesce_devices(&images); 132 flush_dcache_all(); 133 134 /* fixup: ramdisk/fdt/entry depend on U-Boot */ 135 *entry = env_get_ulong("kernel_addr_r", 16, 0); 136 137 return 0; 138 } 139 140 static int is_default_pe_state(u32 pe_state) 141 { 142 #ifdef CONFIG_ARM64 143 return (pe_state == PE_STATE(1, 1, 0, 0)); 144 #else 145 return (pe_state == PE_STATE(0, 0, 0, 0)); 146 #endif 147 } 148 149 static void setup_sync_bits_for_linux(void) 150 { 151 u32 val, num_irq, offset; 152 153 val = gicd_readl(GICD_CTLR); 154 val &= ~0x3; 155 gicd_writel(val, GICD_CTLR); 156 157 num_irq = 32 * ((gicd_readl(GICD_TYPER) & 0x1F) + 1); 158 offset = ((num_irq - 1) / 4) * 4; 159 gicd_writel(0x0, GICD_IPRIORITYRn + offset); 160 } 161 162 static int smc_cpu_on(u32 cpu, u32 pe_state, u32 entry, 163 boot_args_t *args, bool is_linux) 164 { 165 int ret; 166 167 AMP_I("Brought up cpu[%x] with state 0x%x, entry 0x%08x ...", 168 cpu, pe_state, entry); 169 170 /* if target pe state is default arch state, power up cpu directly */ 171 if (is_default_pe_state(pe_state)) 172 goto finish; 173 174 ret = sip_smc_amp_cfg(AMP_PE_STATE, cpu, pe_state, 0); 175 if (ret) { 176 AMP_E("smc pe-state, ret=%d\n", ret); 177 return ret; 178 } 179 180 /* only linux needs boot args */ 181 if (!is_linux) 182 goto finish; 183 184 ret = sip_smc_amp_cfg(AMP_BOOT_ARG01, cpu, args->arg0, args->arg1); 185 if (ret) { 186 AMP_E("smc boot arg01, ret=%d\n", ret); 187 return ret; 188 } 189 190 ret = sip_smc_amp_cfg(AMP_BOOT_ARG23, cpu, args->arg2, args->arg3); 191 if (ret) { 192 AMP_E("smc boot arg23, ret=%d\n", ret); 193 return ret; 194 } 195 196 finish: 197 ret = psci_cpu_on(cpu, entry); 198 if (ret) { 199 printf("cpu up failed, ret=%d\n", ret); 200 return ret; 201 } 202 printf("OK\n"); 203 204 return 0; 205 } 206 207 static int brought_up_amp(void *fit, int noffset, 208 boot_cpu_t *bootcpu, int is_linux) 209 { 210 const char *desc; 211 boot_args_t args; 212 u32 cpu, aarch64, hyp; 213 u32 load, thumb, us; 214 u32 pe_state, entry; 215 int boot_on; 216 int data_size; 217 int i, ret; 218 u8 arch = -ENODATA; 219 220 desc = fdt_getprop(fit, noffset, "description", NULL); 221 cpu = fit_get_u32_default(fit, noffset, "cpu", -ENODATA); 222 hyp = fit_get_u32_default(fit, noffset, "hyp", 0); 223 thumb = fit_get_u32_default(fit, noffset, "thumb", 0); 224 load = fit_get_u32_default(fit, noffset, "load", -ENODATA); 225 us = fit_get_u32_default(fit, noffset, "udelay", 0); 226 boot_on = fit_get_u32_default(fit, noffset, "boot-on", 1); 227 fit_image_get_arch(fit, noffset, &arch); 228 fit_image_get_data_size(fit, noffset, &data_size); 229 memset(&args, 0, sizeof(args)); 230 231 if (!desc || cpu == -ENODATA || arch == -ENODATA || 232 (load == -ENODATA && !is_linux)) { 233 AMP_E("Property missing!\n"); 234 return -EINVAL; 235 } 236 aarch64 = (arch == IH_ARCH_ARM) ? 0 : 1; 237 pe_state = PE_STATE(aarch64, hyp, thumb, 0); 238 entry = load; 239 240 #ifdef DEBUG 241 AMP_I(" desc: %s\n", desc); 242 AMP_I(" cpu: 0x%x\n", cpu); 243 AMP_I(" aarch64: %d\n", aarch64); 244 AMP_I(" hyp: %d\n", hyp); 245 AMP_I(" thumb: %d\n", thumb); 246 AMP_I(" entry: 0x%08x\n", entry); 247 AMP_I(" pe_state: 0x%08x\n", pe_state); 248 AMP_I(" linux-os: %d\n\n", is_linux); 249 #endif 250 251 /* this cpu is boot cpu ? */ 252 if ((read_mpidr() & 0x0fff) == cpu) { 253 bootcpu->arch = arch; 254 bootcpu->entry = entry; 255 bootcpu->state = pe_state; 256 bootcpu->linux_os = is_linux; 257 return 0; 258 } 259 260 /* === only nonboot cpu can reach here === */ 261 262 /* load or check */ 263 if (is_linux) { 264 ret = load_linux_for_nonboot_cpu(cpu, 265 aarch64, load, &entry, &args); 266 if (ret) 267 return ret; 268 /* 269 * Must setup before jump to linux. 270 * This is an appointment on RK amp solution to handle 271 * GIC configure competition. 272 */ 273 setup_sync_bits_for_linux(); 274 } else { 275 if (!sysmem_alloc_base_by_name(desc, 276 (phys_addr_t)load, data_size)) 277 return -ENXIO; 278 } 279 280 /* If linux assign the boot-on state, use it */ 281 for (i = 0; i < ARRAY_SIZE(g_cpus_boot_by_linux); i++) { 282 if (cpu == g_cpus_boot_by_linux[i]) { 283 boot_on = 0; 284 break; 285 } 286 } 287 288 if (!boot_on) 289 return 0; 290 291 /* boot now */ 292 ret = smc_cpu_on(cpu, pe_state, entry, &args, is_linux); 293 if (ret) 294 return ret; 295 296 if (us) 297 udelay(us); 298 299 return 0; 300 } 301 302 static int brought_up_all_amp(void *fit, const char *fit_uname_cfg) 303 { 304 int loadables_index; 305 int linux_noffset; 306 int conf_noffset; 307 int cpu_noffset; 308 int ret; 309 const char *uname; 310 311 conf_noffset = fit_conf_get_node(fit, fit_uname_cfg); 312 if (conf_noffset < 0) 313 return conf_noffset; 314 315 linux_noffset = fdt_subnode_offset(fit, conf_noffset, "linux"); 316 if (linux_noffset > 0) { 317 ret = brought_up_amp(fit, linux_noffset, &g_bootcpu, 1); 318 if (ret) 319 return ret; 320 } 321 322 for (loadables_index = 0; 323 uname = fdt_stringlist_get(fit, conf_noffset, 324 FIT_LOADABLE_PROP, loadables_index, NULL), uname; 325 loadables_index++) { 326 cpu_noffset = fit_image_get_node(fit, uname); 327 if (cpu_noffset < 0) 328 return cpu_noffset; 329 330 ret = brought_up_amp(fit, cpu_noffset, &g_bootcpu, 0); 331 if (ret) 332 return ret; 333 } 334 335 /* === only boot cpu can reach here === */ 336 337 if (!g_bootcpu.linux_os) { 338 flush_dcache_all(); 339 AMP_I("Brought up cpu[%x, self] with state 0x%x, entry 0x%08x ...", 340 (u32)read_mpidr() & 0x0fff, g_bootcpu.state, g_bootcpu.entry); 341 cleanup_before_linux(); 342 printf("OK\n"); 343 #ifdef CONFIG_ARM64 344 armv8_switch_to_el2(0, 0, 0, g_bootcpu.state, (u64)g_bootcpu.entry, 345 g_bootcpu.arch == IH_ARCH_ARM ? ES_TO_AARCH32 : ES_TO_AARCH64); 346 #else 347 void (*armv7_entry)(void); 348 349 armv7_entry = (void (*)(void))g_bootcpu.entry; 350 armv7_entry(); 351 #endif 352 } 353 354 /* return: boot cpu continue to boot linux */ 355 return 0; 356 } 357 358 int amp_cpus_on(void) 359 { 360 struct blk_desc *dev_desc; 361 bootm_headers_t images; 362 disk_partition_t part; 363 void *fit; 364 int ret = 0; 365 366 dev_desc = rockchip_get_bootdev(); 367 if (!dev_desc) 368 return -EIO; 369 370 if (part_get_info_by_name(dev_desc, AMP_PART, &part) < 0) 371 return -ENODEV; 372 373 fit = memalign(ARCH_DMA_MINALIGN, part.size * part.blksz); 374 if (!fit) { 375 AMP_E("No memory, please increase CONFIG_SYS_MALLOC_LEN\n"); 376 return -ENOMEM; 377 } 378 379 if (blk_dread(dev_desc, part.start, part.size, fit) != part.size) { 380 ret = -EIO; 381 goto out; 382 } 383 384 if (fdt_check_header(fit)) { 385 AMP_E("Not fit\n"); 386 ret = -EINVAL; 387 goto out; 388 } 389 390 /* prase linux info */ 391 parse_cpus_boot_by_linux(); 392 393 /* Load loadables */ 394 memset(&images, 0, sizeof(images)); 395 images.fit_uname_cfg = "conf"; 396 images.fit_hdr_os = fit; 397 images.verify = 1; 398 ret = boot_get_loadable(0, NULL, &images, IH_ARCH_DEFAULT, NULL, NULL); 399 if (ret) { 400 AMP_E("Load loadables, ret=%d\n", ret); 401 goto out; 402 } 403 flush_dcache_all(); 404 405 /* Wakeup */ 406 ret = brought_up_all_amp(images.fit_hdr_os, images.fit_uname_cfg); 407 if (ret) 408 AMP_E("Brought up amps, ret=%d\n", ret); 409 out: 410 free(fit); 411 412 return ret; 413 } 414 415 int arm64_switch_amp_pe(bootm_headers_t *images) 416 { 417 images->os.arch = g_bootcpu.arch; 418 return g_bootcpu.state; 419 } 420 421