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