14ddfb6f1SJacky Bai /* 24ddfb6f1SJacky Bai * Copyright 2025 NXP 34ddfb6f1SJacky Bai * 44ddfb6f1SJacky Bai * SPDX-License-Identifier: BSD-3-Clause 54ddfb6f1SJacky Bai */ 64ddfb6f1SJacky Bai 74ddfb6f1SJacky Bai #include <stdbool.h> 84ddfb6f1SJacky Bai 94ddfb6f1SJacky Bai #include "../drivers/arm/gic/v3/gicv3_private.h" 104ddfb6f1SJacky Bai 114ddfb6f1SJacky Bai #include <arch.h> 124ddfb6f1SJacky Bai #include <arch_helpers.h> 134ddfb6f1SJacky Bai #include <common/debug.h> 144ddfb6f1SJacky Bai #include <drivers/arm/css/scmi.h> 154ddfb6f1SJacky Bai #include <drivers/arm/gicv3.h> 164ddfb6f1SJacky Bai #include <lib/psci/psci.h> 174ddfb6f1SJacky Bai #include <scmi_imx9.h> 184ddfb6f1SJacky Bai 194ddfb6f1SJacky Bai #include <imx9_psci_common.h> 204ddfb6f1SJacky Bai #include <imx9_sys_sleep.h> 214ddfb6f1SJacky Bai #include <imx_scmi_client.h> 224ddfb6f1SJacky Bai #include <plat_imx8.h> 234ddfb6f1SJacky Bai 24*0df6ba31SEmanuele Ghidoli #if SYS_PWR_FULL_CTRL == 1 25*0df6ba31SEmanuele Ghidoli #define PLAT_SCMI_SYS_PWR_SHUTDOWN IMX9_SCMI_SYS_PWR_FULL_SHUTDOWN 26*0df6ba31SEmanuele Ghidoli #define PLAT_SCMI_SYS_PWR_COLD_RESET IMX9_SCMI_SYS_PWR_FULL_RESET 27*0df6ba31SEmanuele Ghidoli #else 28*0df6ba31SEmanuele Ghidoli #define PLAT_SCMI_SYS_PWR_SHUTDOWN SCMI_SYS_PWR_SHUTDOWN 29*0df6ba31SEmanuele Ghidoli #define PLAT_SCMI_SYS_PWR_COLD_RESET SCMI_SYS_PWR_COLD_RESET 30*0df6ba31SEmanuele Ghidoli #endif 31*0df6ba31SEmanuele Ghidoli 324ddfb6f1SJacky Bai /* platform secure warm boot entry */ 334ddfb6f1SJacky Bai uintptr_t secure_entrypoint; 344ddfb6f1SJacky Bai 354ddfb6f1SJacky Bai int imx_validate_ns_entrypoint(uintptr_t ns_entrypoint) 364ddfb6f1SJacky Bai { 374ddfb6f1SJacky Bai /* The non-secure entrypoint should be in RAM space */ 384ddfb6f1SJacky Bai if (ns_entrypoint < PLAT_NS_IMAGE_OFFSET) { 394ddfb6f1SJacky Bai return PSCI_E_INVALID_PARAMS; 404ddfb6f1SJacky Bai } 414ddfb6f1SJacky Bai 424ddfb6f1SJacky Bai return PSCI_E_SUCCESS; 434ddfb6f1SJacky Bai } 444ddfb6f1SJacky Bai 454ddfb6f1SJacky Bai int imx_validate_power_state(uint32_t power_state, 464ddfb6f1SJacky Bai psci_power_state_t *req_state) 474ddfb6f1SJacky Bai { 484ddfb6f1SJacky Bai int pwr_lvl = psci_get_pstate_pwrlvl(power_state); 494ddfb6f1SJacky Bai int pwr_type = psci_get_pstate_type(power_state); 504ddfb6f1SJacky Bai int state_id = psci_get_pstate_id(power_state); 514ddfb6f1SJacky Bai 524ddfb6f1SJacky Bai if (pwr_lvl > PLAT_MAX_PWR_LVL) { 534ddfb6f1SJacky Bai return PSCI_E_INVALID_PARAMS; 544ddfb6f1SJacky Bai } 554ddfb6f1SJacky Bai 564ddfb6f1SJacky Bai if (pwr_type == PSTATE_TYPE_STANDBY) { 574ddfb6f1SJacky Bai CORE_PWR_STATE(req_state) = PLAT_MAX_OFF_STATE; 584ddfb6f1SJacky Bai CLUSTER_PWR_STATE(req_state) = PLAT_MAX_RET_STATE; 594ddfb6f1SJacky Bai } 604ddfb6f1SJacky Bai 614ddfb6f1SJacky Bai if (pwr_type == PSTATE_TYPE_POWERDOWN && state_id == 0x33) { 624ddfb6f1SJacky Bai CORE_PWR_STATE(req_state) = PLAT_MAX_OFF_STATE; 634ddfb6f1SJacky Bai CLUSTER_PWR_STATE(req_state) = PLAT_MAX_RET_STATE; 644ddfb6f1SJacky Bai } 654ddfb6f1SJacky Bai 664ddfb6f1SJacky Bai return PSCI_E_SUCCESS; 674ddfb6f1SJacky Bai } 684ddfb6f1SJacky Bai 694ddfb6f1SJacky Bai void imx_set_cpu_boot_entry(uint32_t core_id, uint64_t boot_entry, 704ddfb6f1SJacky Bai uint32_t flag) 714ddfb6f1SJacky Bai { 724ddfb6f1SJacky Bai scmi_core_set_reset_addr(imx9_scmi_handle, 734ddfb6f1SJacky Bai boot_entry, 744ddfb6f1SJacky Bai SCMI_CPU_A55_ID(core_id), 754ddfb6f1SJacky Bai flag); 764ddfb6f1SJacky Bai } 774ddfb6f1SJacky Bai 784ddfb6f1SJacky Bai int imx_pwr_domain_on(u_register_t mpidr) 794ddfb6f1SJacky Bai { 804ddfb6f1SJacky Bai uint32_t core_id = MPIDR_AFFLVL1_VAL(mpidr); 814ddfb6f1SJacky Bai uint32_t mask = DEBUG_WAKEUP_MASK | EVENT_WAKEUP_MASK; 824ddfb6f1SJacky Bai 834ddfb6f1SJacky Bai imx_set_cpu_boot_entry(core_id, 844ddfb6f1SJacky Bai secure_entrypoint, 854ddfb6f1SJacky Bai SCMI_CPU_VEC_FLAGS_BOOT); 864ddfb6f1SJacky Bai 874ddfb6f1SJacky Bai scmi_core_start(imx9_scmi_handle, SCMI_CPU_A55_ID(core_id)); 884ddfb6f1SJacky Bai 894ddfb6f1SJacky Bai /* 904ddfb6f1SJacky Bai * Set NON-IRQ wakeup mask to Disable wakeup on DEBUG_WAKEUP 914ddfb6f1SJacky Bai */ 924ddfb6f1SJacky Bai scmi_core_nonIrq_wake_set(imx9_scmi_handle, SCMI_CPU_A55_ID(core_id), 0U, 1U, mask); 934ddfb6f1SJacky Bai 944ddfb6f1SJacky Bai /* Set the default LPM state for cpuidle. */ 954ddfb6f1SJacky Bai struct scmi_lpm_config cpu_lpm_cfg = { 964ddfb6f1SJacky Bai SCMI_CPU_A55_PD(core_id), 974ddfb6f1SJacky Bai SCMI_CPU_PD_LPM_ON_RUN, 984ddfb6f1SJacky Bai 0U 994ddfb6f1SJacky Bai }; 1004ddfb6f1SJacky Bai 1014ddfb6f1SJacky Bai scmi_core_lpm_mode_set(imx9_scmi_handle, 1024ddfb6f1SJacky Bai SCMI_CPU_A55_ID(core_id), 1034ddfb6f1SJacky Bai 1U, &cpu_lpm_cfg); 1044ddfb6f1SJacky Bai 1054ddfb6f1SJacky Bai return PSCI_E_SUCCESS; 1064ddfb6f1SJacky Bai } 1074ddfb6f1SJacky Bai 1084ddfb6f1SJacky Bai void imx_pwr_domain_on_finish(const psci_power_state_t *target_state) 1094ddfb6f1SJacky Bai { 1104ddfb6f1SJacky Bai uint64_t mpidr = read_mpidr_el1(); 1114ddfb6f1SJacky Bai uint32_t core_id = MPIDR_AFFLVL1_VAL(mpidr); 1124ddfb6f1SJacky Bai 1134ddfb6f1SJacky Bai scmi_core_set_sleep_mode(imx9_scmi_handle, 1144ddfb6f1SJacky Bai SCMI_CPU_A55_ID(core_id), 1154ddfb6f1SJacky Bai SCMI_GIC_WAKEUP, 1164ddfb6f1SJacky Bai SCMI_CPU_SLEEP_WAIT); 1174ddfb6f1SJacky Bai } 1184ddfb6f1SJacky Bai 1194ddfb6f1SJacky Bai void imx_pwr_domain_off(const psci_power_state_t *target_state) 1204ddfb6f1SJacky Bai { 1214ddfb6f1SJacky Bai uint64_t mpidr = read_mpidr_el1(); 1224ddfb6f1SJacky Bai uint32_t core_id = MPIDR_AFFLVL1_VAL(mpidr); 1234ddfb6f1SJacky Bai 1244ddfb6f1SJacky Bai /* Ensure the cluster can be powered off. */ 1254ddfb6f1SJacky Bai write_clusterpwrdn(DSU_CLUSTER_PWR_OFF); 1264ddfb6f1SJacky Bai 1274ddfb6f1SJacky Bai /* Configure core LPM state for hotplug. */ 1284ddfb6f1SJacky Bai struct scmi_lpm_config cpu_lpm_cfg = { 1294ddfb6f1SJacky Bai SCMI_CPU_A55_PD(core_id), 1304ddfb6f1SJacky Bai SCMI_CPU_PD_LPM_ON_RUN_WAIT_STOP, 1314ddfb6f1SJacky Bai 0U 1324ddfb6f1SJacky Bai }; 1334ddfb6f1SJacky Bai /* Set the default LPM state for cpuidle */ 1344ddfb6f1SJacky Bai scmi_core_lpm_mode_set(imx9_scmi_handle, SCMI_CPU_A55_ID(core_id), 1354ddfb6f1SJacky Bai 1U, &cpu_lpm_cfg); 1364ddfb6f1SJacky Bai 1374ddfb6f1SJacky Bai /* 1384ddfb6f1SJacky Bai * Mask all the GPC IRQ wakeup to make sure no IRQ can wakeup this core 1394ddfb6f1SJacky Bai * so we need to use SW_WAKEUP for hotplug purpose 1404ddfb6f1SJacky Bai */ 1414ddfb6f1SJacky Bai scmi_core_Irq_wake_set(imx9_scmi_handle, SCMI_CPU_A55_ID(core_id), 0U, 1424ddfb6f1SJacky Bai IMR_NUM, mask_all); 1434ddfb6f1SJacky Bai 1444ddfb6f1SJacky Bai scmi_core_set_sleep_mode(imx9_scmi_handle, SCMI_CPU_A55_ID(core_id), 1454ddfb6f1SJacky Bai SCMI_GPC_WAKEUP, SCMI_CPU_SLEEP_SUSPEND); 1464ddfb6f1SJacky Bai } 1474ddfb6f1SJacky Bai 1484ddfb6f1SJacky Bai void imx_pwr_domain_suspend(const psci_power_state_t *target_state) 1494ddfb6f1SJacky Bai { 1504ddfb6f1SJacky Bai uint64_t mpidr = read_mpidr_el1(); 1514ddfb6f1SJacky Bai unsigned int core_id = MPIDR_AFFLVL1_VAL(mpidr); 1524ddfb6f1SJacky Bai uint32_t sys_mode; 1534ddfb6f1SJacky Bai 1544ddfb6f1SJacky Bai /* do cpu level config */ 1554ddfb6f1SJacky Bai if (is_local_state_off(CORE_PWR_STATE(target_state))) { 1564ddfb6f1SJacky Bai imx_set_cpu_boot_entry(core_id, secure_entrypoint, SCMI_CPU_VEC_FLAGS_RESUME); 1574ddfb6f1SJacky Bai } 1584ddfb6f1SJacky Bai 1594ddfb6f1SJacky Bai /* config DSU for cluster power down */ 1604ddfb6f1SJacky Bai if (!is_local_state_run(CLUSTER_PWR_STATE(target_state))) { 1614ddfb6f1SJacky Bai /* L3 retention */ 1624ddfb6f1SJacky Bai if (is_local_state_retn(CLUSTER_PWR_STATE(target_state))) { 1634ddfb6f1SJacky Bai write_clusterpwrdn(DSU_CLUSTER_PWR_OFF | BIT(1)); 1644ddfb6f1SJacky Bai } else { 1654ddfb6f1SJacky Bai write_clusterpwrdn(DSU_CLUSTER_PWR_OFF); 1664ddfb6f1SJacky Bai } 1674ddfb6f1SJacky Bai } 1684ddfb6f1SJacky Bai 1694ddfb6f1SJacky Bai if (is_local_state_off(SYSTEM_PWR_STATE(target_state))) { 1704ddfb6f1SJacky Bai imx9_sys_sleep_prepare(core_id); 1714ddfb6f1SJacky Bai 1724ddfb6f1SJacky Bai /* switch to GPC wakeup source, config the target mode to SUSPEND */ 1734ddfb6f1SJacky Bai scmi_core_set_sleep_mode(imx9_scmi_handle, SCMI_CPU_A55_ID(core_id), 1744ddfb6f1SJacky Bai SCMI_GPC_WAKEUP, SCMI_CPU_SLEEP_SUSPEND); 1754ddfb6f1SJacky Bai 1764ddfb6f1SJacky Bai scmi_core_set_sleep_mode(imx9_scmi_handle, IMX9_SCMI_CPU_A55P, 1774ddfb6f1SJacky Bai SCMI_GPC_WAKEUP, SCMI_CPU_SLEEP_SUSPEND); 1784ddfb6f1SJacky Bai 1794ddfb6f1SJacky Bai struct scmi_lpm_config cpu_lpm_cfg[] = { 1804ddfb6f1SJacky Bai { 1814ddfb6f1SJacky Bai SCMI_PWR_MIX_SLICE_IDX_A55P, 1824ddfb6f1SJacky Bai SCMI_CPU_PD_LPM_ON_RUN_WAIT_STOP, 1834ddfb6f1SJacky Bai 0U 1844ddfb6f1SJacky Bai }, 1854ddfb6f1SJacky Bai { 1864ddfb6f1SJacky Bai SCMI_PWR_MIX_SLICE_IDX_NOC, 1874ddfb6f1SJacky Bai SCMI_CPU_PD_LPM_ON_RUN_WAIT_STOP, 1884ddfb6f1SJacky Bai BIT_32(SCMI_PWR_MEM_SLICE_IDX_NOC_OCRAM) 1894ddfb6f1SJacky Bai }, 1904ddfb6f1SJacky Bai { 1914ddfb6f1SJacky Bai SCMI_PWR_MIX_SLICE_IDX_WAKEUP, 1924ddfb6f1SJacky Bai keep_wakeupmix_on ? SCMI_CPU_PD_LPM_ON_ALWAYS : 1934ddfb6f1SJacky Bai SCMI_CPU_PD_LPM_ON_RUN_WAIT_STOP, 1944ddfb6f1SJacky Bai 0U 1954ddfb6f1SJacky Bai } 1964ddfb6f1SJacky Bai }; 1974ddfb6f1SJacky Bai 1984ddfb6f1SJacky Bai /* Set the default LPM state for suspend */ 1994ddfb6f1SJacky Bai scmi_core_lpm_mode_set(imx9_scmi_handle, 2004ddfb6f1SJacky Bai IMX9_SCMI_CPU_A55P, 2014ddfb6f1SJacky Bai ARRAY_SIZE(cpu_lpm_cfg), 2024ddfb6f1SJacky Bai cpu_lpm_cfg); 2034ddfb6f1SJacky Bai 2044ddfb6f1SJacky Bai /* Set the system sleep config */ 2054ddfb6f1SJacky Bai sys_mode = SCMI_IMX_SYS_POWER_STATE_MODE_MASK; 2064ddfb6f1SJacky Bai if (has_netc_irq) { 2074ddfb6f1SJacky Bai sys_mode |= SYS_SLEEP_MODE_H(SM_PERF_LVL_LOW); 2084ddfb6f1SJacky Bai scmi_sys_pwr_state_set(imx9_scmi_handle, 2094ddfb6f1SJacky Bai SCMI_SYS_PWR_FORCEFUL_REQ, 2104ddfb6f1SJacky Bai sys_mode); 2114ddfb6f1SJacky Bai } 2124ddfb6f1SJacky Bai } 2134ddfb6f1SJacky Bai 2144ddfb6f1SJacky Bai } 2154ddfb6f1SJacky Bai void imx_pwr_domain_suspend_finish(const psci_power_state_t *target_state) 2164ddfb6f1SJacky Bai { 2174ddfb6f1SJacky Bai uint64_t mpidr = read_mpidr_el1(); 2184ddfb6f1SJacky Bai unsigned int core_id = MPIDR_AFFLVL1_VAL(mpidr); 2194ddfb6f1SJacky Bai uint32_t sys_mode; 2204ddfb6f1SJacky Bai 2214ddfb6f1SJacky Bai /* system level */ 2224ddfb6f1SJacky Bai if (is_local_state_off(SYSTEM_PWR_STATE(target_state))) { 2234ddfb6f1SJacky Bai imx9_sys_sleep_unprepare(core_id); 2244ddfb6f1SJacky Bai 2254ddfb6f1SJacky Bai sys_mode = SCMI_IMX_SYS_POWER_STATE_MODE_MASK; 2264ddfb6f1SJacky Bai if (has_netc_irq) { 2274ddfb6f1SJacky Bai scmi_sys_pwr_state_set(imx9_scmi_handle, 2284ddfb6f1SJacky Bai SCMI_SYS_PWR_FORCEFUL_REQ, 2294ddfb6f1SJacky Bai sys_mode); 2304ddfb6f1SJacky Bai } 2314ddfb6f1SJacky Bai 2324ddfb6f1SJacky Bai /* switch to GIC wakeup source, config the target mode to WAIT */ 2334ddfb6f1SJacky Bai scmi_core_set_sleep_mode(imx9_scmi_handle, SCMI_CPU_A55_ID(core_id), 2344ddfb6f1SJacky Bai SCMI_GIC_WAKEUP, SCMI_CPU_SLEEP_WAIT); 2354ddfb6f1SJacky Bai 2364ddfb6f1SJacky Bai scmi_core_set_sleep_mode(imx9_scmi_handle, IMX9_SCMI_CPU_A55P, 2374ddfb6f1SJacky Bai SCMI_GIC_WAKEUP, SCMI_CPU_SLEEP_WAIT); 2384ddfb6f1SJacky Bai 2394ddfb6f1SJacky Bai struct scmi_lpm_config cpu_lpm_cfg[] = { 2404ddfb6f1SJacky Bai { 2414ddfb6f1SJacky Bai SCMI_PWR_MIX_SLICE_IDX_A55P, 2424ddfb6f1SJacky Bai SCMI_CPU_PD_LPM_ON_RUN, 2434ddfb6f1SJacky Bai BIT_32(SCMI_PWR_MEM_SLICE_IDX_A55L3) 2444ddfb6f1SJacky Bai }, 2454ddfb6f1SJacky Bai { 2464ddfb6f1SJacky Bai SCMI_PWR_MIX_SLICE_IDX_NOC, 2474ddfb6f1SJacky Bai SCMI_CPU_PD_LPM_ON_ALWAYS, 2484ddfb6f1SJacky Bai 0U 2494ddfb6f1SJacky Bai }, 2504ddfb6f1SJacky Bai { 2514ddfb6f1SJacky Bai SCMI_PWR_MIX_SLICE_IDX_WAKEUP, 2524ddfb6f1SJacky Bai SCMI_CPU_PD_LPM_ON_ALWAYS, 2534ddfb6f1SJacky Bai 0U 2544ddfb6f1SJacky Bai } 2554ddfb6f1SJacky Bai }; 2564ddfb6f1SJacky Bai 2574ddfb6f1SJacky Bai /* Set the default LPM state for RUN MODE */ 2584ddfb6f1SJacky Bai scmi_core_lpm_mode_set(imx9_scmi_handle, 2594ddfb6f1SJacky Bai IMX9_SCMI_CPU_A55P, 2604ddfb6f1SJacky Bai ARRAY_SIZE(cpu_lpm_cfg), 2614ddfb6f1SJacky Bai cpu_lpm_cfg); 2624ddfb6f1SJacky Bai } 2634ddfb6f1SJacky Bai } 2644ddfb6f1SJacky Bai 2654ddfb6f1SJacky Bai void imx_get_sys_suspend_power_state(psci_power_state_t *req_state) 2664ddfb6f1SJacky Bai { 2674ddfb6f1SJacky Bai uint32_t i; 2684ddfb6f1SJacky Bai 2694ddfb6f1SJacky Bai for (i = IMX_PWR_LVL0; i <= PLAT_MAX_PWR_LVL; i++) { 2704ddfb6f1SJacky Bai req_state->pwr_domain_state[i] = PLAT_MAX_OFF_STATE; 2714ddfb6f1SJacky Bai } 2724ddfb6f1SJacky Bai } 2734ddfb6f1SJacky Bai 2744ddfb6f1SJacky Bai void imx_pwr_domain_pwr_down(const psci_power_state_t *target_state) 2754ddfb6f1SJacky Bai { 2764ddfb6f1SJacky Bai } 2774ddfb6f1SJacky Bai 2784ddfb6f1SJacky Bai void imx_system_off(void) 2794ddfb6f1SJacky Bai { 2804ddfb6f1SJacky Bai int ret; 2814ddfb6f1SJacky Bai 2824ddfb6f1SJacky Bai ret = scmi_sys_pwr_state_set(imx9_scmi_handle, 2834ddfb6f1SJacky Bai SCMI_SYS_PWR_FORCEFUL_REQ, 284*0df6ba31SEmanuele Ghidoli PLAT_SCMI_SYS_PWR_SHUTDOWN); 2854ddfb6f1SJacky Bai if (ret) { 2864ddfb6f1SJacky Bai NOTICE("%s failed: %d\n", __func__, ret); 2874ddfb6f1SJacky Bai } 2884ddfb6f1SJacky Bai } 2894ddfb6f1SJacky Bai 2904ddfb6f1SJacky Bai void imx_system_reset(void) 2914ddfb6f1SJacky Bai { 2924ddfb6f1SJacky Bai int ret; 2934ddfb6f1SJacky Bai 2944ddfb6f1SJacky Bai /* TODO: temp workaround for GIC to let reset done */ 2954ddfb6f1SJacky Bai gicd_clr_ctlr(PLAT_GICD_BASE, 2964ddfb6f1SJacky Bai CTLR_ENABLE_G0_BIT | 2974ddfb6f1SJacky Bai CTLR_ENABLE_G1S_BIT | 2984ddfb6f1SJacky Bai CTLR_ENABLE_G1NS_BIT, 2994ddfb6f1SJacky Bai RWP_TRUE); 3004ddfb6f1SJacky Bai 3014ddfb6f1SJacky Bai /* Force: work, Gracefull: not work */ 3024ddfb6f1SJacky Bai ret = scmi_sys_pwr_state_set(imx9_scmi_handle, 3034ddfb6f1SJacky Bai SCMI_SYS_PWR_FORCEFUL_REQ, 304*0df6ba31SEmanuele Ghidoli PLAT_SCMI_SYS_PWR_COLD_RESET); 3054ddfb6f1SJacky Bai if (ret) { 3064ddfb6f1SJacky Bai VERBOSE("%s failed: %d\n", __func__, ret); 3074ddfb6f1SJacky Bai } 3084ddfb6f1SJacky Bai } 309