1/* Copyright (c) 2022 Intel Corporation
2 * SPDX-License-Identifier: Apache-2.0
3 */
4
5#include "asm_memory_management.h"
6
7	.section .text, "ax"
8	.align 64
9power_down_literals:
10	.literal_position
11ipc_flag:
12	.word 0x80000000 // IPC_DIPCTDR_BUSY
13sram_dis_loop_cnt:
14	.word 4096
15
16	.global power_down
17	.type power_down, @function
18
19 /**
20 * @brief Perform power down.
21 *
22 * Depending on arguments, memories are switched off.
23 *
24 * @param A2 - argument for LPSRAM
25 * @param A3 - argument for HPSRAM
26 * @param A4 - send response to ipc
27 */
28
29#define IPC_HOST_BASE			0x00073000
30#define b_disable_lpsram		a2
31#define b_disable_hpsram		a3
32#define b_ipc_response			a4
33#define temp_reg0			a6
34#define temp_reg1			a7
35#define temp_reg2			a8
36#define temp_reg3			a9
37#define temp_reg4			a10
38#define temp_reg5			a11
39#define temp_reg6			a12
40#define p_ipc_regs			a13
41#define u32_ipc_response_mask		a14
42#define pfl_reg				a15
43
44power_down:
45	entry sp, 32
46	/**
47	 * effectively executes:
48	 * xthal_dcache_region_lock(&literals, 128);
49	 * xthal_icache_region_lock(&powerdown, 256);
50	 */
51	movi pfl_reg, power_down_literals
52	dpfl pfl_reg, 0
53	dpfl pfl_reg, 64
54
55	movi pfl_reg, power_down
56	ipfl pfl_reg, 0
57	ipfl pfl_reg, 64
58	ipfl pfl_reg, 128
59	ipfl pfl_reg, 192
60
61	/* move some values to registries before switching off whole memory */
62	/* load address of DIPCTDR register */
63	movi p_ipc_regs, IPC_HOST_BASE
64	movi u32_ipc_response_mask, 0x20000000
65#if CONFIG_XTENSA_MMU
66	/**
67	 * Preload the IPC register to ensure the TLB entry is present.
68	 * This addresses an issue on platforms with an MMU where a
69	 * LoadStoreTLBMissCause exception occurs when accessing hardware
70	 * registers during the power-down process. By preloading the IPC
71	 * register, we ensure that the necessary TLB entry is available,
72	 * preventing a double exception (LoadStoreTLBMissCause followed by
73	 * InstrPIFDataErrorCause) when accessing the IPC register after
74	 * HPSRAM is powered down.
75	 *
76	 * Two solutions were considered:
77	 * 1. Use TLB way9 to lock IPC MMIO registers (Zephyr PR80333)
78	 * 2. Manually force TLB entry to be fetched in power_down (this solution)
79	 *
80	 * The decision was made to proceed with this solution due to its
81	 * simplicity and directness, despite the potential performance benefits
82	 * of the TLB way9 approach. The TLB way9 approach would also reserve
83	 * way9, potentially limiting its use for other purposes in the future.
84	 */
85	l32i pfl_reg, p_ipc_regs, 0
86#endif
87
88_PD_DISABLE_LPSRAM:
89/**
90 * effectively executes:
91 * if (b_disable_lpsram) {
92 *     ace_lpsram_power_down_entire();
93 * }
94 */
95	beqz b_disable_lpsram, _PD_DISABLE_HPSRAM
96	m_ace_lpsram_power_down_entire temp_reg0, temp_reg1, temp_reg2, temp_reg3
97
98_PD_DISABLE_HPSRAM:
99/**
100 * effectively executes:
101 * if (b_disable_hpsram) {
102 *     ace_hpsram_power_down_entire();
103 * }
104 */
105	beqz b_disable_hpsram, _PD_SEND_IPC
106	m_ace_hpsram_power_down_entire temp_reg0, temp_reg1, temp_reg2, temp_reg3
107
108_PD_SEND_IPC:
109	/**
110	 * Send IPC to host informing of PD completion - Clear BUSY
111	 * bit by writing IPC_DIPCTDR_BUSY to IPC_DIPCTDR
112	 * and writing IPC_DIPCTDA_DONE to IPC_DIPCTDA
113	 */
114
115	/**
116	 * effecfively executes:
117	 * if (b_ipc_response)
118	 * {
119	 *     temp_reg0 = *p_ipc_regs;
120	 *     temp_reg0 = temp_reg0 | 0x80000000;
121	 *     temp_reg0 = temp_reg0 | u32_ipc_response_mask;
122	 *     *(p_ipc_regs + 0x180) = 0x0;
123	 *     *(p_ipc_regs + 0x10) = temp_reg0;
124	 * }
125	 */
126	beqz b_ipc_response, _PD_SLEEP
127	/* copy value from IPCxTDR */
128	l32i temp_reg0, p_ipc_regs, 0
129	/* set IPC Busy bit to 1 */
130	movi temp_reg1, 1
131	slli temp_reg1, temp_reg1, 31
132	or temp_reg0, temp_reg0, temp_reg1
133	/* mark the message as a reply */
134	or temp_reg0, temp_reg0, u32_ipc_response_mask
135	/* clear IPCxIDD i.e. message extension */
136	movi temp_reg1, 0
137	s32i temp_reg1, p_ipc_regs, 0x180
138	/* write response to IPCxIDR */
139	s32i temp_reg0, p_ipc_regs, 0x10
140
141_PD_SLEEP:
142/* effecfively executes:
143 * xmp_spin()
144 * waiti 5
145 */
146	movi temp_reg0, 128
147loop:
148	addi temp_reg0, temp_reg0, -1
149	bnez temp_reg0, loop
150
151	extw
152	extw
153	waiti 5
154	1:
155	j 1b
156
157.size power_down , . - power_down
158