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