From 28d3d614b57730bdf364e49259d3c42599d26145 Mon Sep 17 00:00:00 2001 From: Jeenu Viswambharan Date: Wed, 3 Aug 2016 15:54:50 +0100 Subject: [PATCH 1/4] 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 --- docs/firmware-design.md | 2 +- docs/porting-guide.md | 14 ++++++++++++++ include/lib/psci/psci.h | 16 ++++++++++++++++ lib/psci/psci_main.c | 31 +++++++++++++++++++++++++++++++ lib/psci/psci_private.h | 1 + lib/psci/psci_setup.c | 2 ++ 6 files changed, 65 insertions(+), 1 deletion(-) diff --git a/docs/firmware-design.md b/docs/firmware-design.md index 68cad2e3b..90ce4b1bf 100644 --- a/docs/firmware-design.md +++ b/docs/firmware-design.md @@ -716,7 +716,7 @@ required support. |`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 8dad4a051..195c9374d 100644 --- a/docs/porting-guide.md +++ b/docs/porting-guide.md @@ -1832,6 +1832,20 @@ This function can also be used in case the platform wants to support local 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 a583fef7e..02cbbf355 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 @@ -199,6 +201,17 @@ typedef enum { AFF_STATE_ON_PENDING = 2 } 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. */ @@ -293,6 +306,7 @@ typedef struct plat_psci_ops { 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_affinity_info(u_register_t target_affinity, 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 3ad3dd40e..23bd106ca 100644 --- a/lib/psci/psci_main.c +++ b/lib/psci/psci_main.c @@ -295,6 +295,31 @@ long psci_migrate_info_up_cpu(void) 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 @@ u_register_t psci_smc_handler(uint32_t smc_fid, 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 @@ u_register_t psci_smc_handler(uint32_t smc_fid, 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 b795c8e03..781b3b526 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 20d06352c..1ac1f23db 100644 --- a/lib/psci/psci_setup.c +++ b/lib/psci/psci_setup.c @@ -256,6 +256,8 @@ int psci_setup(uintptr_t mailbox_ep) 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); From 1298ae023484077dcab596eb8e963cef96fe9605 Mon Sep 17 00:00:00 2001 From: Jeenu Viswambharan Date: Thu, 4 Aug 2016 09:43:15 +0100 Subject: [PATCH 2/4] FVP: Implement support for NODE_HW_STATE This patch implements FVP platform hook to support NODE_HW_STATE PSCI API. The platform hook validates the given MPIDR and reads corresponding status from FVP power controller, and returns expected values for the PSCI call. Change-Id: I286c92637da11858db2c8aba8ba079389032de6d --- plat/arm/board/fvp/fvp_pm.c | 39 ++++++++++++++++++++++++++++++++++++- 1 file changed, 38 insertions(+), 1 deletion(-) diff --git a/plat/arm/board/fvp/fvp_pm.c b/plat/arm/board/fvp/fvp_pm.c index 3976ef2b2..66c0c3df9 100644 --- a/plat/arm/board/fvp/fvp_pm.c +++ b/plat/arm/board/fvp/fvp_pm.c @@ -287,6 +287,42 @@ static void __dead2 fvp_system_reset(void) panic(); } +static int fvp_node_hw_state(u_register_t target_cpu, + unsigned int power_level) +{ + unsigned int psysr; + int ret; + + /* + * The format of 'power_level' is implementation-defined, but 0 must + * mean a CPU. We also allow 1 to denote the cluster + */ + if (power_level != ARM_PWR_LVL0 && power_level != ARM_PWR_LVL1) + return PSCI_E_INVALID_PARAMS; + + /* + * Read the status of the given MPDIR from FVP power controller. The + * power controller only gives us on/off status, so map that to expected + * return values of the PSCI call + */ + psysr = fvp_pwrc_read_psysr(target_cpu); + if (psysr == PSYSR_INVALID) + return PSCI_E_INVALID_PARAMS; + + switch (power_level) { + case ARM_PWR_LVL0: + ret = (psysr & PSYSR_AFF_L0) ? HW_ON : HW_OFF; + break; + case ARM_PWR_LVL1: + ret = (psysr & PSYSR_AFF_L1) ? HW_ON : HW_OFF; + break; + default: + assert(0); + } + + return ret; +} + /******************************************************************************* * Export the platform handlers via plat_arm_psci_pm_ops. The ARM Standard * platform layer will take care of registering the handlers with PSCI. @@ -301,5 +337,6 @@ const plat_psci_ops_t plat_arm_psci_pm_ops = { .system_off = fvp_system_off, .system_reset = fvp_system_reset, .validate_power_state = arm_validate_power_state, - .validate_ns_entrypoint = arm_validate_ns_entrypoint + .validate_ns_entrypoint = arm_validate_ns_entrypoint, + .get_node_hw_state = fvp_node_hw_state }; From 05b128f25edb351c537e55f90851189c35d45855 Mon Sep 17 00:00:00 2001 From: Jeenu Viswambharan Date: Thu, 4 Aug 2016 12:44:52 +0100 Subject: [PATCH 3/4] SCPI: Add function to query CSS power state This patch adds the function scpi_get_css_power_state to perform the 'Get CSS Power State' SCP command and handle its response. The function parses SCP response to obtain power states of requested cluster and CPUs within. Change-Id: I3ea26e48dff1a139da73f6c1e0893f21accaf9f0 --- plat/arm/css/common/css_scpi.c | 69 ++++++++++++++++++++++++++++++++++ plat/arm/css/common/css_scpi.h | 29 +++++++++++++- 2 files changed, 97 insertions(+), 1 deletion(-) diff --git a/plat/arm/css/common/css_scpi.c b/plat/arm/css/common/css_scpi.c index 02d573c9f..90a8939d9 100644 --- a/plat/arm/css/common/css_scpi.c +++ b/plat/arm/css/common/css_scpi.c @@ -41,11 +41,18 @@ #define SCPI_SHARED_MEM_AP_TO_SCP (PLAT_CSS_SCP_COM_SHARED_MEM_BASE \ + 0x100) +/* Header and payload addresses for commands from AP to SCP */ #define SCPI_CMD_HEADER_AP_TO_SCP \ ((scpi_cmd_t *) SCPI_SHARED_MEM_AP_TO_SCP) #define SCPI_CMD_PAYLOAD_AP_TO_SCP \ ((void *) (SCPI_SHARED_MEM_AP_TO_SCP + sizeof(scpi_cmd_t))) +/* Header and payload addresses for responses from SCP to AP */ +#define SCPI_RES_HEADER_SCP_TO_AP \ + ((scpi_cmd_t *) SCPI_SHARED_MEM_SCP_TO_AP) +#define SCPI_RES_PAYLOAD_SCP_TO_AP \ + ((void *) (SCPI_SHARED_MEM_SCP_TO_AP + sizeof(scpi_cmd_t))) + /* ID of the MHU slot used for the SCPI protocol */ #define SCPI_MHU_SLOT_ID 0 @@ -163,6 +170,68 @@ void scpi_set_css_power_state(unsigned mpidr, scpi_power_state_t cpu_state, scpi_secure_message_end(); } +/* + * Query and obtain CSS power state from SCP. + * + * In response to the query, SCP returns power states of all CPUs in all + * clusters of the system. The returned response is then filtered based on the + * supplied MPIDR. Power states of requested cluster and CPUs within are updated + * via. supplied non-NULL pointer arguments. + * + * Returns 0 on success, or -1 on errors. + */ +int scpi_get_css_power_state(unsigned int mpidr, unsigned int *cpu_state_p, + unsigned int *cluster_state_p) +{ + scpi_cmd_t *cmd; + scpi_cmd_t response; + int power_state, cpu, cluster, rc = -1; + + /* + * Extract CPU and cluster membership of the given MPIDR. SCPI caters + * for only up to 0xf clusters, and 8 CPUs per cluster + */ + cpu = mpidr & MPIDR_AFFLVL_MASK; + cluster = (mpidr >> MPIDR_AFF1_SHIFT) & MPIDR_AFFLVL_MASK; + if (cpu >= 8 || cluster >= 0xf) + return -1; + + scpi_secure_message_start(); + + /* Populate request headers */ + cmd = memset(SCPI_CMD_HEADER_AP_TO_SCP, 0, sizeof(*cmd)); + cmd->id = SCPI_CMD_GET_CSS_POWER_STATE; + + /* + * Send message and wait for SCP's response + */ + scpi_secure_message_send(0); + scpi_secure_message_receive(&response); + + if (response.status != SCP_OK) + goto exit; + + /* Validate SCP response */ + if (!CHECK_RESPONSE(response, cluster)) + goto exit; + + /* Extract power states for required cluster */ + power_state = *(((uint16_t *) SCPI_RES_PAYLOAD_SCP_TO_AP) + cluster); + if (CLUSTER_ID(power_state) != cluster) + goto exit; + + /* Update power state via. pointers */ + if (cluster_state_p) + *cluster_state_p = CLUSTER_POWER_STATE(power_state); + if (cpu_state_p) + *cpu_state_p = CPU_POWER_STATE(power_state); + rc = 0; + +exit: + scpi_secure_message_end(); + return rc; +} + uint32_t scpi_sys_power_state(scpi_system_state_t system_state) { scpi_cmd_t *cmd; diff --git a/plat/arm/css/common/css_scpi.h b/plat/arm/css/common/css_scpi.h index 4a601f3ef..1fb55e4dd 100644 --- a/plat/arm/css/common/css_scpi.h +++ b/plat/arm/css/common/css_scpi.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014-2015, ARM Limited and Contributors. All rights reserved. + * Copyright (c) 2014-2016, ARM Limited and Contributors. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: @@ -81,9 +81,34 @@ typedef uint32_t scpi_status_t; typedef enum { SCPI_CMD_SCP_READY = 0x01, SCPI_CMD_SET_CSS_POWER_STATE = 0x03, + SCPI_CMD_GET_CSS_POWER_STATE = 0x04, SCPI_CMD_SYS_POWER_STATE = 0x05 } scpi_command_t; +/* + * Macros to parse SCP response to GET_CSS_POWER_STATE command + * + * [3:0] : cluster ID + * [7:4] : cluster state: 0 = on; 3 = off; rest are reserved + * [15:8]: on/off state for individual CPUs in the cluster + * + * Payload is in little-endian + */ +#define CLUSTER_ID(_resp) ((_resp) & 0xf) +#define CLUSTER_POWER_STATE(_resp) (((_resp) >> 4) & 0xf) + +/* Result is a bit mask of CPU on/off states in the cluster */ +#define CPU_POWER_STATE(_resp) (((_resp) >> 8) & 0xff) + +/* + * For GET_CSS_POWER_STATE, SCP returns the power states of every cluster. The + * size of response depends on the number of clusters in the system. The + * SCP-to-AP payload contains 2 bytes per cluster. Make sure the response is + * large enough to contain power states of a given cluster + */ +#define CHECK_RESPONSE(_resp, _clus) \ + (_resp.size >= (((_clus) + 1) * 2)) + typedef enum { scpi_power_on = 0, scpi_power_retention = 1, @@ -101,6 +126,8 @@ extern void scpi_set_css_power_state(unsigned mpidr, scpi_power_state_t cpu_state, scpi_power_state_t cluster_state, scpi_power_state_t css_state); +int scpi_get_css_power_state(unsigned int mpidr, unsigned int *cpu_state_p, + unsigned int *cluster_state_p); uint32_t scpi_sys_power_state(scpi_system_state_t system_state); From 3cc17aae72287b993bcb2b626d7715ff9ed77790 Mon Sep 17 00:00:00 2001 From: Jeenu Viswambharan Date: Thu, 4 Aug 2016 09:43:15 +0100 Subject: [PATCH 4/4] CSS: Implement support for NODE_HW_STATE This patch implements CSS platform hook to support NODE_HW_STATE PSCI API. The platform hook queries SCP to obtain CSS power state. Power states returned by SCP are then converted to expected PSCI return codes. Juno's PSCI operation structure is modified to use the CSS implementation. Change-Id: I4a5edac0e5895dd77b51398cbd78f934831dafc0 --- include/plat/arm/css/common/css_def.h | 10 +++++++ include/plat/arm/css/common/css_pm.h | 1 + plat/arm/board/juno/juno_pm.c | 5 ++-- plat/arm/css/common/css_pm.c | 40 ++++++++++++++++++++++++++- 4 files changed, 53 insertions(+), 3 deletions(-) diff --git a/include/plat/arm/css/common/css_def.h b/include/plat/arm/css/common/css_def.h index 636daf294..173de1b4d 100644 --- a/include/plat/arm/css/common/css_def.h +++ b/include/plat/arm/css/common/css_def.h @@ -148,5 +148,15 @@ /* Trusted mailbox base address common to all CSS */ #define PLAT_ARM_TRUSTED_MAILBOX_BASE ARM_TRUSTED_SRAM_BASE +/* + * Parsing of CPU and Cluster states, as returned by 'Get CSS Power State' SCP + * command + */ +#define CSS_CLUSTER_PWR_STATE_ON 0 +#define CSS_CLUSTER_PWR_STATE_OFF 3 + +#define CSS_CPU_PWR_STATE_ON 1 +#define CSS_CPU_PWR_STATE_OFF 0 +#define CSS_CPU_PWR_STATE(state, n) (((state) >> (n)) & 1) #endif /* __CSS_DEF_H__ */ diff --git a/include/plat/arm/css/common/css_pm.h b/include/plat/arm/css/common/css_pm.h index ea6a5d251..4a6ca8161 100644 --- a/include/plat/arm/css/common/css_pm.h +++ b/include/plat/arm/css/common/css_pm.h @@ -45,5 +45,6 @@ void __dead2 css_system_off(void); void __dead2 css_system_reset(void); void css_cpu_standby(plat_local_state_t cpu_state); void css_get_sys_suspend_power_state(psci_power_state_t *req_state); +int css_node_hw_state(u_register_t mpidr, unsigned int power_level); #endif /* __CSS_PM_H__ */ diff --git a/plat/arm/board/juno/juno_pm.c b/plat/arm/board/juno/juno_pm.c index cbf994a47..c355d94df 100644 --- a/plat/arm/board/juno/juno_pm.c +++ b/plat/arm/board/juno/juno_pm.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, ARM Limited and Contributors. All rights reserved. + * Copyright (c) 2015-2016, ARM Limited and Contributors. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: @@ -88,5 +88,6 @@ const plat_psci_ops_t plat_arm_psci_pm_ops = { .validate_power_state = juno_validate_power_state, .validate_ns_entrypoint = arm_validate_ns_entrypoint, .get_sys_suspend_power_state = css_get_sys_suspend_power_state, - .translate_power_state_by_mpidr = juno_translate_power_state_by_mpidr + .translate_power_state_by_mpidr = juno_translate_power_state_by_mpidr, + .get_node_hw_state = css_node_hw_state }; diff --git a/plat/arm/css/common/css_pm.c b/plat/arm/css/common/css_pm.c index 801d93757..7607f6177 100644 --- a/plat/arm/css/common/css_pm.c +++ b/plat/arm/css/common/css_pm.c @@ -298,6 +298,43 @@ void css_get_sys_suspend_power_state(psci_power_state_t *req_state) req_state->pwr_domain_state[i] = ARM_LOCAL_STATE_OFF; } +/******************************************************************************* + * Handler to query CPU/cluster power states from SCP + ******************************************************************************/ +int css_node_hw_state(u_register_t mpidr, unsigned int power_level) +{ + int rc, element; + unsigned int cpu_state, cluster_state; + + /* + * The format of 'power_level' is implementation-defined, but 0 must + * mean a CPU. We also allow 1 to denote the cluster + */ + if (power_level != ARM_PWR_LVL0 && power_level != ARM_PWR_LVL1) + return PSCI_E_INVALID_PARAMS; + + /* Query SCP */ + rc = scpi_get_css_power_state(mpidr, &cpu_state, &cluster_state); + if (rc != 0) + return PSCI_E_INVALID_PARAMS; + + /* Map power states of CPU and cluster to expected PSCI return codes */ + if (power_level == ARM_PWR_LVL0) { + /* + * The CPU state returned by SCP is an 8-bit bit mask + * corresponding to each CPU in the cluster + */ + element = mpidr & MPIDR_AFFLVL_MASK; + return CSS_CPU_PWR_STATE(cpu_state, element) == + CSS_CPU_PWR_STATE_ON ? HW_ON : HW_OFF; + } else { + assert(cluster_state == CSS_CLUSTER_PWR_STATE_ON || + cluster_state == CSS_CLUSTER_PWR_STATE_OFF); + return cluster_state == CSS_CLUSTER_PWR_STATE_ON ? HW_ON : + HW_OFF; + } +} + /******************************************************************************* * Export the platform handlers via plat_arm_psci_pm_ops. The ARM Standard * platform will take care of registering the handlers with PSCI. @@ -312,5 +349,6 @@ const plat_psci_ops_t plat_arm_psci_pm_ops = { .system_off = css_system_off, .system_reset = css_system_reset, .validate_power_state = arm_validate_power_state, - .validate_ns_entrypoint = arm_validate_ns_entrypoint + .validate_ns_entrypoint = arm_validate_ns_entrypoint, + .get_node_hw_state = css_node_hw_state };