xref: /rk3399_ARM-atf/plat/imx/imx8ulp/imx8ulp_psci.c (revision daa4478a3cb2f86501c37e5a301cd4d6a6e60ee6)
1fcd41e86SJacky Bai /*
2fcd41e86SJacky Bai  * Copyright 2021-2024 NXP
3fcd41e86SJacky Bai  *
4fcd41e86SJacky Bai  * SPDX-License-Identifier: BSD-3-Clause
5fcd41e86SJacky Bai  */
6fcd41e86SJacky Bai 
7fcd41e86SJacky Bai #include <stdbool.h>
8fcd41e86SJacky Bai 
9fcd41e86SJacky Bai #include <arch.h>
10fcd41e86SJacky Bai #include <arch_helpers.h>
11fcd41e86SJacky Bai #include <common/debug.h>
12fcd41e86SJacky Bai #include <drivers/arm/gicv3.h>
13fcd41e86SJacky Bai #include <lib/mmio.h>
14fcd41e86SJacky Bai #include <lib/psci/psci.h>
15fcd41e86SJacky Bai 
16fcd41e86SJacky Bai #include <plat_imx8.h>
17*daa4478aSJacky Bai #include <upower_api.h>
18fcd41e86SJacky Bai 
19fcd41e86SJacky Bai static uintptr_t secure_entrypoint;
20fcd41e86SJacky Bai 
21fcd41e86SJacky Bai #define CORE_PWR_STATE(state) ((state)->pwr_domain_state[MPIDR_AFFLVL0])
22fcd41e86SJacky Bai #define CLUSTER_PWR_STATE(state) ((state)->pwr_domain_state[MPIDR_AFFLVL1])
23fcd41e86SJacky Bai #define SYSTEM_PWR_STATE(state) ((state)->pwr_domain_state[PLAT_MAX_PWR_LVL])
24fcd41e86SJacky Bai 
25fcd41e86SJacky Bai #define RVBARADDRx(c)		(IMX_SIM1_BASE + 0x5c + 0x4 * (c))
26fcd41e86SJacky Bai #define WKPUx(c)		(IMX_SIM1_BASE + 0x3c + 0x4 * (c))
27fcd41e86SJacky Bai #define AD_COREx_LPMODE(c)	(IMX_CMC1_BASE + 0x50 + 0x4 * (c))
28fcd41e86SJacky Bai 
29*daa4478aSJacky Bai #define PMIC_CFG(v, m, msk)		\
30*daa4478aSJacky Bai 	{				\
31*daa4478aSJacky Bai 		.volt = (v),		\
32*daa4478aSJacky Bai 		.mode = (m),		\
33*daa4478aSJacky Bai 		.mode_msk = (msk),	\
34*daa4478aSJacky Bai 	}
35*daa4478aSJacky Bai 
36*daa4478aSJacky Bai #define PAD_CFG(c, r, t)		\
37*daa4478aSJacky Bai 	{				\
38*daa4478aSJacky Bai 		.pad_close = (c),	\
39*daa4478aSJacky Bai 		.pad_reset = (r),	\
40*daa4478aSJacky Bai 		.pad_tqsleep = (t)	\
41*daa4478aSJacky Bai 	}
42*daa4478aSJacky Bai 
43*daa4478aSJacky Bai #define BIAS_CFG(m, n, p, mbias)	\
44*daa4478aSJacky Bai 	{				\
45*daa4478aSJacky Bai 		.dombias_cfg = {	\
46*daa4478aSJacky Bai 			.mode = (m),	\
47*daa4478aSJacky Bai 			.rbbn = (n),	\
48*daa4478aSJacky Bai 			.rbbp = (p),	\
49*daa4478aSJacky Bai 		},			\
50*daa4478aSJacky Bai 		.membias_cfg = {mbias},	\
51*daa4478aSJacky Bai 	}
52*daa4478aSJacky Bai 
53*daa4478aSJacky Bai #define SWT_BOARD(swt_on, msk)	\
54*daa4478aSJacky Bai 	{			\
55*daa4478aSJacky Bai 		.on = (swt_on),	\
56*daa4478aSJacky Bai 		.mask = (msk),	\
57*daa4478aSJacky Bai 	}
58*daa4478aSJacky Bai 
59*daa4478aSJacky Bai #define SWT_MEM(a, p, m)	\
60*daa4478aSJacky Bai 	{			\
61*daa4478aSJacky Bai 		.array = (a),	\
62*daa4478aSJacky Bai 		.perif = (p),	\
63*daa4478aSJacky Bai 		.mask = (m),	\
64*daa4478aSJacky Bai 	}
65*daa4478aSJacky Bai 
66fcd41e86SJacky Bai static int imx_pwr_set_cpu_entry(unsigned int cpu, unsigned int entry)
67fcd41e86SJacky Bai {
68fcd41e86SJacky Bai 	mmio_write_32(RVBARADDRx(cpu), entry);
69fcd41e86SJacky Bai 
70fcd41e86SJacky Bai 	/* set update bit */
71fcd41e86SJacky Bai 	mmio_write_32(IMX_SIM1_BASE + 0x8, mmio_read_32(IMX_SIM1_BASE + 0x8) | BIT_32(24 + cpu));
72fcd41e86SJacky Bai 	/* wait for ack */
73fcd41e86SJacky Bai 	while (!(mmio_read_32(IMX_SIM1_BASE + 0x8) & BIT_32(26 + cpu))) {
74fcd41e86SJacky Bai 	}
75fcd41e86SJacky Bai 
76fcd41e86SJacky Bai 	/* clear update bit */
77fcd41e86SJacky Bai 	mmio_write_32(IMX_SIM1_BASE + 0x8, mmio_read_32(IMX_SIM1_BASE + 0x8) & ~BIT_32(24 + cpu));
78fcd41e86SJacky Bai 	/* clear ack bit */
79fcd41e86SJacky Bai 	mmio_write_32(IMX_SIM1_BASE + 0x8, mmio_read_32(IMX_SIM1_BASE + 0x8) | BIT_32(26 + cpu));
80fcd41e86SJacky Bai 
81fcd41e86SJacky Bai 	return 0;
82fcd41e86SJacky Bai }
83fcd41e86SJacky Bai 
84fcd41e86SJacky Bai int imx_pwr_domain_on(u_register_t mpidr)
85fcd41e86SJacky Bai {
86fcd41e86SJacky Bai 	unsigned int cpu = MPIDR_AFFLVL0_VAL(mpidr);
87fcd41e86SJacky Bai 
88fcd41e86SJacky Bai 	imx_pwr_set_cpu_entry(cpu, secure_entrypoint);
89fcd41e86SJacky Bai 
90fcd41e86SJacky Bai 	mmio_write_32(IMX_CMC1_BASE + 0x18, 0x3f);
91fcd41e86SJacky Bai 	mmio_write_32(IMX_CMC1_BASE + 0x50 + 0x4 * cpu, 0);
92fcd41e86SJacky Bai 
93fcd41e86SJacky Bai 	/* enable wku wakeup for idle */
94fcd41e86SJacky Bai 	mmio_write_32(IMX_SIM1_BASE + 0x3c + 0x4 * cpu, 0xffffffff);
95fcd41e86SJacky Bai 
96fcd41e86SJacky Bai 	return PSCI_E_SUCCESS;
97fcd41e86SJacky Bai }
98fcd41e86SJacky Bai 
99fcd41e86SJacky Bai void imx_pwr_domain_on_finish(const psci_power_state_t *target_state)
100fcd41e86SJacky Bai {
101fcd41e86SJacky Bai 	imx_pwr_set_cpu_entry(0, IMX_ROM_ENTRY);
102fcd41e86SJacky Bai 	plat_gic_pcpu_init();
103fcd41e86SJacky Bai 	plat_gic_cpuif_enable();
104fcd41e86SJacky Bai }
105fcd41e86SJacky Bai 
106fcd41e86SJacky Bai int imx_validate_ns_entrypoint(uintptr_t ns_entrypoint)
107fcd41e86SJacky Bai {
108fcd41e86SJacky Bai 	return PSCI_E_SUCCESS;
109fcd41e86SJacky Bai }
110fcd41e86SJacky Bai 
111fcd41e86SJacky Bai void imx_pwr_domain_off(const psci_power_state_t *target_state)
112fcd41e86SJacky Bai {
113fcd41e86SJacky Bai 	unsigned int cpu = MPIDR_AFFLVL0_VAL(read_mpidr_el1());
114fcd41e86SJacky Bai 
115fcd41e86SJacky Bai 	plat_gic_cpuif_disable();
116fcd41e86SJacky Bai 
117fcd41e86SJacky Bai 	/* disable wakeup */
118fcd41e86SJacky Bai 	mmio_write_32(WKPUx(cpu), 0);
119fcd41e86SJacky Bai 
120*daa4478aSJacky Bai 	/* set core power mode to PD */
121fcd41e86SJacky Bai 	mmio_write_32(AD_COREx_LPMODE(cpu), 0x3);
122fcd41e86SJacky Bai }
123*daa4478aSJacky Bai /* APD power mode config */
124*daa4478aSJacky Bai ps_apd_pwr_mode_cfgs_t apd_pwr_mode_cfgs = {
125*daa4478aSJacky Bai 	[ADMA_PWR_MODE] = {
126*daa4478aSJacky Bai 		.swt_board_offs = 0x120,
127*daa4478aSJacky Bai 		.swt_mem_offs = 0x128,
128*daa4478aSJacky Bai 		.pmic_cfg = PMIC_CFG(0x23, 0x0, 0x2),
129*daa4478aSJacky Bai 		.pad_cfg = PAD_CFG(0x0, 0x0, 0x0deb7a00),
130*daa4478aSJacky Bai 		.bias_cfg = BIAS_CFG(0x0, 0x2, 0x2, 0x0),
131*daa4478aSJacky Bai 	},
132*daa4478aSJacky Bai 
133*daa4478aSJacky Bai 	[ACT_PWR_MODE] = {
134*daa4478aSJacky Bai 		.swt_board_offs = 0x110,
135*daa4478aSJacky Bai 		.swt_mem_offs = 0x118,
136*daa4478aSJacky Bai 		.pmic_cfg = PMIC_CFG(0x23, 0x2, 0x2),
137*daa4478aSJacky Bai 		.pad_cfg = PAD_CFG(0x0, 0x0, 0x0deb7a00),
138*daa4478aSJacky Bai 		.bias_cfg = BIAS_CFG(0x0, 0x2, 0x2, 0x0),
139*daa4478aSJacky Bai 	},
140*daa4478aSJacky Bai };
141*daa4478aSJacky Bai 
142*daa4478aSJacky Bai /* APD power switch config */
143*daa4478aSJacky Bai ps_apd_swt_cfgs_t apd_swt_cfgs = {
144*daa4478aSJacky Bai 	[ADMA_PWR_MODE] = {
145*daa4478aSJacky Bai 		.swt_board[0] = SWT_BOARD(0x74, 0x7c),
146*daa4478aSJacky Bai 		.swt_mem[0] = SWT_MEM(0x0001fffd, 0x0001fffd, 0x1fffd),
147*daa4478aSJacky Bai 		.swt_mem[1] = SWT_MEM(0x0, 0x0, 0x0),
148*daa4478aSJacky Bai 	},
149*daa4478aSJacky Bai 
150*daa4478aSJacky Bai 	[ACT_PWR_MODE] = {
151*daa4478aSJacky Bai 		.swt_board[0] = SWT_BOARD(0x74, 0x7c),
152*daa4478aSJacky Bai 		.swt_mem[0] = SWT_MEM(0x0001fffd, 0x0001fffd, 0x1fffd),
153*daa4478aSJacky Bai 		.swt_mem[1] = SWT_MEM(0x0, 0x0, 0x0),
154*daa4478aSJacky Bai 	},
155*daa4478aSJacky Bai };
156*daa4478aSJacky Bai 
157*daa4478aSJacky Bai struct ps_pwr_mode_cfg_t *pwr_sys_cfg = (struct ps_pwr_mode_cfg_t *)UPWR_DRAM_SHARED_BASE_ADDR;
158*daa4478aSJacky Bai 
159*daa4478aSJacky Bai void imx_set_pwr_mode_cfg(abs_pwr_mode_t mode)
160*daa4478aSJacky Bai {
161*daa4478aSJacky Bai 	if (mode >= NUM_PWR_MODES) {
162*daa4478aSJacky Bai 		return;
163*daa4478aSJacky Bai 	}
164*daa4478aSJacky Bai 
165*daa4478aSJacky Bai 	/* apd power mode config */
166*daa4478aSJacky Bai 	memcpy(&pwr_sys_cfg->ps_apd_pwr_mode_cfg[mode], &apd_pwr_mode_cfgs[mode],
167*daa4478aSJacky Bai 		 sizeof(struct ps_apd_pwr_mode_cfg_t));
168*daa4478aSJacky Bai 
169*daa4478aSJacky Bai 	/* apd power switch config */
170*daa4478aSJacky Bai 	memcpy(&pwr_sys_cfg->ps_apd_swt_cfg[mode], &apd_swt_cfgs[mode], sizeof(swt_config_t));
171*daa4478aSJacky Bai }
172*daa4478aSJacky Bai 
173*daa4478aSJacky Bai void imx_domain_suspend(const psci_power_state_t *target_state)
174*daa4478aSJacky Bai {
175*daa4478aSJacky Bai 	unsigned int cpu = MPIDR_AFFLVL0_VAL(read_mpidr_el1());
176*daa4478aSJacky Bai 
177*daa4478aSJacky Bai 	if (is_local_state_off(CORE_PWR_STATE(target_state))) {
178*daa4478aSJacky Bai 		plat_gic_cpuif_disable();
179*daa4478aSJacky Bai 		imx_pwr_set_cpu_entry(cpu, secure_entrypoint);
180*daa4478aSJacky Bai 		/* core put into power down */
181*daa4478aSJacky Bai 		mmio_write_32(IMX_CMC1_BASE + 0x50 + 0x4 * cpu, 0x3);
182*daa4478aSJacky Bai 		/* FIXME config wakeup interrupt in WKPU */
183*daa4478aSJacky Bai 		mmio_write_32(IMX_SIM1_BASE + 0x3c + 0x4 * cpu, 0x7fffffe3);
184*daa4478aSJacky Bai 	} else {
185*daa4478aSJacky Bai 		/* for core standby/retention mode */
186*daa4478aSJacky Bai 		mmio_write_32(IMX_CMC1_BASE + 0x50 + 0x4 * cpu, 0x1);
187*daa4478aSJacky Bai 		mmio_write_32(IMX_SIM1_BASE + 0x3c + 0x4 * cpu, 0x7fffffe3);
188*daa4478aSJacky Bai 		dsb();
189*daa4478aSJacky Bai 		write_scr_el3(read_scr_el3() | SCR_FIQ_BIT);
190*daa4478aSJacky Bai 		isb();
191*daa4478aSJacky Bai 	}
192*daa4478aSJacky Bai 
193*daa4478aSJacky Bai 	if (!is_local_state_run(CLUSTER_PWR_STATE(target_state))) {
194*daa4478aSJacky Bai 		/* TODO imx_set_wakeup() based on GIC config*/
195*daa4478aSJacky Bai 
196*daa4478aSJacky Bai 		/*
197*daa4478aSJacky Bai 		 * just for sleep mode for now, need to update to
198*daa4478aSJacky Bai 		 * support more mode, same for suspend finish call back.
199*daa4478aSJacky Bai 		 */
200*daa4478aSJacky Bai 		mmio_write_32(IMX_CMC1_BASE + 0x10, 0x1);
201*daa4478aSJacky Bai 		mmio_write_32(IMX_CMC1_BASE + 0x20, 0x1);
202*daa4478aSJacky Bai 	}
203*daa4478aSJacky Bai 
204*daa4478aSJacky Bai 	/* TODO, may need to add more system level config here */
205*daa4478aSJacky Bai 	if (is_local_state_off(SYSTEM_PWR_STATE(target_state))) {
206*daa4478aSJacky Bai 		/*
207*daa4478aSJacky Bai 		 * low power mode config info used by upower
208*daa4478aSJacky Bai 		 * to do low power mode transition.
209*daa4478aSJacky Bai 		 */
210*daa4478aSJacky Bai 		imx_set_pwr_mode_cfg(ADMA_PWR_MODE);
211*daa4478aSJacky Bai 		imx_set_pwr_mode_cfg(ACT_PWR_MODE);
212*daa4478aSJacky Bai 	}
213*daa4478aSJacky Bai }
214*daa4478aSJacky Bai 
215*daa4478aSJacky Bai void imx_domain_suspend_finish(const psci_power_state_t *target_state)
216*daa4478aSJacky Bai {
217*daa4478aSJacky Bai 	unsigned int cpu = MPIDR_AFFLVL0_VAL(read_mpidr_el1());
218*daa4478aSJacky Bai 
219*daa4478aSJacky Bai 	if (is_local_state_off(SYSTEM_PWR_STATE(target_state))) {
220*daa4478aSJacky Bai 		/* TODO reverse setting for system level */
221*daa4478aSJacky Bai 	}
222*daa4478aSJacky Bai 
223*daa4478aSJacky Bai 	if (!is_local_state_run(CLUSTER_PWR_STATE(target_state))) {
224*daa4478aSJacky Bai 		mmio_write_32(IMX_CMC1_BASE + 0x20, 0x0);
225*daa4478aSJacky Bai 		mmio_write_32(IMX_CMC1_BASE + 0x10, 0x0);
226*daa4478aSJacky Bai 	}
227*daa4478aSJacky Bai 
228*daa4478aSJacky Bai 	/* clear core's LPM setting */
229*daa4478aSJacky Bai 	mmio_write_32(IMX_CMC1_BASE + 0x50 + 0x4 * cpu, 0x0);
230*daa4478aSJacky Bai 	mmio_write_32(IMX_SIM1_BASE + 0x3c + 0x4 * cpu, 0x0);
231*daa4478aSJacky Bai 
232*daa4478aSJacky Bai 	if (is_local_state_off(CORE_PWR_STATE(target_state))) {
233*daa4478aSJacky Bai 		imx_pwr_set_cpu_entry(0, IMX_ROM_ENTRY);
234*daa4478aSJacky Bai 		plat_gic_cpuif_enable();
235*daa4478aSJacky Bai 	} else {
236*daa4478aSJacky Bai 		dsb();
237*daa4478aSJacky Bai 		write_scr_el3(read_scr_el3() & (~SCR_FIQ_BIT));
238*daa4478aSJacky Bai 		isb();
239*daa4478aSJacky Bai 	}
240*daa4478aSJacky Bai }
241fcd41e86SJacky Bai 
242fcd41e86SJacky Bai void __dead2 imx8ulp_pwr_domain_pwr_down_wfi(const psci_power_state_t *target_state)
243fcd41e86SJacky Bai {
244fcd41e86SJacky Bai 	while (1) {
245fcd41e86SJacky Bai 		wfi();
246fcd41e86SJacky Bai 	}
247fcd41e86SJacky Bai }
248fcd41e86SJacky Bai 
249fcd41e86SJacky Bai void __dead2 imx8ulp_system_reset(void)
250fcd41e86SJacky Bai {
251fcd41e86SJacky Bai 	imx_pwr_set_cpu_entry(0, IMX_ROM_ENTRY);
252fcd41e86SJacky Bai 
253fcd41e86SJacky Bai 	/* Write invalid command to WDOG CNT to trigger reset */
254fcd41e86SJacky Bai 	mmio_write_32(IMX_WDOG3_BASE + 0x4, 0x12345678);
255fcd41e86SJacky Bai 
256fcd41e86SJacky Bai 	while (true) {
257fcd41e86SJacky Bai 		wfi();
258fcd41e86SJacky Bai 	}
259fcd41e86SJacky Bai }
260fcd41e86SJacky Bai 
261*daa4478aSJacky Bai int imx_validate_power_state(unsigned int power_state,
262*daa4478aSJacky Bai 			 psci_power_state_t *req_state)
263*daa4478aSJacky Bai {
264*daa4478aSJacky Bai 	int pwr_lvl = psci_get_pstate_pwrlvl(power_state);
265*daa4478aSJacky Bai 	int pwr_type = psci_get_pstate_type(power_state);
266*daa4478aSJacky Bai 
267*daa4478aSJacky Bai 	if (pwr_lvl > PLAT_MAX_PWR_LVL) {
268*daa4478aSJacky Bai 		return PSCI_E_INVALID_PARAMS;
269*daa4478aSJacky Bai 	}
270*daa4478aSJacky Bai 
271*daa4478aSJacky Bai 	if (pwr_type == PSTATE_TYPE_STANDBY) {
272*daa4478aSJacky Bai 		CORE_PWR_STATE(req_state) = PLAT_MAX_RET_STATE;
273*daa4478aSJacky Bai 		CLUSTER_PWR_STATE(req_state) = PLAT_MAX_RET_STATE;
274*daa4478aSJacky Bai 	}
275*daa4478aSJacky Bai 
276*daa4478aSJacky Bai 	/* No power down state support */
277*daa4478aSJacky Bai 	if (pwr_type == PSTATE_TYPE_POWERDOWN) {
278*daa4478aSJacky Bai 		return PSCI_E_INVALID_PARAMS;
279*daa4478aSJacky Bai 	}
280*daa4478aSJacky Bai 
281*daa4478aSJacky Bai 	return PSCI_E_SUCCESS;
282*daa4478aSJacky Bai }
283*daa4478aSJacky Bai 
284*daa4478aSJacky Bai void imx_get_sys_suspend_power_state(psci_power_state_t *req_state)
285*daa4478aSJacky Bai {
286*daa4478aSJacky Bai 	unsigned int i;
287*daa4478aSJacky Bai 
288*daa4478aSJacky Bai 	for (i = IMX_PWR_LVL0; i <= PLAT_MAX_PWR_LVL; i++) {
289*daa4478aSJacky Bai 		req_state->pwr_domain_state[i] = PLAT_POWER_DOWN_OFF_STATE;
290*daa4478aSJacky Bai 	}
291*daa4478aSJacky Bai }
292*daa4478aSJacky Bai 
293fcd41e86SJacky Bai static const plat_psci_ops_t imx_plat_psci_ops = {
294fcd41e86SJacky Bai 	.pwr_domain_on = imx_pwr_domain_on,
295fcd41e86SJacky Bai 	.pwr_domain_on_finish = imx_pwr_domain_on_finish,
296fcd41e86SJacky Bai 	.validate_ns_entrypoint = imx_validate_ns_entrypoint,
297fcd41e86SJacky Bai 	.system_reset = imx8ulp_system_reset,
298fcd41e86SJacky Bai 	.pwr_domain_off = imx_pwr_domain_off,
299*daa4478aSJacky Bai 	.pwr_domain_suspend = imx_domain_suspend,
300*daa4478aSJacky Bai 	.pwr_domain_suspend_finish = imx_domain_suspend_finish,
301*daa4478aSJacky Bai 	.get_sys_suspend_power_state = imx_get_sys_suspend_power_state,
302*daa4478aSJacky Bai 	.validate_power_state = imx_validate_power_state,
303fcd41e86SJacky Bai 	.pwr_domain_pwr_down_wfi = imx8ulp_pwr_domain_pwr_down_wfi,
304fcd41e86SJacky Bai };
305fcd41e86SJacky Bai 
306fcd41e86SJacky Bai int plat_setup_psci_ops(uintptr_t sec_entrypoint,
307fcd41e86SJacky Bai 			const plat_psci_ops_t **psci_ops)
308fcd41e86SJacky Bai {
309fcd41e86SJacky Bai 	secure_entrypoint = sec_entrypoint;
310fcd41e86SJacky Bai 	imx_pwr_set_cpu_entry(0, sec_entrypoint);
311fcd41e86SJacky Bai 	*psci_ops = &imx_plat_psci_ops;
312fcd41e86SJacky Bai 
313fcd41e86SJacky Bai 	mmio_write_32(IMX_CMC1_BASE + 0x18, 0x3f);
314fcd41e86SJacky Bai 	mmio_write_32(IMX_SIM1_BASE + 0x3c, 0xffffffff);
315fcd41e86SJacky Bai 
316fcd41e86SJacky Bai 	return 0;
317fcd41e86SJacky Bai }
318