diff options
-rw-r--r-- | docs/porting-guide.md | 16 | ||||
-rw-r--r-- | include/bl31/services/psci.h | 2 | ||||
-rw-r--r-- | services/std_svc/psci/psci_entry.S | 3 | ||||
-rw-r--r-- | services/std_svc/psci/psci_off.c | 15 | ||||
-rw-r--r-- | services/std_svc/psci/psci_suspend.c | 9 |
5 files changed, 36 insertions, 9 deletions
diff --git a/docs/porting-guide.md b/docs/porting-guide.md index 0cd36134f..8947defb0 100644 --- a/docs/porting-guide.md +++ b/docs/porting-guide.md @@ -1715,6 +1715,22 @@ latter case, the power domain is expected to save enough state so that it can resume execution by restoring this state when its powered on (see `pwr_domain_suspend_finish()`). +#### plat_psci_ops.pwr_domain_pwr_down_wfi() + +This is an optional function and, if implemented, is expected to perform +platform specific actions including the `wfi` invocation which allows the +CPU to powerdown. Since this function is invoked outside the PSCI locks, +the actions performed in this hook must be local to the CPU or the platform +must ensure that races between multiple CPUs cannot occur. + +The `target_state` has a similar meaning as described in the `pwr_domain_off()` +operation and it encodes the platform coordinated target local power states for +the CPU power domain and its parent power domain levels. This function must +not return back to the caller. + +If this function is not implemented by the platform, PSCI generic +implementation invokes `psci_power_down_wfi()` for power down. + #### plat_psci_ops.pwr_domain_on_finish() This function is called by the PSCI implementation after the calling CPU is diff --git a/include/bl31/services/psci.h b/include/bl31/services/psci.h index acf07869f..95e77809b 100644 --- a/include/bl31/services/psci.h +++ b/include/bl31/services/psci.h @@ -265,6 +265,8 @@ typedef struct plat_psci_ops { void (*pwr_domain_on_finish)(const psci_power_state_t *target_state); void (*pwr_domain_suspend_finish)( const psci_power_state_t *target_state); + void (*pwr_domain_pwr_down_wfi)( + const psci_power_state_t *target_state) __dead2; void (*system_off)(void) __dead2; void (*system_reset)(void) __dead2; int (*validate_power_state)(unsigned int power_state, diff --git a/services/std_svc/psci/psci_entry.S b/services/std_svc/psci/psci_entry.S index 5f4f91c59..f8c0afa25 100644 --- a/services/std_svc/psci/psci_entry.S +++ b/services/std_svc/psci/psci_entry.S @@ -106,7 +106,6 @@ endfunc psci_entrypoint func psci_power_down_wfi dsb sy // ensure write buffer empty wfi -wfi_spill: - b wfi_spill + bl plat_panic_handler endfunc psci_power_down_wfi diff --git a/services/std_svc/psci/psci_off.c b/services/std_svc/psci/psci_off.c index cef66689e..686666d54 100644 --- a/services/std_svc/psci/psci_off.c +++ b/services/std_svc/psci/psci_off.c @@ -138,11 +138,16 @@ exit: dsbish(); inv_cpu_data(psci_svc_cpu_data.aff_info_state); - /* - * Enter a wfi loop which will allow the power controller to - * physically power down this cpu. - */ - psci_power_down_wfi(); + if (psci_plat_pm_ops->pwr_domain_pwr_down_wfi) { + /* This function must not return */ + psci_plat_pm_ops->pwr_domain_pwr_down_wfi(&state_info); + } else { + /* + * Enter a wfi loop which will allow the power + * controller to physically power down this cpu. + */ + psci_power_down_wfi(); + } } return rc; diff --git a/services/std_svc/psci/psci_suspend.c b/services/std_svc/psci/psci_suspend.c index 367bb32a6..8c6ab6b40 100644 --- a/services/std_svc/psci/psci_suspend.c +++ b/services/std_svc/psci/psci_suspend.c @@ -189,8 +189,13 @@ exit: if (skip_wfi) return; - if (is_power_down_state) - psci_power_down_wfi(); + if (is_power_down_state) { + /* The function calls below must not return */ + if (psci_plat_pm_ops->pwr_domain_pwr_down_wfi) + psci_plat_pm_ops->pwr_domain_pwr_down_wfi(state_info); + else + psci_power_down_wfi(); + } /* * We will reach here if only retention/standby states have been |