1 // SPDX-License-Identifier: BSD-3-Clause
2 //
3 // Copyright(c) 2019 Intel Corporation. All rights reserved.
4 //
5 // Author: Marcin Maka <marcin.maka@linux.intel.com>
6
7 #include <rtos/wait.h>
8 #include <cavs/lps_ctx.h>
9 #include <cavs/lps_wait.h>
10 #include <cavs/mem_window.h>
11 #include <sof/common.h>
12 #include <sof/platform.h>
13 #include <rtos/interrupt.h>
14 #include <sof/lib/memory.h>
15 #include <sof/lib/pm_runtime.h>
16 #include <sof/lib/shim.h>
17 #include <rtos/task.h>
18
19 #include <stdint.h>
20
21 #ifdef __ZEPHYR__
22 /* TODO: declare local copy to avoid naming collisions with Zephyr and SOF */
23 /* headers until common functions can be separated out */
24 int memcpy_s(void *dest, size_t dest_size, const void *src, size_t count);
25 #endif
26
27 #define LPSRAM_MAGIC_VALUE 0x13579BDF
28
29 #define LPSRAM_HEADER_SIZE 0xc00
30
31 struct lpsram_header {
32 uint32_t alt_reset_vector;
33 uint32_t adsp_lpsram_magic;
34 void *lp_restore_vector;
35 uint32_t reserved;
36
37 /* align total size of the structure to the full header size
38 * with bypass vector area
39 */
40 uint8_t rom_bypass_vectors_reserved[LPSRAM_HEADER_SIZE - 16];
41 };
42
43 #define LPSRAM_HEADER_BYPASS_ADDR (LP_SRAM_BASE - SRAM_ALIAS_OFFSET)
44
45 #define LPS_POWER_FLOW_D0_D0I3 1
46 #define LPS_POWER_FLOW_D0I3_D0 0
47
48 #define LPS_BOOT_STACK_SIZE 4096
49 #define PG_TASK_STACK_SIZE 4096
50
51 __aligned(PLATFORM_DCACHE_ALIGN) uint8_t lps_boot_stack[LPS_BOOT_STACK_SIZE];
52 __aligned(PLATFORM_DCACHE_ALIGN) lps_ctx lps_restore;
53 static void *pg_task_ctx;
54 static uint8_t pg_task_stack[PG_TASK_STACK_SIZE];
55
56 static void platform_pg_int_handler(void *arg);
57
platform_pg_task(void)58 static void platform_pg_task(void)
59 {
60 struct lpsram_header *lpsram_hdr = (struct lpsram_header *)
61 LPSRAM_HEADER_BYPASS_ADDR;
62 size_t vector_size;
63 size_t offset_to_entry;
64 int schedule_irq;
65
66 _xtos_set_intlevel(5);
67 xthal_window_spill();
68
69 offset_to_entry = (uint32_t)&lps_pic_restore_vector
70 - (uint32_t)&lps_pic_restore_vector_literals;
71 vector_size = ALIGN_UP_COMPILE((size_t)&lps_pic_restore_vector_end
72 - (size_t)&lps_pic_restore_vector_literals, 4);
73
74 /* Half of area is available,
75 * another half is reserved for custom vectors in future (like WHM)
76 */
77 memcpy_s((void *)LPS_RESTORE_VECTOR_ADDR, LPS_RESTORE_VECTOR_SIZE,
78 &lps_pic_restore_vector_literals, vector_size);
79 dcache_writeback_invalidate_region((__sparse_force void __sparse_cache *)
80 LPS_RESTORE_VECTOR_ADDR, vector_size);
81
82 /* set magic and vector in LPSRAM */
83 lpsram_hdr->adsp_lpsram_magic = LPSRAM_MAGIC_VALUE;
84 lpsram_hdr->lp_restore_vector = (void *)(LPS_RESTORE_VECTOR_ADDR
85 + offset_to_entry);
86
87 /* re-register to change the direction (arg) */
88 schedule_irq = interrupt_get_irq(IRQ_NUM_SOFTWARE3, NULL);
89 interrupt_register(schedule_irq,
90 platform_pg_int_handler,
91 (void *)LPS_POWER_FLOW_D0I3_D0);
92
93 /* enable all INTs that should turn the dsp on */
94 arch_interrupt_enable_mask(BIT(PLATFORM_SCHEDULE_IRQ) |
95 BIT(IRQ_NUM_EXT_LEVEL2) |
96 BIT(IRQ_NUM_EXT_LEVEL5));
97
98 while (1) {
99 /* flush caches and handle int or pwr off */
100 xthal_dcache_all_writeback_inv();
101 arch_wait_for_interrupt(0);
102 }
103 }
104
platform_pg_int_handler(void * arg)105 static void platform_pg_int_handler(void *arg)
106 {
107 uint32_t dir = (uint32_t)arg;
108
109 if (dir == LPS_POWER_FLOW_D0_D0I3) {
110 pm_runtime_put(PM_RUNTIME_DSP, PLATFORM_PRIMARY_CORE_ID);
111
112 /* init power flow task */
113 if (!pg_task_ctx)
114 task_context_alloc(&pg_task_ctx);
115 task_context_init(pg_task_ctx, platform_pg_task, NULL, NULL,
116 PLATFORM_PRIMARY_CORE_ID,
117 pg_task_stack, sizeof(pg_task_stack));
118
119 /* set TCB to power flow task */
120 task_context_set(pg_task_ctx);
121
122 arch_interrupt_disable_mask(0xffffffff);
123 } else {
124 pm_runtime_get(PM_RUNTIME_DSP, PLATFORM_PRIMARY_CORE_ID);
125
126 /* set TCB to the one stored in platform_power_gate() */
127 task_context_set(lps_restore.task_ctx);
128 arch_interrupt_disable_mask(0xffffffff);
129 #if CONFIG_MEM_WND
130 platform_memory_windows_init(0);
131 #endif
132 arch_interrupt_enable_mask(lps_restore.intenable);
133 }
134 }
135
lps_wait_for_interrupt(int level)136 void lps_wait_for_interrupt(int level)
137 {
138 int schedule_irq;
139
140 /* store the current state */
141
142 lps_restore.intenable = arch_interrupt_get_enabled();
143
144 lps_restore.threadptr = cpu_read_threadptr();
145 lps_restore.task_ctx = (void *)task_context_get();
146 lps_restore.memmap_vecbase_reset = cpu_read_vecbase();
147 lps_restore.vector_level_2 = (void *)cpu_read_excsave2();
148 lps_restore.vector_level_3 = (void *)cpu_read_excsave3();
149 lps_restore.vector_level_4 = (void *)cpu_read_excsave4();
150 lps_restore.vector_level_5 = (void *)cpu_read_excsave5();
151
152 /* use SW INT handler to do the context switch directly there */
153 schedule_irq = interrupt_get_irq(IRQ_NUM_SOFTWARE3, NULL);
154 interrupt_register(schedule_irq,
155 platform_pg_int_handler,
156 (void *)LPS_POWER_FLOW_D0_D0I3);
157 arch_interrupt_disable_mask(0xffffffff);
158 _xtos_set_intlevel(0);
159 interrupt_enable(schedule_irq, NULL);
160 interrupt_set(schedule_irq);
161 }
162
163