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/include/plat/arm/css/common/css_def.h b/include/plat/arm/css/common/css_def.h index 636daf2..173de1b 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 ea6a5d2..4a6ca81 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_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/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 aa2ed51..263ab68 100644 --- a/lib/psci/psci_setup.c +++ b/lib/psci/psci_setup.c @@ -263,6 +263,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); diff --git a/plat/arm/board/fvp/fvp_pm.c b/plat/arm/board/fvp/fvp_pm.c index 3976ef2..66c0c3d 100644 --- a/plat/arm/board/fvp/fvp_pm.c +++ b/plat/arm/board/fvp/fvp_pm.c @@ -287,6 +287,42 @@ 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 @@ .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 }; diff --git a/plat/arm/board/juno/juno_pm.c b/plat/arm/board/juno/juno_pm.c index cbf994a..c355d94 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 @@ .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 801d937..7607f61 100644 --- a/plat/arm/css/common/css_pm.c +++ b/plat/arm/css/common/css_pm.c @@ -299,6 +299,43 @@ } /******************************************************************************* + * 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 @@ .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 }; diff --git a/plat/arm/css/common/css_scpi.c b/plat/arm/css/common/css_scpi.c index 02d573c..90a8939 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 @@ 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 4a601f3..1fb55e4 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 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 @@ 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);