1*4ddfb6f1SJacky Bai /* 2*4ddfb6f1SJacky Bai * Copyright 2025 NXP 3*4ddfb6f1SJacky Bai * 4*4ddfb6f1SJacky Bai * SPDX-License-Identifier: BSD-3-Clause 5*4ddfb6f1SJacky Bai */ 6*4ddfb6f1SJacky Bai 7*4ddfb6f1SJacky Bai #include <stdbool.h> 8*4ddfb6f1SJacky Bai 9*4ddfb6f1SJacky Bai #include "../drivers/arm/gic/v3/gicv3_private.h" 10*4ddfb6f1SJacky Bai 11*4ddfb6f1SJacky Bai #include <arch.h> 12*4ddfb6f1SJacky Bai #include <arch_helpers.h> 13*4ddfb6f1SJacky Bai #include <common/debug.h> 14*4ddfb6f1SJacky Bai #include <drivers/arm/css/scmi.h> 15*4ddfb6f1SJacky Bai #include <drivers/arm/gicv3.h> 16*4ddfb6f1SJacky Bai #include <lib/psci/psci.h> 17*4ddfb6f1SJacky Bai #include <scmi_imx9.h> 18*4ddfb6f1SJacky Bai 19*4ddfb6f1SJacky Bai #include <imx9_psci_common.h> 20*4ddfb6f1SJacky Bai #include <imx9_sys_sleep.h> 21*4ddfb6f1SJacky Bai #include <imx_scmi_client.h> 22*4ddfb6f1SJacky Bai #include <plat_imx8.h> 23*4ddfb6f1SJacky Bai 24*4ddfb6f1SJacky Bai /* platform secure warm boot entry */ 25*4ddfb6f1SJacky Bai uintptr_t secure_entrypoint; 26*4ddfb6f1SJacky Bai 27*4ddfb6f1SJacky Bai int imx_validate_ns_entrypoint(uintptr_t ns_entrypoint) 28*4ddfb6f1SJacky Bai { 29*4ddfb6f1SJacky Bai /* The non-secure entrypoint should be in RAM space */ 30*4ddfb6f1SJacky Bai if (ns_entrypoint < PLAT_NS_IMAGE_OFFSET) { 31*4ddfb6f1SJacky Bai return PSCI_E_INVALID_PARAMS; 32*4ddfb6f1SJacky Bai } 33*4ddfb6f1SJacky Bai 34*4ddfb6f1SJacky Bai return PSCI_E_SUCCESS; 35*4ddfb6f1SJacky Bai } 36*4ddfb6f1SJacky Bai 37*4ddfb6f1SJacky Bai int imx_validate_power_state(uint32_t power_state, 38*4ddfb6f1SJacky Bai psci_power_state_t *req_state) 39*4ddfb6f1SJacky Bai { 40*4ddfb6f1SJacky Bai int pwr_lvl = psci_get_pstate_pwrlvl(power_state); 41*4ddfb6f1SJacky Bai int pwr_type = psci_get_pstate_type(power_state); 42*4ddfb6f1SJacky Bai int state_id = psci_get_pstate_id(power_state); 43*4ddfb6f1SJacky Bai 44*4ddfb6f1SJacky Bai if (pwr_lvl > PLAT_MAX_PWR_LVL) { 45*4ddfb6f1SJacky Bai return PSCI_E_INVALID_PARAMS; 46*4ddfb6f1SJacky Bai } 47*4ddfb6f1SJacky Bai 48*4ddfb6f1SJacky Bai if (pwr_type == PSTATE_TYPE_STANDBY) { 49*4ddfb6f1SJacky Bai CORE_PWR_STATE(req_state) = PLAT_MAX_OFF_STATE; 50*4ddfb6f1SJacky Bai CLUSTER_PWR_STATE(req_state) = PLAT_MAX_RET_STATE; 51*4ddfb6f1SJacky Bai } 52*4ddfb6f1SJacky Bai 53*4ddfb6f1SJacky Bai if (pwr_type == PSTATE_TYPE_POWERDOWN && state_id == 0x33) { 54*4ddfb6f1SJacky Bai CORE_PWR_STATE(req_state) = PLAT_MAX_OFF_STATE; 55*4ddfb6f1SJacky Bai CLUSTER_PWR_STATE(req_state) = PLAT_MAX_RET_STATE; 56*4ddfb6f1SJacky Bai } 57*4ddfb6f1SJacky Bai 58*4ddfb6f1SJacky Bai return PSCI_E_SUCCESS; 59*4ddfb6f1SJacky Bai } 60*4ddfb6f1SJacky Bai 61*4ddfb6f1SJacky Bai void imx_set_cpu_boot_entry(uint32_t core_id, uint64_t boot_entry, 62*4ddfb6f1SJacky Bai uint32_t flag) 63*4ddfb6f1SJacky Bai { 64*4ddfb6f1SJacky Bai scmi_core_set_reset_addr(imx9_scmi_handle, 65*4ddfb6f1SJacky Bai boot_entry, 66*4ddfb6f1SJacky Bai SCMI_CPU_A55_ID(core_id), 67*4ddfb6f1SJacky Bai flag); 68*4ddfb6f1SJacky Bai } 69*4ddfb6f1SJacky Bai 70*4ddfb6f1SJacky Bai int imx_pwr_domain_on(u_register_t mpidr) 71*4ddfb6f1SJacky Bai { 72*4ddfb6f1SJacky Bai uint32_t core_id = MPIDR_AFFLVL1_VAL(mpidr); 73*4ddfb6f1SJacky Bai uint32_t mask = DEBUG_WAKEUP_MASK | EVENT_WAKEUP_MASK; 74*4ddfb6f1SJacky Bai 75*4ddfb6f1SJacky Bai imx_set_cpu_boot_entry(core_id, 76*4ddfb6f1SJacky Bai secure_entrypoint, 77*4ddfb6f1SJacky Bai SCMI_CPU_VEC_FLAGS_BOOT); 78*4ddfb6f1SJacky Bai 79*4ddfb6f1SJacky Bai scmi_core_start(imx9_scmi_handle, SCMI_CPU_A55_ID(core_id)); 80*4ddfb6f1SJacky Bai 81*4ddfb6f1SJacky Bai /* 82*4ddfb6f1SJacky Bai * Set NON-IRQ wakeup mask to Disable wakeup on DEBUG_WAKEUP 83*4ddfb6f1SJacky Bai */ 84*4ddfb6f1SJacky Bai scmi_core_nonIrq_wake_set(imx9_scmi_handle, SCMI_CPU_A55_ID(core_id), 0U, 1U, mask); 85*4ddfb6f1SJacky Bai 86*4ddfb6f1SJacky Bai /* Set the default LPM state for cpuidle. */ 87*4ddfb6f1SJacky Bai struct scmi_lpm_config cpu_lpm_cfg = { 88*4ddfb6f1SJacky Bai SCMI_CPU_A55_PD(core_id), 89*4ddfb6f1SJacky Bai SCMI_CPU_PD_LPM_ON_RUN, 90*4ddfb6f1SJacky Bai 0U 91*4ddfb6f1SJacky Bai }; 92*4ddfb6f1SJacky Bai 93*4ddfb6f1SJacky Bai scmi_core_lpm_mode_set(imx9_scmi_handle, 94*4ddfb6f1SJacky Bai SCMI_CPU_A55_ID(core_id), 95*4ddfb6f1SJacky Bai 1U, &cpu_lpm_cfg); 96*4ddfb6f1SJacky Bai 97*4ddfb6f1SJacky Bai return PSCI_E_SUCCESS; 98*4ddfb6f1SJacky Bai } 99*4ddfb6f1SJacky Bai 100*4ddfb6f1SJacky Bai void imx_pwr_domain_on_finish(const psci_power_state_t *target_state) 101*4ddfb6f1SJacky Bai { 102*4ddfb6f1SJacky Bai uint64_t mpidr = read_mpidr_el1(); 103*4ddfb6f1SJacky Bai uint32_t core_id = MPIDR_AFFLVL1_VAL(mpidr); 104*4ddfb6f1SJacky Bai 105*4ddfb6f1SJacky Bai scmi_core_set_sleep_mode(imx9_scmi_handle, 106*4ddfb6f1SJacky Bai SCMI_CPU_A55_ID(core_id), 107*4ddfb6f1SJacky Bai SCMI_GIC_WAKEUP, 108*4ddfb6f1SJacky Bai SCMI_CPU_SLEEP_WAIT); 109*4ddfb6f1SJacky Bai } 110*4ddfb6f1SJacky Bai 111*4ddfb6f1SJacky Bai void imx_pwr_domain_off(const psci_power_state_t *target_state) 112*4ddfb6f1SJacky Bai { 113*4ddfb6f1SJacky Bai uint64_t mpidr = read_mpidr_el1(); 114*4ddfb6f1SJacky Bai uint32_t core_id = MPIDR_AFFLVL1_VAL(mpidr); 115*4ddfb6f1SJacky Bai 116*4ddfb6f1SJacky Bai /* Ensure the cluster can be powered off. */ 117*4ddfb6f1SJacky Bai write_clusterpwrdn(DSU_CLUSTER_PWR_OFF); 118*4ddfb6f1SJacky Bai 119*4ddfb6f1SJacky Bai /* Configure core LPM state for hotplug. */ 120*4ddfb6f1SJacky Bai struct scmi_lpm_config cpu_lpm_cfg = { 121*4ddfb6f1SJacky Bai SCMI_CPU_A55_PD(core_id), 122*4ddfb6f1SJacky Bai SCMI_CPU_PD_LPM_ON_RUN_WAIT_STOP, 123*4ddfb6f1SJacky Bai 0U 124*4ddfb6f1SJacky Bai }; 125*4ddfb6f1SJacky Bai /* Set the default LPM state for cpuidle */ 126*4ddfb6f1SJacky Bai scmi_core_lpm_mode_set(imx9_scmi_handle, SCMI_CPU_A55_ID(core_id), 127*4ddfb6f1SJacky Bai 1U, &cpu_lpm_cfg); 128*4ddfb6f1SJacky Bai 129*4ddfb6f1SJacky Bai /* 130*4ddfb6f1SJacky Bai * Mask all the GPC IRQ wakeup to make sure no IRQ can wakeup this core 131*4ddfb6f1SJacky Bai * so we need to use SW_WAKEUP for hotplug purpose 132*4ddfb6f1SJacky Bai */ 133*4ddfb6f1SJacky Bai scmi_core_Irq_wake_set(imx9_scmi_handle, SCMI_CPU_A55_ID(core_id), 0U, 134*4ddfb6f1SJacky Bai IMR_NUM, mask_all); 135*4ddfb6f1SJacky Bai 136*4ddfb6f1SJacky Bai scmi_core_set_sleep_mode(imx9_scmi_handle, SCMI_CPU_A55_ID(core_id), 137*4ddfb6f1SJacky Bai SCMI_GPC_WAKEUP, SCMI_CPU_SLEEP_SUSPEND); 138*4ddfb6f1SJacky Bai } 139*4ddfb6f1SJacky Bai 140*4ddfb6f1SJacky Bai void imx_pwr_domain_suspend(const psci_power_state_t *target_state) 141*4ddfb6f1SJacky Bai { 142*4ddfb6f1SJacky Bai uint64_t mpidr = read_mpidr_el1(); 143*4ddfb6f1SJacky Bai unsigned int core_id = MPIDR_AFFLVL1_VAL(mpidr); 144*4ddfb6f1SJacky Bai uint32_t sys_mode; 145*4ddfb6f1SJacky Bai 146*4ddfb6f1SJacky Bai /* do cpu level config */ 147*4ddfb6f1SJacky Bai if (is_local_state_off(CORE_PWR_STATE(target_state))) { 148*4ddfb6f1SJacky Bai imx_set_cpu_boot_entry(core_id, secure_entrypoint, SCMI_CPU_VEC_FLAGS_RESUME); 149*4ddfb6f1SJacky Bai } 150*4ddfb6f1SJacky Bai 151*4ddfb6f1SJacky Bai /* config DSU for cluster power down */ 152*4ddfb6f1SJacky Bai if (!is_local_state_run(CLUSTER_PWR_STATE(target_state))) { 153*4ddfb6f1SJacky Bai /* L3 retention */ 154*4ddfb6f1SJacky Bai if (is_local_state_retn(CLUSTER_PWR_STATE(target_state))) { 155*4ddfb6f1SJacky Bai write_clusterpwrdn(DSU_CLUSTER_PWR_OFF | BIT(1)); 156*4ddfb6f1SJacky Bai } else { 157*4ddfb6f1SJacky Bai write_clusterpwrdn(DSU_CLUSTER_PWR_OFF); 158*4ddfb6f1SJacky Bai } 159*4ddfb6f1SJacky Bai } 160*4ddfb6f1SJacky Bai 161*4ddfb6f1SJacky Bai if (is_local_state_off(SYSTEM_PWR_STATE(target_state))) { 162*4ddfb6f1SJacky Bai imx9_sys_sleep_prepare(core_id); 163*4ddfb6f1SJacky Bai 164*4ddfb6f1SJacky Bai /* switch to GPC wakeup source, config the target mode to SUSPEND */ 165*4ddfb6f1SJacky Bai scmi_core_set_sleep_mode(imx9_scmi_handle, SCMI_CPU_A55_ID(core_id), 166*4ddfb6f1SJacky Bai SCMI_GPC_WAKEUP, SCMI_CPU_SLEEP_SUSPEND); 167*4ddfb6f1SJacky Bai 168*4ddfb6f1SJacky Bai scmi_core_set_sleep_mode(imx9_scmi_handle, IMX9_SCMI_CPU_A55P, 169*4ddfb6f1SJacky Bai SCMI_GPC_WAKEUP, SCMI_CPU_SLEEP_SUSPEND); 170*4ddfb6f1SJacky Bai 171*4ddfb6f1SJacky Bai struct scmi_lpm_config cpu_lpm_cfg[] = { 172*4ddfb6f1SJacky Bai { 173*4ddfb6f1SJacky Bai SCMI_PWR_MIX_SLICE_IDX_A55P, 174*4ddfb6f1SJacky Bai SCMI_CPU_PD_LPM_ON_RUN_WAIT_STOP, 175*4ddfb6f1SJacky Bai 0U 176*4ddfb6f1SJacky Bai }, 177*4ddfb6f1SJacky Bai { 178*4ddfb6f1SJacky Bai SCMI_PWR_MIX_SLICE_IDX_NOC, 179*4ddfb6f1SJacky Bai SCMI_CPU_PD_LPM_ON_RUN_WAIT_STOP, 180*4ddfb6f1SJacky Bai BIT_32(SCMI_PWR_MEM_SLICE_IDX_NOC_OCRAM) 181*4ddfb6f1SJacky Bai }, 182*4ddfb6f1SJacky Bai { 183*4ddfb6f1SJacky Bai SCMI_PWR_MIX_SLICE_IDX_WAKEUP, 184*4ddfb6f1SJacky Bai keep_wakeupmix_on ? SCMI_CPU_PD_LPM_ON_ALWAYS : 185*4ddfb6f1SJacky Bai SCMI_CPU_PD_LPM_ON_RUN_WAIT_STOP, 186*4ddfb6f1SJacky Bai 0U 187*4ddfb6f1SJacky Bai } 188*4ddfb6f1SJacky Bai }; 189*4ddfb6f1SJacky Bai 190*4ddfb6f1SJacky Bai /* Set the default LPM state for suspend */ 191*4ddfb6f1SJacky Bai scmi_core_lpm_mode_set(imx9_scmi_handle, 192*4ddfb6f1SJacky Bai IMX9_SCMI_CPU_A55P, 193*4ddfb6f1SJacky Bai ARRAY_SIZE(cpu_lpm_cfg), 194*4ddfb6f1SJacky Bai cpu_lpm_cfg); 195*4ddfb6f1SJacky Bai 196*4ddfb6f1SJacky Bai /* Set the system sleep config */ 197*4ddfb6f1SJacky Bai sys_mode = SCMI_IMX_SYS_POWER_STATE_MODE_MASK; 198*4ddfb6f1SJacky Bai if (has_netc_irq) { 199*4ddfb6f1SJacky Bai sys_mode |= SYS_SLEEP_MODE_H(SM_PERF_LVL_LOW); 200*4ddfb6f1SJacky Bai scmi_sys_pwr_state_set(imx9_scmi_handle, 201*4ddfb6f1SJacky Bai SCMI_SYS_PWR_FORCEFUL_REQ, 202*4ddfb6f1SJacky Bai sys_mode); 203*4ddfb6f1SJacky Bai } 204*4ddfb6f1SJacky Bai } 205*4ddfb6f1SJacky Bai 206*4ddfb6f1SJacky Bai } 207*4ddfb6f1SJacky Bai void imx_pwr_domain_suspend_finish(const psci_power_state_t *target_state) 208*4ddfb6f1SJacky Bai { 209*4ddfb6f1SJacky Bai uint64_t mpidr = read_mpidr_el1(); 210*4ddfb6f1SJacky Bai unsigned int core_id = MPIDR_AFFLVL1_VAL(mpidr); 211*4ddfb6f1SJacky Bai uint32_t sys_mode; 212*4ddfb6f1SJacky Bai 213*4ddfb6f1SJacky Bai /* system level */ 214*4ddfb6f1SJacky Bai if (is_local_state_off(SYSTEM_PWR_STATE(target_state))) { 215*4ddfb6f1SJacky Bai imx9_sys_sleep_unprepare(core_id); 216*4ddfb6f1SJacky Bai 217*4ddfb6f1SJacky Bai sys_mode = SCMI_IMX_SYS_POWER_STATE_MODE_MASK; 218*4ddfb6f1SJacky Bai if (has_netc_irq) { 219*4ddfb6f1SJacky Bai scmi_sys_pwr_state_set(imx9_scmi_handle, 220*4ddfb6f1SJacky Bai SCMI_SYS_PWR_FORCEFUL_REQ, 221*4ddfb6f1SJacky Bai sys_mode); 222*4ddfb6f1SJacky Bai } 223*4ddfb6f1SJacky Bai 224*4ddfb6f1SJacky Bai /* switch to GIC wakeup source, config the target mode to WAIT */ 225*4ddfb6f1SJacky Bai scmi_core_set_sleep_mode(imx9_scmi_handle, SCMI_CPU_A55_ID(core_id), 226*4ddfb6f1SJacky Bai SCMI_GIC_WAKEUP, SCMI_CPU_SLEEP_WAIT); 227*4ddfb6f1SJacky Bai 228*4ddfb6f1SJacky Bai scmi_core_set_sleep_mode(imx9_scmi_handle, IMX9_SCMI_CPU_A55P, 229*4ddfb6f1SJacky Bai SCMI_GIC_WAKEUP, SCMI_CPU_SLEEP_WAIT); 230*4ddfb6f1SJacky Bai 231*4ddfb6f1SJacky Bai struct scmi_lpm_config cpu_lpm_cfg[] = { 232*4ddfb6f1SJacky Bai { 233*4ddfb6f1SJacky Bai SCMI_PWR_MIX_SLICE_IDX_A55P, 234*4ddfb6f1SJacky Bai SCMI_CPU_PD_LPM_ON_RUN, 235*4ddfb6f1SJacky Bai BIT_32(SCMI_PWR_MEM_SLICE_IDX_A55L3) 236*4ddfb6f1SJacky Bai }, 237*4ddfb6f1SJacky Bai { 238*4ddfb6f1SJacky Bai SCMI_PWR_MIX_SLICE_IDX_NOC, 239*4ddfb6f1SJacky Bai SCMI_CPU_PD_LPM_ON_ALWAYS, 240*4ddfb6f1SJacky Bai 0U 241*4ddfb6f1SJacky Bai }, 242*4ddfb6f1SJacky Bai { 243*4ddfb6f1SJacky Bai SCMI_PWR_MIX_SLICE_IDX_WAKEUP, 244*4ddfb6f1SJacky Bai SCMI_CPU_PD_LPM_ON_ALWAYS, 245*4ddfb6f1SJacky Bai 0U 246*4ddfb6f1SJacky Bai } 247*4ddfb6f1SJacky Bai }; 248*4ddfb6f1SJacky Bai 249*4ddfb6f1SJacky Bai /* Set the default LPM state for RUN MODE */ 250*4ddfb6f1SJacky Bai scmi_core_lpm_mode_set(imx9_scmi_handle, 251*4ddfb6f1SJacky Bai IMX9_SCMI_CPU_A55P, 252*4ddfb6f1SJacky Bai ARRAY_SIZE(cpu_lpm_cfg), 253*4ddfb6f1SJacky Bai cpu_lpm_cfg); 254*4ddfb6f1SJacky Bai } 255*4ddfb6f1SJacky Bai } 256*4ddfb6f1SJacky Bai 257*4ddfb6f1SJacky Bai void imx_get_sys_suspend_power_state(psci_power_state_t *req_state) 258*4ddfb6f1SJacky Bai { 259*4ddfb6f1SJacky Bai uint32_t i; 260*4ddfb6f1SJacky Bai 261*4ddfb6f1SJacky Bai for (i = IMX_PWR_LVL0; i <= PLAT_MAX_PWR_LVL; i++) { 262*4ddfb6f1SJacky Bai req_state->pwr_domain_state[i] = PLAT_MAX_OFF_STATE; 263*4ddfb6f1SJacky Bai } 264*4ddfb6f1SJacky Bai } 265*4ddfb6f1SJacky Bai 266*4ddfb6f1SJacky Bai void imx_pwr_domain_pwr_down(const psci_power_state_t *target_state) 267*4ddfb6f1SJacky Bai { 268*4ddfb6f1SJacky Bai } 269*4ddfb6f1SJacky Bai 270*4ddfb6f1SJacky Bai void imx_system_off(void) 271*4ddfb6f1SJacky Bai { 272*4ddfb6f1SJacky Bai int ret; 273*4ddfb6f1SJacky Bai 274*4ddfb6f1SJacky Bai ret = scmi_sys_pwr_state_set(imx9_scmi_handle, 275*4ddfb6f1SJacky Bai SCMI_SYS_PWR_FORCEFUL_REQ, 276*4ddfb6f1SJacky Bai SCMI_SYS_PWR_SHUTDOWN); 277*4ddfb6f1SJacky Bai if (ret) { 278*4ddfb6f1SJacky Bai NOTICE("%s failed: %d\n", __func__, ret); 279*4ddfb6f1SJacky Bai } 280*4ddfb6f1SJacky Bai } 281*4ddfb6f1SJacky Bai 282*4ddfb6f1SJacky Bai void imx_system_reset(void) 283*4ddfb6f1SJacky Bai { 284*4ddfb6f1SJacky Bai int ret; 285*4ddfb6f1SJacky Bai 286*4ddfb6f1SJacky Bai /* TODO: temp workaround for GIC to let reset done */ 287*4ddfb6f1SJacky Bai gicd_clr_ctlr(PLAT_GICD_BASE, 288*4ddfb6f1SJacky Bai CTLR_ENABLE_G0_BIT | 289*4ddfb6f1SJacky Bai CTLR_ENABLE_G1S_BIT | 290*4ddfb6f1SJacky Bai CTLR_ENABLE_G1NS_BIT, 291*4ddfb6f1SJacky Bai RWP_TRUE); 292*4ddfb6f1SJacky Bai 293*4ddfb6f1SJacky Bai /* Force: work, Gracefull: not work */ 294*4ddfb6f1SJacky Bai ret = scmi_sys_pwr_state_set(imx9_scmi_handle, 295*4ddfb6f1SJacky Bai SCMI_SYS_PWR_FORCEFUL_REQ, 296*4ddfb6f1SJacky Bai SCMI_SYS_PWR_COLD_RESET); 297*4ddfb6f1SJacky Bai if (ret) { 298*4ddfb6f1SJacky Bai VERBOSE("%s failed: %d\n", __func__, ret); 299*4ddfb6f1SJacky Bai } 300*4ddfb6f1SJacky Bai } 301