Add optional PSCI STAT residency & count functions

This patch adds following optional PSCI STAT functions:

- PSCI_STAT_RESIDENCY: This call returns the amount of time spent
  in power_state in microseconds, by the node represented by the
  `target_cpu` and the highest level of `power_state`.

- PSCI_STAT_COUNT: This call returns the number of times a
  `power_state` has been used by the node represented by the
  `target_cpu` and the highest power level of `power_state`.

These APIs provides residency statistics for power states that has
been used by the platform. They are implemented according to v1.0
of the PSCI specification.

By default this optional feature is disabled in the PSCI
implementation. To enable it, set the boolean flag
`ENABLE_PSCI_STAT` to 1. This also sets `ENABLE_PMF` to 1.

Change-Id: Ie62e9d37d6d416ccb1813acd7f616d1ddd3e8aff
This commit is contained in:
Yatharth Kochar 2016-05-09 18:26:35 +01:00 committed by Soby Mathew
parent a31d8983f4
commit 170fb93dec
13 changed files with 497 additions and 5 deletions

View File

@ -106,6 +106,8 @@ SPIN_ON_BL1_EXIT := 0
PL011_GENERIC_UART := 0
# Flag to enable Performance Measurement Framework
ENABLE_PMF := 0
# Flag to enable PSCI STATs functionality
ENABLE_PSCI_STAT := 0
################################################################################
# Checkpatch script options
@ -374,6 +376,10 @@ ifneq (${GENERATE_COT},0)
endif
endif
# Make sure PMF is enabled if PSCI STAT is enabled.
ifeq (${ENABLE_PSCI_STAT},1)
ENABLE_PMF := 1
endif
################################################################################
# Auxiliary tools (fip_create, cert_create, etc)
@ -412,6 +418,7 @@ $(eval $(call assert_boolean,ENABLE_PLAT_COMPAT))
$(eval $(call assert_boolean,SPIN_ON_BL1_EXIT))
$(eval $(call assert_boolean,PL011_GENERIC_UART))
$(eval $(call assert_boolean,ENABLE_PMF))
$(eval $(call assert_boolean,ENABLE_PSCI_STAT))
################################################################################
@ -440,6 +447,7 @@ $(eval $(call add_define,ENABLE_PLAT_COMPAT))
$(eval $(call add_define,SPIN_ON_BL1_EXIT))
$(eval $(call add_define,PL011_GENERIC_UART))
$(eval $(call add_define,ENABLE_PMF))
$(eval $(call add_define,ENABLE_PSCI_STAT))
# Define the EL3_PAYLOAD_BASE flag only if it is provided.
ifdef EL3_PAYLOAD_BASE
$(eval $(call add_define,EL3_PAYLOAD_BASE))
@ -451,7 +459,6 @@ else
endif
endif
################################################################################
# Include BL specific makefiles
################################################################################

View File

@ -63,6 +63,10 @@ ifeq (${ENABLE_PMF}, 1)
BL31_SOURCES += lib/pmf/pmf_main.c
endif
ifeq (${ENABLE_PSCI_STAT}, 1)
BL31_SOURCES += services/std_svc/psci/psci_stat.c
endif
BL31_LINKERFILE := bl31/bl31.ld.S
# Flag used to indicate if Crash reporting via console should be included

View File

@ -718,8 +718,8 @@ required support.
|`CPU_HW_STATE` | No | |
|`SYSTEM_SUSPEND` | Yes* | |
|`PSCI_SET_SUSPEND_MODE`| No | |
|`PSCI_STAT_RESIDENCY` | No | |
|`PSCI_STAT_COUNT` | No | |
|`PSCI_STAT_RESIDENCY` | Yes* | |
|`PSCI_STAT_COUNT` | Yes* | |
*Note : These PSCI APIs require platform power management hooks to be
registered with the generic PSCI code to be supported.

View File

@ -189,9 +189,20 @@ platform port to define additional platform porting constants in
Defines the local power state corresponding to the deepest retention state
possible at every power domain level in the platform. This macro should be
a value less than PLAT_MAX_OFF_STATE and greater than 0. It is used by the
PSCI implementation to distuiguish between retention and power down local
PSCI implementation to distinguish between retention and power down local
power states within PSCI_CPU_SUSPEND call.
* **#define : PLAT_MAX_PWR_LVL_STATES**
Defines the maximum number of local power states per power domain level
that the platform supports. The default value of this macro is 2 since
most platforms just support a maximum of two local power states at each
power domain level (power-down and retention). If the platform needs to
account for more local power states, then it must redefine this macro.
Currently, this macro is used by the Generic PSCI implementation to size
the array used for PSCI_STAT_COUNT/RESIDENCY accounting.
* **#define : BL1_RO_BASE**
Defines the base address in secure ROM where BL1 originally lives. Must be
@ -1792,6 +1803,34 @@ domain level specific local states to suspend to system affinity level. The
`pwr_domain_suspend()` will be invoked with the coordinated target state to
enter system suspend.
#### plat_psci_ops.get_pwr_lvl_state_idx()
This is an optional function and, if implemented, is invoked by the PSCI
implementation to convert the `local_state` (first argument) at a specified
`pwr_lvl` (second argument) to an index between 0 and
`PLAT_MAX_PWR_LVL_STATES` - 1. This function is only needed if the platform
supports more than two local power states at each power domain level, that is
`PLAT_MAX_PWR_LVL_STATES` is greater than 2, and needs to account for these
local power states.
#### plat_psci_ops.translate_power_state_by_mpidr()
This is an optional function and, if implemented, verifies the `power_state`
(second argument) parameter of the PSCI API corresponding to a target power
domain. The target power domain is identified by using both `MPIDR` (first
argument) and the power domain level encoded in `power_state`. The power domain
level specific local states are to be extracted from `power_state` and be
populated in the `output_state` (third argument) array. The functionality
is similar to the `validate_power_state` function described above and is
envisaged to be used in case the validity of `power_state` depend on the
targeted power domain. If the `power_state` is invalid for the targeted power
domain, the platform must return PSCI_E_INVALID_PARAMS as error. If this
function is not implemented, then the generic implementation relies on
`validate_power_state` function to translate the `power_state`.
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].
3.6 Interrupt Management framework (in BL31)
----------------------------------------------

View File

@ -411,6 +411,11 @@ performed.
* `ENABLE_PMF`: Boolean option to enable support for optional Performance
Measurement Framework(PMF). Default is 0.
* `ENABLE_PSCI_STAT`: Boolean option to enable support for optional PSCI
functions `PSCI_STAT_RESIDENCY` and `PSCI_STAT_COUNT`. Default is 0.
Enabling this option enables the `ENABLE_PMF` build option as well.
The PMF is used for collecting the statistics.
#### ARM development platform specific build options
* `ARM_TSP_RAM_LOCATION`: location of the TSP binary. Options:

View File

@ -80,6 +80,10 @@
#define PSCI_FEATURES 0x8400000A
#define PSCI_SYSTEM_SUSPEND_AARCH32 0x8400000E
#define PSCI_SYSTEM_SUSPEND_AARCH64 0xc400000E
#define PSCI_STAT_RESIDENCY_AARCH32 0x84000010
#define PSCI_STAT_RESIDENCY_AARCH64 0xc4000010
#define PSCI_STAT_COUNT_AARCH32 0x84000011
#define PSCI_STAT_COUNT_AARCH64 0xc4000011
/* Macro to help build the psci capabilities bitfield */
#define define_psci_cap(x) (1 << (x & 0x1f))
@ -87,7 +91,11 @@
/*
* Number of PSCI calls (above) implemented
*/
#if ENABLE_PSCI_STAT
#define PSCI_NUM_CALLS 22
#else
#define PSCI_NUM_CALLS 18
#endif
/*******************************************************************************
* PSCI Migrate and friends
@ -274,6 +282,11 @@ typedef struct plat_psci_ops {
int (*validate_ns_entrypoint)(uintptr_t ns_entrypoint);
void (*get_sys_suspend_power_state)(
psci_power_state_t *req_state);
int (*get_pwr_lvl_state_idx)(plat_local_state_t pwr_domain_state,
int pwrlvl);
int (*translate_power_state_by_mpidr)(u_register_t mpidr,
unsigned int power_state,
psci_power_state_t *output_state);
} plat_psci_ops_t;
/*******************************************************************************

View File

@ -711,6 +711,16 @@ void psci_power_up_finish(void)
psci_acquire_pwr_domain_locks(end_pwrlvl,
cpu_idx);
#if ENABLE_PSCI_STAT
/*
* Capture power up time-stamp.
* No cache maintenance is required as caches are off
* and writes are direct to the main memory.
*/
PMF_CAPTURE_TIMESTAMP(psci_svc, PSCI_STAT_ID_EXIT_LOW_PWR,
PMF_NO_CACHE_MAINT);
#endif
psci_get_target_local_pwr_states(end_pwrlvl, &state_info);
/*
@ -736,6 +746,16 @@ void psci_power_up_finish(void)
*/
psci_set_pwr_domains_to_run(end_pwrlvl);
#if ENABLE_PSCI_STAT
/*
* Update PSCI stats.
* Caches are off when writing stats data on the power down path.
* Since caches are now enabled, it's necessary to do cache
* maintenance before reading that same data.
*/
psci_stats_update_pwr_up(end_pwrlvl, &state_info, PMF_CACHE_MAINT);
#endif
/*
* This loop releases the lock corresponding to each power level
* in the reverse order to which they were acquired.

View File

@ -110,11 +110,32 @@ int psci_cpu_suspend(unsigned int power_state,
*/
cpu_pd_state = state_info.pwr_domain_state[PSCI_CPU_PWR_LVL];
psci_set_cpu_local_state(cpu_pd_state);
#if ENABLE_PSCI_STAT
/*
* Capture time-stamp before CPU standby
* No cache maintenance is needed as caches
* are ON through out the CPU standby operation.
*/
PMF_CAPTURE_TIMESTAMP(psci_svc, PSCI_STAT_ID_ENTER_LOW_PWR,
PMF_NO_CACHE_MAINT);
#endif
psci_plat_pm_ops->cpu_standby(cpu_pd_state);
/* Upon exit from standby, set the state back to RUN. */
psci_set_cpu_local_state(PSCI_LOCAL_STATE_RUN);
#if ENABLE_PSCI_STAT
/* Capture time-stamp after CPU standby */
PMF_CAPTURE_TIMESTAMP(psci_svc, PSCI_STAT_ID_EXIT_LOW_PWR,
PMF_NO_CACHE_MAINT);
/* Update PSCI stats */
psci_stats_update_pwr_up(PSCI_CPU_PWR_LVL, &state_info,
PMF_NO_CACHE_MAINT);
#endif
return PSCI_E_SUCCESS;
}
@ -368,6 +389,14 @@ uint64_t psci_smc_handler(uint32_t smc_fid,
case PSCI_FEATURES:
SMC_RET1(handle, psci_features(x1));
#if ENABLE_PSCI_STAT
case PSCI_STAT_RESIDENCY_AARCH32:
SMC_RET1(handle, psci_stat_residency(x1, x2));
case PSCI_STAT_COUNT_AARCH32:
SMC_RET1(handle, psci_stat_count(x1, x2));
#endif
default:
break;
}
@ -393,6 +422,14 @@ uint64_t psci_smc_handler(uint32_t smc_fid,
case PSCI_SYSTEM_SUSPEND_AARCH64:
SMC_RET1(handle, psci_system_suspend(x1, x2));
#if ENABLE_PSCI_STAT
case PSCI_STAT_RESIDENCY_AARCH64:
SMC_RET1(handle, psci_stat_residency(x1, x2));
case PSCI_STAT_COUNT_AARCH64:
SMC_RET1(handle, psci_stat_count(x1, x2));
#endif
default:
break;
}

View File

@ -100,6 +100,11 @@ int psci_do_cpu_off(unsigned int end_pwrlvl)
*/
psci_do_state_coordination(end_pwrlvl, &state_info);
#if ENABLE_PSCI_STAT
/* Update the last cpu for each level till end_pwrlvl */
psci_stats_update_pwr_down(end_pwrlvl, &state_info);
#endif
/*
* Arch. management. Perform the necessary steps to flush all
* cpu caches.
@ -112,6 +117,16 @@ int psci_do_cpu_off(unsigned int end_pwrlvl)
*/
psci_plat_pm_ops->pwr_domain_off(&state_info);
#if ENABLE_PSCI_STAT
/*
* Capture time-stamp while entering low power state.
* No cache maintenance needed because caches are off
* and writes are direct to main memory.
*/
PMF_CAPTURE_TIMESTAMP(psci_svc, PSCI_STAT_ID_ENTER_LOW_PWR,
PMF_NO_CACHE_MAINT);
#endif
exit:
/*
* Release the locks corresponding to each power level in the

View File

@ -35,6 +35,7 @@
#include <bakery_lock.h>
#include <bl_common.h>
#include <cpu_data.h>
#include <pmf.h>
#include <psci.h>
#include <spinlock.h>
@ -67,7 +68,9 @@
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_SYSTEM_SUSPEND_AARCH64))
define_psci_cap(PSCI_SYSTEM_SUSPEND_AARCH64) | \
define_psci_cap(PSCI_STAT_RESIDENCY_AARCH64) | \
define_psci_cap(PSCI_STAT_COUNT_AARCH64))
/*
* Helper macros to get/set the fields of PSCI per-cpu data.
@ -102,6 +105,15 @@
#define is_cpu_standby_req(is_power_down_state, retn_lvl) \
(((!(is_power_down_state)) && ((retn_lvl) == 0)) ? 1 : 0)
/* Following are used as ID's to capture time-stamp */
#define PSCI_STAT_ID_ENTER_LOW_PWR 0
#define PSCI_STAT_ID_EXIT_LOW_PWR 1
#define PSCI_STAT_TOTAL_IDS 2
/* Declare PMF service functions for PSCI */
PMF_DECLARE_CAPTURE_TIMESTAMP(psci_svc)
PMF_DECLARE_GET_TIMESTAMP(psci_svc)
/*******************************************************************************
* The following two data structures implement the power domain tree. The tree
* is used to track the state of all the nodes i.e. power domain instances
@ -228,4 +240,15 @@ void psci_do_pwrup_cache_maintenance(void);
void __dead2 psci_system_off(void);
void __dead2 psci_system_reset(void);
/* Private exported functions from psci_stat.c */
void psci_stats_update_pwr_down(unsigned int end_pwrlvl,
const psci_power_state_t *state_info);
void psci_stats_update_pwr_up(unsigned int end_pwrlvl,
const psci_power_state_t *state_info,
unsigned int flags);
u_register_t psci_stat_residency(u_register_t target_cpu,
unsigned int power_state);
u_register_t psci_stat_count(u_register_t target_cpu,
unsigned int power_state);
#endif /* __PSCI_PRIVATE_H__ */

View File

@ -252,5 +252,10 @@ int psci_setup(void)
if (psci_plat_pm_ops->system_reset)
psci_caps |= define_psci_cap(PSCI_SYSTEM_RESET);
#if ENABLE_PSCI_STAT
psci_caps |= define_psci_cap(PSCI_STAT_RESIDENCY_AARCH64);
psci_caps |= define_psci_cap(PSCI_STAT_COUNT_AARCH64);
#endif
return 0;
}

View File

@ -0,0 +1,309 @@
/*
* Copyright (c) 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:
*
* Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* Neither the name of ARM nor the names of its contributors may be used
* to endorse or promote products derived from this software without specific
* prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
#include <assert.h>
#include <debug.h>
#include <platform.h>
#include <platform_def.h>
#include "psci_private.h"
#ifndef PLAT_MAX_PWR_LVL_STATES
#define PLAT_MAX_PWR_LVL_STATES 2
#endif
/* Ticks elapsed in one second by a signal of 1 MHz */
#define MHZ_TICKS_PER_SEC 1000000
/* Following structure is used for PSCI STAT */
typedef struct psci_stat {
u_register_t residency;
u_register_t count;
} psci_stat_t;
/*
* Following is used to keep track of the last cpu
* that goes to power down in non cpu power domains.
*/
static int last_cpu_in_non_cpu_pd[PSCI_NUM_NON_CPU_PWR_DOMAINS] = {-1};
/*
* Following are used to store PSCI STAT values for
* CPU and non CPU power domains.
*/
static psci_stat_t psci_cpu_stat[PLATFORM_CORE_COUNT]
[PLAT_MAX_PWR_LVL_STATES];
static psci_stat_t psci_non_cpu_stat[PSCI_NUM_NON_CPU_PWR_DOMAINS]
[PLAT_MAX_PWR_LVL_STATES];
/* Register PMF PSCI service */
PMF_REGISTER_SERVICE(psci_svc, PMF_PSCI_STAT_SVC_ID,
PSCI_STAT_TOTAL_IDS, PMF_STORE_ENABLE)
/* The divisor to use to convert raw timestamp into microseconds */
u_register_t residency_div;
/*
* This macro calculates the stats residency in microseconds,
* taking in account the wrap around condition.
*/
#define calc_stat_residency(_pwrupts, _pwrdnts, _res) \
do { \
if (_pwrupts < _pwrdnts) \
_res = UINT64_MAX - _pwrdnts + _pwrupts;\
else \
_res = _pwrupts - _pwrdnts; \
/* Convert timestamp into microseconds */ \
_res = _res/residency_div; \
} while (0)
/*
* This functions returns the index into the `psci_stat_t` array given the
* local power state and power domain level. If the platform implements the
* `get_pwr_lvl_state_idx` pm hook, then that will be used to return the index.
*/
static int get_stat_idx(plat_local_state_t local_state, int pwr_lvl)
{
int idx;
if (psci_plat_pm_ops->get_pwr_lvl_state_idx == NULL) {
assert(PLAT_MAX_PWR_LVL_STATES == 2);
if (is_local_state_retn(local_state))
return 0;
assert(is_local_state_off(local_state));
return 1;
}
idx = psci_plat_pm_ops->get_pwr_lvl_state_idx(local_state, pwr_lvl);
assert((idx >= 0) && (idx < PLAT_MAX_PWR_LVL_STATES));
return idx;
}
/*******************************************************************************
* This function is passed the target local power states for each power
* domain (state_info) between the current CPU domain and its ancestors until
* the target power level (end_pwrlvl).
*
* Then, for each level (apart from the CPU level) until the 'end_pwrlvl', it
* updates the `last_cpu_in_non_cpu_pd[]` with last power down cpu id.
*
* This function will only be invoked with data cache enabled and while
* powering down a core.
******************************************************************************/
void psci_stats_update_pwr_down(unsigned int end_pwrlvl,
const psci_power_state_t *state_info)
{
int lvl, parent_idx, cpu_idx = plat_my_core_pos();
assert(end_pwrlvl <= PLAT_MAX_PWR_LVL);
assert(state_info);
parent_idx = psci_cpu_pd_nodes[cpu_idx].parent_node;
for (lvl = PSCI_CPU_PWR_LVL + 1; lvl <= end_pwrlvl; lvl++) {
/* Break early if the target power state is RUN */
if (is_local_state_run(state_info->pwr_domain_state[lvl]))
break;
/*
* The power domain is entering a low power state, so this is
* the last CPU for this power domain
*/
last_cpu_in_non_cpu_pd[parent_idx] = cpu_idx;
parent_idx = psci_non_cpu_pd_nodes[parent_idx].parent_node;
}
}
/*******************************************************************************
* This function updates the PSCI STATS(residency time and count) for CPU
* and NON-CPU power domains.
* It is called with caches enabled and locks acquired(for NON-CPU domain)
******************************************************************************/
void psci_stats_update_pwr_up(unsigned int end_pwrlvl,
const psci_power_state_t *state_info,
unsigned int flags)
{
int parent_idx, cpu_idx = plat_my_core_pos();
int lvl, stat_idx;
plat_local_state_t local_state;
unsigned long long pwrup_ts = 0, pwrdn_ts = 0;
u_register_t residency;
assert(end_pwrlvl <= PLAT_MAX_PWR_LVL);
assert(state_info);
/* Initialize the residency divisor if not already initialized */
if (!residency_div) {
/* Pre-calculate divisor so that it can be directly used to
convert time-stamp into microseconds */
residency_div = read_cntfrq_el0() / MHZ_TICKS_PER_SEC;
assert(residency_div);
}
/* Get power down time-stamp for current CPU */
PMF_GET_TIMESTAMP_BY_INDEX(psci_svc, PSCI_STAT_ID_ENTER_LOW_PWR,
cpu_idx, flags, pwrdn_ts);
/* In the case of 1st power on just return */
if (!pwrdn_ts)
return;
/* Get power up time-stamp for current CPU */
PMF_GET_TIMESTAMP_BY_INDEX(psci_svc, PSCI_STAT_ID_EXIT_LOW_PWR,
cpu_idx, flags, pwrup_ts);
/* Get the index into the stats array */
local_state = state_info->pwr_domain_state[PSCI_CPU_PWR_LVL];
stat_idx = get_stat_idx(local_state, PSCI_CPU_PWR_LVL);
/* Calculate stats residency */
calc_stat_residency(pwrup_ts, pwrdn_ts, residency);
/* Update CPU stats. */
psci_cpu_stat[cpu_idx][stat_idx].residency += residency;
psci_cpu_stat[cpu_idx][stat_idx].count++;
/*
* Check what power domains above CPU were off
* prior to this CPU powering on.
*/
parent_idx = psci_cpu_pd_nodes[cpu_idx].parent_node;
for (lvl = PSCI_CPU_PWR_LVL + 1; lvl <= end_pwrlvl; lvl++) {
local_state = state_info->pwr_domain_state[lvl];
if (is_local_state_run(local_state)) {
/* Break early */
break;
}
assert(last_cpu_in_non_cpu_pd[parent_idx] != -1);
/* Get power down time-stamp for last CPU */
PMF_GET_TIMESTAMP_BY_INDEX(psci_svc, PSCI_STAT_ID_ENTER_LOW_PWR,
last_cpu_in_non_cpu_pd[parent_idx],
flags, pwrdn_ts);
/* Initialize back to reset value */
last_cpu_in_non_cpu_pd[parent_idx] = -1;
/* Get the index into the stats array */
stat_idx = get_stat_idx(local_state, lvl);
/* Calculate stats residency */
calc_stat_residency(pwrup_ts, pwrdn_ts, residency);
/* Update non cpu stats */
psci_non_cpu_stat[parent_idx][stat_idx].residency += residency;
psci_non_cpu_stat[parent_idx][stat_idx].count++;
parent_idx = psci_non_cpu_pd_nodes[parent_idx].parent_node;
}
}
/*******************************************************************************
* This function returns the appropriate count and residency time of the
* local state for the highest power level expressed in the `power_state`
* for the node represented by `target_cpu`.
******************************************************************************/
int psci_get_stat(u_register_t target_cpu, unsigned int power_state,
psci_stat_t *psci_stat)
{
int rc, pwrlvl, lvl, parent_idx, stat_idx, target_idx;
psci_power_state_t state_info = { {PSCI_LOCAL_STATE_RUN} };
plat_local_state_t local_state;
/* Validate the target_cpu parameter and determine the cpu index */
target_idx = plat_core_pos_by_mpidr(target_cpu);
if (target_idx == -1)
return PSCI_E_INVALID_PARAMS;
/* Validate the power_state parameter */
if (!psci_plat_pm_ops->translate_power_state_by_mpidr)
rc = psci_validate_power_state(power_state, &state_info);
else
rc = psci_plat_pm_ops->translate_power_state_by_mpidr(
target_cpu, power_state, &state_info);
if (rc != PSCI_E_SUCCESS)
return PSCI_E_INVALID_PARAMS;
/* Find the highest power level */
pwrlvl = psci_find_target_suspend_lvl(&state_info);
if (pwrlvl == PSCI_INVALID_PWR_LVL)
return PSCI_E_INVALID_PARAMS;
/* Get the index into the stats array */
local_state = state_info.pwr_domain_state[pwrlvl];
stat_idx = get_stat_idx(local_state, pwrlvl);
if (pwrlvl > PSCI_CPU_PWR_LVL) {
/* Get the power domain index */
parent_idx = psci_cpu_pd_nodes[target_idx].parent_node;
for (lvl = PSCI_CPU_PWR_LVL + 1; lvl < pwrlvl; lvl++)
parent_idx = psci_non_cpu_pd_nodes[parent_idx].parent_node;
/* Get the non cpu power domain stats */
*psci_stat = psci_non_cpu_stat[parent_idx][stat_idx];
} else {
/* Get the cpu power domain stats */
*psci_stat = psci_cpu_stat[target_idx][stat_idx];
}
return PSCI_E_SUCCESS;
}
/* This is the top level function for PSCI_STAT_RESIDENCY SMC. */
u_register_t psci_stat_residency(u_register_t target_cpu,
unsigned int power_state)
{
psci_stat_t psci_stat;
int rc = psci_get_stat(target_cpu, power_state, &psci_stat);
if (rc == PSCI_E_SUCCESS)
return psci_stat.residency;
else
return 0;
}
/* This is the top level function for PSCI_STAT_COUNT SMC. */
u_register_t psci_stat_count(u_register_t target_cpu,
unsigned int power_state)
{
psci_stat_t psci_stat;
int rc = psci_get_stat(target_cpu, power_state, &psci_stat);
if (rc == PSCI_E_SUCCESS)
return psci_stat.count;
else
return 0;
}

View File

@ -168,6 +168,11 @@ void psci_cpu_suspend_start(entry_point_info_t *ep,
*/
psci_do_state_coordination(end_pwrlvl, state_info);
#if ENABLE_PSCI_STAT
/* Update the last cpu for each level till end_pwrlvl */
psci_stats_update_pwr_down(end_pwrlvl, state_info);
#endif
if (is_power_down_state)
psci_suspend_to_pwrdown_start(end_pwrlvl, ep, state_info);
@ -179,6 +184,16 @@ void psci_cpu_suspend_start(entry_point_info_t *ep,
*/
psci_plat_pm_ops->pwr_domain_suspend(state_info);
#if ENABLE_PSCI_STAT
/*
* Capture time-stamp while entering low power state.
* No cache maintenance needed because caches are off
* and writes are direct to main memory.
*/
PMF_CAPTURE_TIMESTAMP(psci_svc, PSCI_STAT_ID_ENTER_LOW_PWR,
PMF_NO_CACHE_MAINT);
#endif
exit:
/*
* Release the locks corresponding to each power level in the