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
imx_validate_ns_entrypoint(uintptr_t ns_entrypoint)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
imx_validate_power_state(uint32_t power_state,psci_power_state_t * req_state)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
imx_set_cpu_boot_entry(uint32_t core_id,uint64_t boot_entry,uint32_t flag)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
imx_pwr_domain_on(u_register_t mpidr)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
imx_pwr_domain_on_finish(const psci_power_state_t * target_state)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
imx_pwr_domain_off(const psci_power_state_t * target_state)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
imx_pwr_domain_suspend(const psci_power_state_t * target_state)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 }
imx_pwr_domain_suspend_finish(const psci_power_state_t * target_state)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
imx_get_sys_suspend_power_state(psci_power_state_t * req_state)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
imx_pwr_domain_pwr_down(const psci_power_state_t * target_state)2744ddfb6f1SJacky Bai void imx_pwr_domain_pwr_down(const psci_power_state_t *target_state)
2754ddfb6f1SJacky Bai {
2764ddfb6f1SJacky Bai }
2774ddfb6f1SJacky Bai
imx_system_off(void)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
imx_system_reset(void)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