PSCI: Add support for PSCI NODE_HW_STATE API
This patch adds support for NODE_HW_STATE PSCI API by introducing a new
PSCI platform hook (get_node_hw_state). The implementation validates
supplied arguments, and then invokes this platform-defined hook and
returns its result to the caller. PSCI capabilities are updated
accordingly.
Also updates porting and firmware design guides.
Change-Id: I808e55bdf0c157002a7c104b875779fe50a68a30
diff --git a/docs/firmware-design.md b/docs/firmware-design.md
index 68cad2e..90ce4b1 100644
--- a/docs/firmware-design.md
+++ b/docs/firmware-design.md
@@ -716,7 +716,7 @@
|`PSCI_FEATURES` | Yes | |
|`CPU_FREEZE` | No | |
|`CPU_DEFAULT_SUSPEND` | No | |
-|`CPU_HW_STATE` | No | |
+|`NODE_HW_STATE` | Yes* | |
|`SYSTEM_SUSPEND` | Yes* | |
|`PSCI_SET_SUSPEND_MODE`| No | |
|`PSCI_STAT_RESIDENCY` | Yes* | |
diff --git a/docs/porting-guide.md b/docs/porting-guide.md
index 8dad4a0..195c937 100644
--- a/docs/porting-guide.md
+++ b/docs/porting-guide.md
@@ -1832,6 +1832,20 @@
power state encoding for `power_state` parameter of PSCI_STAT_COUNT/RESIDENCY
APIs as described in Section 5.18 of [PSCI].
+#### plat_psci_ops.get_node_hw_state()
+
+This is an optional function. If implemented this function is intended to return
+the power state of a node (identified by the first parameter, the `MPIDR`) in
+the power domain topology (identified by the second parameter, `power_level`),
+as retrieved from a power controller or equivalent component on the platform.
+Upon successful completion, the implementation must map and return the final
+status among `HW_ON`, `HW_OFF` or `HW_STANDBY`. Upon encountering failures, it
+must return either `PSCI_E_INVALID_PARAMS` or `PSCI_E_NOT_SUPPORTED` as
+appropriate.
+
+Implementations are not expected to handle `power_levels` greater than
+`PLAT_MAX_PWR_LVL`.
+
3.6 Interrupt Management framework (in BL31)
----------------------------------------------
BL31 implements an Interrupt Management Framework (IMF) to manage interrupts
diff --git a/include/lib/psci/psci.h b/include/lib/psci/psci.h
index a583fef..02cbbf3 100644
--- a/include/lib/psci/psci.h
+++ b/include/lib/psci/psci.h
@@ -78,6 +78,8 @@
#define PSCI_SYSTEM_OFF 0x84000008
#define PSCI_SYSTEM_RESET 0x84000009
#define PSCI_FEATURES 0x8400000A
+#define PSCI_NODE_HW_STATE_AARCH32 0x8400000d
+#define PSCI_NODE_HW_STATE_AARCH64 0xc400000d
#define PSCI_SYSTEM_SUSPEND_AARCH32 0x8400000E
#define PSCI_SYSTEM_SUSPEND_AARCH64 0xc400000E
#define PSCI_STAT_RESIDENCY_AARCH32 0x84000010
@@ -200,6 +202,17 @@
} aff_info_state_t;
/*
+ * These are the power states reported by PSCI_NODE_HW_STATE API for the
+ * specified CPU. The definitions of these states can be found in Section 5.15.3
+ * of PSCI specification (ARM DEN 0022C).
+ */
+typedef enum {
+ HW_ON = 0,
+ HW_OFF = 1,
+ HW_STANDBY = 2
+} node_hw_state_t;
+
+/*
* Macro to represent invalid affinity level within PSCI.
*/
#define PSCI_INVALID_PWR_LVL (PLAT_MAX_PWR_LVL + 1)
@@ -293,6 +306,7 @@
int (*translate_power_state_by_mpidr)(u_register_t mpidr,
unsigned int power_state,
psci_power_state_t *output_state);
+ int (*get_node_hw_state)(u_register_t mpidr, unsigned int power_level);
} plat_psci_ops_t;
/*******************************************************************************
@@ -330,6 +344,8 @@
int psci_migrate(u_register_t target_cpu);
int psci_migrate_info_type(void);
long psci_migrate_info_up_cpu(void);
+int psci_node_hw_state(u_register_t target_cpu,
+ unsigned int power_level);
int psci_features(unsigned int psci_fid);
void __dead2 psci_power_down_wfi(void);
void psci_arch_setup(void);
diff --git a/lib/psci/psci_main.c b/lib/psci/psci_main.c
index 3ad3dd4..23bd106 100644
--- a/lib/psci/psci_main.c
+++ b/lib/psci/psci_main.c
@@ -295,6 +295,31 @@
return resident_cpu_mpidr;
}
+int psci_node_hw_state(u_register_t target_cpu,
+ unsigned int power_level)
+{
+ int rc;
+
+ /* Validate target_cpu */
+ rc = psci_validate_mpidr(target_cpu);
+ if (rc != PSCI_E_SUCCESS)
+ return PSCI_E_INVALID_PARAMS;
+
+ /* Validate power_level against PLAT_MAX_PWR_LVL */
+ if (power_level > PLAT_MAX_PWR_LVL)
+ return PSCI_E_INVALID_PARAMS;
+
+ /*
+ * Dispatch this call to platform to query power controller, and pass on
+ * to the caller what it returns
+ */
+ assert(psci_plat_pm_ops->get_node_hw_state);
+ rc = psci_plat_pm_ops->get_node_hw_state(target_cpu, power_level);
+ assert((rc >= HW_ON && rc <= HW_STANDBY) || rc == PSCI_E_NOT_SUPPORTED
+ || rc == PSCI_E_INVALID_PARAMS);
+ return rc;
+}
+
int psci_features(unsigned int psci_fid)
{
unsigned int local_caps = psci_caps;
@@ -378,6 +403,9 @@
case PSCI_MIG_INFO_UP_CPU_AARCH32:
return psci_migrate_info_up_cpu();
+ case PSCI_NODE_HW_STATE_AARCH32:
+ return psci_node_hw_state(x1, x2);
+
case PSCI_SYSTEM_SUSPEND_AARCH32:
return psci_system_suspend(x1, x2);
@@ -422,6 +450,9 @@
case PSCI_MIG_INFO_UP_CPU_AARCH64:
return psci_migrate_info_up_cpu();
+ case PSCI_NODE_HW_STATE_AARCH64:
+ return psci_node_hw_state(x1, x2);
+
case PSCI_SYSTEM_SUSPEND_AARCH64:
return psci_system_suspend(x1, x2);
diff --git a/lib/psci/psci_private.h b/lib/psci/psci_private.h
index b795c8e..781b3b5 100644
--- a/lib/psci/psci_private.h
+++ b/lib/psci/psci_private.h
@@ -68,6 +68,7 @@
define_psci_cap(PSCI_AFFINITY_INFO_AARCH64) | \
define_psci_cap(PSCI_MIG_AARCH64) | \
define_psci_cap(PSCI_MIG_INFO_UP_CPU_AARCH64) | \
+ define_psci_cap(PSCI_NODE_HW_STATE_AARCH64) | \
define_psci_cap(PSCI_SYSTEM_SUSPEND_AARCH64) | \
define_psci_cap(PSCI_STAT_RESIDENCY_AARCH64) | \
define_psci_cap(PSCI_STAT_COUNT_AARCH64))
diff --git a/lib/psci/psci_setup.c b/lib/psci/psci_setup.c
index 20d0635..1ac1f23 100644
--- a/lib/psci/psci_setup.c
+++ b/lib/psci/psci_setup.c
@@ -256,6 +256,8 @@
psci_caps |= define_psci_cap(PSCI_SYSTEM_OFF);
if (psci_plat_pm_ops->system_reset)
psci_caps |= define_psci_cap(PSCI_SYSTEM_RESET);
+ if (psci_plat_pm_ops->get_node_hw_state)
+ psci_caps |= define_psci_cap(PSCI_NODE_HW_STATE_AARCH64);
#if ENABLE_PSCI_STAT
psci_caps |= define_psci_cap(PSCI_STAT_RESIDENCY_AARCH64);