mirror of
https://github.com/ARM-software/arm-trusted-firmware.git
synced 2025-04-27 23:35:10 +00:00
Merge pull request #702 from jeenu-arm/psci-node-hw-state
Support for PSCI NODE_HW_STATE
This commit is contained in:
commit
7a1b279430
13 changed files with 253 additions and 6 deletions
|
@ -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* | |
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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__ */
|
||||
|
|
|
@ -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__ */
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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))
|
||||
|
|
|
@ -263,6 +263,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);
|
||||
|
|
|
@ -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
|
||||
};
|
||||
|
|
|
@ -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
|
||||
};
|
||||
|
|
|
@ -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
|
||||
};
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue