Skip to content
GitLab
Menu
Projects
Groups
Snippets
Loading...
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Sign in / Register
Toggle navigation
Menu
Open sidebar
adam.huang
Arm Trusted Firmware
Commits
7a1b2794
Commit
7a1b2794
authored
Sep 19, 2016
by
danh-arm
Committed by
GitHub
Sep 19, 2016
Browse files
Merge pull request #702 from jeenu-arm/psci-node-hw-state
Support for PSCI NODE_HW_STATE
parents
368d4ebf
3cc17aae
Changes
13
Hide whitespace changes
Inline
Side-by-side
docs/firmware-design.md
View file @
7a1b2794
...
...
@@ -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
*
| |
...
...
docs/porting-guide.md
View file @
7a1b2794
...
...
@@ -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
...
...
include/lib/psci/psci.h
View file @
7a1b2794
...
...
@@ -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
);
...
...
include/plat/arm/css/common/css_def.h
View file @
7a1b2794
...
...
@@ -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__ */
include/plat/arm/css/common/css_pm.h
View file @
7a1b2794
...
...
@@ -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__ */
lib/psci/psci_main.c
View file @
7a1b2794
...
...
@@ -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
);
...
...
lib/psci/psci_private.h
View file @
7a1b2794
...
...
@@ -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))
...
...
lib/psci/psci_setup.c
View file @
7a1b2794
...
...
@@ -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
);
...
...
plat/arm/board/fvp/fvp_pm.c
View file @
7a1b2794
...
...
@@ -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
};
plat/arm/board/juno/juno_pm.c
View file @
7a1b2794
/*
* 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
};
plat/arm/css/common/css_pm.c
View file @
7a1b2794
...
...
@@ -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
};
plat/arm/css/common/css_scpi.c
View file @
7a1b2794
...
...
@@ -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
;
...
...
plat/arm/css/common/css_scpi.h
View file @
7a1b2794
/*
* Copyright (c) 2014-201
5
, ARM Limited and Contributors. All rights reserved.
* Copyright (c) 2014-201
6
, 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
);
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
.
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment