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
fit_get_u32_default(const void * fit,int noffset,const char * prop,u32 def)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
parse_cpus_boot_by_linux(void)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
load_linux_for_nonboot_cpu(u32 cpu,u32 aarch64,u32 load,u32 * entry,boot_args_t * args)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
is_default_pe_state(u32 pe_state)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
setup_sync_bits_for_linux(void)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
smc_cpu_on(u32 cpu,u32 pe_state,u32 entry,boot_args_t * args,bool is_linux)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
fit_standalone_release(char * id,uintptr_t entry_point)209 __weak int fit_standalone_release(char *id, uintptr_t entry_point)
210 {
211 return 0;
212 }
213
standalone_handler(const char * id,u32 entry_point,int data_size)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
brought_up_amp(void * fit,int noffset,boot_cpu_t * bootcpu,int is_linux)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
brought_up_all_amp(void * fit,const char * fit_uname_cfg)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
amp_cpus_on(void)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
arm64_switch_amp_pe(bootm_headers_t * images)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