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> 17*daa4478aSJacky Bai #include <upower_api.h> 18fcd41e86SJacky Bai 19fcd41e86SJacky Bai static uintptr_t secure_entrypoint; 20fcd41e86SJacky Bai 21fcd41e86SJacky Bai #define CORE_PWR_STATE(state) ((state)->pwr_domain_state[MPIDR_AFFLVL0]) 22fcd41e86SJacky Bai #define CLUSTER_PWR_STATE(state) ((state)->pwr_domain_state[MPIDR_AFFLVL1]) 23fcd41e86SJacky Bai #define SYSTEM_PWR_STATE(state) ((state)->pwr_domain_state[PLAT_MAX_PWR_LVL]) 24fcd41e86SJacky Bai 25fcd41e86SJacky Bai #define RVBARADDRx(c) (IMX_SIM1_BASE + 0x5c + 0x4 * (c)) 26fcd41e86SJacky Bai #define WKPUx(c) (IMX_SIM1_BASE + 0x3c + 0x4 * (c)) 27fcd41e86SJacky Bai #define AD_COREx_LPMODE(c) (IMX_CMC1_BASE + 0x50 + 0x4 * (c)) 28fcd41e86SJacky Bai 29*daa4478aSJacky Bai #define PMIC_CFG(v, m, msk) \ 30*daa4478aSJacky Bai { \ 31*daa4478aSJacky Bai .volt = (v), \ 32*daa4478aSJacky Bai .mode = (m), \ 33*daa4478aSJacky Bai .mode_msk = (msk), \ 34*daa4478aSJacky Bai } 35*daa4478aSJacky Bai 36*daa4478aSJacky Bai #define PAD_CFG(c, r, t) \ 37*daa4478aSJacky Bai { \ 38*daa4478aSJacky Bai .pad_close = (c), \ 39*daa4478aSJacky Bai .pad_reset = (r), \ 40*daa4478aSJacky Bai .pad_tqsleep = (t) \ 41*daa4478aSJacky Bai } 42*daa4478aSJacky Bai 43*daa4478aSJacky Bai #define BIAS_CFG(m, n, p, mbias) \ 44*daa4478aSJacky Bai { \ 45*daa4478aSJacky Bai .dombias_cfg = { \ 46*daa4478aSJacky Bai .mode = (m), \ 47*daa4478aSJacky Bai .rbbn = (n), \ 48*daa4478aSJacky Bai .rbbp = (p), \ 49*daa4478aSJacky Bai }, \ 50*daa4478aSJacky Bai .membias_cfg = {mbias}, \ 51*daa4478aSJacky Bai } 52*daa4478aSJacky Bai 53*daa4478aSJacky Bai #define SWT_BOARD(swt_on, msk) \ 54*daa4478aSJacky Bai { \ 55*daa4478aSJacky Bai .on = (swt_on), \ 56*daa4478aSJacky Bai .mask = (msk), \ 57*daa4478aSJacky Bai } 58*daa4478aSJacky Bai 59*daa4478aSJacky Bai #define SWT_MEM(a, p, m) \ 60*daa4478aSJacky Bai { \ 61*daa4478aSJacky Bai .array = (a), \ 62*daa4478aSJacky Bai .perif = (p), \ 63*daa4478aSJacky Bai .mask = (m), \ 64*daa4478aSJacky Bai } 65*daa4478aSJacky Bai 66fcd41e86SJacky Bai static int imx_pwr_set_cpu_entry(unsigned int cpu, unsigned int entry) 67fcd41e86SJacky Bai { 68fcd41e86SJacky Bai mmio_write_32(RVBARADDRx(cpu), entry); 69fcd41e86SJacky Bai 70fcd41e86SJacky Bai /* set update bit */ 71fcd41e86SJacky Bai mmio_write_32(IMX_SIM1_BASE + 0x8, mmio_read_32(IMX_SIM1_BASE + 0x8) | BIT_32(24 + cpu)); 72fcd41e86SJacky Bai /* wait for ack */ 73fcd41e86SJacky Bai while (!(mmio_read_32(IMX_SIM1_BASE + 0x8) & BIT_32(26 + cpu))) { 74fcd41e86SJacky Bai } 75fcd41e86SJacky Bai 76fcd41e86SJacky Bai /* clear update bit */ 77fcd41e86SJacky Bai mmio_write_32(IMX_SIM1_BASE + 0x8, mmio_read_32(IMX_SIM1_BASE + 0x8) & ~BIT_32(24 + cpu)); 78fcd41e86SJacky Bai /* clear ack bit */ 79fcd41e86SJacky Bai mmio_write_32(IMX_SIM1_BASE + 0x8, mmio_read_32(IMX_SIM1_BASE + 0x8) | BIT_32(26 + cpu)); 80fcd41e86SJacky Bai 81fcd41e86SJacky Bai return 0; 82fcd41e86SJacky Bai } 83fcd41e86SJacky Bai 84fcd41e86SJacky Bai int imx_pwr_domain_on(u_register_t mpidr) 85fcd41e86SJacky Bai { 86fcd41e86SJacky Bai unsigned int cpu = MPIDR_AFFLVL0_VAL(mpidr); 87fcd41e86SJacky Bai 88fcd41e86SJacky Bai imx_pwr_set_cpu_entry(cpu, secure_entrypoint); 89fcd41e86SJacky Bai 90fcd41e86SJacky Bai mmio_write_32(IMX_CMC1_BASE + 0x18, 0x3f); 91fcd41e86SJacky Bai mmio_write_32(IMX_CMC1_BASE + 0x50 + 0x4 * cpu, 0); 92fcd41e86SJacky Bai 93fcd41e86SJacky Bai /* enable wku wakeup for idle */ 94fcd41e86SJacky Bai mmio_write_32(IMX_SIM1_BASE + 0x3c + 0x4 * cpu, 0xffffffff); 95fcd41e86SJacky Bai 96fcd41e86SJacky Bai return PSCI_E_SUCCESS; 97fcd41e86SJacky Bai } 98fcd41e86SJacky Bai 99fcd41e86SJacky Bai void imx_pwr_domain_on_finish(const psci_power_state_t *target_state) 100fcd41e86SJacky Bai { 101fcd41e86SJacky Bai imx_pwr_set_cpu_entry(0, IMX_ROM_ENTRY); 102fcd41e86SJacky Bai plat_gic_pcpu_init(); 103fcd41e86SJacky Bai plat_gic_cpuif_enable(); 104fcd41e86SJacky Bai } 105fcd41e86SJacky Bai 106fcd41e86SJacky Bai int imx_validate_ns_entrypoint(uintptr_t ns_entrypoint) 107fcd41e86SJacky Bai { 108fcd41e86SJacky Bai return PSCI_E_SUCCESS; 109fcd41e86SJacky Bai } 110fcd41e86SJacky Bai 111fcd41e86SJacky Bai void imx_pwr_domain_off(const psci_power_state_t *target_state) 112fcd41e86SJacky Bai { 113fcd41e86SJacky Bai unsigned int cpu = MPIDR_AFFLVL0_VAL(read_mpidr_el1()); 114fcd41e86SJacky Bai 115fcd41e86SJacky Bai plat_gic_cpuif_disable(); 116fcd41e86SJacky Bai 117fcd41e86SJacky Bai /* disable wakeup */ 118fcd41e86SJacky Bai mmio_write_32(WKPUx(cpu), 0); 119fcd41e86SJacky Bai 120*daa4478aSJacky Bai /* set core power mode to PD */ 121fcd41e86SJacky Bai mmio_write_32(AD_COREx_LPMODE(cpu), 0x3); 122fcd41e86SJacky Bai } 123*daa4478aSJacky Bai /* APD power mode config */ 124*daa4478aSJacky Bai ps_apd_pwr_mode_cfgs_t apd_pwr_mode_cfgs = { 125*daa4478aSJacky Bai [ADMA_PWR_MODE] = { 126*daa4478aSJacky Bai .swt_board_offs = 0x120, 127*daa4478aSJacky Bai .swt_mem_offs = 0x128, 128*daa4478aSJacky Bai .pmic_cfg = PMIC_CFG(0x23, 0x0, 0x2), 129*daa4478aSJacky Bai .pad_cfg = PAD_CFG(0x0, 0x0, 0x0deb7a00), 130*daa4478aSJacky Bai .bias_cfg = BIAS_CFG(0x0, 0x2, 0x2, 0x0), 131*daa4478aSJacky Bai }, 132*daa4478aSJacky Bai 133*daa4478aSJacky Bai [ACT_PWR_MODE] = { 134*daa4478aSJacky Bai .swt_board_offs = 0x110, 135*daa4478aSJacky Bai .swt_mem_offs = 0x118, 136*daa4478aSJacky Bai .pmic_cfg = PMIC_CFG(0x23, 0x2, 0x2), 137*daa4478aSJacky Bai .pad_cfg = PAD_CFG(0x0, 0x0, 0x0deb7a00), 138*daa4478aSJacky Bai .bias_cfg = BIAS_CFG(0x0, 0x2, 0x2, 0x0), 139*daa4478aSJacky Bai }, 140*daa4478aSJacky Bai }; 141*daa4478aSJacky Bai 142*daa4478aSJacky Bai /* APD power switch config */ 143*daa4478aSJacky Bai ps_apd_swt_cfgs_t apd_swt_cfgs = { 144*daa4478aSJacky Bai [ADMA_PWR_MODE] = { 145*daa4478aSJacky Bai .swt_board[0] = SWT_BOARD(0x74, 0x7c), 146*daa4478aSJacky Bai .swt_mem[0] = SWT_MEM(0x0001fffd, 0x0001fffd, 0x1fffd), 147*daa4478aSJacky Bai .swt_mem[1] = SWT_MEM(0x0, 0x0, 0x0), 148*daa4478aSJacky Bai }, 149*daa4478aSJacky Bai 150*daa4478aSJacky Bai [ACT_PWR_MODE] = { 151*daa4478aSJacky Bai .swt_board[0] = SWT_BOARD(0x74, 0x7c), 152*daa4478aSJacky Bai .swt_mem[0] = SWT_MEM(0x0001fffd, 0x0001fffd, 0x1fffd), 153*daa4478aSJacky Bai .swt_mem[1] = SWT_MEM(0x0, 0x0, 0x0), 154*daa4478aSJacky Bai }, 155*daa4478aSJacky Bai }; 156*daa4478aSJacky Bai 157*daa4478aSJacky Bai struct ps_pwr_mode_cfg_t *pwr_sys_cfg = (struct ps_pwr_mode_cfg_t *)UPWR_DRAM_SHARED_BASE_ADDR; 158*daa4478aSJacky Bai 159*daa4478aSJacky Bai void imx_set_pwr_mode_cfg(abs_pwr_mode_t mode) 160*daa4478aSJacky Bai { 161*daa4478aSJacky Bai if (mode >= NUM_PWR_MODES) { 162*daa4478aSJacky Bai return; 163*daa4478aSJacky Bai } 164*daa4478aSJacky Bai 165*daa4478aSJacky Bai /* apd power mode config */ 166*daa4478aSJacky Bai memcpy(&pwr_sys_cfg->ps_apd_pwr_mode_cfg[mode], &apd_pwr_mode_cfgs[mode], 167*daa4478aSJacky Bai sizeof(struct ps_apd_pwr_mode_cfg_t)); 168*daa4478aSJacky Bai 169*daa4478aSJacky Bai /* apd power switch config */ 170*daa4478aSJacky Bai memcpy(&pwr_sys_cfg->ps_apd_swt_cfg[mode], &apd_swt_cfgs[mode], sizeof(swt_config_t)); 171*daa4478aSJacky Bai } 172*daa4478aSJacky Bai 173*daa4478aSJacky Bai void imx_domain_suspend(const psci_power_state_t *target_state) 174*daa4478aSJacky Bai { 175*daa4478aSJacky Bai unsigned int cpu = MPIDR_AFFLVL0_VAL(read_mpidr_el1()); 176*daa4478aSJacky Bai 177*daa4478aSJacky Bai if (is_local_state_off(CORE_PWR_STATE(target_state))) { 178*daa4478aSJacky Bai plat_gic_cpuif_disable(); 179*daa4478aSJacky Bai imx_pwr_set_cpu_entry(cpu, secure_entrypoint); 180*daa4478aSJacky Bai /* core put into power down */ 181*daa4478aSJacky Bai mmio_write_32(IMX_CMC1_BASE + 0x50 + 0x4 * cpu, 0x3); 182*daa4478aSJacky Bai /* FIXME config wakeup interrupt in WKPU */ 183*daa4478aSJacky Bai mmio_write_32(IMX_SIM1_BASE + 0x3c + 0x4 * cpu, 0x7fffffe3); 184*daa4478aSJacky Bai } else { 185*daa4478aSJacky Bai /* for core standby/retention mode */ 186*daa4478aSJacky Bai mmio_write_32(IMX_CMC1_BASE + 0x50 + 0x4 * cpu, 0x1); 187*daa4478aSJacky Bai mmio_write_32(IMX_SIM1_BASE + 0x3c + 0x4 * cpu, 0x7fffffe3); 188*daa4478aSJacky Bai dsb(); 189*daa4478aSJacky Bai write_scr_el3(read_scr_el3() | SCR_FIQ_BIT); 190*daa4478aSJacky Bai isb(); 191*daa4478aSJacky Bai } 192*daa4478aSJacky Bai 193*daa4478aSJacky Bai if (!is_local_state_run(CLUSTER_PWR_STATE(target_state))) { 194*daa4478aSJacky Bai /* TODO imx_set_wakeup() based on GIC config*/ 195*daa4478aSJacky Bai 196*daa4478aSJacky Bai /* 197*daa4478aSJacky Bai * just for sleep mode for now, need to update to 198*daa4478aSJacky Bai * support more mode, same for suspend finish call back. 199*daa4478aSJacky Bai */ 200*daa4478aSJacky Bai mmio_write_32(IMX_CMC1_BASE + 0x10, 0x1); 201*daa4478aSJacky Bai mmio_write_32(IMX_CMC1_BASE + 0x20, 0x1); 202*daa4478aSJacky Bai } 203*daa4478aSJacky Bai 204*daa4478aSJacky Bai /* TODO, may need to add more system level config here */ 205*daa4478aSJacky Bai if (is_local_state_off(SYSTEM_PWR_STATE(target_state))) { 206*daa4478aSJacky Bai /* 207*daa4478aSJacky Bai * low power mode config info used by upower 208*daa4478aSJacky Bai * to do low power mode transition. 209*daa4478aSJacky Bai */ 210*daa4478aSJacky Bai imx_set_pwr_mode_cfg(ADMA_PWR_MODE); 211*daa4478aSJacky Bai imx_set_pwr_mode_cfg(ACT_PWR_MODE); 212*daa4478aSJacky Bai } 213*daa4478aSJacky Bai } 214*daa4478aSJacky Bai 215*daa4478aSJacky Bai void imx_domain_suspend_finish(const psci_power_state_t *target_state) 216*daa4478aSJacky Bai { 217*daa4478aSJacky Bai unsigned int cpu = MPIDR_AFFLVL0_VAL(read_mpidr_el1()); 218*daa4478aSJacky Bai 219*daa4478aSJacky Bai if (is_local_state_off(SYSTEM_PWR_STATE(target_state))) { 220*daa4478aSJacky Bai /* TODO reverse setting for system level */ 221*daa4478aSJacky Bai } 222*daa4478aSJacky Bai 223*daa4478aSJacky Bai if (!is_local_state_run(CLUSTER_PWR_STATE(target_state))) { 224*daa4478aSJacky Bai mmio_write_32(IMX_CMC1_BASE + 0x20, 0x0); 225*daa4478aSJacky Bai mmio_write_32(IMX_CMC1_BASE + 0x10, 0x0); 226*daa4478aSJacky Bai } 227*daa4478aSJacky Bai 228*daa4478aSJacky Bai /* clear core's LPM setting */ 229*daa4478aSJacky Bai mmio_write_32(IMX_CMC1_BASE + 0x50 + 0x4 * cpu, 0x0); 230*daa4478aSJacky Bai mmio_write_32(IMX_SIM1_BASE + 0x3c + 0x4 * cpu, 0x0); 231*daa4478aSJacky Bai 232*daa4478aSJacky Bai if (is_local_state_off(CORE_PWR_STATE(target_state))) { 233*daa4478aSJacky Bai imx_pwr_set_cpu_entry(0, IMX_ROM_ENTRY); 234*daa4478aSJacky Bai plat_gic_cpuif_enable(); 235*daa4478aSJacky Bai } else { 236*daa4478aSJacky Bai dsb(); 237*daa4478aSJacky Bai write_scr_el3(read_scr_el3() & (~SCR_FIQ_BIT)); 238*daa4478aSJacky Bai isb(); 239*daa4478aSJacky Bai } 240*daa4478aSJacky Bai } 241fcd41e86SJacky Bai 242fcd41e86SJacky Bai void __dead2 imx8ulp_pwr_domain_pwr_down_wfi(const psci_power_state_t *target_state) 243fcd41e86SJacky Bai { 244fcd41e86SJacky Bai while (1) { 245fcd41e86SJacky Bai wfi(); 246fcd41e86SJacky Bai } 247fcd41e86SJacky Bai } 248fcd41e86SJacky Bai 249fcd41e86SJacky Bai void __dead2 imx8ulp_system_reset(void) 250fcd41e86SJacky Bai { 251fcd41e86SJacky Bai imx_pwr_set_cpu_entry(0, IMX_ROM_ENTRY); 252fcd41e86SJacky Bai 253fcd41e86SJacky Bai /* Write invalid command to WDOG CNT to trigger reset */ 254fcd41e86SJacky Bai mmio_write_32(IMX_WDOG3_BASE + 0x4, 0x12345678); 255fcd41e86SJacky Bai 256fcd41e86SJacky Bai while (true) { 257fcd41e86SJacky Bai wfi(); 258fcd41e86SJacky Bai } 259fcd41e86SJacky Bai } 260fcd41e86SJacky Bai 261*daa4478aSJacky Bai int imx_validate_power_state(unsigned int power_state, 262*daa4478aSJacky Bai psci_power_state_t *req_state) 263*daa4478aSJacky Bai { 264*daa4478aSJacky Bai int pwr_lvl = psci_get_pstate_pwrlvl(power_state); 265*daa4478aSJacky Bai int pwr_type = psci_get_pstate_type(power_state); 266*daa4478aSJacky Bai 267*daa4478aSJacky Bai if (pwr_lvl > PLAT_MAX_PWR_LVL) { 268*daa4478aSJacky Bai return PSCI_E_INVALID_PARAMS; 269*daa4478aSJacky Bai } 270*daa4478aSJacky Bai 271*daa4478aSJacky Bai if (pwr_type == PSTATE_TYPE_STANDBY) { 272*daa4478aSJacky Bai CORE_PWR_STATE(req_state) = PLAT_MAX_RET_STATE; 273*daa4478aSJacky Bai CLUSTER_PWR_STATE(req_state) = PLAT_MAX_RET_STATE; 274*daa4478aSJacky Bai } 275*daa4478aSJacky Bai 276*daa4478aSJacky Bai /* No power down state support */ 277*daa4478aSJacky Bai if (pwr_type == PSTATE_TYPE_POWERDOWN) { 278*daa4478aSJacky Bai return PSCI_E_INVALID_PARAMS; 279*daa4478aSJacky Bai } 280*daa4478aSJacky Bai 281*daa4478aSJacky Bai return PSCI_E_SUCCESS; 282*daa4478aSJacky Bai } 283*daa4478aSJacky Bai 284*daa4478aSJacky Bai void imx_get_sys_suspend_power_state(psci_power_state_t *req_state) 285*daa4478aSJacky Bai { 286*daa4478aSJacky Bai unsigned int i; 287*daa4478aSJacky Bai 288*daa4478aSJacky Bai for (i = IMX_PWR_LVL0; i <= PLAT_MAX_PWR_LVL; i++) { 289*daa4478aSJacky Bai req_state->pwr_domain_state[i] = PLAT_POWER_DOWN_OFF_STATE; 290*daa4478aSJacky Bai } 291*daa4478aSJacky Bai } 292*daa4478aSJacky Bai 293fcd41e86SJacky Bai static const plat_psci_ops_t imx_plat_psci_ops = { 294fcd41e86SJacky Bai .pwr_domain_on = imx_pwr_domain_on, 295fcd41e86SJacky Bai .pwr_domain_on_finish = imx_pwr_domain_on_finish, 296fcd41e86SJacky Bai .validate_ns_entrypoint = imx_validate_ns_entrypoint, 297fcd41e86SJacky Bai .system_reset = imx8ulp_system_reset, 298fcd41e86SJacky Bai .pwr_domain_off = imx_pwr_domain_off, 299*daa4478aSJacky Bai .pwr_domain_suspend = imx_domain_suspend, 300*daa4478aSJacky Bai .pwr_domain_suspend_finish = imx_domain_suspend_finish, 301*daa4478aSJacky Bai .get_sys_suspend_power_state = imx_get_sys_suspend_power_state, 302*daa4478aSJacky Bai .validate_power_state = imx_validate_power_state, 303fcd41e86SJacky Bai .pwr_domain_pwr_down_wfi = imx8ulp_pwr_domain_pwr_down_wfi, 304fcd41e86SJacky Bai }; 305fcd41e86SJacky Bai 306fcd41e86SJacky Bai int plat_setup_psci_ops(uintptr_t sec_entrypoint, 307fcd41e86SJacky Bai const plat_psci_ops_t **psci_ops) 308fcd41e86SJacky Bai { 309fcd41e86SJacky Bai secure_entrypoint = sec_entrypoint; 310fcd41e86SJacky Bai imx_pwr_set_cpu_entry(0, sec_entrypoint); 311fcd41e86SJacky Bai *psci_ops = &imx_plat_psci_ops; 312fcd41e86SJacky Bai 313fcd41e86SJacky Bai mmio_write_32(IMX_CMC1_BASE + 0x18, 0x3f); 314fcd41e86SJacky Bai mmio_write_32(IMX_SIM1_BASE + 0x3c, 0xffffffff); 315fcd41e86SJacky Bai 316fcd41e86SJacky Bai return 0; 317fcd41e86SJacky Bai } 318