This is more like a workaround for platforms like i.MX8MQ that have an issue related to the wake_request GIC signal integration.
See E11171 here: https://www.nxp.com/docs/en/errata/IMX8MDQLQ_0N14W.pdf
The workaround mentioned in the document has the dissadvantage of waking up all the sleeping cores instead of just one.
This patchset adds another psci op for core 'poking' (waking up). On i.MX8MQ, the plat specific implementation writes the corresponding bits in GPC to wake up independently each core.
Abel Vesa (2): psci: Add cpu wake operation plat: imx8mq: Trigger SW power up per core
include/lib/psci/psci.h | 3 +++ lib/psci/psci_main.c | 17 +++++++++++++++++ lib/psci/psci_setup.c | 2 ++ plat/imx/imx8m/imx8mq/gpc.c | 7 +++++++ plat/imx/imx8m/imx8mq/imx8mq_psci.c | 8 ++++++++ plat/imx/imx8m/include/gpc.h | 2 ++ 6 files changed, 39 insertions(+)
This can be used to do the necessary preparation in order to wake up a specific core. It calls the platform specific pwr_domain_pwr_down_wfi_wakeup.
Signed-off-by: Abel Vesa abel.vesa@nxp.com --- include/lib/psci/psci.h | 3 +++ lib/psci/psci_main.c | 17 +++++++++++++++++ lib/psci/psci_setup.c | 2 ++ 3 files changed, 22 insertions(+)
diff --git a/include/lib/psci/psci.h b/include/lib/psci/psci.h index fe279d4..72525d1 100644 --- a/include/lib/psci/psci.h +++ b/include/lib/psci/psci.h @@ -41,7 +41,9 @@ ******************************************************************************/ #define PSCI_VERSION U(0x84000000) #define PSCI_CPU_SUSPEND_AARCH32 U(0x84000001) +#define PSCI_CPU_WAKE_AARCH32 U(0x8400000b) #define PSCI_CPU_SUSPEND_AARCH64 U(0xc4000001) +#define PSCI_CPU_WAKE_AARCH64 U(0xc400000b) #define PSCI_CPU_OFF U(0x84000002) #define PSCI_CPU_ON_AARCH32 U(0x84000003) #define PSCI_CPU_ON_AARCH64 U(0xc4000003) @@ -305,6 +307,7 @@ typedef struct plat_psci_ops { const psci_power_state_t *target_state); void __dead2 (*pwr_domain_pwr_down_wfi)( const psci_power_state_t *target_state); + int (*pwr_domain_pwr_down_wfi_wakeup)(unsigned int index); void __dead2 (*system_off)(void); void __dead2 (*system_reset)(void); int (*validate_power_state)(unsigned int power_state, diff --git a/lib/psci/psci_main.c b/lib/psci/psci_main.c index 5c0e952..bdf6d46 100644 --- a/lib/psci/psci_main.c +++ b/lib/psci/psci_main.c @@ -51,6 +51,13 @@ unsigned int psci_version(void) return PSCI_MAJOR_VER | PSCI_MINOR_VER; }
+int psci_cpu_wake(unsigned int index) +{ + if (psci_plat_pm_ops->pwr_domain_pwr_down_wfi_wakeup != NULL) + psci_plat_pm_ops->pwr_domain_pwr_down_wfi_wakeup(index); + return 0; +} + int psci_cpu_suspend(unsigned int power_state, uintptr_t entrypoint, u_register_t context_id) @@ -408,6 +415,11 @@ u_register_t psci_smc_handler(uint32_t smc_fid, ret = (u_register_t)psci_cpu_suspend(r1, r2, r3); break;
+ case PSCI_CPU_WAKE_AARCH32: + ret = (u_register_t) + psci_cpu_wake(r1); + break; + case PSCI_CPU_ON_AARCH32: ret = (u_register_t)psci_cpu_on(r1, r2, r3); break; @@ -486,6 +498,11 @@ u_register_t psci_smc_handler(uint32_t smc_fid, psci_cpu_suspend((unsigned int)x1, x2, x3); break;
+ case PSCI_CPU_WAKE_AARCH64: + ret = (u_register_t) + psci_cpu_wake((unsigned int)x1); + break; + case PSCI_CPU_ON_AARCH64: ret = (u_register_t)psci_cpu_on(x1, x2, x3); break; diff --git a/lib/psci/psci_setup.c b/lib/psci/psci_setup.c index b9467d3..9c2179e 100644 --- a/lib/psci/psci_setup.c +++ b/lib/psci/psci_setup.c @@ -235,6 +235,8 @@ int __init psci_setup(const psci_lib_args_t *lib_args) if ((psci_plat_pm_ops->pwr_domain_on != NULL) && (psci_plat_pm_ops->pwr_domain_on_finish != NULL)) psci_caps |= define_psci_cap(PSCI_CPU_ON_AARCH64); + if (psci_plat_pm_ops->pwr_domain_pwr_down_wfi_wakeup != NULL) + psci_caps |= define_psci_cap(PSCI_CPU_WAKE_AARCH64); if ((psci_plat_pm_ops->pwr_domain_suspend != NULL) && (psci_plat_pm_ops->pwr_domain_suspend_finish != NULL)) { psci_caps |= define_psci_cap(PSCI_CPU_SUSPEND_AARCH64);
This is a fix for errata E11171 described here: https://www.nxp.com/docs/en/errata/IMX8MDQLQ_0N14W.pdf
By writing the per-core corresponding bits in GPC register CPU_PGC_UP_TRG, we're able to wake up independently each core.
Signed-off-by: Abel Vesa abel.vesa@nxp.com --- plat/imx/imx8m/imx8mq/gpc.c | 7 +++++++ plat/imx/imx8m/imx8mq/imx8mq_psci.c | 8 ++++++++ plat/imx/imx8m/include/gpc.h | 2 ++ 3 files changed, 17 insertions(+)
diff --git a/plat/imx/imx8m/imx8mq/gpc.c b/plat/imx/imx8m/imx8mq/gpc.c index 02c640b..3eb3efc 100644 --- a/plat/imx/imx8m/imx8mq/gpc.c +++ b/plat/imx/imx8m/imx8mq/gpc.c @@ -26,6 +26,13 @@ void imx_set_cpu_pwr_off(unsigned int core_id) mmio_setbits_32(IMX_GPC_BASE + COREx_PGC_PCR(core_id), 0x1); };
+/* wake up the core */ +void imx_wake_cpu(unsigned int core_id) +{ + /* assert the power up bit for the specific core */ + mmio_setbits_32(IMX_GPC_BASE + CPU_PGC_UP_TRG, COREx_A53_SW_PUP_REQ(core_id)); +} + /* if out of lpm, we need to do reverse steps */ void imx_set_cpu_lpm(unsigned int core_id, bool pdn) { diff --git a/plat/imx/imx8m/imx8mq/imx8mq_psci.c b/plat/imx/imx8m/imx8mq/imx8mq_psci.c index 04e191f..84699cd 100644 --- a/plat/imx/imx8m/imx8mq/imx8mq_psci.c +++ b/plat/imx/imx8m/imx8mq/imx8mq_psci.c @@ -95,6 +95,13 @@ void imx_domain_suspend_finish(const psci_power_state_t *target_state) } }
+int imx_pwr_domain_pwr_down_wfi_wakeup(unsigned int index) +{ + imx_wake_cpu(index); + + return 0; +} + void imx_get_sys_suspend_power_state(psci_power_state_t *req_state) { unsigned int i; @@ -115,6 +122,7 @@ static const plat_psci_ops_t imx_plat_psci_ops = { .pwr_domain_suspend = imx_domain_suspend, .pwr_domain_suspend_finish = imx_domain_suspend_finish, .pwr_domain_pwr_down_wfi = imx_pwr_domain_pwr_down_wfi, + .pwr_domain_pwr_down_wfi_wakeup = imx_pwr_domain_pwr_down_wfi_wakeup, .get_sys_suspend_power_state = imx_get_sys_suspend_power_state, .system_reset = imx_system_reset, .system_off = imx_system_off, diff --git a/plat/imx/imx8m/include/gpc.h b/plat/imx/imx8m/include/gpc.h index 139132c..a51eef6 100644 --- a/plat/imx/imx8m/include/gpc.h +++ b/plat/imx/imx8m/include/gpc.h @@ -86,6 +86,7 @@ #define COREx_LPM_PUP(core_id) ((core_id) < 2 ? (1 << ((core_id) * 2 + 9)) : (1 << ((core_id) * 2 + 21))) #define SLTx_CFG(n) ((SLT0_CFG + ((n) * 4))) #define SLT_COREx_PUP(core_id) (0x2 << ((core_id) * 2)) +#define COREx_A53_SW_PUP_REQ(core_id) (1 << core_id)
#define IRQ_IMR_NUM 4 #define IMR_MASK_ALL 0xffffffff @@ -95,6 +96,7 @@ void imx_gpc_init(void); void imx_set_cpu_secure_entry(unsigned int core_index, uintptr_t sec_entrypoint); void imx_set_cpu_pwr_off(unsigned int core_index); void imx_set_cpu_pwr_on(unsigned int core_index); +void imx_wake_cpu(unsigned int core_id); void imx_set_cpu_lpm(unsigned int core_index, bool pdn); void imx_set_cluster_standby(bool retention); void imx_set_cluster_powerdown(unsigned int last_core, uint8_t power_state);
tf-a@lists.trustedfirmware.org