From 762688bff24277590f888a45ab404a5d327efd92 Mon Sep 17 00:00:00 2001 From: Anson Huang Date: Thu, 12 Jul 2018 14:17:19 +0800 Subject: [PATCH] imx: imx8qx: add domain suspend/resume support Add domain suspend/resume support, Linux kernel can "echo mem > /sys/power/state" to put system into suspend mode, all CPUs and cluster will be powered off and can be waked up if irq pending in GIC, tested on i.MX8QX MEK board. Signed-off-by: Anson Huang --- plat/imx/common/imx8_psci.c | 17 +++++++++++ plat/imx/common/include/plat_imx8.h | 4 +++ plat/imx/imx8qx/imx8qx_psci.c | 46 ++++++++++++++++++++++++----- plat/imx/imx8qx/platform.mk | 1 + 4 files changed, 61 insertions(+), 7 deletions(-) diff --git a/plat/imx/common/imx8_psci.c b/plat/imx/common/imx8_psci.c index 37eda3e6a..22a531bf8 100644 --- a/plat/imx/common/imx8_psci.c +++ b/plat/imx/common/imx8_psci.c @@ -27,3 +27,20 @@ void __dead2 imx_system_reset(void) panic(); } +int imx_validate_power_state(unsigned int power_state, + psci_power_state_t *req_state) +{ + /* TODO */ + return PSCI_E_INVALID_PARAMS; +} + +void imx_get_sys_suspend_power_state(psci_power_state_t *req_state) +{ + unsigned int i; + + /* CPU & cluster off, system in retention */ + for (i = MPIDR_AFFLVL0; i < PLAT_MAX_PWR_LVL; i++) + req_state->pwr_domain_state[i] = PLAT_MAX_OFF_STATE; + req_state->pwr_domain_state[PLAT_MAX_PWR_LVL] = PLAT_MAX_RET_STATE; +} + diff --git a/plat/imx/common/include/plat_imx8.h b/plat/imx/common/include/plat_imx8.h index 3b5f4ec6b..a333bfbe1 100644 --- a/plat/imx/common/include/plat_imx8.h +++ b/plat/imx/common/include/plat_imx8.h @@ -8,6 +8,7 @@ #define __PLAT_IMX8_H__ #include +#include unsigned int plat_calc_core_pos(uint64_t mpidr); void imx_mailbox_init(uintptr_t base_addr); @@ -19,4 +20,7 @@ void plat_gic_pcpu_init(void); void __dead2 imx_system_off(void); void __dead2 imx_system_reset(void); +int imx_validate_power_state(unsigned int power_state, + psci_power_state_t *req_state); +void imx_get_sys_suspend_power_state(psci_power_state_t *req_state); #endif /*__PLAT_IMX8_H__ */ diff --git a/plat/imx/imx8qx/imx8qx_psci.c b/plat/imx/imx8qx/imx8qx_psci.c index 7a6e822b7..f1df267a8 100644 --- a/plat/imx/imx8qx/imx8qx_psci.c +++ b/plat/imx/imx8qx/imx8qx_psci.c @@ -18,13 +18,6 @@ const static int ap_core_index[PLATFORM_CORE_COUNT] = { SC_R_A35_0, SC_R_A35_1, SC_R_A35_2, SC_R_A35_3 }; -plat_local_state_t plat_get_target_pwr_state(unsigned int lvl, - const plat_local_state_t *target_state, - unsigned int ncpu) -{ - return 0; -} - int imx_pwr_domain_on(u_register_t mpidr) { int ret = PSCI_E_SUCCESS; @@ -71,6 +64,29 @@ void imx_pwr_domain_off(const psci_power_state_t *target_state) tf_printf("turn off core:%d\n", cpu_id); } +void imx_domain_suspend(const psci_power_state_t *target_state) +{ + u_register_t mpidr = read_mpidr_el1(); + unsigned int cpu_id = MPIDR_AFFLVL0_VAL(mpidr); + + plat_gic_cpuif_disable(); + + sc_pm_set_cpu_resume_addr(ipc_handle, ap_core_index[cpu_id], BL31_BASE); + sc_pm_req_cpu_low_power_mode(ipc_handle, ap_core_index[cpu_id], + SC_PM_PW_MODE_OFF, SC_PM_WAKE_SRC_GIC); +} + +void imx_domain_suspend_finish(const psci_power_state_t *target_state) +{ + u_register_t mpidr = read_mpidr_el1(); + unsigned int cpu_id = MPIDR_AFFLVL0_VAL(mpidr); + + sc_pm_req_low_power_mode(ipc_handle, ap_core_index[cpu_id], + SC_PM_PW_MODE_ON); + + plat_gic_cpuif_enable(); +} + static const plat_psci_ops_t imx_plat_psci_ops = { .pwr_domain_on = imx_pwr_domain_on, .pwr_domain_on_finish = imx_pwr_domain_on_finish, @@ -78,6 +94,10 @@ static const plat_psci_ops_t imx_plat_psci_ops = { .system_off = imx_system_off, .system_reset = imx_system_reset, .pwr_domain_off = imx_pwr_domain_off, + .pwr_domain_suspend = imx_domain_suspend, + .pwr_domain_suspend_finish = imx_domain_suspend_finish, + .get_sys_suspend_power_state = imx_get_sys_suspend_power_state, + .validate_power_state = imx_validate_power_state, }; int plat_setup_psci_ops(uintptr_t sec_entrypoint, @@ -86,5 +106,17 @@ int plat_setup_psci_ops(uintptr_t sec_entrypoint, imx_mailbox_init(sec_entrypoint); *psci_ops = &imx_plat_psci_ops; + /* Request low power mode for A35 cluster, only need to do once */ + sc_pm_req_low_power_mode(ipc_handle, SC_R_A35, SC_PM_PW_MODE_OFF); + + /* Request RUN and LP modes for DDR, system interconnect etc. */ + sc_pm_req_sys_if_power_mode(ipc_handle, SC_R_A35, + SC_PM_SYS_IF_DDR, SC_PM_PW_MODE_ON, SC_PM_PW_MODE_STBY); + sc_pm_req_sys_if_power_mode(ipc_handle, SC_R_A35, + SC_PM_SYS_IF_MU, SC_PM_PW_MODE_ON, SC_PM_PW_MODE_STBY); + sc_pm_req_sys_if_power_mode(ipc_handle, SC_R_A35, + SC_PM_SYS_IF_INTERCONNECT, SC_PM_PW_MODE_ON, + SC_PM_PW_MODE_STBY); + return 0; } diff --git a/plat/imx/imx8qx/platform.mk b/plat/imx/imx8qx/platform.mk index 7d6e3fdfe..067661890 100644 --- a/plat/imx/imx8qx/platform.mk +++ b/plat/imx/imx8qx/platform.mk @@ -21,6 +21,7 @@ BL31_SOURCES += plat/imx/common/lpuart_console.S \ plat/imx/imx8qx/imx8qx_psci.c \ plat/imx/common/imx8_topology.c \ plat/imx/common/imx8_psci.c \ + plat/common/plat_psci_common.c \ lib/xlat_tables/xlat_tables_common.c \ lib/xlat_tables/aarch64/xlat_tables.c \ lib/cpus/aarch64/cortex_a35.S \