1 /* Copyright 2023 The ChromiumOS Authors
2 * SPDX-License-Identifier: Apache-2.0
3 */
4
5 #include <zephyr/devicetree.h>
6 #include <zephyr/sys/libc-hooks.h>
7 #include <string.h>
8 #include <kernel_internal.h>
9
10 extern char _mtk_adsp_sram_end[];
11
12 #define SRAM_START DT_REG_ADDR(DT_NODELABEL(sram0))
13 #define SRAM_SIZE DT_REG_SIZE(DT_NODELABEL(sram0))
14 #define SRAM_END (SRAM_START + SRAM_SIZE)
15
16 extern char _mtk_adsp_dram_end[];
17
18 #define DRAM_START DT_REG_ADDR(DT_NODELABEL(dram0))
19 #define DRAM_SIZE DT_REG_SIZE(DT_NODELABEL(dram0))
20 #define DRAM_END (DRAM_START + DRAM_SIZE)
21
22 #define DMA_START DT_REG_ADDR(DT_NODELABEL(dram1))
23 #define DMA_SIZE DT_REG_SIZE(DT_NODELABEL(dram1))
24 #define DMA_END (DMA_START + DMA_SIZE)
25
26
27 #ifdef CONFIG_SOC_MT8196
28 #define INIT_STACK "0x90400000"
29 #define LOG_BASE 0x90580000
30 #define LOG_LEN 0x80000
31 #else
32 #define INIT_STACK "0x60e00000"
33 #define LOG_BASE 0x60700000
34 #define LOG_LEN 0x100000
35 #endif
36
37 /* The MT8196 interrupt controller is very simple at runtime, with
38 * just an enable and status register needed, like its
39 * predecessors. But it has routing control which resets to "nothing
40 * enabled", so needs a driver.
41 *
42 * There are 64 interrupt inputs to the controller, controlled by
43 * pairs of words (the "intc64" type below). Each interrupt is
44 * associated with one[1] of 16 "groups", each of which directs to a
45 * different Xtensa architectural interrupt. So each Xtensa interrupt
46 * can be configured to handle any subset of interrupt inputs.
47 *
48 * The mapping of groups to Xtensa interrupts is given below. Note
49 * particularly that the final two groups are NMIs directed to an
50 * interrupt level higher than EXCM_LEVEL, so cannot be safely used
51 * for OS code (they'll interrupt spinlocks), but an app might exploit
52 * them for e.g. debug or watchdog hooks.
53 *
54 * GroupNum XtensaIRQ XtensaLevel
55 * 0-5 0-5 1 (L1 is shared w/exceptions, poor choice)
56 * 6-7 7-8 1
57 * 8-10 9-11 2
58 * 11-13 16-18 3
59 * 14,15 20,21 4 (Unmaskable! Do not use w/Zephyr code!)
60 *
61 * Naming of the inputs looks like this, though obviously only a small
62 * fraction have been validated (or are even useful for an audio DSP):
63 *
64 * 0: CCU 20: USB1 40: WDT
65 * 1: SCP 21: SCPVOW 41: CONNSYS1
66 * 2: SPM 22: CCIF3_C0 42: CONNSYS3
67 * 3: PCIE 23: CCIF3_C1 43: CONNSYS4
68 * 4: INFRA_HANG 24: PWR_CTRL 44: CONNSYS2
69 * 5: PERI_TIMEOUT 25: DMA_C0 45: IPIC
70 * 6: MBOX_C0 26: DMA_C1 46: AXI_DMA2
71 * 7: MBOX_C1 27: AXI_DMA0 47: AXI_DMA3
72 * 8: TIMER0 28: AXI_DMA1 48: APSRC_DDREN
73 * 9: TIMER1 29: AUDIO_C0 49: LAT_MON_EMI
74 * 10: IPC_C0 30: AUDIO_C1 50: LAT_MON_INFRA
75 * 11: IPC_C1 31: HIFI5_WDT_C0 51: DEVAPC_VIO
76 * 12: IPC1_RSV 32: HIFI5_WDT_C1 52: AO_INFRA_HANG
77 * 13: C2C_SW_C0 33: APU_MBOX_C0 53: BUS_TRA_EMI
78 * 14: C2C_SW_C1 34: APU_MBOX_C1 54: BUS_TRA_INFRA
79 * 15: UART 35: TIMER2 55: L2SRAM_VIO
80 * 16: UART_BT 36: PWR_ON_C0_IRQ 56: L2SRAM_SETERR
81 * 17: LATENCY_MON 37: PWR_ON_C1_IRQ 57: PCIERC_GRP2
82 * 18: BUS_TRACKER 38: WAKEUP_SRC_C0 58: PCIERC_GRP3
83 * 19: USB0 39: WAKEUP_SRC_C1 59: IRQ_MAX_CHANNEL
84 *
85 * [1] It is legal and works as expected for an interrupt to be part
86 * of more than one group (more than one interrupt fires to handle
87 * it), though I don't understand why an application would want to
88 * do that.
89 */
90
91 struct intc64 { uint32_t lo, hi; };
92
93 struct intc_8196 {
94 struct intc64 input; /* Raw (?) input signal, normally high */
95 struct intc64 status; /* Latched input, inverted (active == 1) */
96 struct intc64 enable; /* Interrupt enable */
97 struct intc64 polarity; /* 1 == active low */
98 struct intc64 wake_enable;
99 struct intc64 _unused;
100 struct intc64 stage1_enable;
101 struct intc64 sw_trigger;
102 struct intc64 groups[16]; /* set bit == "member of group" */
103 struct intc64 group_status[16]; /* status, but masked by group */
104 };
105
106 #define INTC (*(volatile struct intc_8196 *)0x1a014000)
107
set_group_bit(volatile struct intc64 * g,uint32_t bit,bool val)108 static void set_group_bit(volatile struct intc64 *g, uint32_t bit, bool val)
109 {
110 volatile uint32_t *p = bit < 32 ? &g->lo : &g->hi;
111 volatile uint32_t mask = BIT(bit & 0x1f);
112
113 *p = val ? (*p | mask) : (*p & ~mask);
114 }
115
mt8196_intc_set_irq_group(uint32_t irq,uint32_t group)116 static void mt8196_intc_set_irq_group(uint32_t irq, uint32_t group)
117 {
118 for (int i = 0; i < 16; i++) {
119 set_group_bit(&INTC.groups[i], irq, i == group);
120 }
121 }
122
mt8196_intc_init(void)123 void mt8196_intc_init(void)
124 {
125 struct intc64 zero = { 0, 0 };
126
127 INTC.enable = zero;
128 INTC.polarity.lo = 0xffffffff;
129 INTC.polarity.hi = 0xffffffff;
130 INTC.wake_enable = zero;
131 INTC.stage1_enable = zero;
132 for (int i = 0; i < ARRAY_SIZE(INTC.groups); i++) {
133 INTC.groups[i] = zero;
134 }
135
136 /* Now wire up known interrupts for existing drivers to their
137 * legacy settings
138 */
139 mt8196_intc_set_irq_group(6, 2); /* mbox0 in group 2 */
140 mt8196_intc_set_irq_group(7, 2); /* mbox1 in group 2 */
141 mt8196_intc_set_irq_group(8, 1); /* ostimer in group 1 */
142 }
143
144 /* This is the true boot vector. This device allows for direct
145 * setting of the alternate reset vector, so we let it link wherever
146 * it lands and extract its address in the loader. This represents
147 * the minimum amount of effort required to successfully call a C
148 * function (and duplicates a few versions elsewhere in the tree:
149 * really this should move to the arch layer). The initial stack
150 * really should be the end of _interrupt_stacks[0]
151 */
152 __asm__(".align 4\n\t"
153 ".global mtk_adsp_boot_entry\n\t"
154 "mtk_adsp_boot_entry:\n\t"
155 " movi a0, 0x4002f\n\t" /* WOE|EXCM|INTLVL=15 */
156 " wsr a0, PS\n\t"
157 " movi a0, 0\n\t"
158 " wsr a0, WINDOWBASE\n\t"
159 " movi a0, 1\n\t"
160 " wsr a0, WINDOWSTART\n\t"
161 " rsync\n\t"
162 " movi a1, " INIT_STACK "\n\t"
163 " call4 c_boot\n\t");
164
165 /* Unfortunately the SOF kernel loader doesn't understand the boot
166 * vector in the ELF/rimage file yet, so we still need a stub to get
167 * actual audio firmware to load. Leave a stub in place that jumps to
168 * our "real" vector. Note that this is frustratingly pessimal: the
169 * kernel wants the entry point to be at the start of the SRAM region,
170 * but (1) Xtensa can only load an immediate from addresses LOWER than
171 * a L32R instruction, which we can't do and so need to jump across a
172 * region to put one, and (2) the vector table that gets displaced has
173 * a 1024 byte alignment requirement, forcing us to waste ~1011 bytes
174 * needlessly.
175 */
176 __asm__(".pushsection .sof_entry.text\n\t"
177 " j 2f\n"
178 ".align 4\n\t"
179 "1:\n\t"
180 " .word mtk_adsp_boot_entry\n"
181 "2:\n\t"
182 " l32r a0, 1b\n\t"
183 " jx a0\n\t"
184 ".popsection");
185
186 /* Initial MPU configuration, needed to enable caching */
enable_mpu(void)187 static void enable_mpu(void)
188 {
189 /* Note: we set the linked/in-use-by-zephyr regions of both
190 * SRAM and DRAM cached for performance. The remainder is
191 * left uncached, as it's likely to be shared with the host
192 * and/or DMA. This seems like a good default choice pending
193 * proper MPU integration
194 */
195 static const uint32_t mpu[][2] = {
196 { 0x00000000, 0x06000 }, /* inaccessible null region */
197 { 0x10000000, 0x06f00 }, /* MMIO registers */
198 { 0x1d000000, 0x06000 }, /* inaccessible */
199 { SRAM_START, 0xf7f00 }, /* cached SRAM */
200 { SRAM_END, 0x06000 }, /* inaccessible */
201 { DRAM_START, 0xf7f00 }, /* cached DRAM */
202 { (uint32_t)&_mtk_adsp_dram_end, 0x06f00 }, /* uncached DRAM */
203 { DRAM_END, 0x06000 }, /* inaccessible top of mem */
204 { DMA_START, 0x06f00 }, /* uncached host "DMA" area */
205 { DMA_END, 0x06000 }, /* inaccessible top of mem */
206 };
207
208 /* Must write BACKWARDS FROM THE END to avoid introducing a
209 * non-monotonic segment at the current instruction fetch. The
210 * exception triggers even if all the segments involved are
211 * disabled!
212 */
213 int32_t nseg = ARRAY_SIZE(mpu);
214
215 for (int32_t i = 31; i >= 32 - nseg; i--) {
216 int32_t mpuidx = i - (32 - nseg);
217 uint32_t addren = mpu[mpuidx][0] | 1;
218 uint32_t segprot = (mpu[mpuidx][1]) | i;
219
220 /* If an active pipelined instruction fetch is in the
221 * same segment, wptlb must be preceded by a memw in
222 * the same cache line. Jumping to an aligned-by-8
223 * address ensures that the following two (3-byte)
224 * instructions are in the same 8 byte-aligned region.
225 */
226 __asm__ volatile(" j 1f\n"
227 ".align 8\n"
228 "1:\n"
229 " memw\n"
230 " wptlb %1, %0"
231 :: "r"(addren), "r"(segprot));
232 }
233 }
234
235 /* Temporary console output, pending integration of a winstream
236 * backend. This simply appends a null-terminated string to an
237 * otherwise unused 1M region of shared DRAM (it's a hole in the SOF
238 * memory map before the DMA memory, so untouched by existing audio
239 * firmware), making early debugging much easier: it can be read
240 * directly out of /dev/mem (with e.g. dd | hexdump) and survives
241 * device resets/panics/etc. But it doesn't handle more than 1M of
242 * output, there's no way to detect a reset of the stream, and in fact
243 * it's actually racy with device startup as if you read too early
244 * you'll see the old run and not the new one. And it's wasteful,
245 * even if this device has a ton of usably-mapped DRAM
246 *
247 * Also note that the storage for the buffer and length value get
248 * reset by the DRAM clear near the end of c_boot(). If you want to
249 * use this for extremely early logging you'll need to stub out the
250 * dram clear and also set buf[0] to 0 manually (as it isn't affected
251 * by device reset).
252 */
253 #ifndef CONFIG_WINSTREAM_CONSOLE
arch_printk_char_out(int c)254 int arch_printk_char_out(int c)
255 {
256 char volatile * const buf = (void *)LOG_BASE;
257 const size_t max = LOG_LEN - 4;
258 int volatile * const len = (int *)&buf[max];
259
260 if (*len < max) {
261 buf[*len + 1] = 0;
262 buf[(*len)++] = c;
263 }
264 return 0;
265 }
266 #endif
267
268 /* Define this here as a simple uncached array, no special linkage requirements */
269 __nocache char _winstream_console_buf[CONFIG_WINSTREAM_CONSOLE_STATIC_SIZE];
270
c_boot(void)271 void c_boot(void)
272 {
273 extern char _bss_start, _bss_end, z_xtensa_vecbase; /* Linker-emitted */
274 uint32_t memctl = 0xffffff00; /* enable all caches */
275
276 /* Clear bss before doing anything else, device memory is
277 * persistent across resets (!) and we'd like our static
278 * variables to be actually zero. Do this without using
279 * memset() out of pedantry (because we don't know which libc is
280 * in use or whether it requires statics).
281 */
282 for (char *p = &_bss_start; p < &_bss_end; p++) {
283 *p = 0;
284 }
285
286 /* Set up MPU memory regions, both for protection and to
287 * enable caching (the hardware defaults is "uncached rwx
288 * memory everywhere").
289 */
290 enable_mpu();
291
292 /* But the CPU core won't actually use the cache without MEMCTL... */
293 __asm__ volatile("wsr %0, MEMCTL; rsync" :: "r"(memctl));
294
295 /* Need the vector base set to receive exceptions and
296 * interrupts (including register window exceptions, meaning
297 * we can't make C function calls until this is done!)
298 */
299 __asm__ volatile("wsr %0, VECBASE; rsync" :: "r"(&z_xtensa_vecbase));
300
301 #ifdef CONFIG_SOC_SERIES_MT8195
302 mtk_adsp_cpu_freq_init();
303 #endif
304
305 /* Likewise, memory power is external to the device, and the
306 * kernel SOF loader doesn't zero it, so zero our unlinked
307 * memory to prevent possible pollution from previous runs.
308 * This region is uncached, no need to flush.
309 */
310 memset(_mtk_adsp_sram_end, 0, SRAM_END - (uint32_t)&_mtk_adsp_sram_end);
311 memset(_mtk_adsp_dram_end, 0, DRAM_END - (uint32_t)&_mtk_adsp_dram_end);
312
313 /* Clear pending interrupts. Note that this hardware has a
314 * habit of starting with all its timer interrupts flagged.
315 * These have to be cleared by writing to the equivalent
316 * CCOMPAREn register. Assumes XCHAL_NUM_TIMERS == 3...
317 */
318 uint32_t val = 0;
319
320 __asm__ volatile("wsr %0, CCOMPARE0" :: "r"(val));
321 __asm__ volatile("wsr %0, CCOMPARE1" :: "r"(val));
322 __asm__ volatile("wsr %0, CCOMPARE2" :: "r"(val));
323 __ASSERT_NO_MSG(XCHAL_NUM_TIMERS == 3);
324 val = 0xffffffff;
325 __asm__ volatile("wsr %0, INTCLEAR" :: "r"(val));
326
327 /* Default console, a driver can override this later */
328 __stdout_hook_install(arch_printk_char_out);
329
330 #ifdef CONFIG_SOC_MT8196
331 mt8196_intc_init();
332 #endif
333
334 void z_prep_c(void);
335 z_prep_c();
336 }
337