feat(psci): add support for OS-initiated mode
This patch adds a `psci_validate_state_coordination` function that is
called by `psci_cpu_suspend_start` in OS-initiated mode.
This function validates the request per sections 4.2.3.2, 5.4.5, and 6.3
of the PSCI spec (DEN0022D.b):
- The requested power states are consistent with the system's state
- The calling core is the last running core at the requested power level
This function differs from `psci_do_state_coordination` in that:
- The `psci_req_local_pwr_states` map is not modified if the request
were to be denied
- The `state_info` argument is never modified since it contains the
power states requested by the calling OS
This is conditionally compiled into the build depending on the value of
the `PSCI_OS_INIT_MODE` build option.
Change-Id: I667041c842d2856e9d128c98db4d5ae4e4552df3
Signed-off-by: Wing Li <wingers@google.com>
diff --git a/lib/psci/psci_suspend.c b/lib/psci/psci_suspend.c
index f71994d..861b875 100644
--- a/lib/psci/psci_suspend.c
+++ b/lib/psci/psci_suspend.c
@@ -75,6 +75,14 @@
PUBLISH_EVENT(psci_suspend_pwrdown_start);
+#if PSCI_OS_INIT_MODE
+#ifdef PLAT_MAX_CPU_SUSPEND_PWR_LVL
+ end_pwrlvl = PLAT_MAX_CPU_SUSPEND_PWR_LVL;
+#else
+ end_pwrlvl = PLAT_MAX_PWR_LVL;
+#endif
+#endif
+
/* Save PSCI target power level for the suspend finisher handler */
psci_set_suspend_pwrlvl(end_pwrlvl);
@@ -151,12 +159,13 @@
* the state transition has been done, no further error is expected and it is
* not possible to undo any of the actions taken beyond that point.
******************************************************************************/
-void psci_cpu_suspend_start(const entry_point_info_t *ep,
- unsigned int end_pwrlvl,
- psci_power_state_t *state_info,
- unsigned int is_power_down_state)
+int psci_cpu_suspend_start(const entry_point_info_t *ep,
+ unsigned int end_pwrlvl,
+ psci_power_state_t *state_info,
+ unsigned int is_power_down_state)
{
- int skip_wfi = 0;
+ int rc = PSCI_E_SUCCESS;
+ bool skip_wfi = false;
unsigned int idx = plat_my_core_pos();
unsigned int parent_nodes[PLAT_MAX_PWR_LVL] = {0};
@@ -183,16 +192,32 @@
* detection that a wake-up interrupt has fired.
*/
if (read_isr_el1() != 0U) {
- skip_wfi = 1;
+ skip_wfi = true;
goto exit;
}
- /*
- * This function is passed the requested state info and
- * it returns the negotiated state info for each power level upto
- * the end level specified.
- */
- psci_do_state_coordination(end_pwrlvl, state_info);
+#if PSCI_OS_INIT_MODE
+ if (psci_suspend_mode == OS_INIT) {
+ /*
+ * This function validates the requested state info for
+ * OS-initiated mode.
+ */
+ rc = psci_validate_state_coordination(end_pwrlvl, state_info);
+ if (rc != PSCI_E_SUCCESS) {
+ skip_wfi = true;
+ goto exit;
+ }
+ } else {
+#endif
+ /*
+ * This function is passed the requested state info and
+ * it returns the negotiated state info for each power level upto
+ * the end level specified.
+ */
+ psci_do_state_coordination(end_pwrlvl, state_info);
+#if PSCI_OS_INIT_MODE
+ }
+#endif
#if ENABLE_PSCI_STAT
/* Update the last cpu for each level till end_pwrlvl */
@@ -208,7 +233,16 @@
* platform defined mailbox with the psci entrypoint,
* program the power controller etc.
*/
+
+#if PSCI_OS_INIT_MODE
+ rc = psci_plat_pm_ops->pwr_domain_suspend(state_info);
+ if (rc != PSCI_E_SUCCESS) {
+ skip_wfi = true;
+ goto exit;
+ }
+#else
psci_plat_pm_ops->pwr_domain_suspend(state_info);
+#endif
#if ENABLE_PSCI_STAT
plat_psci_stat_accounting_start(state_info);
@@ -221,8 +255,9 @@
*/
psci_release_pwr_domain_locks(end_pwrlvl, parent_nodes);
- if (skip_wfi == 1)
- return;
+ if (skip_wfi) {
+ return rc;
+ }
if (is_power_down_state != 0U) {
#if ENABLE_RUNTIME_INSTRUMENTATION
@@ -269,6 +304,8 @@
* context retaining suspend finisher.
*/
psci_suspend_to_standby_finisher(idx, end_pwrlvl);
+
+ return rc;
}
/*******************************************************************************