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