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 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; 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 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 126fcd41e86SJacky Bai int imx_validate_ns_entrypoint(uintptr_t ns_entrypoint) 127fcd41e86SJacky Bai { 128fcd41e86SJacky Bai return PSCI_E_SUCCESS; 129fcd41e86SJacky Bai } 130fcd41e86SJacky Bai 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 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 */ 29236af80c2SJacky Bai upower_pmic_i2c_read(0x22, &volt); 29336af80c2SJacky Bai pd_pmic_reg_cfgs[3].i2c_data = volt; 29436af80c2SJacky Bai memcpy(&pwr_sys_cfg->ps_apd_pmic_reg_data_cfg, &pd_pmic_reg_cfgs, 29536af80c2SJacky Bai sizeof(ps_apd_pmic_reg_data_cfgs_t)); 29636af80c2SJacky Bai } 29736af80c2SJacky Bai } 298daa4478aSJacky Bai } 299daa4478aSJacky Bai 300daa4478aSJacky Bai void imx_domain_suspend(const psci_power_state_t *target_state) 301daa4478aSJacky Bai { 302daa4478aSJacky Bai unsigned int cpu = MPIDR_AFFLVL0_VAL(read_mpidr_el1()); 303daa4478aSJacky Bai 304daa4478aSJacky Bai if (is_local_state_off(CORE_PWR_STATE(target_state))) { 305daa4478aSJacky Bai plat_gic_cpuif_disable(); 306daa4478aSJacky Bai imx_pwr_set_cpu_entry(cpu, secure_entrypoint); 307daa4478aSJacky Bai /* core put into power down */ 308daa4478aSJacky Bai mmio_write_32(IMX_CMC1_BASE + 0x50 + 0x4 * cpu, 0x3); 309daa4478aSJacky Bai /* FIXME config wakeup interrupt in WKPU */ 310daa4478aSJacky Bai mmio_write_32(IMX_SIM1_BASE + 0x3c + 0x4 * cpu, 0x7fffffe3); 311daa4478aSJacky Bai } else { 312daa4478aSJacky Bai /* for core standby/retention mode */ 313daa4478aSJacky Bai mmio_write_32(IMX_CMC1_BASE + 0x50 + 0x4 * cpu, 0x1); 314daa4478aSJacky Bai mmio_write_32(IMX_SIM1_BASE + 0x3c + 0x4 * cpu, 0x7fffffe3); 315daa4478aSJacky Bai dsb(); 316daa4478aSJacky Bai write_scr_el3(read_scr_el3() | SCR_FIQ_BIT); 317daa4478aSJacky Bai isb(); 318daa4478aSJacky Bai } 319daa4478aSJacky Bai 320478af8d3SJacky Bai if (is_local_state_retn(CLUSTER_PWR_STATE(target_state))) { 321daa4478aSJacky Bai /* 322daa4478aSJacky Bai * just for sleep mode for now, need to update to 323478af8d3SJacky Bai * support more modes, same for suspend finish call back. 324daa4478aSJacky Bai */ 325daa4478aSJacky Bai mmio_write_32(IMX_CMC1_BASE + 0x10, 0x1); 326daa4478aSJacky Bai mmio_write_32(IMX_CMC1_BASE + 0x20, 0x1); 327478af8d3SJacky Bai 328478af8d3SJacky Bai } else if (is_local_state_off(CLUSTER_PWR_STATE(target_state))) { 329478af8d3SJacky Bai /* 330478af8d3SJacky Bai * for cluster off state, put cluster into power down mode, 331478af8d3SJacky Bai * config the cluster clock to be off. 332478af8d3SJacky Bai */ 333478af8d3SJacky Bai mmio_write_32(IMX_CMC1_BASE + 0x10, 0x7); 334478af8d3SJacky Bai mmio_write_32(IMX_CMC1_BASE + 0x20, 0xf); 335daa4478aSJacky Bai } 336daa4478aSJacky Bai 337daa4478aSJacky Bai if (is_local_state_off(SYSTEM_PWR_STATE(target_state))) { 338daa4478aSJacky Bai /* 339daa4478aSJacky Bai * low power mode config info used by upower 340daa4478aSJacky Bai * to do low power mode transition. 341daa4478aSJacky Bai */ 342daa4478aSJacky Bai imx_set_pwr_mode_cfg(ADMA_PWR_MODE); 343daa4478aSJacky Bai imx_set_pwr_mode_cfg(ACT_PWR_MODE); 344478af8d3SJacky Bai imx_set_pwr_mode_cfg(PD_PWR_MODE); 345478af8d3SJacky Bai 346478af8d3SJacky Bai /* clear the upower wakeup */ 347478af8d3SJacky Bai upwr_xcp_set_rtd_apd_llwu(APD_DOMAIN, 0, NULL); 348478af8d3SJacky Bai upower_wait_resp(); 349478af8d3SJacky Bai 350478af8d3SJacky Bai /* enable the USB wakeup */ 351478af8d3SJacky Bai usb_wakeup_enable(true); 352478af8d3SJacky Bai 353478af8d3SJacky Bai /* config the WUU to enabled the wakeup source */ 354478af8d3SJacky Bai mmio_write_32(IMX_PCC3_BASE + 0x98, 0xc0800000); 355478af8d3SJacky Bai 356478af8d3SJacky Bai /* !!! clear all the pad wakeup pending event */ 357478af8d3SJacky Bai mmio_write_32(IMX_WUU1_BASE + 0x20, 0xffffffff); 358478af8d3SJacky Bai 359478af8d3SJacky Bai /* enable upower usb phy wakeup by default */ 360478af8d3SJacky Bai mmio_setbits_32(IMX_WUU1_BASE + 0x18, BIT(4) | BIT(1) | BIT(0)); 361478af8d3SJacky Bai 362478af8d3SJacky Bai /* enabled all pad wakeup by default */ 363478af8d3SJacky Bai mmio_write_32(IMX_WUU1_BASE + 0x8, 0xffffffff); 364478af8d3SJacky Bai 365478af8d3SJacky Bai /* save the AD domain context before entering PD mode */ 366478af8d3SJacky Bai imx_apd_ctx_save(cpu); 367daa4478aSJacky Bai } 368daa4478aSJacky Bai } 369daa4478aSJacky Bai 3704fafccb9SJacky Bai #define DRAM_LPM_STATUS U(0x2802b004) 371daa4478aSJacky Bai void imx_domain_suspend_finish(const psci_power_state_t *target_state) 372daa4478aSJacky Bai { 373daa4478aSJacky Bai unsigned int cpu = MPIDR_AFFLVL0_VAL(read_mpidr_el1()); 374daa4478aSJacky Bai 375daa4478aSJacky Bai if (is_local_state_off(SYSTEM_PWR_STATE(target_state))) { 376478af8d3SJacky Bai /* restore the ap domain context */ 377478af8d3SJacky Bai imx_apd_ctx_restore(cpu); 378478af8d3SJacky Bai 379478af8d3SJacky Bai /* clear the upower wakeup */ 380478af8d3SJacky Bai upwr_xcp_set_rtd_apd_llwu(APD_DOMAIN, 0, NULL); 381478af8d3SJacky Bai upower_wait_resp(); 382478af8d3SJacky Bai 383478af8d3SJacky Bai /* disable all pad wakeup */ 384478af8d3SJacky Bai mmio_write_32(IMX_WUU1_BASE + 0x8, 0x0); 385478af8d3SJacky Bai 386478af8d3SJacky Bai /* clear all the pad wakeup pending event */ 387478af8d3SJacky Bai mmio_write_32(IMX_WUU1_BASE + 0x20, 0xffffffff); 388478af8d3SJacky Bai 389478af8d3SJacky Bai /* 390478af8d3SJacky Bai * disable the usb wakeup after resume to make sure the pending 391478af8d3SJacky Bai * usb wakeup in WUU can be cleared successfully, otherwise, 392478af8d3SJacky Bai * APD will resume failed in next PD mode. 393478af8d3SJacky Bai */ 394478af8d3SJacky Bai usb_wakeup_enable(false); 395478af8d3SJacky Bai 396478af8d3SJacky Bai /* re-init the SCMI channel */ 397478af8d3SJacky Bai imx8ulp_init_scmi_server(); 398daa4478aSJacky Bai } 399daa4478aSJacky Bai 4004fafccb9SJacky Bai /* 4014fafccb9SJacky Bai * wait for DDR is ready when DDR is under the RTD 4024fafccb9SJacky Bai * side control for power saving 4034fafccb9SJacky Bai */ 4044fafccb9SJacky Bai while (mmio_read_32(DRAM_LPM_STATUS) != 0) { 4054fafccb9SJacky Bai ; 4064fafccb9SJacky Bai } 4074fafccb9SJacky Bai 408c514d3cfSJacky Bai /* 409c514d3cfSJacky Bai * when resume from low power mode, need to delay for a while 410c514d3cfSJacky Bai * before access the CMC register. 411c514d3cfSJacky Bai */ 412c514d3cfSJacky Bai udelay(5); 413c514d3cfSJacky Bai 414478af8d3SJacky Bai /* clear cluster's LPM setting. */ 415daa4478aSJacky Bai mmio_write_32(IMX_CMC1_BASE + 0x20, 0x0); 416daa4478aSJacky Bai mmio_write_32(IMX_CMC1_BASE + 0x10, 0x0); 417daa4478aSJacky Bai 418daa4478aSJacky Bai /* clear core's LPM setting */ 419daa4478aSJacky Bai mmio_write_32(IMX_CMC1_BASE + 0x50 + 0x4 * cpu, 0x0); 420daa4478aSJacky Bai mmio_write_32(IMX_SIM1_BASE + 0x3c + 0x4 * cpu, 0x0); 421daa4478aSJacky Bai 422daa4478aSJacky Bai if (is_local_state_off(CORE_PWR_STATE(target_state))) { 423daa4478aSJacky Bai imx_pwr_set_cpu_entry(0, IMX_ROM_ENTRY); 424daa4478aSJacky Bai plat_gic_cpuif_enable(); 425daa4478aSJacky Bai } else { 426daa4478aSJacky Bai dsb(); 427daa4478aSJacky Bai write_scr_el3(read_scr_el3() & (~SCR_FIQ_BIT)); 428daa4478aSJacky Bai isb(); 429daa4478aSJacky Bai } 430daa4478aSJacky Bai } 431fcd41e86SJacky Bai 432fcd41e86SJacky Bai void __dead2 imx8ulp_pwr_domain_pwr_down_wfi(const psci_power_state_t *target_state) 433fcd41e86SJacky Bai { 434fcd41e86SJacky Bai while (1) { 435fcd41e86SJacky Bai wfi(); 436fcd41e86SJacky Bai } 437fcd41e86SJacky Bai } 438fcd41e86SJacky Bai 439fcd41e86SJacky Bai void __dead2 imx8ulp_system_reset(void) 440fcd41e86SJacky Bai { 441fcd41e86SJacky Bai imx_pwr_set_cpu_entry(0, IMX_ROM_ENTRY); 442fcd41e86SJacky Bai 443fcd41e86SJacky Bai /* Write invalid command to WDOG CNT to trigger reset */ 444fcd41e86SJacky Bai mmio_write_32(IMX_WDOG3_BASE + 0x4, 0x12345678); 445fcd41e86SJacky Bai 446fcd41e86SJacky Bai while (true) { 447fcd41e86SJacky Bai wfi(); 448fcd41e86SJacky Bai } 449fcd41e86SJacky Bai } 450fcd41e86SJacky Bai 451daa4478aSJacky Bai int imx_validate_power_state(unsigned int power_state, 452daa4478aSJacky Bai psci_power_state_t *req_state) 453daa4478aSJacky Bai { 454daa4478aSJacky Bai int pwr_lvl = psci_get_pstate_pwrlvl(power_state); 455daa4478aSJacky Bai int pwr_type = psci_get_pstate_type(power_state); 456daa4478aSJacky Bai 457daa4478aSJacky Bai if (pwr_lvl > PLAT_MAX_PWR_LVL) { 458daa4478aSJacky Bai return PSCI_E_INVALID_PARAMS; 459daa4478aSJacky Bai } 460daa4478aSJacky Bai 461daa4478aSJacky Bai if (pwr_type == PSTATE_TYPE_STANDBY) { 462daa4478aSJacky Bai CORE_PWR_STATE(req_state) = PLAT_MAX_RET_STATE; 463daa4478aSJacky Bai CLUSTER_PWR_STATE(req_state) = PLAT_MAX_RET_STATE; 464daa4478aSJacky Bai } 465daa4478aSJacky Bai 466daa4478aSJacky Bai /* No power down state support */ 467daa4478aSJacky Bai if (pwr_type == PSTATE_TYPE_POWERDOWN) { 468daa4478aSJacky Bai return PSCI_E_INVALID_PARAMS; 469daa4478aSJacky Bai } 470daa4478aSJacky Bai 471daa4478aSJacky Bai return PSCI_E_SUCCESS; 472daa4478aSJacky Bai } 473daa4478aSJacky Bai 474daa4478aSJacky Bai void imx_get_sys_suspend_power_state(psci_power_state_t *req_state) 475daa4478aSJacky Bai { 476daa4478aSJacky Bai unsigned int i; 477daa4478aSJacky Bai 478daa4478aSJacky Bai for (i = IMX_PWR_LVL0; i <= PLAT_MAX_PWR_LVL; i++) { 479daa4478aSJacky Bai req_state->pwr_domain_state[i] = PLAT_POWER_DOWN_OFF_STATE; 480daa4478aSJacky Bai } 481daa4478aSJacky Bai } 482daa4478aSJacky Bai 483891c547eSJacky Bai void __dead2 imx_system_off(void) 484891c547eSJacky Bai { 485891c547eSJacky Bai unsigned int i; 486891c547eSJacky Bai 487891c547eSJacky Bai /* config the all the core into OFF mode and IRQ masked. */ 488891c547eSJacky Bai for (i = 0U; i < PLATFORM_CORE_COUNT; i++) { 489891c547eSJacky Bai /* disable wakeup from wkpu */ 490891c547eSJacky Bai mmio_write_32(WKPUx(i), 0x0); 491891c547eSJacky Bai 492891c547eSJacky Bai /* reset the core reset entry to 0x1000 */ 493891c547eSJacky Bai imx_pwr_set_cpu_entry(i, 0x1000); 494891c547eSJacky Bai 495891c547eSJacky Bai /* config the core power mode to off */ 496891c547eSJacky Bai mmio_write_32(AD_COREx_LPMODE(i), 0x3); 497891c547eSJacky Bai } 498891c547eSJacky Bai 499891c547eSJacky Bai plat_gic_cpuif_disable(); 500891c547eSJacky Bai 50136af80c2SJacky Bai /* power off all the pad */ 50236af80c2SJacky Bai apd_io_pad_off(); 50336af80c2SJacky Bai 504891c547eSJacky Bai /* Config the power mode info for entering DPD mode and ACT mode */ 505891c547eSJacky Bai imx_set_pwr_mode_cfg(ADMA_PWR_MODE); 506891c547eSJacky Bai imx_set_pwr_mode_cfg(ACT_PWR_MODE); 507891c547eSJacky Bai imx_set_pwr_mode_cfg(DPD_PWR_MODE); 508891c547eSJacky Bai 509891c547eSJacky Bai /* Set the APD domain into DPD mode */ 510891c547eSJacky Bai mmio_write_32(IMX_CMC1_BASE + 0x10, 0x7); 511891c547eSJacky Bai mmio_write_32(IMX_CMC1_BASE + 0x20, 0x1f); 512891c547eSJacky Bai 513891c547eSJacky Bai /* make sure no pending upower wakeup */ 514891c547eSJacky Bai upwr_xcp_set_rtd_apd_llwu(APD_DOMAIN, 0, NULL); 515891c547eSJacky Bai upower_wait_resp(); 516891c547eSJacky Bai 517891c547eSJacky Bai /* enable the upower wakeup from wuu, act as APD boot up method */ 518891c547eSJacky Bai mmio_write_32(IMX_PCC3_BASE + 0x98, 0xc0800000); 519891c547eSJacky Bai mmio_setbits_32(IMX_WUU1_BASE + 0x18, BIT(4)); 520891c547eSJacky Bai 521891c547eSJacky Bai /* make sure no pad wakeup event is pending */ 522891c547eSJacky Bai mmio_write_32(IMX_WUU1_BASE + 0x20, 0xffffffff); 523891c547eSJacky Bai 524891c547eSJacky Bai wfi(); 525891c547eSJacky Bai 526891c547eSJacky Bai ERROR("power off failed.\n"); 527891c547eSJacky Bai panic(); 528891c547eSJacky Bai } 529891c547eSJacky Bai 530fcd41e86SJacky Bai static const plat_psci_ops_t imx_plat_psci_ops = { 531fcd41e86SJacky Bai .pwr_domain_on = imx_pwr_domain_on, 532fcd41e86SJacky Bai .pwr_domain_on_finish = imx_pwr_domain_on_finish, 533fcd41e86SJacky Bai .validate_ns_entrypoint = imx_validate_ns_entrypoint, 534891c547eSJacky Bai .system_off = imx_system_off, 535fcd41e86SJacky Bai .system_reset = imx8ulp_system_reset, 536fcd41e86SJacky Bai .pwr_domain_off = imx_pwr_domain_off, 537daa4478aSJacky Bai .pwr_domain_suspend = imx_domain_suspend, 538daa4478aSJacky Bai .pwr_domain_suspend_finish = imx_domain_suspend_finish, 539daa4478aSJacky Bai .get_sys_suspend_power_state = imx_get_sys_suspend_power_state, 540daa4478aSJacky Bai .validate_power_state = imx_validate_power_state, 541*db5fe4f4SBoyan Karatotev .pwr_domain_pwr_down = imx8ulp_pwr_domain_pwr_down_wfi, 542fcd41e86SJacky Bai }; 543fcd41e86SJacky Bai 544fcd41e86SJacky Bai int plat_setup_psci_ops(uintptr_t sec_entrypoint, 545fcd41e86SJacky Bai const plat_psci_ops_t **psci_ops) 546fcd41e86SJacky Bai { 547fcd41e86SJacky Bai secure_entrypoint = sec_entrypoint; 548fcd41e86SJacky Bai imx_pwr_set_cpu_entry(0, sec_entrypoint); 549fcd41e86SJacky Bai *psci_ops = &imx_plat_psci_ops; 550fcd41e86SJacky Bai 551fcd41e86SJacky Bai mmio_write_32(IMX_CMC1_BASE + 0x18, 0x3f); 552fcd41e86SJacky Bai mmio_write_32(IMX_SIM1_BASE + 0x3c, 0xffffffff); 553fcd41e86SJacky Bai 554fcd41e86SJacky Bai return 0; 555fcd41e86SJacky Bai } 556