diff options
author | Andrew Thoelke <andrew.thoelke@arm.com> | 2014-05-23 11:00:04 +0100 |
---|---|---|
committer | Andrew Thoelke <andrew.thoelke@arm.com> | 2014-05-23 11:00:04 +0100 |
commit | 3ea8540d3c582787218a8c621c36d1468e9dea93 (patch) | |
tree | 75bba25cf63e478038edc5ff0ab92e5f18e80776 | |
parent | 1a4f19e36a9c591fc88052525fec5f13932b74cd (diff) | |
parent | 317ba09021b90ef50c13a1c44b4d2091736b6a86 (diff) | |
download | trusted-firmware-a-3ea8540d3c582787218a8c621c36d1468e9dea93.tar.gz |
Merge pull request #67 from achingupta:ag/psci_standby_bug_fix
-rw-r--r-- | include/bl31/services/psci.h | 1 | ||||
-rw-r--r-- | services/std_svc/psci/psci_entry.S | 18 | ||||
-rw-r--r-- | services/std_svc/psci/psci_main.c | 48 |
3 files changed, 47 insertions, 20 deletions
diff --git a/include/bl31/services/psci.h b/include/bl31/services/psci.h index 570fe5b8ab..b6e272c3a4 100644 --- a/include/bl31/services/psci.h +++ b/include/bl31/services/psci.h @@ -190,6 +190,7 @@ extern void psci_system_reset(void); extern int psci_cpu_on(unsigned long, unsigned long, unsigned long); +extern void __dead2 psci_power_down_wfi(void); extern void psci_aff_on_finish_entry(void); extern void psci_aff_suspend_finish_entry(void); extern void psci_register_spd_pm_hook(const spd_pm_ops_t *); diff --git a/services/std_svc/psci/psci_entry.S b/services/std_svc/psci/psci_entry.S index 256c538dd4..3d0181a8ed 100644 --- a/services/std_svc/psci/psci_entry.S +++ b/services/std_svc/psci/psci_entry.S @@ -37,6 +37,7 @@ .globl psci_aff_suspend_finish_entry .globl __psci_cpu_off .globl __psci_cpu_suspend + .globl psci_power_down_wfi /* ----------------------------------------------------- * This cpu has been physically powered up. Depending @@ -120,9 +121,6 @@ func __psci_cpu_off mrs x0, mpidr_el1 bl platform_set_coherent_stack bl psci_cpu_off - mov x1, #PSCI_E_SUCCESS - cmp x0, x1 - b.eq final_wfi mov sp, x19 ldp x19, x20, [sp,#0] add sp, sp, #0x10 @@ -144,9 +142,6 @@ func __psci_cpu_suspend mov x1, x21 mov x2, x22 bl psci_cpu_suspend - mov x1, #PSCI_E_SUCCESS - cmp x0, x1 - b.eq final_wfi mov sp, x19 ldp x21, x22, [sp,#0x10] ldp x19, x20, [sp,#0] @@ -154,7 +149,16 @@ func __psci_cpu_suspend func_epilogue ret -func final_wfi + /* -------------------------------------------- + * This function is called to indicate to the + * power controller that it is safe to power + * down this cpu. It should not exit the wfi + * and will be released from reset upon power + * up. 'wfi_spill' is used to catch erroneous + * exits from wfi. + * -------------------------------------------- + */ +func psci_power_down_wfi dsb sy // ensure write buffer empty wfi wfi_spill: diff --git a/services/std_svc/psci/psci_main.c b/services/std_svc/psci/psci_main.c index 1bcf21661f..c0866fb64f 100644 --- a/services/std_svc/psci/psci_main.c +++ b/services/std_svc/psci/psci_main.c @@ -90,23 +90,37 @@ int psci_cpu_suspend(unsigned int power_state, if (target_afflvl > MPIDR_MAX_AFFLVL) return PSCI_E_INVALID_PARAMS; + /* Determine the 'state type' in the 'power_state' parameter */ pstate_type = psci_get_pstate_type(power_state); + + /* + * Ensure that we have a platform specific handler for entering + * a standby state. + */ if (pstate_type == PSTATE_TYPE_STANDBY) { - if (psci_plat_pm_ops->affinst_standby) - rc = psci_plat_pm_ops->affinst_standby(power_state); - else + if (!psci_plat_pm_ops->affinst_standby) return PSCI_E_INVALID_PARAMS; - } else { - mpidr = read_mpidr(); - rc = psci_afflvl_suspend(mpidr, - entrypoint, - context_id, - power_state, - MPIDR_AFFLVL0, - target_afflvl); + + rc = psci_plat_pm_ops->affinst_standby(power_state); + assert(rc == PSCI_E_INVALID_PARAMS || rc == PSCI_E_SUCCESS); + return rc; } - assert(rc == PSCI_E_INVALID_PARAMS || rc == PSCI_E_SUCCESS); + /* + * Do what is needed to enter the power down state. Upon success, + * enter the final wfi which will power down this cpu else return + * an error. + */ + mpidr = read_mpidr(); + rc = psci_afflvl_suspend(mpidr, + entrypoint, + context_id, + power_state, + MPIDR_AFFLVL0, + target_afflvl); + if (rc == PSCI_E_SUCCESS) + psci_power_down_wfi(); + assert(rc == PSCI_E_INVALID_PARAMS); return rc; } @@ -127,10 +141,18 @@ int psci_cpu_off(void) rc = psci_afflvl_off(mpidr, MPIDR_AFFLVL0, target_afflvl); /* + * Check if all actions needed to safely power down this cpu have + * successfully completed. Enter a wfi loop which will allow the + * power controller to physically power down this cpu. + */ + if (rc == PSCI_E_SUCCESS) + psci_power_down_wfi(); + + /* * The only error cpu_off can return is E_DENIED. So check if that's * indeed the case. */ - assert (rc == PSCI_E_SUCCESS || rc == PSCI_E_DENIED); + assert (rc == PSCI_E_DENIED); return rc; } |