1 /*
2  * Copyright (c) 2019 Intel Corporation
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 
7 #include <zephyr/device.h>
8 #include <zephyr/init.h>
9 #include <zephyr/kernel.h>
10 #include <zephyr/kernel_structs.h>
11 #include <zephyr/toolchain.h>
12 #include <zephyr/sys/__assert.h>
13 #include <zephyr/sys/sys_io.h>
14 
15 #include <xtensa/config/core-isa.h>
16 
17 #include <zephyr/logging/log.h>
18 LOG_MODULE_REGISTER(soc_mp, CONFIG_SOC_LOG_LEVEL);
19 
20 #include <zsr.h>
21 #include <cavs-idc.h>
22 #include <soc.h>
23 #include <zephyr/cache.h>
24 #include <adsp_shim.h>
25 #include <adsp_memory.h>
26 #include <cpu_init.h>
27 
28 struct cpustart_rec {
29 	uint32_t        cpu;
30 	arch_cpustart_t	fn;
31 	void            *arg;
32 };
33 
34 static struct cpustart_rec start_rec;
35 const uint32_t *z_mp_start_cpu = &start_rec.cpu;
36 
37 char *z_mp_stack_top;
38 
39 /* Vestigial silliness: An old mechanism for core startup would embed
40  * a "manifest" of code to copy to LP-SRAM at startup (vs. the tiny
41  * trampoline we use here).  This was constructed in the linker
42  * script, and the first word would encode the number of entries.  As
43  * it happens, SOF still emits the code to copy this data, so it needs
44  * to see this symbol point to a zero.
45  */
46 uint32_t _loader_storage_manifest_start;
47 
48 /* Simple array of CPUs that are active and available for an IPI.  The
49  * IDC interrupt is ALSO used to bring a CPU out of reset, so we need
50  * to be absolutely sure we don't try to IPI a CPU that isn't ready to
51  * start, or else we'll launch it into garbage and crash the DSP.
52  */
53 bool soc_cpus_active[CONFIG_MP_MAX_NUM_CPUS];
54 
55 #define NOP4 "nop; nop; nop; nop;"
56 #define NOP32 NOP4 NOP4 NOP4 NOP4 NOP4 NOP4 NOP4 NOP4
57 #define NOP128 NOP32 NOP32 NOP32 NOP32
58 /* Tiny assembly stub for calling z_mp_entry() on the auxiliary CPUs.
59  * Mask interrupts, clear the register window state and set the stack
60  * pointer.  This represents the minimum work required to run C code
61  * safely.
62  *
63  * Note that alignment is absolutely required: the IDC protocol passes
64  * only the upper 30 bits of the address to the second CPU.
65  */
66 __asm__(".section .text.z_soc_mp_asm_entry, \"x\" \n\t"
67 	".align 4                   \n\t"
68 	".global z_soc_mp_asm_entry \n\t"
69 	"z_soc_mp_asm_entry:        \n\t"
70 	"  movi  a0, 0x4002f        \n\t" /* WOE | UM | INTLEVEL(max) */
71 	"  wsr   a0, PS             \n\t"
72 	"  movi  a0, 0              \n\t"
73 	"  wsr   a0, WINDOWBASE     \n\t"
74 	"  movi  a0, 1              \n\t"
75 	"  wsr   a0, WINDOWSTART    \n\t"
76 	"  rsync                    \n\t"
77 	"  movi  a1, z_mp_start_cpu \n\t"
78 	"  l32i  a1, a1, 0          \n\t"
79 	"  l32i  a1, a1, 0          \n\t"
80 	"  rsr   a2, PRID           \n\t"
81 	"  sub   a2, a2, a1         \n\t"
82 	"  bnez  a2, soc_mp_idle    \n\t"
83 	"  movi  a1, z_mp_stack_top \n\t"
84 	"  l32i  a1, a1, 0          \n\t"
85 	"  call4 z_mp_entry         \n\t"
86 	"soc_mp_idle:               \n\t"
87 #ifdef CONFIG_XTENSA_WAITI_BUG
88 	NOP128
89 	"  isync                    \n\t"
90 	"  extw                     \n\t"
91 #endif
92 	"  waiti 0                  \n\t" /* Power-gating is allowed, we'll exit via reset */
93 	"  j soc_mp_idle            \n\t");
94 
95 #undef NOP128
96 #undef NOP32
97 #undef NOP4
98 
z_mp_entry(void)99 __imr void z_mp_entry(void)
100 {
101 	cpu_early_init();
102 	/* Set up the CPU pointer. */
103 	_cpu_t *cpu = &_kernel.cpus[start_rec.cpu];
104 
105 	__asm__ volatile("wsr %0, " ZSR_CPU_STR :: "r"(cpu));
106 
107 	soc_mp_startup(start_rec.cpu);
108 	soc_cpus_active[start_rec.cpu] = true;
109 	start_rec.fn(start_rec.arg);
110 	__ASSERT(false, "arch_start_cpu() handler should never return");
111 }
112 
arch_cpu_active(int cpu_num)113 bool arch_cpu_active(int cpu_num)
114 {
115 	return soc_cpus_active[cpu_num];
116 }
117 
arch_start_cpu(int cpu_num,k_thread_stack_t * stack,int sz,arch_cpustart_t fn,void * arg)118 void arch_start_cpu(int cpu_num, k_thread_stack_t *stack, int sz,
119 		    arch_cpustart_t fn, void *arg)
120 {
121 	__ASSERT_NO_MSG(!soc_cpus_active[cpu_num]);
122 
123 	start_rec.cpu = cpu_num;
124 	start_rec.fn = fn;
125 	start_rec.arg = arg;
126 
127 	z_mp_stack_top = Z_KERNEL_STACK_BUFFER(stack) + sz;
128 
129 	soc_start_core(cpu_num);
130 }
131 
132 /* Fallback stub for external SOF code */
cavs_idc_smp_init(const struct device * dev)133 __imr int cavs_idc_smp_init(const struct device *dev)
134 {
135 	ARG_UNUSED(dev);
136 	return 0;
137 }
138