Merge pull request #633 from soby-mathew/sm/psci_wfi_hook
PSCI: Add pwr_domain_pwr_down_wfi() hook in plat_psci_ops
diff --git a/docs/porting-guide.md b/docs/porting-guide.md
index 0cd3613..8947def 100644
--- a/docs/porting-guide.md
+++ b/docs/porting-guide.md
@@ -1715,6 +1715,22 @@
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 acf0786..95e7780 100644
--- a/include/bl31/services/psci.h
+++ b/include/bl31/services/psci.h
@@ -265,6 +265,8 @@
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 5f4f91c..f8c0afa 100644
--- a/services/std_svc/psci/psci_entry.S
+++ b/services/std_svc/psci/psci_entry.S
@@ -106,7 +106,6 @@
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 cef6668..686666d 100644
--- a/services/std_svc/psci/psci_off.c
+++ b/services/std_svc/psci/psci_off.c
@@ -138,11 +138,16 @@
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 367bb32..8c6ab6b 100644
--- a/services/std_svc/psci/psci_suspend.c
+++ b/services/std_svc/psci/psci_suspend.c
@@ -189,8 +189,13 @@
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