xref: /rk3399_ARM-atf/plat/imx/imx8ulp/imx8ulp_psci.c (revision e1d5c3c8f435424394367e2ff19240b1b8a3073c)
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>
17daa4478aSJacky Bai #include <upower_api.h>
18fcd41e86SJacky Bai 
19478af8d3SJacky Bai extern void cgc1_save(void);
20478af8d3SJacky Bai extern void cgc1_restore(void);
21478af8d3SJacky Bai extern void imx_apd_ctx_save(unsigned int cpu);
22478af8d3SJacky Bai extern void imx_apd_ctx_restore(unsigned int cpu);
23478af8d3SJacky Bai extern void usb_wakeup_enable(bool enable);
2436af80c2SJacky Bai extern void upower_wait_resp(void);
2536af80c2SJacky Bai extern bool is_lpav_owned_by_apd(void);
2636af80c2SJacky Bai extern void apd_io_pad_off(void);
2736af80c2SJacky Bai extern int upower_pmic_i2c_read(uint32_t reg_addr, uint32_t *reg_val);
28478af8d3SJacky Bai 
29fcd41e86SJacky Bai static uintptr_t secure_entrypoint;
30fcd41e86SJacky Bai 
31fcd41e86SJacky Bai #define CORE_PWR_STATE(state) ((state)->pwr_domain_state[MPIDR_AFFLVL0])
32fcd41e86SJacky Bai #define CLUSTER_PWR_STATE(state) ((state)->pwr_domain_state[MPIDR_AFFLVL1])
33fcd41e86SJacky Bai #define SYSTEM_PWR_STATE(state) ((state)->pwr_domain_state[PLAT_MAX_PWR_LVL])
34fcd41e86SJacky Bai 
35fcd41e86SJacky Bai #define RVBARADDRx(c)		(IMX_SIM1_BASE + 0x5c + 0x4 * (c))
36fcd41e86SJacky Bai #define WKPUx(c)		(IMX_SIM1_BASE + 0x3c + 0x4 * (c))
37fcd41e86SJacky Bai #define AD_COREx_LPMODE(c)	(IMX_CMC1_BASE + 0x50 + 0x4 * (c))
38fcd41e86SJacky Bai 
39daa4478aSJacky Bai #define PMIC_CFG(v, m, msk)		\
40daa4478aSJacky Bai 	{				\
41daa4478aSJacky Bai 		.volt = (v),		\
42daa4478aSJacky Bai 		.mode = (m),		\
43daa4478aSJacky Bai 		.mode_msk = (msk),	\
44daa4478aSJacky Bai 	}
45daa4478aSJacky Bai 
46daa4478aSJacky Bai #define PAD_CFG(c, r, t)		\
47daa4478aSJacky Bai 	{				\
48daa4478aSJacky Bai 		.pad_close = (c),	\
49daa4478aSJacky Bai 		.pad_reset = (r),	\
50daa4478aSJacky Bai 		.pad_tqsleep = (t)	\
51daa4478aSJacky Bai 	}
52daa4478aSJacky Bai 
53daa4478aSJacky Bai #define BIAS_CFG(m, n, p, mbias)	\
54daa4478aSJacky Bai 	{				\
55daa4478aSJacky Bai 		.dombias_cfg = {	\
56daa4478aSJacky Bai 			.mode = (m),	\
57daa4478aSJacky Bai 			.rbbn = (n),	\
58daa4478aSJacky Bai 			.rbbp = (p),	\
59daa4478aSJacky Bai 		},			\
60daa4478aSJacky Bai 		.membias_cfg = {mbias},	\
61daa4478aSJacky Bai 	}
62daa4478aSJacky Bai 
63daa4478aSJacky Bai #define SWT_BOARD(swt_on, msk)	\
64daa4478aSJacky Bai 	{			\
65daa4478aSJacky Bai 		.on = (swt_on),	\
66daa4478aSJacky Bai 		.mask = (msk),	\
67daa4478aSJacky Bai 	}
68daa4478aSJacky Bai 
69daa4478aSJacky Bai #define SWT_MEM(a, p, m)	\
70daa4478aSJacky Bai 	{			\
71daa4478aSJacky Bai 		.array = (a),	\
72daa4478aSJacky Bai 		.perif = (p),	\
73daa4478aSJacky Bai 		.mask = (m),	\
74daa4478aSJacky Bai 	}
75daa4478aSJacky Bai 
76fcd41e86SJacky Bai static int imx_pwr_set_cpu_entry(unsigned int cpu, unsigned int entry)
77fcd41e86SJacky Bai {
78fcd41e86SJacky Bai 	mmio_write_32(RVBARADDRx(cpu), entry);
79fcd41e86SJacky Bai 
80fcd41e86SJacky Bai 	/* set update bit */
81fcd41e86SJacky Bai 	mmio_write_32(IMX_SIM1_BASE + 0x8, mmio_read_32(IMX_SIM1_BASE + 0x8) | BIT_32(24 + cpu));
82fcd41e86SJacky Bai 	/* wait for ack */
83fcd41e86SJacky Bai 	while (!(mmio_read_32(IMX_SIM1_BASE + 0x8) & BIT_32(26 + cpu))) {
84fcd41e86SJacky Bai 	}
85fcd41e86SJacky Bai 
86fcd41e86SJacky Bai 	/* clear update bit */
87fcd41e86SJacky Bai 	mmio_write_32(IMX_SIM1_BASE + 0x8, mmio_read_32(IMX_SIM1_BASE + 0x8) & ~BIT_32(24 + cpu));
88fcd41e86SJacky Bai 	/* clear ack bit */
89fcd41e86SJacky Bai 	mmio_write_32(IMX_SIM1_BASE + 0x8, mmio_read_32(IMX_SIM1_BASE + 0x8) | BIT_32(26 + cpu));
90fcd41e86SJacky Bai 
91fcd41e86SJacky Bai 	return 0;
92fcd41e86SJacky Bai }
93fcd41e86SJacky Bai 
94*e1d5c3c8SJacky Bai static volatile uint32_t cgc1_nicclk;
95fcd41e86SJacky Bai int imx_pwr_domain_on(u_register_t mpidr)
96fcd41e86SJacky Bai {
97fcd41e86SJacky Bai 	unsigned int cpu = MPIDR_AFFLVL0_VAL(mpidr);
98fcd41e86SJacky Bai 
99fcd41e86SJacky Bai 	imx_pwr_set_cpu_entry(cpu, secure_entrypoint);
100fcd41e86SJacky Bai 
101*e1d5c3c8SJacky Bai 	/* slow down the APD NIC bus clock */
102*e1d5c3c8SJacky Bai 	cgc1_nicclk = mmio_read_32(IMX_CGC1_BASE + 0x34);
103*e1d5c3c8SJacky Bai 	mmio_clrbits_32(IMX_CGC1_BASE + 0x34, GENMASK_32(29, 28));
104*e1d5c3c8SJacky Bai 
105fcd41e86SJacky Bai 	mmio_write_32(IMX_CMC1_BASE + 0x18, 0x3f);
106fcd41e86SJacky Bai 	mmio_write_32(IMX_CMC1_BASE + 0x50 + 0x4 * cpu, 0);
107fcd41e86SJacky Bai 
108fcd41e86SJacky Bai 	/* enable wku wakeup for idle */
109fcd41e86SJacky Bai 	mmio_write_32(IMX_SIM1_BASE + 0x3c + 0x4 * cpu, 0xffffffff);
110fcd41e86SJacky Bai 
111fcd41e86SJacky Bai 	return PSCI_E_SUCCESS;
112fcd41e86SJacky Bai }
113fcd41e86SJacky Bai 
114fcd41e86SJacky Bai void imx_pwr_domain_on_finish(const psci_power_state_t *target_state)
115fcd41e86SJacky Bai {
116fcd41e86SJacky Bai 	imx_pwr_set_cpu_entry(0, IMX_ROM_ENTRY);
117fcd41e86SJacky Bai 	plat_gic_pcpu_init();
118fcd41e86SJacky Bai 	plat_gic_cpuif_enable();
119*e1d5c3c8SJacky Bai 
120*e1d5c3c8SJacky Bai 	/* set APD NIC back to orignally setting */
121*e1d5c3c8SJacky Bai 	mmio_write_32(IMX_CGC1_BASE + 0x34, cgc1_nicclk);
122fcd41e86SJacky Bai }
123fcd41e86SJacky Bai 
124fcd41e86SJacky Bai int imx_validate_ns_entrypoint(uintptr_t ns_entrypoint)
125fcd41e86SJacky Bai {
126fcd41e86SJacky Bai 	return PSCI_E_SUCCESS;
127fcd41e86SJacky Bai }
128fcd41e86SJacky Bai 
129fcd41e86SJacky Bai void imx_pwr_domain_off(const psci_power_state_t *target_state)
130fcd41e86SJacky Bai {
131fcd41e86SJacky Bai 	unsigned int cpu = MPIDR_AFFLVL0_VAL(read_mpidr_el1());
132fcd41e86SJacky Bai 
133fcd41e86SJacky Bai 	plat_gic_cpuif_disable();
134fcd41e86SJacky Bai 
135fcd41e86SJacky Bai 	/* disable wakeup */
136fcd41e86SJacky Bai 	mmio_write_32(WKPUx(cpu), 0);
137fcd41e86SJacky Bai 
138daa4478aSJacky Bai 	/* set core power mode to PD */
139fcd41e86SJacky Bai 	mmio_write_32(AD_COREx_LPMODE(cpu), 0x3);
140fcd41e86SJacky Bai }
141478af8d3SJacky Bai 
142daa4478aSJacky Bai /* APD power mode config */
143daa4478aSJacky Bai ps_apd_pwr_mode_cfgs_t apd_pwr_mode_cfgs = {
144891c547eSJacky Bai 	[DPD_PWR_MODE] = {
145891c547eSJacky Bai 		.swt_board_offs = 0x180,
146891c547eSJacky Bai 		.swt_mem_offs = 0x188,
14736af80c2SJacky Bai 		.pmic_cfg = PMIC_CFG(0x23, 0x0, 0x2),
14836af80c2SJacky Bai 		.pad_cfg = PAD_CFG(0x0, 0xc, 0x01e80a02),
149891c547eSJacky Bai 		.bias_cfg = BIAS_CFG(0x0, 0x2, 0x2, 0x0),
150891c547eSJacky Bai 	},
151891c547eSJacky Bai 
152478af8d3SJacky Bai 	/* PD */
153478af8d3SJacky Bai 	[PD_PWR_MODE] = {
154478af8d3SJacky Bai 		.swt_board_offs = 0x170,
155478af8d3SJacky Bai 		.swt_mem_offs = 0x178,
15636af80c2SJacky Bai 		.pmic_cfg = PMIC_CFG(0x23, 0x0, 0x2),
15736af80c2SJacky Bai 		.pad_cfg = PAD_CFG(0x0, 0xc, 0x01e80a00),
158478af8d3SJacky Bai 		.bias_cfg = BIAS_CFG(0x0, 0x2, 0x2, 0x0),
159478af8d3SJacky Bai 	},
160478af8d3SJacky Bai 
161daa4478aSJacky Bai 	[ADMA_PWR_MODE] = {
162daa4478aSJacky Bai 		.swt_board_offs = 0x120,
163daa4478aSJacky Bai 		.swt_mem_offs = 0x128,
164daa4478aSJacky Bai 		.pmic_cfg = PMIC_CFG(0x23, 0x0, 0x2),
165daa4478aSJacky Bai 		.pad_cfg = PAD_CFG(0x0, 0x0, 0x0deb7a00),
16636af80c2SJacky Bai 		.bias_cfg = BIAS_CFG(0x2, 0x2, 0x2, 0x0),
167daa4478aSJacky Bai 	},
168daa4478aSJacky Bai 
169daa4478aSJacky Bai 	[ACT_PWR_MODE] = {
170daa4478aSJacky Bai 		.swt_board_offs = 0x110,
171daa4478aSJacky Bai 		.swt_mem_offs = 0x118,
17236af80c2SJacky Bai 		.pmic_cfg = PMIC_CFG(0x23, 0x0, 0x2),
173daa4478aSJacky Bai 		.pad_cfg = PAD_CFG(0x0, 0x0, 0x0deb7a00),
17436af80c2SJacky Bai 		.bias_cfg = BIAS_CFG(0x2, 0x2, 0x2, 0x0),
175daa4478aSJacky Bai 	},
176daa4478aSJacky Bai };
177daa4478aSJacky Bai 
178daa4478aSJacky Bai /* APD power switch config */
179daa4478aSJacky Bai ps_apd_swt_cfgs_t apd_swt_cfgs = {
180891c547eSJacky Bai 	[DPD_PWR_MODE] = {
18136af80c2SJacky Bai 		.swt_board[0] = SWT_BOARD(0x0, 0x1fffc),
182891c547eSJacky Bai 		.swt_mem[0] = SWT_MEM(0x0, 0x0, 0x1ffff),
183891c547eSJacky Bai 		.swt_mem[1] = SWT_MEM(0x003fffff, 0x003fffff, 0x0),
184891c547eSJacky Bai 	},
185891c547eSJacky Bai 
186478af8d3SJacky Bai 	[PD_PWR_MODE] = {
18736af80c2SJacky Bai 		.swt_board[0] = SWT_BOARD(0x0, 0x00001fffc),
188478af8d3SJacky Bai 		.swt_mem[0] = SWT_MEM(0x00010c00, 0x0, 0x1ffff),
189478af8d3SJacky Bai 		.swt_mem[1] = SWT_MEM(0x003fffff, 0x003f0000, 0x0),
190478af8d3SJacky Bai 	},
191478af8d3SJacky Bai 
192daa4478aSJacky Bai 	[ADMA_PWR_MODE] = {
19336af80c2SJacky Bai 		.swt_board[0] = SWT_BOARD(0x15f74, 0x15f74),
194478af8d3SJacky Bai 		.swt_mem[0] = SWT_MEM(0x0001fffd, 0x0001fffd, 0x1ffff),
195478af8d3SJacky Bai 		.swt_mem[1] = SWT_MEM(0x003fffff, 0x003fffff, 0x0),
196daa4478aSJacky Bai 	},
197daa4478aSJacky Bai 
198daa4478aSJacky Bai 	[ACT_PWR_MODE] = {
19936af80c2SJacky Bai 		.swt_board[0] = SWT_BOARD(0x15f74, 0x15f74),
200478af8d3SJacky Bai 		.swt_mem[0] = SWT_MEM(0x0001fffd, 0x0001fffd, 0x1ffff),
201478af8d3SJacky Bai 		.swt_mem[1] = SWT_MEM(0x003fffff, 0x003fffff, 0x0),
202daa4478aSJacky Bai 	},
203daa4478aSJacky Bai };
204daa4478aSJacky Bai 
20536af80c2SJacky Bai /* PMIC config for power down, LDO1 should be OFF */
20636af80c2SJacky Bai ps_apd_pmic_reg_data_cfgs_t pd_pmic_reg_cfgs = {
20736af80c2SJacky Bai 	[0] = {
20836af80c2SJacky Bai 		.tag = PMIC_REG_VALID_TAG,
20936af80c2SJacky Bai 		.power_mode = PD_PWR_MODE,
21036af80c2SJacky Bai 		.i2c_addr = 0x30,
21136af80c2SJacky Bai 		.i2c_data = 0x9c,
21236af80c2SJacky Bai 	},
21336af80c2SJacky Bai 	[1] = {
21436af80c2SJacky Bai 		.tag = PMIC_REG_VALID_TAG,
21536af80c2SJacky Bai 		.power_mode = PD_PWR_MODE,
21636af80c2SJacky Bai 		.i2c_addr = 0x22,
21736af80c2SJacky Bai 		.i2c_data = 0xb,
21836af80c2SJacky Bai 	},
21936af80c2SJacky Bai 	[2] = {
22036af80c2SJacky Bai 		.tag = PMIC_REG_VALID_TAG,
22136af80c2SJacky Bai 		.power_mode = ACT_PWR_MODE,
22236af80c2SJacky Bai 		.i2c_addr = 0x30,
22336af80c2SJacky Bai 		.i2c_data = 0x9d,
22436af80c2SJacky Bai 	},
22536af80c2SJacky Bai 	[3] = {
22636af80c2SJacky Bai 		.tag = PMIC_REG_VALID_TAG,
22736af80c2SJacky Bai 		.power_mode = ACT_PWR_MODE,
22836af80c2SJacky Bai 		.i2c_addr = 0x22,
22936af80c2SJacky Bai 		.i2c_data = 0x28,
23036af80c2SJacky Bai 	},
23136af80c2SJacky Bai };
23236af80c2SJacky Bai 
23336af80c2SJacky Bai /* PMIC config for deep power down, BUCK3 should be OFF */
23436af80c2SJacky Bai ps_apd_pmic_reg_data_cfgs_t dpd_pmic_reg_cfgs = {
23536af80c2SJacky Bai 	[0] = {
23636af80c2SJacky Bai 		.tag = PMIC_REG_VALID_TAG,
23736af80c2SJacky Bai 		.power_mode = DPD_PWR_MODE,
23836af80c2SJacky Bai 		.i2c_addr = 0x21,
23936af80c2SJacky Bai 		.i2c_data = 0x78,
24036af80c2SJacky Bai 	},
24136af80c2SJacky Bai 	[1] = {
24236af80c2SJacky Bai 		.tag = PMIC_REG_VALID_TAG,
24336af80c2SJacky Bai 		.power_mode = DPD_PWR_MODE,
24436af80c2SJacky Bai 		.i2c_addr = 0x30,
24536af80c2SJacky Bai 		.i2c_data = 0x9c,
24636af80c2SJacky Bai 	},
24736af80c2SJacky Bai 	[2] = {
24836af80c2SJacky Bai 		.tag = PMIC_REG_VALID_TAG,
24936af80c2SJacky Bai 		.power_mode = ACT_PWR_MODE,
25036af80c2SJacky Bai 		.i2c_addr = 0x21,
25136af80c2SJacky Bai 		.i2c_data = 0x79,
25236af80c2SJacky Bai 	},
25336af80c2SJacky Bai 	[3] = {
25436af80c2SJacky Bai 		.tag = PMIC_REG_VALID_TAG,
25536af80c2SJacky Bai 		.power_mode = ACT_PWR_MODE,
25636af80c2SJacky Bai 		.i2c_addr = 0x30,
25736af80c2SJacky Bai 		.i2c_data = 0x9d,
25836af80c2SJacky Bai 	},
25936af80c2SJacky Bai };
26036af80c2SJacky Bai 
261daa4478aSJacky Bai struct ps_pwr_mode_cfg_t *pwr_sys_cfg = (struct ps_pwr_mode_cfg_t *)UPWR_DRAM_SHARED_BASE_ADDR;
262daa4478aSJacky Bai 
263daa4478aSJacky Bai void imx_set_pwr_mode_cfg(abs_pwr_mode_t mode)
264daa4478aSJacky Bai {
26536af80c2SJacky Bai 	uint32_t volt;
26636af80c2SJacky Bai 
267daa4478aSJacky Bai 	if (mode >= NUM_PWR_MODES) {
268daa4478aSJacky Bai 		return;
269daa4478aSJacky Bai 	}
270daa4478aSJacky Bai 
271daa4478aSJacky Bai 	/* apd power mode config */
272daa4478aSJacky Bai 	memcpy(&pwr_sys_cfg->ps_apd_pwr_mode_cfg[mode], &apd_pwr_mode_cfgs[mode],
273daa4478aSJacky Bai 		 sizeof(struct ps_apd_pwr_mode_cfg_t));
274daa4478aSJacky Bai 
275daa4478aSJacky Bai 	/* apd power switch config */
276daa4478aSJacky Bai 	memcpy(&pwr_sys_cfg->ps_apd_swt_cfg[mode], &apd_swt_cfgs[mode], sizeof(swt_config_t));
27736af80c2SJacky Bai 
27836af80c2SJacky Bai 	/*
27936af80c2SJacky Bai 	 * BUCK3 & LDO1 can only be shutdown when LPAV is owned by APD side
28036af80c2SJacky Bai 	 * otherwise RTD side is responsible to control them in low power mode.
28136af80c2SJacky Bai 	 */
28236af80c2SJacky Bai 	if (is_lpav_owned_by_apd()) {
28336af80c2SJacky Bai 		/* power off the BUCK3 in DPD mode */
28436af80c2SJacky Bai 		if (mode == DPD_PWR_MODE) {
28536af80c2SJacky Bai 			memcpy(&pwr_sys_cfg->ps_apd_pmic_reg_data_cfg, &dpd_pmic_reg_cfgs,
28636af80c2SJacky Bai 				 sizeof(ps_apd_pmic_reg_data_cfgs_t));
28736af80c2SJacky Bai 		/* LDO1 should be power off in PD mode */
28836af80c2SJacky Bai 		} else if (mode == PD_PWR_MODE) {
28936af80c2SJacky Bai 			/* overwrite the buck3 voltage setting in active mode */
29036af80c2SJacky Bai 			upower_pmic_i2c_read(0x22, &volt);
29136af80c2SJacky Bai 			pd_pmic_reg_cfgs[3].i2c_data = volt;
29236af80c2SJacky Bai 			memcpy(&pwr_sys_cfg->ps_apd_pmic_reg_data_cfg, &pd_pmic_reg_cfgs,
29336af80c2SJacky Bai 				 sizeof(ps_apd_pmic_reg_data_cfgs_t));
29436af80c2SJacky Bai 		}
29536af80c2SJacky Bai 	}
296daa4478aSJacky Bai }
297daa4478aSJacky Bai 
298daa4478aSJacky Bai void imx_domain_suspend(const psci_power_state_t *target_state)
299daa4478aSJacky Bai {
300daa4478aSJacky Bai 	unsigned int cpu = MPIDR_AFFLVL0_VAL(read_mpidr_el1());
301daa4478aSJacky Bai 
302daa4478aSJacky Bai 	if (is_local_state_off(CORE_PWR_STATE(target_state))) {
303daa4478aSJacky Bai 		plat_gic_cpuif_disable();
304daa4478aSJacky Bai 		imx_pwr_set_cpu_entry(cpu, secure_entrypoint);
305daa4478aSJacky Bai 		/* core put into power down */
306daa4478aSJacky Bai 		mmio_write_32(IMX_CMC1_BASE + 0x50 + 0x4 * cpu, 0x3);
307daa4478aSJacky Bai 		/* FIXME config wakeup interrupt in WKPU */
308daa4478aSJacky Bai 		mmio_write_32(IMX_SIM1_BASE + 0x3c + 0x4 * cpu, 0x7fffffe3);
309daa4478aSJacky Bai 	} else {
310daa4478aSJacky Bai 		/* for core standby/retention mode */
311daa4478aSJacky Bai 		mmio_write_32(IMX_CMC1_BASE + 0x50 + 0x4 * cpu, 0x1);
312daa4478aSJacky Bai 		mmio_write_32(IMX_SIM1_BASE + 0x3c + 0x4 * cpu, 0x7fffffe3);
313daa4478aSJacky Bai 		dsb();
314daa4478aSJacky Bai 		write_scr_el3(read_scr_el3() | SCR_FIQ_BIT);
315daa4478aSJacky Bai 		isb();
316daa4478aSJacky Bai 	}
317daa4478aSJacky Bai 
318478af8d3SJacky Bai 	if (is_local_state_retn(CLUSTER_PWR_STATE(target_state))) {
319daa4478aSJacky Bai 		/*
320daa4478aSJacky Bai 		 * just for sleep mode for now, need to update to
321478af8d3SJacky Bai 		 * support more modes, same for suspend finish call back.
322daa4478aSJacky Bai 		 */
323daa4478aSJacky Bai 		mmio_write_32(IMX_CMC1_BASE + 0x10, 0x1);
324daa4478aSJacky Bai 		mmio_write_32(IMX_CMC1_BASE + 0x20, 0x1);
325478af8d3SJacky Bai 
326478af8d3SJacky Bai 	} else if (is_local_state_off(CLUSTER_PWR_STATE(target_state))) {
327478af8d3SJacky Bai 		/*
328478af8d3SJacky Bai 		 * for cluster off state, put cluster into power down mode,
329478af8d3SJacky Bai 		 * config the cluster clock to be off.
330478af8d3SJacky Bai 		 */
331478af8d3SJacky Bai 		mmio_write_32(IMX_CMC1_BASE + 0x10, 0x7);
332478af8d3SJacky Bai 		mmio_write_32(IMX_CMC1_BASE + 0x20, 0xf);
333daa4478aSJacky Bai 	}
334daa4478aSJacky Bai 
335daa4478aSJacky Bai 	if (is_local_state_off(SYSTEM_PWR_STATE(target_state))) {
336daa4478aSJacky Bai 		/*
337daa4478aSJacky Bai 		 * low power mode config info used by upower
338daa4478aSJacky Bai 		 * to do low power mode transition.
339daa4478aSJacky Bai 		 */
340daa4478aSJacky Bai 		imx_set_pwr_mode_cfg(ADMA_PWR_MODE);
341daa4478aSJacky Bai 		imx_set_pwr_mode_cfg(ACT_PWR_MODE);
342478af8d3SJacky Bai 		imx_set_pwr_mode_cfg(PD_PWR_MODE);
343478af8d3SJacky Bai 
344478af8d3SJacky Bai 		/* clear the upower wakeup */
345478af8d3SJacky Bai 		upwr_xcp_set_rtd_apd_llwu(APD_DOMAIN, 0, NULL);
346478af8d3SJacky Bai 		upower_wait_resp();
347478af8d3SJacky Bai 
348478af8d3SJacky Bai 		/* enable the USB wakeup */
349478af8d3SJacky Bai 		usb_wakeup_enable(true);
350478af8d3SJacky Bai 
351478af8d3SJacky Bai 		/* config the WUU to enabled the wakeup source */
352478af8d3SJacky Bai 		mmio_write_32(IMX_PCC3_BASE + 0x98, 0xc0800000);
353478af8d3SJacky Bai 
354478af8d3SJacky Bai 		/* !!! clear all the pad wakeup pending event */
355478af8d3SJacky Bai 		mmio_write_32(IMX_WUU1_BASE + 0x20, 0xffffffff);
356478af8d3SJacky Bai 
357478af8d3SJacky Bai 		/* enable upower usb phy wakeup by default */
358478af8d3SJacky Bai 		mmio_setbits_32(IMX_WUU1_BASE + 0x18, BIT(4) | BIT(1) | BIT(0));
359478af8d3SJacky Bai 
360478af8d3SJacky Bai 		/* enabled all pad wakeup by default */
361478af8d3SJacky Bai 		mmio_write_32(IMX_WUU1_BASE + 0x8, 0xffffffff);
362478af8d3SJacky Bai 
363478af8d3SJacky Bai 		/* save the AD domain context before entering PD mode */
364478af8d3SJacky Bai 		imx_apd_ctx_save(cpu);
365daa4478aSJacky Bai 	}
366daa4478aSJacky Bai }
367daa4478aSJacky Bai 
368478af8d3SJacky Bai extern void imx8ulp_init_scmi_server(void);
369daa4478aSJacky Bai void imx_domain_suspend_finish(const psci_power_state_t *target_state)
370daa4478aSJacky Bai {
371daa4478aSJacky Bai 	unsigned int cpu = MPIDR_AFFLVL0_VAL(read_mpidr_el1());
372daa4478aSJacky Bai 
373daa4478aSJacky Bai 	if (is_local_state_off(SYSTEM_PWR_STATE(target_state))) {
374478af8d3SJacky Bai 		/* restore the ap domain context */
375478af8d3SJacky Bai 		imx_apd_ctx_restore(cpu);
376478af8d3SJacky Bai 
377478af8d3SJacky Bai 		/* clear the upower wakeup */
378478af8d3SJacky Bai 		upwr_xcp_set_rtd_apd_llwu(APD_DOMAIN, 0, NULL);
379478af8d3SJacky Bai 		upower_wait_resp();
380478af8d3SJacky Bai 
381478af8d3SJacky Bai 		/* disable all pad wakeup */
382478af8d3SJacky Bai 		mmio_write_32(IMX_WUU1_BASE + 0x8, 0x0);
383478af8d3SJacky Bai 
384478af8d3SJacky Bai 		/* clear all the pad wakeup pending event */
385478af8d3SJacky Bai 		mmio_write_32(IMX_WUU1_BASE + 0x20, 0xffffffff);
386478af8d3SJacky Bai 
387478af8d3SJacky Bai 		/*
388478af8d3SJacky Bai 		 * disable the usb wakeup after resume to make sure the pending
389478af8d3SJacky Bai 		 * usb wakeup in WUU can be cleared successfully, otherwise,
390478af8d3SJacky Bai 		 * APD will resume failed in next PD mode.
391478af8d3SJacky Bai 		 */
392478af8d3SJacky Bai 		usb_wakeup_enable(false);
393478af8d3SJacky Bai 
394478af8d3SJacky Bai 		/* re-init the SCMI channel */
395478af8d3SJacky Bai 		imx8ulp_init_scmi_server();
396daa4478aSJacky Bai 	}
397daa4478aSJacky Bai 
398478af8d3SJacky Bai 	/* clear cluster's LPM setting. */
399daa4478aSJacky Bai 	mmio_write_32(IMX_CMC1_BASE + 0x20, 0x0);
400daa4478aSJacky Bai 	mmio_write_32(IMX_CMC1_BASE + 0x10, 0x0);
401daa4478aSJacky Bai 
402daa4478aSJacky Bai 	/* clear core's LPM setting */
403daa4478aSJacky Bai 	mmio_write_32(IMX_CMC1_BASE + 0x50 + 0x4 * cpu, 0x0);
404daa4478aSJacky Bai 	mmio_write_32(IMX_SIM1_BASE + 0x3c + 0x4 * cpu, 0x0);
405daa4478aSJacky Bai 
406daa4478aSJacky Bai 	if (is_local_state_off(CORE_PWR_STATE(target_state))) {
407daa4478aSJacky Bai 		imx_pwr_set_cpu_entry(0, IMX_ROM_ENTRY);
408daa4478aSJacky Bai 		plat_gic_cpuif_enable();
409daa4478aSJacky Bai 	} else {
410daa4478aSJacky Bai 		dsb();
411daa4478aSJacky Bai 		write_scr_el3(read_scr_el3() & (~SCR_FIQ_BIT));
412daa4478aSJacky Bai 		isb();
413daa4478aSJacky Bai 	}
414daa4478aSJacky Bai }
415fcd41e86SJacky Bai 
416fcd41e86SJacky Bai void __dead2 imx8ulp_pwr_domain_pwr_down_wfi(const psci_power_state_t *target_state)
417fcd41e86SJacky Bai {
418fcd41e86SJacky Bai 	while (1) {
419fcd41e86SJacky Bai 		wfi();
420fcd41e86SJacky Bai 	}
421fcd41e86SJacky Bai }
422fcd41e86SJacky Bai 
423fcd41e86SJacky Bai void __dead2 imx8ulp_system_reset(void)
424fcd41e86SJacky Bai {
425fcd41e86SJacky Bai 	imx_pwr_set_cpu_entry(0, IMX_ROM_ENTRY);
426fcd41e86SJacky Bai 
427fcd41e86SJacky Bai 	/* Write invalid command to WDOG CNT to trigger reset */
428fcd41e86SJacky Bai 	mmio_write_32(IMX_WDOG3_BASE + 0x4, 0x12345678);
429fcd41e86SJacky Bai 
430fcd41e86SJacky Bai 	while (true) {
431fcd41e86SJacky Bai 		wfi();
432fcd41e86SJacky Bai 	}
433fcd41e86SJacky Bai }
434fcd41e86SJacky Bai 
435daa4478aSJacky Bai int imx_validate_power_state(unsigned int power_state,
436daa4478aSJacky Bai 			 psci_power_state_t *req_state)
437daa4478aSJacky Bai {
438daa4478aSJacky Bai 	int pwr_lvl = psci_get_pstate_pwrlvl(power_state);
439daa4478aSJacky Bai 	int pwr_type = psci_get_pstate_type(power_state);
440daa4478aSJacky Bai 
441daa4478aSJacky Bai 	if (pwr_lvl > PLAT_MAX_PWR_LVL) {
442daa4478aSJacky Bai 		return PSCI_E_INVALID_PARAMS;
443daa4478aSJacky Bai 	}
444daa4478aSJacky Bai 
445daa4478aSJacky Bai 	if (pwr_type == PSTATE_TYPE_STANDBY) {
446daa4478aSJacky Bai 		CORE_PWR_STATE(req_state) = PLAT_MAX_RET_STATE;
447daa4478aSJacky Bai 		CLUSTER_PWR_STATE(req_state) = PLAT_MAX_RET_STATE;
448daa4478aSJacky Bai 	}
449daa4478aSJacky Bai 
450daa4478aSJacky Bai 	/* No power down state support */
451daa4478aSJacky Bai 	if (pwr_type == PSTATE_TYPE_POWERDOWN) {
452daa4478aSJacky Bai 		return PSCI_E_INVALID_PARAMS;
453daa4478aSJacky Bai 	}
454daa4478aSJacky Bai 
455daa4478aSJacky Bai 	return PSCI_E_SUCCESS;
456daa4478aSJacky Bai }
457daa4478aSJacky Bai 
458daa4478aSJacky Bai void imx_get_sys_suspend_power_state(psci_power_state_t *req_state)
459daa4478aSJacky Bai {
460daa4478aSJacky Bai 	unsigned int i;
461daa4478aSJacky Bai 
462daa4478aSJacky Bai 	for (i = IMX_PWR_LVL0; i <= PLAT_MAX_PWR_LVL; i++) {
463daa4478aSJacky Bai 		req_state->pwr_domain_state[i] = PLAT_POWER_DOWN_OFF_STATE;
464daa4478aSJacky Bai 	}
465daa4478aSJacky Bai }
466daa4478aSJacky Bai 
467891c547eSJacky Bai void __dead2 imx_system_off(void)
468891c547eSJacky Bai {
469891c547eSJacky Bai 	unsigned int i;
470891c547eSJacky Bai 
471891c547eSJacky Bai 	/* config the all the core into OFF mode and IRQ masked. */
472891c547eSJacky Bai 	for (i = 0U; i < PLATFORM_CORE_COUNT; i++) {
473891c547eSJacky Bai 		/* disable wakeup from wkpu */
474891c547eSJacky Bai 		mmio_write_32(WKPUx(i), 0x0);
475891c547eSJacky Bai 
476891c547eSJacky Bai 		/* reset the core reset entry to 0x1000 */
477891c547eSJacky Bai 		imx_pwr_set_cpu_entry(i, 0x1000);
478891c547eSJacky Bai 
479891c547eSJacky Bai 		/* config the core power mode to off */
480891c547eSJacky Bai 		mmio_write_32(AD_COREx_LPMODE(i), 0x3);
481891c547eSJacky Bai 	}
482891c547eSJacky Bai 
483891c547eSJacky Bai 	plat_gic_cpuif_disable();
484891c547eSJacky Bai 
48536af80c2SJacky Bai 	/* power off all the pad */
48636af80c2SJacky Bai 	apd_io_pad_off();
48736af80c2SJacky Bai 
488891c547eSJacky Bai 	/* Config the power mode info for entering DPD mode and ACT mode */
489891c547eSJacky Bai 	imx_set_pwr_mode_cfg(ADMA_PWR_MODE);
490891c547eSJacky Bai 	imx_set_pwr_mode_cfg(ACT_PWR_MODE);
491891c547eSJacky Bai 	imx_set_pwr_mode_cfg(DPD_PWR_MODE);
492891c547eSJacky Bai 
493891c547eSJacky Bai 	/* Set the APD domain into DPD mode */
494891c547eSJacky Bai 	mmio_write_32(IMX_CMC1_BASE + 0x10, 0x7);
495891c547eSJacky Bai 	mmio_write_32(IMX_CMC1_BASE + 0x20, 0x1f);
496891c547eSJacky Bai 
497891c547eSJacky Bai 	/* make sure no pending upower wakeup */
498891c547eSJacky Bai 	upwr_xcp_set_rtd_apd_llwu(APD_DOMAIN, 0, NULL);
499891c547eSJacky Bai 	upower_wait_resp();
500891c547eSJacky Bai 
501891c547eSJacky Bai 	/* enable the upower wakeup from wuu, act as APD boot up method  */
502891c547eSJacky Bai 	mmio_write_32(IMX_PCC3_BASE + 0x98, 0xc0800000);
503891c547eSJacky Bai 	mmio_setbits_32(IMX_WUU1_BASE + 0x18, BIT(4));
504891c547eSJacky Bai 
505891c547eSJacky Bai 	/* make sure no pad wakeup event is pending */
506891c547eSJacky Bai 	mmio_write_32(IMX_WUU1_BASE + 0x20, 0xffffffff);
507891c547eSJacky Bai 
508891c547eSJacky Bai 	wfi();
509891c547eSJacky Bai 
510891c547eSJacky Bai 	ERROR("power off failed.\n");
511891c547eSJacky Bai 	panic();
512891c547eSJacky Bai }
513891c547eSJacky Bai 
514fcd41e86SJacky Bai static const plat_psci_ops_t imx_plat_psci_ops = {
515fcd41e86SJacky Bai 	.pwr_domain_on = imx_pwr_domain_on,
516fcd41e86SJacky Bai 	.pwr_domain_on_finish = imx_pwr_domain_on_finish,
517fcd41e86SJacky Bai 	.validate_ns_entrypoint = imx_validate_ns_entrypoint,
518891c547eSJacky Bai 	.system_off = imx_system_off,
519fcd41e86SJacky Bai 	.system_reset = imx8ulp_system_reset,
520fcd41e86SJacky Bai 	.pwr_domain_off = imx_pwr_domain_off,
521daa4478aSJacky Bai 	.pwr_domain_suspend = imx_domain_suspend,
522daa4478aSJacky Bai 	.pwr_domain_suspend_finish = imx_domain_suspend_finish,
523daa4478aSJacky Bai 	.get_sys_suspend_power_state = imx_get_sys_suspend_power_state,
524daa4478aSJacky Bai 	.validate_power_state = imx_validate_power_state,
525fcd41e86SJacky Bai 	.pwr_domain_pwr_down_wfi = imx8ulp_pwr_domain_pwr_down_wfi,
526fcd41e86SJacky Bai };
527fcd41e86SJacky Bai 
528fcd41e86SJacky Bai int plat_setup_psci_ops(uintptr_t sec_entrypoint,
529fcd41e86SJacky Bai 			const plat_psci_ops_t **psci_ops)
530fcd41e86SJacky Bai {
531fcd41e86SJacky Bai 	secure_entrypoint = sec_entrypoint;
532fcd41e86SJacky Bai 	imx_pwr_set_cpu_entry(0, sec_entrypoint);
533fcd41e86SJacky Bai 	*psci_ops = &imx_plat_psci_ops;
534fcd41e86SJacky Bai 
535fcd41e86SJacky Bai 	mmio_write_32(IMX_CMC1_BASE + 0x18, 0x3f);
536fcd41e86SJacky Bai 	mmio_write_32(IMX_SIM1_BASE + 0x3c, 0xffffffff);
537fcd41e86SJacky Bai 
538fcd41e86SJacky Bai 	return 0;
539fcd41e86SJacky Bai }
540