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