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>
13c514d3cfSJacky Bai #include <drivers/delay_timer.h>
14fcd41e86SJacky Bai #include <lib/mmio.h>
15fcd41e86SJacky Bai #include <lib/psci/psci.h>
16fcd41e86SJacky Bai
17fcd41e86SJacky Bai #include <plat_imx8.h>
18daa4478aSJacky Bai #include <upower_api.h>
19fcd41e86SJacky Bai
20478af8d3SJacky Bai extern void cgc1_save(void);
21478af8d3SJacky Bai extern void cgc1_restore(void);
22478af8d3SJacky Bai extern void imx_apd_ctx_save(unsigned int cpu);
23478af8d3SJacky Bai extern void imx_apd_ctx_restore(unsigned int cpu);
24478af8d3SJacky Bai extern void usb_wakeup_enable(bool enable);
2536af80c2SJacky Bai extern void upower_wait_resp(void);
2636af80c2SJacky Bai extern bool is_lpav_owned_by_apd(void);
2736af80c2SJacky Bai extern void apd_io_pad_off(void);
2836af80c2SJacky Bai extern int upower_pmic_i2c_read(uint32_t reg_addr, uint32_t *reg_val);
294fafccb9SJacky Bai extern void imx8ulp_init_scmi_server(void);
30478af8d3SJacky Bai
31fcd41e86SJacky Bai static uintptr_t secure_entrypoint;
32fcd41e86SJacky Bai
33fcd41e86SJacky Bai #define CORE_PWR_STATE(state) ((state)->pwr_domain_state[MPIDR_AFFLVL0])
34fcd41e86SJacky Bai #define CLUSTER_PWR_STATE(state) ((state)->pwr_domain_state[MPIDR_AFFLVL1])
35fcd41e86SJacky Bai #define SYSTEM_PWR_STATE(state) ((state)->pwr_domain_state[PLAT_MAX_PWR_LVL])
36fcd41e86SJacky Bai
37fcd41e86SJacky Bai #define RVBARADDRx(c) (IMX_SIM1_BASE + 0x5c + 0x4 * (c))
38fcd41e86SJacky Bai #define WKPUx(c) (IMX_SIM1_BASE + 0x3c + 0x4 * (c))
39fcd41e86SJacky Bai #define AD_COREx_LPMODE(c) (IMX_CMC1_BASE + 0x50 + 0x4 * (c))
40fcd41e86SJacky Bai
41daa4478aSJacky Bai #define PMIC_CFG(v, m, msk) \
42daa4478aSJacky Bai { \
43daa4478aSJacky Bai .volt = (v), \
44daa4478aSJacky Bai .mode = (m), \
45daa4478aSJacky Bai .mode_msk = (msk), \
46daa4478aSJacky Bai }
47daa4478aSJacky Bai
48daa4478aSJacky Bai #define PAD_CFG(c, r, t) \
49daa4478aSJacky Bai { \
50daa4478aSJacky Bai .pad_close = (c), \
51daa4478aSJacky Bai .pad_reset = (r), \
52daa4478aSJacky Bai .pad_tqsleep = (t) \
53daa4478aSJacky Bai }
54daa4478aSJacky Bai
55daa4478aSJacky Bai #define BIAS_CFG(m, n, p, mbias) \
56daa4478aSJacky Bai { \
57daa4478aSJacky Bai .dombias_cfg = { \
58daa4478aSJacky Bai .mode = (m), \
59daa4478aSJacky Bai .rbbn = (n), \
60daa4478aSJacky Bai .rbbp = (p), \
61daa4478aSJacky Bai }, \
62daa4478aSJacky Bai .membias_cfg = {mbias}, \
63daa4478aSJacky Bai }
64daa4478aSJacky Bai
65daa4478aSJacky Bai #define SWT_BOARD(swt_on, msk) \
66daa4478aSJacky Bai { \
67daa4478aSJacky Bai .on = (swt_on), \
68daa4478aSJacky Bai .mask = (msk), \
69daa4478aSJacky Bai }
70daa4478aSJacky Bai
71daa4478aSJacky Bai #define SWT_MEM(a, p, m) \
72daa4478aSJacky Bai { \
73daa4478aSJacky Bai .array = (a), \
74daa4478aSJacky Bai .perif = (p), \
75daa4478aSJacky Bai .mask = (m), \
76daa4478aSJacky Bai }
77daa4478aSJacky Bai
imx_pwr_set_cpu_entry(unsigned int cpu,unsigned int entry)78fcd41e86SJacky Bai static int imx_pwr_set_cpu_entry(unsigned int cpu, unsigned int entry)
79fcd41e86SJacky Bai {
80fcd41e86SJacky Bai mmio_write_32(RVBARADDRx(cpu), entry);
81fcd41e86SJacky Bai
82fcd41e86SJacky Bai /* set update bit */
83fcd41e86SJacky Bai mmio_write_32(IMX_SIM1_BASE + 0x8, mmio_read_32(IMX_SIM1_BASE + 0x8) | BIT_32(24 + cpu));
84fcd41e86SJacky Bai /* wait for ack */
85fcd41e86SJacky Bai while (!(mmio_read_32(IMX_SIM1_BASE + 0x8) & BIT_32(26 + cpu))) {
86fcd41e86SJacky Bai }
87fcd41e86SJacky Bai
88fcd41e86SJacky Bai /* clear update bit */
89fcd41e86SJacky Bai mmio_write_32(IMX_SIM1_BASE + 0x8, mmio_read_32(IMX_SIM1_BASE + 0x8) & ~BIT_32(24 + cpu));
90fcd41e86SJacky Bai /* clear ack bit */
91fcd41e86SJacky Bai mmio_write_32(IMX_SIM1_BASE + 0x8, mmio_read_32(IMX_SIM1_BASE + 0x8) | BIT_32(26 + cpu));
92fcd41e86SJacky Bai
93fcd41e86SJacky Bai return 0;
94fcd41e86SJacky Bai }
95fcd41e86SJacky Bai
96e1d5c3c8SJacky Bai static volatile uint32_t cgc1_nicclk;
imx_pwr_domain_on(u_register_t mpidr)97fcd41e86SJacky Bai int imx_pwr_domain_on(u_register_t mpidr)
98fcd41e86SJacky Bai {
99fcd41e86SJacky Bai unsigned int cpu = MPIDR_AFFLVL0_VAL(mpidr);
100fcd41e86SJacky Bai
101fcd41e86SJacky Bai imx_pwr_set_cpu_entry(cpu, secure_entrypoint);
102fcd41e86SJacky Bai
103e1d5c3c8SJacky Bai /* slow down the APD NIC bus clock */
104e1d5c3c8SJacky Bai cgc1_nicclk = mmio_read_32(IMX_CGC1_BASE + 0x34);
105e1d5c3c8SJacky Bai mmio_clrbits_32(IMX_CGC1_BASE + 0x34, GENMASK_32(29, 28));
106e1d5c3c8SJacky Bai
107fcd41e86SJacky Bai mmio_write_32(IMX_CMC1_BASE + 0x18, 0x3f);
108fcd41e86SJacky Bai mmio_write_32(IMX_CMC1_BASE + 0x50 + 0x4 * cpu, 0);
109fcd41e86SJacky Bai
110fcd41e86SJacky Bai /* enable wku wakeup for idle */
111fcd41e86SJacky Bai mmio_write_32(IMX_SIM1_BASE + 0x3c + 0x4 * cpu, 0xffffffff);
112fcd41e86SJacky Bai
113fcd41e86SJacky Bai return PSCI_E_SUCCESS;
114fcd41e86SJacky Bai }
115fcd41e86SJacky Bai
imx_pwr_domain_on_finish(const psci_power_state_t * target_state)116fcd41e86SJacky Bai void imx_pwr_domain_on_finish(const psci_power_state_t *target_state)
117fcd41e86SJacky Bai {
118fcd41e86SJacky Bai imx_pwr_set_cpu_entry(0, IMX_ROM_ENTRY);
119fcd41e86SJacky Bai plat_gic_pcpu_init();
120fcd41e86SJacky Bai plat_gic_cpuif_enable();
121e1d5c3c8SJacky Bai
122e1d5c3c8SJacky Bai /* set APD NIC back to orignally setting */
123e1d5c3c8SJacky Bai mmio_write_32(IMX_CGC1_BASE + 0x34, cgc1_nicclk);
124fcd41e86SJacky Bai }
125fcd41e86SJacky Bai
imx_validate_ns_entrypoint(uintptr_t ns_entrypoint)126fcd41e86SJacky Bai int imx_validate_ns_entrypoint(uintptr_t ns_entrypoint)
127fcd41e86SJacky Bai {
128fcd41e86SJacky Bai return PSCI_E_SUCCESS;
129fcd41e86SJacky Bai }
130fcd41e86SJacky Bai
imx_pwr_domain_off(const psci_power_state_t * target_state)131fcd41e86SJacky Bai void imx_pwr_domain_off(const psci_power_state_t *target_state)
132fcd41e86SJacky Bai {
133fcd41e86SJacky Bai unsigned int cpu = MPIDR_AFFLVL0_VAL(read_mpidr_el1());
134fcd41e86SJacky Bai
135fcd41e86SJacky Bai plat_gic_cpuif_disable();
136fcd41e86SJacky Bai
137fcd41e86SJacky Bai /* disable wakeup */
138fcd41e86SJacky Bai mmio_write_32(WKPUx(cpu), 0);
139fcd41e86SJacky Bai
140daa4478aSJacky Bai /* set core power mode to PD */
141fcd41e86SJacky Bai mmio_write_32(AD_COREx_LPMODE(cpu), 0x3);
142fcd41e86SJacky Bai }
143478af8d3SJacky Bai
144daa4478aSJacky Bai /* APD power mode config */
145daa4478aSJacky Bai ps_apd_pwr_mode_cfgs_t apd_pwr_mode_cfgs = {
146891c547eSJacky Bai [DPD_PWR_MODE] = {
147891c547eSJacky Bai .swt_board_offs = 0x180,
148891c547eSJacky Bai .swt_mem_offs = 0x188,
14936af80c2SJacky Bai .pmic_cfg = PMIC_CFG(0x23, 0x0, 0x2),
15036af80c2SJacky Bai .pad_cfg = PAD_CFG(0x0, 0xc, 0x01e80a02),
151891c547eSJacky Bai .bias_cfg = BIAS_CFG(0x0, 0x2, 0x2, 0x0),
152891c547eSJacky Bai },
153891c547eSJacky Bai
154478af8d3SJacky Bai /* PD */
155478af8d3SJacky Bai [PD_PWR_MODE] = {
156478af8d3SJacky Bai .swt_board_offs = 0x170,
157478af8d3SJacky Bai .swt_mem_offs = 0x178,
15836af80c2SJacky Bai .pmic_cfg = PMIC_CFG(0x23, 0x0, 0x2),
15936af80c2SJacky Bai .pad_cfg = PAD_CFG(0x0, 0xc, 0x01e80a00),
160478af8d3SJacky Bai .bias_cfg = BIAS_CFG(0x0, 0x2, 0x2, 0x0),
161478af8d3SJacky Bai },
162478af8d3SJacky Bai
163daa4478aSJacky Bai [ADMA_PWR_MODE] = {
164daa4478aSJacky Bai .swt_board_offs = 0x120,
165daa4478aSJacky Bai .swt_mem_offs = 0x128,
166daa4478aSJacky Bai .pmic_cfg = PMIC_CFG(0x23, 0x0, 0x2),
167daa4478aSJacky Bai .pad_cfg = PAD_CFG(0x0, 0x0, 0x0deb7a00),
16836af80c2SJacky Bai .bias_cfg = BIAS_CFG(0x2, 0x2, 0x2, 0x0),
169daa4478aSJacky Bai },
170daa4478aSJacky Bai
171daa4478aSJacky Bai [ACT_PWR_MODE] = {
172daa4478aSJacky Bai .swt_board_offs = 0x110,
173daa4478aSJacky Bai .swt_mem_offs = 0x118,
17436af80c2SJacky Bai .pmic_cfg = PMIC_CFG(0x23, 0x0, 0x2),
175daa4478aSJacky Bai .pad_cfg = PAD_CFG(0x0, 0x0, 0x0deb7a00),
17636af80c2SJacky Bai .bias_cfg = BIAS_CFG(0x2, 0x2, 0x2, 0x0),
177daa4478aSJacky Bai },
178daa4478aSJacky Bai };
179daa4478aSJacky Bai
180daa4478aSJacky Bai /* APD power switch config */
181daa4478aSJacky Bai ps_apd_swt_cfgs_t apd_swt_cfgs = {
182891c547eSJacky Bai [DPD_PWR_MODE] = {
18336af80c2SJacky Bai .swt_board[0] = SWT_BOARD(0x0, 0x1fffc),
184891c547eSJacky Bai .swt_mem[0] = SWT_MEM(0x0, 0x0, 0x1ffff),
185891c547eSJacky Bai .swt_mem[1] = SWT_MEM(0x003fffff, 0x003fffff, 0x0),
186891c547eSJacky Bai },
187891c547eSJacky Bai
188478af8d3SJacky Bai [PD_PWR_MODE] = {
18936af80c2SJacky Bai .swt_board[0] = SWT_BOARD(0x0, 0x00001fffc),
190478af8d3SJacky Bai .swt_mem[0] = SWT_MEM(0x00010c00, 0x0, 0x1ffff),
191478af8d3SJacky Bai .swt_mem[1] = SWT_MEM(0x003fffff, 0x003f0000, 0x0),
192478af8d3SJacky Bai },
193478af8d3SJacky Bai
194daa4478aSJacky Bai [ADMA_PWR_MODE] = {
19536af80c2SJacky Bai .swt_board[0] = SWT_BOARD(0x15f74, 0x15f74),
196478af8d3SJacky Bai .swt_mem[0] = SWT_MEM(0x0001fffd, 0x0001fffd, 0x1ffff),
197478af8d3SJacky Bai .swt_mem[1] = SWT_MEM(0x003fffff, 0x003fffff, 0x0),
198daa4478aSJacky Bai },
199daa4478aSJacky Bai
200daa4478aSJacky Bai [ACT_PWR_MODE] = {
20136af80c2SJacky Bai .swt_board[0] = SWT_BOARD(0x15f74, 0x15f74),
202478af8d3SJacky Bai .swt_mem[0] = SWT_MEM(0x0001fffd, 0x0001fffd, 0x1ffff),
203478af8d3SJacky Bai .swt_mem[1] = SWT_MEM(0x003fffff, 0x003fffff, 0x0),
204daa4478aSJacky Bai },
205daa4478aSJacky Bai };
206daa4478aSJacky Bai
20736af80c2SJacky Bai /* PMIC config for power down, LDO1 should be OFF */
20836af80c2SJacky Bai ps_apd_pmic_reg_data_cfgs_t pd_pmic_reg_cfgs = {
20936af80c2SJacky Bai [0] = {
21036af80c2SJacky Bai .tag = PMIC_REG_VALID_TAG,
21136af80c2SJacky Bai .power_mode = PD_PWR_MODE,
21236af80c2SJacky Bai .i2c_addr = 0x30,
21336af80c2SJacky Bai .i2c_data = 0x9c,
21436af80c2SJacky Bai },
21536af80c2SJacky Bai [1] = {
21636af80c2SJacky Bai .tag = PMIC_REG_VALID_TAG,
21736af80c2SJacky Bai .power_mode = PD_PWR_MODE,
21836af80c2SJacky Bai .i2c_addr = 0x22,
21936af80c2SJacky Bai .i2c_data = 0xb,
22036af80c2SJacky Bai },
22136af80c2SJacky Bai [2] = {
22236af80c2SJacky Bai .tag = PMIC_REG_VALID_TAG,
22336af80c2SJacky Bai .power_mode = ACT_PWR_MODE,
22436af80c2SJacky Bai .i2c_addr = 0x30,
22536af80c2SJacky Bai .i2c_data = 0x9d,
22636af80c2SJacky Bai },
22736af80c2SJacky Bai [3] = {
22836af80c2SJacky Bai .tag = PMIC_REG_VALID_TAG,
22936af80c2SJacky Bai .power_mode = ACT_PWR_MODE,
23036af80c2SJacky Bai .i2c_addr = 0x22,
23136af80c2SJacky Bai .i2c_data = 0x28,
23236af80c2SJacky Bai },
23336af80c2SJacky Bai };
23436af80c2SJacky Bai
23536af80c2SJacky Bai /* PMIC config for deep power down, BUCK3 should be OFF */
23636af80c2SJacky Bai ps_apd_pmic_reg_data_cfgs_t dpd_pmic_reg_cfgs = {
23736af80c2SJacky Bai [0] = {
23836af80c2SJacky Bai .tag = PMIC_REG_VALID_TAG,
23936af80c2SJacky Bai .power_mode = DPD_PWR_MODE,
24036af80c2SJacky Bai .i2c_addr = 0x21,
24136af80c2SJacky Bai .i2c_data = 0x78,
24236af80c2SJacky Bai },
24336af80c2SJacky Bai [1] = {
24436af80c2SJacky Bai .tag = PMIC_REG_VALID_TAG,
24536af80c2SJacky Bai .power_mode = DPD_PWR_MODE,
24636af80c2SJacky Bai .i2c_addr = 0x30,
24736af80c2SJacky Bai .i2c_data = 0x9c,
24836af80c2SJacky Bai },
24936af80c2SJacky Bai [2] = {
25036af80c2SJacky Bai .tag = PMIC_REG_VALID_TAG,
25136af80c2SJacky Bai .power_mode = ACT_PWR_MODE,
25236af80c2SJacky Bai .i2c_addr = 0x21,
25336af80c2SJacky Bai .i2c_data = 0x79,
25436af80c2SJacky Bai },
25536af80c2SJacky Bai [3] = {
25636af80c2SJacky Bai .tag = PMIC_REG_VALID_TAG,
25736af80c2SJacky Bai .power_mode = ACT_PWR_MODE,
25836af80c2SJacky Bai .i2c_addr = 0x30,
25936af80c2SJacky Bai .i2c_data = 0x9d,
26036af80c2SJacky Bai },
26136af80c2SJacky Bai };
26236af80c2SJacky Bai
263daa4478aSJacky Bai struct ps_pwr_mode_cfg_t *pwr_sys_cfg = (struct ps_pwr_mode_cfg_t *)UPWR_DRAM_SHARED_BASE_ADDR;
264daa4478aSJacky Bai
imx_set_pwr_mode_cfg(abs_pwr_mode_t mode)265daa4478aSJacky Bai void imx_set_pwr_mode_cfg(abs_pwr_mode_t mode)
266daa4478aSJacky Bai {
26736af80c2SJacky Bai uint32_t volt;
26836af80c2SJacky Bai
269daa4478aSJacky Bai if (mode >= NUM_PWR_MODES) {
270daa4478aSJacky Bai return;
271daa4478aSJacky Bai }
272daa4478aSJacky Bai
273daa4478aSJacky Bai /* apd power mode config */
274daa4478aSJacky Bai memcpy(&pwr_sys_cfg->ps_apd_pwr_mode_cfg[mode], &apd_pwr_mode_cfgs[mode],
275daa4478aSJacky Bai sizeof(struct ps_apd_pwr_mode_cfg_t));
276daa4478aSJacky Bai
277daa4478aSJacky Bai /* apd power switch config */
278daa4478aSJacky Bai memcpy(&pwr_sys_cfg->ps_apd_swt_cfg[mode], &apd_swt_cfgs[mode], sizeof(swt_config_t));
27936af80c2SJacky Bai
28036af80c2SJacky Bai /*
28136af80c2SJacky Bai * BUCK3 & LDO1 can only be shutdown when LPAV is owned by APD side
28236af80c2SJacky Bai * otherwise RTD side is responsible to control them in low power mode.
28336af80c2SJacky Bai */
28436af80c2SJacky Bai if (is_lpav_owned_by_apd()) {
28536af80c2SJacky Bai /* power off the BUCK3 in DPD mode */
28636af80c2SJacky Bai if (mode == DPD_PWR_MODE) {
28736af80c2SJacky Bai memcpy(&pwr_sys_cfg->ps_apd_pmic_reg_data_cfg, &dpd_pmic_reg_cfgs,
28836af80c2SJacky Bai sizeof(ps_apd_pmic_reg_data_cfgs_t));
28936af80c2SJacky Bai /* LDO1 should be power off in PD mode */
29036af80c2SJacky Bai } else if (mode == PD_PWR_MODE) {
29136af80c2SJacky Bai /* overwrite the buck3 voltage setting in active mode */
292*e551dbd2SBoyan Karatotev if (upower_pmic_i2c_read(0x22, &volt) != 0) {
293*e551dbd2SBoyan Karatotev panic();
294*e551dbd2SBoyan Karatotev }
29536af80c2SJacky Bai pd_pmic_reg_cfgs[3].i2c_data = volt;
29636af80c2SJacky Bai memcpy(&pwr_sys_cfg->ps_apd_pmic_reg_data_cfg, &pd_pmic_reg_cfgs,
29736af80c2SJacky Bai sizeof(ps_apd_pmic_reg_data_cfgs_t));
29836af80c2SJacky Bai }
29936af80c2SJacky Bai }
300daa4478aSJacky Bai }
301daa4478aSJacky Bai
imx_domain_suspend(const psci_power_state_t * target_state)302daa4478aSJacky Bai void imx_domain_suspend(const psci_power_state_t *target_state)
303daa4478aSJacky Bai {
304daa4478aSJacky Bai unsigned int cpu = MPIDR_AFFLVL0_VAL(read_mpidr_el1());
305daa4478aSJacky Bai
306daa4478aSJacky Bai if (is_local_state_off(CORE_PWR_STATE(target_state))) {
307daa4478aSJacky Bai plat_gic_cpuif_disable();
308daa4478aSJacky Bai imx_pwr_set_cpu_entry(cpu, secure_entrypoint);
309daa4478aSJacky Bai /* core put into power down */
310daa4478aSJacky Bai mmio_write_32(IMX_CMC1_BASE + 0x50 + 0x4 * cpu, 0x3);
311daa4478aSJacky Bai /* FIXME config wakeup interrupt in WKPU */
312daa4478aSJacky Bai mmio_write_32(IMX_SIM1_BASE + 0x3c + 0x4 * cpu, 0x7fffffe3);
313daa4478aSJacky Bai } else {
314daa4478aSJacky Bai /* for core standby/retention mode */
315daa4478aSJacky Bai mmio_write_32(IMX_CMC1_BASE + 0x50 + 0x4 * cpu, 0x1);
316daa4478aSJacky Bai mmio_write_32(IMX_SIM1_BASE + 0x3c + 0x4 * cpu, 0x7fffffe3);
317daa4478aSJacky Bai dsb();
318daa4478aSJacky Bai write_scr_el3(read_scr_el3() | SCR_FIQ_BIT);
319daa4478aSJacky Bai isb();
320daa4478aSJacky Bai }
321daa4478aSJacky Bai
322478af8d3SJacky Bai if (is_local_state_retn(CLUSTER_PWR_STATE(target_state))) {
323daa4478aSJacky Bai /*
324daa4478aSJacky Bai * just for sleep mode for now, need to update to
325478af8d3SJacky Bai * support more modes, same for suspend finish call back.
326daa4478aSJacky Bai */
327daa4478aSJacky Bai mmio_write_32(IMX_CMC1_BASE + 0x10, 0x1);
328daa4478aSJacky Bai mmio_write_32(IMX_CMC1_BASE + 0x20, 0x1);
329478af8d3SJacky Bai
330478af8d3SJacky Bai } else if (is_local_state_off(CLUSTER_PWR_STATE(target_state))) {
331478af8d3SJacky Bai /*
332478af8d3SJacky Bai * for cluster off state, put cluster into power down mode,
333478af8d3SJacky Bai * config the cluster clock to be off.
334478af8d3SJacky Bai */
335478af8d3SJacky Bai mmio_write_32(IMX_CMC1_BASE + 0x10, 0x7);
336478af8d3SJacky Bai mmio_write_32(IMX_CMC1_BASE + 0x20, 0xf);
337daa4478aSJacky Bai }
338daa4478aSJacky Bai
339daa4478aSJacky Bai if (is_local_state_off(SYSTEM_PWR_STATE(target_state))) {
340daa4478aSJacky Bai /*
341daa4478aSJacky Bai * low power mode config info used by upower
342daa4478aSJacky Bai * to do low power mode transition.
343daa4478aSJacky Bai */
344daa4478aSJacky Bai imx_set_pwr_mode_cfg(ADMA_PWR_MODE);
345daa4478aSJacky Bai imx_set_pwr_mode_cfg(ACT_PWR_MODE);
346478af8d3SJacky Bai imx_set_pwr_mode_cfg(PD_PWR_MODE);
347478af8d3SJacky Bai
348478af8d3SJacky Bai /* clear the upower wakeup */
349478af8d3SJacky Bai upwr_xcp_set_rtd_apd_llwu(APD_DOMAIN, 0, NULL);
350478af8d3SJacky Bai upower_wait_resp();
351478af8d3SJacky Bai
352478af8d3SJacky Bai /* enable the USB wakeup */
353478af8d3SJacky Bai usb_wakeup_enable(true);
354478af8d3SJacky Bai
355478af8d3SJacky Bai /* config the WUU to enabled the wakeup source */
356478af8d3SJacky Bai mmio_write_32(IMX_PCC3_BASE + 0x98, 0xc0800000);
357478af8d3SJacky Bai
358478af8d3SJacky Bai /* !!! clear all the pad wakeup pending event */
359478af8d3SJacky Bai mmio_write_32(IMX_WUU1_BASE + 0x20, 0xffffffff);
360478af8d3SJacky Bai
361478af8d3SJacky Bai /* enable upower usb phy wakeup by default */
362478af8d3SJacky Bai mmio_setbits_32(IMX_WUU1_BASE + 0x18, BIT(4) | BIT(1) | BIT(0));
363478af8d3SJacky Bai
364478af8d3SJacky Bai /* enabled all pad wakeup by default */
365478af8d3SJacky Bai mmio_write_32(IMX_WUU1_BASE + 0x8, 0xffffffff);
366478af8d3SJacky Bai
367478af8d3SJacky Bai /* save the AD domain context before entering PD mode */
368478af8d3SJacky Bai imx_apd_ctx_save(cpu);
369daa4478aSJacky Bai }
370daa4478aSJacky Bai }
371daa4478aSJacky Bai
3724fafccb9SJacky Bai #define DRAM_LPM_STATUS U(0x2802b004)
imx_domain_suspend_finish(const psci_power_state_t * target_state)373daa4478aSJacky Bai void imx_domain_suspend_finish(const psci_power_state_t *target_state)
374daa4478aSJacky Bai {
375daa4478aSJacky Bai unsigned int cpu = MPIDR_AFFLVL0_VAL(read_mpidr_el1());
376daa4478aSJacky Bai
377daa4478aSJacky Bai if (is_local_state_off(SYSTEM_PWR_STATE(target_state))) {
378478af8d3SJacky Bai /* restore the ap domain context */
379478af8d3SJacky Bai imx_apd_ctx_restore(cpu);
380478af8d3SJacky Bai
381478af8d3SJacky Bai /* clear the upower wakeup */
382478af8d3SJacky Bai upwr_xcp_set_rtd_apd_llwu(APD_DOMAIN, 0, NULL);
383478af8d3SJacky Bai upower_wait_resp();
384478af8d3SJacky Bai
385478af8d3SJacky Bai /* disable all pad wakeup */
386478af8d3SJacky Bai mmio_write_32(IMX_WUU1_BASE + 0x8, 0x0);
387478af8d3SJacky Bai
388478af8d3SJacky Bai /* clear all the pad wakeup pending event */
389478af8d3SJacky Bai mmio_write_32(IMX_WUU1_BASE + 0x20, 0xffffffff);
390478af8d3SJacky Bai
391478af8d3SJacky Bai /*
392478af8d3SJacky Bai * disable the usb wakeup after resume to make sure the pending
393478af8d3SJacky Bai * usb wakeup in WUU can be cleared successfully, otherwise,
394478af8d3SJacky Bai * APD will resume failed in next PD mode.
395478af8d3SJacky Bai */
396478af8d3SJacky Bai usb_wakeup_enable(false);
397478af8d3SJacky Bai
398478af8d3SJacky Bai /* re-init the SCMI channel */
399478af8d3SJacky Bai imx8ulp_init_scmi_server();
400daa4478aSJacky Bai }
401daa4478aSJacky Bai
4024fafccb9SJacky Bai /*
4034fafccb9SJacky Bai * wait for DDR is ready when DDR is under the RTD
4044fafccb9SJacky Bai * side control for power saving
4054fafccb9SJacky Bai */
4064fafccb9SJacky Bai while (mmio_read_32(DRAM_LPM_STATUS) != 0) {
4074fafccb9SJacky Bai ;
4084fafccb9SJacky Bai }
4094fafccb9SJacky Bai
410c514d3cfSJacky Bai /*
411c514d3cfSJacky Bai * when resume from low power mode, need to delay for a while
412c514d3cfSJacky Bai * before access the CMC register.
413c514d3cfSJacky Bai */
414c514d3cfSJacky Bai udelay(5);
415c514d3cfSJacky Bai
416478af8d3SJacky Bai /* clear cluster's LPM setting. */
417daa4478aSJacky Bai mmio_write_32(IMX_CMC1_BASE + 0x20, 0x0);
418daa4478aSJacky Bai mmio_write_32(IMX_CMC1_BASE + 0x10, 0x0);
419daa4478aSJacky Bai
420daa4478aSJacky Bai /* clear core's LPM setting */
421daa4478aSJacky Bai mmio_write_32(IMX_CMC1_BASE + 0x50 + 0x4 * cpu, 0x0);
422daa4478aSJacky Bai mmio_write_32(IMX_SIM1_BASE + 0x3c + 0x4 * cpu, 0x0);
423daa4478aSJacky Bai
424daa4478aSJacky Bai if (is_local_state_off(CORE_PWR_STATE(target_state))) {
425daa4478aSJacky Bai imx_pwr_set_cpu_entry(0, IMX_ROM_ENTRY);
426daa4478aSJacky Bai plat_gic_cpuif_enable();
427daa4478aSJacky Bai } else {
428daa4478aSJacky Bai dsb();
429daa4478aSJacky Bai write_scr_el3(read_scr_el3() & (~SCR_FIQ_BIT));
430daa4478aSJacky Bai isb();
431daa4478aSJacky Bai }
432daa4478aSJacky Bai }
433fcd41e86SJacky Bai
imx8ulp_pwr_domain_pwr_down_wfi(const psci_power_state_t * target_state)434fcd41e86SJacky Bai void __dead2 imx8ulp_pwr_domain_pwr_down_wfi(const psci_power_state_t *target_state)
435fcd41e86SJacky Bai {
436fcd41e86SJacky Bai while (1) {
437fcd41e86SJacky Bai wfi();
438fcd41e86SJacky Bai }
439fcd41e86SJacky Bai }
440fcd41e86SJacky Bai
imx8ulp_system_reset(void)441fcd41e86SJacky Bai void __dead2 imx8ulp_system_reset(void)
442fcd41e86SJacky Bai {
443fcd41e86SJacky Bai imx_pwr_set_cpu_entry(0, IMX_ROM_ENTRY);
444fcd41e86SJacky Bai
445fcd41e86SJacky Bai /* Write invalid command to WDOG CNT to trigger reset */
446fcd41e86SJacky Bai mmio_write_32(IMX_WDOG3_BASE + 0x4, 0x12345678);
447fcd41e86SJacky Bai
448fcd41e86SJacky Bai while (true) {
449fcd41e86SJacky Bai wfi();
450fcd41e86SJacky Bai }
451fcd41e86SJacky Bai }
452fcd41e86SJacky Bai
imx_validate_power_state(unsigned int power_state,psci_power_state_t * req_state)453daa4478aSJacky Bai int imx_validate_power_state(unsigned int power_state,
454daa4478aSJacky Bai psci_power_state_t *req_state)
455daa4478aSJacky Bai {
456daa4478aSJacky Bai int pwr_lvl = psci_get_pstate_pwrlvl(power_state);
457daa4478aSJacky Bai int pwr_type = psci_get_pstate_type(power_state);
458daa4478aSJacky Bai
459daa4478aSJacky Bai if (pwr_lvl > PLAT_MAX_PWR_LVL) {
460daa4478aSJacky Bai return PSCI_E_INVALID_PARAMS;
461daa4478aSJacky Bai }
462daa4478aSJacky Bai
463daa4478aSJacky Bai if (pwr_type == PSTATE_TYPE_STANDBY) {
464daa4478aSJacky Bai CORE_PWR_STATE(req_state) = PLAT_MAX_RET_STATE;
465daa4478aSJacky Bai CLUSTER_PWR_STATE(req_state) = PLAT_MAX_RET_STATE;
466daa4478aSJacky Bai }
467daa4478aSJacky Bai
468daa4478aSJacky Bai /* No power down state support */
469daa4478aSJacky Bai if (pwr_type == PSTATE_TYPE_POWERDOWN) {
470daa4478aSJacky Bai return PSCI_E_INVALID_PARAMS;
471daa4478aSJacky Bai }
472daa4478aSJacky Bai
473daa4478aSJacky Bai return PSCI_E_SUCCESS;
474daa4478aSJacky Bai }
475daa4478aSJacky Bai
imx_get_sys_suspend_power_state(psci_power_state_t * req_state)476daa4478aSJacky Bai void imx_get_sys_suspend_power_state(psci_power_state_t *req_state)
477daa4478aSJacky Bai {
478daa4478aSJacky Bai unsigned int i;
479daa4478aSJacky Bai
480daa4478aSJacky Bai for (i = IMX_PWR_LVL0; i <= PLAT_MAX_PWR_LVL; i++) {
481daa4478aSJacky Bai req_state->pwr_domain_state[i] = PLAT_POWER_DOWN_OFF_STATE;
482daa4478aSJacky Bai }
483daa4478aSJacky Bai }
484daa4478aSJacky Bai
imx_system_off(void)485891c547eSJacky Bai void __dead2 imx_system_off(void)
486891c547eSJacky Bai {
487891c547eSJacky Bai unsigned int i;
488891c547eSJacky Bai
489891c547eSJacky Bai /* config the all the core into OFF mode and IRQ masked. */
490891c547eSJacky Bai for (i = 0U; i < PLATFORM_CORE_COUNT; i++) {
491891c547eSJacky Bai /* disable wakeup from wkpu */
492891c547eSJacky Bai mmio_write_32(WKPUx(i), 0x0);
493891c547eSJacky Bai
494891c547eSJacky Bai /* reset the core reset entry to 0x1000 */
495891c547eSJacky Bai imx_pwr_set_cpu_entry(i, 0x1000);
496891c547eSJacky Bai
497891c547eSJacky Bai /* config the core power mode to off */
498891c547eSJacky Bai mmio_write_32(AD_COREx_LPMODE(i), 0x3);
499891c547eSJacky Bai }
500891c547eSJacky Bai
501891c547eSJacky Bai plat_gic_cpuif_disable();
502891c547eSJacky Bai
50336af80c2SJacky Bai /* power off all the pad */
50436af80c2SJacky Bai apd_io_pad_off();
50536af80c2SJacky Bai
506891c547eSJacky Bai /* Config the power mode info for entering DPD mode and ACT mode */
507891c547eSJacky Bai imx_set_pwr_mode_cfg(ADMA_PWR_MODE);
508891c547eSJacky Bai imx_set_pwr_mode_cfg(ACT_PWR_MODE);
509891c547eSJacky Bai imx_set_pwr_mode_cfg(DPD_PWR_MODE);
510891c547eSJacky Bai
511891c547eSJacky Bai /* Set the APD domain into DPD mode */
512891c547eSJacky Bai mmio_write_32(IMX_CMC1_BASE + 0x10, 0x7);
513891c547eSJacky Bai mmio_write_32(IMX_CMC1_BASE + 0x20, 0x1f);
514891c547eSJacky Bai
515891c547eSJacky Bai /* make sure no pending upower wakeup */
516891c547eSJacky Bai upwr_xcp_set_rtd_apd_llwu(APD_DOMAIN, 0, NULL);
517891c547eSJacky Bai upower_wait_resp();
518891c547eSJacky Bai
519891c547eSJacky Bai /* enable the upower wakeup from wuu, act as APD boot up method */
520891c547eSJacky Bai mmio_write_32(IMX_PCC3_BASE + 0x98, 0xc0800000);
521891c547eSJacky Bai mmio_setbits_32(IMX_WUU1_BASE + 0x18, BIT(4));
522891c547eSJacky Bai
523891c547eSJacky Bai /* make sure no pad wakeup event is pending */
524891c547eSJacky Bai mmio_write_32(IMX_WUU1_BASE + 0x20, 0xffffffff);
525891c547eSJacky Bai
526891c547eSJacky Bai wfi();
527891c547eSJacky Bai
528891c547eSJacky Bai ERROR("power off failed.\n");
529891c547eSJacky Bai panic();
530891c547eSJacky Bai }
531891c547eSJacky Bai
532fcd41e86SJacky Bai static const plat_psci_ops_t imx_plat_psci_ops = {
533fcd41e86SJacky Bai .pwr_domain_on = imx_pwr_domain_on,
534fcd41e86SJacky Bai .pwr_domain_on_finish = imx_pwr_domain_on_finish,
535fcd41e86SJacky Bai .validate_ns_entrypoint = imx_validate_ns_entrypoint,
536891c547eSJacky Bai .system_off = imx_system_off,
537fcd41e86SJacky Bai .system_reset = imx8ulp_system_reset,
538fcd41e86SJacky Bai .pwr_domain_off = imx_pwr_domain_off,
539daa4478aSJacky Bai .pwr_domain_suspend = imx_domain_suspend,
540daa4478aSJacky Bai .pwr_domain_suspend_finish = imx_domain_suspend_finish,
541daa4478aSJacky Bai .get_sys_suspend_power_state = imx_get_sys_suspend_power_state,
542daa4478aSJacky Bai .validate_power_state = imx_validate_power_state,
543db5fe4f4SBoyan Karatotev .pwr_domain_pwr_down = imx8ulp_pwr_domain_pwr_down_wfi,
544fcd41e86SJacky Bai };
545fcd41e86SJacky Bai
plat_setup_psci_ops(uintptr_t sec_entrypoint,const plat_psci_ops_t ** psci_ops)546fcd41e86SJacky Bai int plat_setup_psci_ops(uintptr_t sec_entrypoint,
547fcd41e86SJacky Bai const plat_psci_ops_t **psci_ops)
548fcd41e86SJacky Bai {
549fcd41e86SJacky Bai secure_entrypoint = sec_entrypoint;
550fcd41e86SJacky Bai imx_pwr_set_cpu_entry(0, sec_entrypoint);
551fcd41e86SJacky Bai *psci_ops = &imx_plat_psci_ops;
552fcd41e86SJacky Bai
553fcd41e86SJacky Bai mmio_write_32(IMX_CMC1_BASE + 0x18, 0x3f);
554fcd41e86SJacky Bai mmio_write_32(IMX_SIM1_BASE + 0x3c, 0xffffffff);
555fcd41e86SJacky Bai
556fcd41e86SJacky Bai return 0;
557fcd41e86SJacky Bai }
558