Merge pull request #652 from soby-mathew/sm/pmf_psci_stat

Introduce PMF and implement PSCI STAT APIs
This commit is contained in:
danh-arm 2016-07-04 16:32:24 +01:00 committed by GitHub
commit 10b93d7975
20 changed files with 1290 additions and 9 deletions

View File

@ -104,7 +104,10 @@ COLD_BOOT_SINGLE_CPU := 0
SPIN_ON_BL1_EXIT := 0
# Build PL011 UART driver in minimal generic UART mode
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
@ -373,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)
@ -410,6 +417,8 @@ $(eval $(call assert_boolean,ERROR_DEPRECATED))
$(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))
################################################################################
@ -437,6 +446,8 @@ $(eval $(call add_define,ERROR_DEPRECATED))
$(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))
@ -448,7 +459,6 @@ else
endif
endif
################################################################################
# Include BL specific makefiles
################################################################################

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2013-2014, ARM Limited and Contributors. All rights reserved.
* Copyright (c) 2013-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:
@ -58,6 +58,14 @@ SECTIONS
KEEP(*(rt_svc_descs))
__RT_SVC_DESCS_END__ = .;
#if ENABLE_PMF
/* Ensure 8-byte alignment for descriptors and ensure inclusion */
. = ALIGN(8);
__PMF_SVC_DESCS_START__ = .;
KEEP(*(pmf_svc_descs))
__PMF_SVC_DESCS_END__ = .;
#endif /* ENABLE_PMF */
/*
* Ensure 8-byte alignment for cpu_ops so that its fields are also
* aligned. Also ensure cpu_ops inclusion.
@ -132,6 +140,24 @@ SECTIONS
"PLAT_PERCPU_BAKERY_LOCK_SIZE does not match bakery lock requirements");
#endif
#endif
#if ENABLE_PMF
/*
* Time-stamps are stored in normal .bss memory
*
* The compiler will allocate enough memory for one CPU's time-stamps,
* the remaining memory for other CPU's is allocated by the
* linker script
*/
. = ALIGN(CACHE_WRITEBACK_GRANULE);
__PMF_TIMESTAMP_START__ = .;
KEEP(*(pmf_timestamp_array))
. = ALIGN(CACHE_WRITEBACK_GRANULE);
__PMF_PERCPU_TIMESTAMP_END__ = .;
__PERCPU_TIMESTAMP_SIZE__ = ABSOLUTE(. - __PMF_TIMESTAMP_START__);
. = . + (__PERCPU_TIMESTAMP_SIZE__ * (PLATFORM_CORE_COUNT - 1));
__PMF_TIMESTAMP_END__ = .;
#endif /* ENABLE_PMF */
__BSS_END__ = .;
} >RAM

View File

@ -1,5 +1,5 @@
#
# Copyright (c) 2013-2015, ARM Limited and Contributors. All rights reserved.
# Copyright (c) 2013-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:
@ -59,6 +59,14 @@ else
BL31_SOURCES += lib/locks/bakery/bakery_lock_normal.c
endif
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

@ -408,6 +408,14 @@ performed.
* `HANDLE_EA_EL3_FIRST`: When defined External Aborts and SError Interrupts
will be always trapped in EL3 i.e. in BL31 at runtime.
* `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;
/*******************************************************************************

199
include/lib/pmf.h Normal file
View File

@ -0,0 +1,199 @@
/*
* 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.
*/
#ifndef __PMF_H__
#define __PMF_H__
#include <cassert.h>
#include <pmf_helpers.h>
/*
* Constants used for/by PMF services.
*/
#define PMF_ARM_TIF_IMPL_ID (0x41000000)
#define PMF_TID_SHIFT 0
#define PMF_TID_MASK (0xFF << PMF_TID_SHIFT)
#define PMF_SVC_ID_SHIFT 10
#define PMF_SVC_ID_MASK (0x3F << PMF_SVC_ID_SHIFT)
#define PMF_IMPL_ID_SHIFT 24
#define PMF_IMPL_ID_MASK (0xFF << PMF_IMPL_ID_SHIFT)
/*
* Flags passed to PMF_REGISTER_SERVICE
*/
#define PMF_STORE_ENABLE (1 << 0)
#define PMF_DUMP_ENABLE (1 << 1)
/*
* Flags passed to PMF_GET_TIMESTAMP_XXX
* and PMF_CAPTURE_TIMESTAMP
*/
#define PMF_CACHE_MAINT (1 << 0)
#define PMF_NO_CACHE_MAINT 0
/*
* Defines for PMF SMC function ids.
*/
#define PMF_SMC_GET_TIMESTAMP_32 0x82000010
#define PMF_SMC_GET_TIMESTAMP_64 0xC2000010
#define PMF_NUM_SMC_CALLS 2
/*
* The macros below are used to identify
* PMF calls from the SMC function ID.
*/
#define PMF_FID_MASK 0xffe0u
#define PMF_FID_VALUE 0u
#define is_pmf_fid(_fid) (((_fid) & PMF_FID_MASK) == PMF_FID_VALUE)
/* Following are the supported PMF service IDs */
#define PMF_PSCI_STAT_SVC_ID 0
#if ENABLE_PMF
/*
* Convenience macros for capturing time-stamp.
*/
#define PMF_DECLARE_CAPTURE_TIMESTAMP(_name) \
void pmf_capture_timestamp_with_cache_maint_ ## _name( \
unsigned int tid, \
unsigned long long ts); \
void pmf_capture_timestamp_ ## _name( \
unsigned int tid, \
unsigned long long ts);
#define PMF_CAPTURE_TIMESTAMP(_name, _tid, _flags) \
do { \
unsigned long long ts = read_cntpct_el0(); \
if ((_flags) & PMF_CACHE_MAINT) \
pmf_capture_timestamp_with_cache_maint_ ## _name((_tid), ts);\
else \
pmf_capture_timestamp_ ## _name((_tid), ts); \
} while (0)
#define PMF_CAPTURE_AND_GET_TIMESTAMP(_name, _tid, _flags, _tsval) \
do { \
(_tsval) = read_cntpct_el0(); \
CASSERT(sizeof(_tsval) == sizeof(unsigned long long), invalid_tsval_size);\
if ((_flags) & PMF_CACHE_MAINT) \
pmf_capture_timestamp_with_cache_maint_ ## _name((_tid), (_tsval));\
else \
pmf_capture_timestamp_ ## _name((_tid), (_tsval));\
} while (0)
#define PMF_WRITE_TIMESTAMP(_name, _tid, _flags, _wrval) \
do { \
CASSERT(sizeof(_wrval) == sizeof(unsigned long long), invalid_wrval_size);\
if ((_flags) & PMF_CACHE_MAINT) \
pmf_capture_timestamp_with_cache_maint_ ## _name((_tid), (_wrval));\
else \
pmf_capture_timestamp_ ## _name((_tid), (_wrval));\
} while (0)
/*
* Convenience macros for retrieving time-stamp.
*/
#define PMF_DECLARE_GET_TIMESTAMP(_name) \
unsigned long long pmf_get_timestamp_by_index_ ## _name(\
unsigned int tid, \
unsigned int cpuid, \
unsigned int flags); \
unsigned long long pmf_get_timestamp_by_mpidr_ ## _name(\
unsigned int tid, \
u_register_t mpidr, \
unsigned int flags);
#define PMF_GET_TIMESTAMP_BY_MPIDR(_name, _tid, _mpidr, _flags, _tsval)\
_tsval = pmf_get_timestamp_by_mpidr_ ## _name(_tid, _mpidr, _flags)
#define PMF_GET_TIMESTAMP_BY_INDEX(_name, _tid, _cpuid, _flags, _tsval)\
_tsval = pmf_get_timestamp_by_index_ ## _name(_tid, _cpuid, _flags)
/* Convenience macros to register a PMF service.*/
/*
* This macro is used to register a PMF Service. It allocates PMF memory
* and defines default service-specific PMF functions.
*/
#define PMF_REGISTER_SERVICE(_name, _svcid, _totalid, _flags) \
PMF_ALLOCATE_TIMESTAMP_MEMORY(_name, _totalid) \
PMF_DEFINE_CAPTURE_TIMESTAMP(_name, _flags) \
PMF_DEFINE_GET_TIMESTAMP(_name)
/*
* This macro is used to register a PMF service, including an
* SMC interface to that service.
*/
#define PMF_REGISTER_SERVICE_SMC(_name, _svcid, _totalid, _flags)\
PMF_REGISTER_SERVICE(_name, _svcid, _totalid, _flags) \
PMF_DEFINE_SERVICE_DESC(_name, PMF_ARM_TIF_IMPL_ID, \
_svcid, _totalid, NULL, \
pmf_get_timestamp_by_mpidr_ ## _name)
/*
* This macro is used to register a PMF service that has an SMC interface
* but provides its own service-specific PMF functions.
*/
#define PMF_REGISTER_SERVICE_SMC_OWN(_name, _implid, _svcid, _totalid, \
_init, _getts) \
PMF_DEFINE_SERVICE_DESC(_name, _implid, _svcid, _totalid, \
_init, _getts)
#else
#define PMF_REGISTER_SERVICE(_name, _svcid, _totalid, _flags)
#define PMF_REGISTER_SERVICE_SMC(_name, _svcid, _totalid, _flags)
#define PMF_REGISTER_SERVICE_SMC_OWN(_name, _implid, _svcid, _totalid, \
_init, _getts)
#define PMF_DECLARE_CAPTURE_TIMESTAMP(_name)
#define PMF_DECLARE_GET_TIMESTAMP(_name)
#define PMF_CAPTURE_TIMESTAMP(_name, _tid, _flags)
#define PMF_GET_TIMESTAMP_BY_MPIDR(_name, _tid, _mpidr, _flags, _tsval)
#define PMF_GET_TIMESTAMP_BY_INDEX(_name, _tid, _cpuid, _flags, _tsval)
#endif /* ENABLE_PMF */
/*******************************************************************************
* Function & variable prototypes
******************************************************************************/
/* PMF common functions */
int pmf_get_timestamp_smc(unsigned int tid,
u_register_t mpidr,
unsigned int flags,
unsigned long long *ts);
int pmf_setup(void);
uintptr_t pmf_smc_handler(unsigned int smc_fid,
u_register_t x1,
u_register_t x2,
u_register_t x3,
u_register_t x4,
void *cookie,
void *handle,
u_register_t flags);
#endif /* __PMF_H__ */

166
include/lib/pmf_helpers.h Normal file
View File

@ -0,0 +1,166 @@
/*
* 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.
*/
#ifndef __PMF_HELPERS_H__
#define __PMF_HELPERS_H__
#include <arch_helpers.h>
#include <assert.h>
#include <platform.h>
#include <pmf.h>
#include <stdint.h>
#include <stddef.h>
/*
* Prototype for PMF service functions.
*/
typedef int (*pmf_svc_init_t)(void);
typedef unsigned long long (*pmf_svc_get_ts_t)(unsigned int tid,
u_register_t mpidr,
unsigned int flags);
/*
* This is the definition of PMF service desc.
*/
typedef struct pmf_svc_desc {
/* Structure version information */
param_header_t h;
/* Name of the PMF service */
const char *name;
/* PMF service config: Implementer id, Service id and total id*/
unsigned int svc_config;
/* PMF service initialization handler */
pmf_svc_init_t init;
/* PMF service time-stamp retrieval handler */
pmf_svc_get_ts_t get_ts;
} pmf_svc_desc_t;
/*
* Convenience macro to allocate memory for a PMF service.
*/
#define PMF_ALLOCATE_TIMESTAMP_MEMORY(_name, _total_id) \
static unsigned long long pmf_ts_mem_ ## _name[_total_id] \
__section("pmf_timestamp_array") __used;
/*
* Convenience macro to validate tid index for the given TS array.
*/
#define PMF_VALIDATE_TID(_name, _tid) \
assert((_tid & PMF_TID_MASK) < (ARRAY_SIZE(pmf_ts_mem_ ## _name)))
/*
* Convenience macros for capturing time-stamp.
*/
#define PMF_DEFINE_CAPTURE_TIMESTAMP(_name, _flags) \
void pmf_capture_timestamp_ ## _name( \
unsigned int tid, \
unsigned long long ts) \
{ \
CASSERT(_flags, select_proper_config); \
PMF_VALIDATE_TID(_name, tid); \
uintptr_t base_addr = (uintptr_t) pmf_ts_mem_ ## _name; \
if ((_flags) & PMF_STORE_ENABLE) \
__pmf_store_timestamp(base_addr, tid, ts); \
if ((_flags) & PMF_DUMP_ENABLE) \
__pmf_dump_timestamp(tid, ts); \
} \
void pmf_capture_timestamp_with_cache_maint_ ## _name( \
unsigned int tid, \
unsigned long long ts) \
{ \
CASSERT(_flags, select_proper_config); \
PMF_VALIDATE_TID(_name, tid); \
uintptr_t base_addr = (uintptr_t) pmf_ts_mem_ ## _name; \
if ((_flags) & PMF_STORE_ENABLE) \
__pmf_store_timestamp_with_cache_maint(base_addr, tid, ts);\
if ((_flags) & PMF_DUMP_ENABLE) \
__pmf_dump_timestamp(tid, ts); \
}
/*
* Convenience macros for retrieving time-stamp.
*/
#define PMF_DEFINE_GET_TIMESTAMP(_name) \
unsigned long long pmf_get_timestamp_by_index_ ## _name( \
unsigned int tid, unsigned int cpuid, unsigned int flags)\
{ \
PMF_VALIDATE_TID(_name, tid); \
uintptr_t base_addr = (uintptr_t) pmf_ts_mem_ ## _name; \
return __pmf_get_timestamp(base_addr, tid, cpuid, flags);\
} \
unsigned long long pmf_get_timestamp_by_mpidr_ ## _name( \
unsigned int tid, u_register_t mpidr, unsigned int flags)\
{ \
PMF_VALIDATE_TID(_name, tid); \
uintptr_t base_addr = (uintptr_t) pmf_ts_mem_ ## _name; \
return __pmf_get_timestamp(base_addr, tid, \
plat_core_pos_by_mpidr(mpidr), flags); \
}
/*
* Convenience macro to register a PMF service.
* This is needed for services that require SMC handling.
*/
#define PMF_DEFINE_SERVICE_DESC(_name, _implid, _svcid, _totalid, \
_init, _getts_by_mpidr) \
static const pmf_svc_desc_t __pmf_desc_ ## _name \
__section("pmf_svc_descs") __used = { \
.h.type = PARAM_EP, \
.h.version = VERSION_1, \
.h.size = sizeof(pmf_svc_desc_t), \
.h.attr = 0, \
.name = #_name, \
.svc_config = ((((_implid) << PMF_IMPL_ID_SHIFT) & \
PMF_IMPL_ID_MASK) | \
(((_svcid) << PMF_SVC_ID_SHIFT) & \
PMF_SVC_ID_MASK) | \
(((_totalid) << PMF_TID_SHIFT) & \
PMF_TID_MASK)), \
.init = _init, \
.get_ts = _getts_by_mpidr \
};
/* PMF internal functions */
void __pmf_dump_timestamp(unsigned int tid, unsigned long long ts);
void __pmf_store_timestamp(uintptr_t base_addr,
unsigned int tid,
unsigned long long ts);
void __pmf_store_timestamp_with_cache_maint(uintptr_t base_addr,
unsigned int tid,
unsigned long long ts);
unsigned long long __pmf_get_timestamp(uintptr_t base_addr,
unsigned int tid,
unsigned int cpuid,
unsigned int flags);
#endif /* __PMF_HELPERS_H__ */

280
lib/pmf/pmf_main.c Normal file
View File

@ -0,0 +1,280 @@
/*
* 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 <arch.h>
#include <arch_helpers.h>
#include <assert.h>
#include <debug.h>
#include <errno.h>
#include <platform.h>
#include <pmf.h>
#include <string.h>
/*******************************************************************************
* The 'pmf_svc_descs' array holds the PMF service descriptors exported by
* services by placing them in the 'pmf_svc_descs' linker section.
* The 'pmf_svc_descs_indices' array holds the index of a descriptor in the
* 'pmf_svc_descs' array. The TIF[15:10] bits in the time-stamp id are used
* to get an index into the 'pmf_svc_descs_indices' array. This gives the
* index of the descriptor in the 'pmf_svc_descs' array which contains the
* service function pointers.
******************************************************************************/
extern uintptr_t __PMF_SVC_DESCS_START__;
extern uintptr_t __PMF_SVC_DESCS_END__;
#define PMF_SVC_DESCS_START ((uintptr_t)(&__PMF_SVC_DESCS_START__))
#define PMF_SVC_DESCS_END ((uintptr_t)(&__PMF_SVC_DESCS_END__))
extern void *__PERCPU_TIMESTAMP_SIZE__;
#define PMF_PERCPU_TIMESTAMP_SIZE ((uintptr_t)&__PERCPU_TIMESTAMP_SIZE__)
extern uintptr_t __PMF_TIMESTAMP_START__;
#define PMF_TIMESTAMP_ARRAY_START ((uintptr_t)&__PMF_TIMESTAMP_START__)
extern uintptr_t __PMF_TIMESTAMP_END__;
#define PMF_TIMESTAMP_ARRAY_END ((uintptr_t)&__PMF_TIMESTAMP_END__)
#define PMF_SVC_DESCS_MAX 10
/*
* This is used to traverse through registered PMF services.
*/
static pmf_svc_desc_t *pmf_svc_descs;
/*
* This array is used to store registered PMF services in sorted order.
*/
static int pmf_svc_descs_indices[PMF_SVC_DESCS_MAX];
/*
* This is used to track total number of successfully registered PMF services.
*/
static int pmf_num_services;
/*
* This is the main PMF function that initialize registered
* PMF services and also sort them in ascending order.
*/
int pmf_setup(void)
{
int rc, ii, jj = 0;
int pmf_svc_descs_num, temp_val;
/* If no PMF services are registered then simply bail out */
pmf_svc_descs_num = (PMF_SVC_DESCS_END - PMF_SVC_DESCS_START)/
sizeof(pmf_svc_desc_t);
if (pmf_svc_descs_num == 0)
return 0;
assert(pmf_svc_descs_num < PMF_SVC_DESCS_MAX);
pmf_svc_descs = (pmf_svc_desc_t *) PMF_SVC_DESCS_START;
for (ii = 0; ii < pmf_svc_descs_num; ii++) {
assert(pmf_svc_descs[ii].get_ts);
/*
* Call the initialization routine for this
* PMF service, if it is defined.
*/
if (pmf_svc_descs[ii].init) {
rc = pmf_svc_descs[ii].init();
if (rc) {
WARN("Could not initialize PMF"
"service %s - skipping \n",
pmf_svc_descs[ii].name);
continue;
}
}
/* Update the pmf_svc_descs_indices array */
pmf_svc_descs_indices[jj++] = ii;
}
pmf_num_services = jj;
/*
* Sort the successfully registered PMF services
* according to service ID
*/
for (ii = 1; ii < pmf_num_services; ii++) {
for (jj = 0; jj < (pmf_num_services - ii); jj++) {
if ((pmf_svc_descs[jj].svc_config & PMF_SVC_ID_MASK) >
(pmf_svc_descs[jj + 1].svc_config &
PMF_SVC_ID_MASK)) {
temp_val = pmf_svc_descs_indices[jj];
pmf_svc_descs_indices[jj] =
pmf_svc_descs_indices[jj+1];
pmf_svc_descs_indices[jj+1] = temp_val;
}
}
}
return 0;
}
/*
* This function implements binary search to find registered
* PMF service based on Service ID provided in `tid` argument.
*/
static pmf_svc_desc_t *get_service(unsigned int tid)
{
int low = 0;
int mid;
int high = pmf_num_services;
unsigned int svc_id = tid & PMF_SVC_ID_MASK;
int index;
unsigned int desc_svc_id;
if (pmf_num_services == 0)
return NULL;
assert(pmf_svc_descs);
do {
mid = (low + high) / 2;
index = pmf_svc_descs_indices[mid];
desc_svc_id = pmf_svc_descs[index].svc_config & PMF_SVC_ID_MASK;
if (svc_id < desc_svc_id)
high = mid - 1;
if (svc_id > desc_svc_id)
low = mid + 1;
} while ((svc_id != desc_svc_id) && (low <= high));
/*
* Make sure the Service found supports the tid range.
*/
if ((svc_id == desc_svc_id) && ((tid & PMF_TID_MASK) <
(pmf_svc_descs[index].svc_config & PMF_TID_MASK)))
return (pmf_svc_desc_t *)&pmf_svc_descs[index];
return NULL;
}
/*
* This function gets the time-stamp value for the PMF services
* registered for SMC interface based on `tid` and `mpidr`.
*/
int pmf_get_timestamp_smc(unsigned int tid,
u_register_t mpidr,
unsigned int flags,
unsigned long long *ts_value)
{
pmf_svc_desc_t *svc_desc;
assert(ts_value);
/* Search for registered service. */
svc_desc = get_service(tid);
if ((svc_desc == NULL) || (plat_core_pos_by_mpidr(mpidr) < 0)) {
*ts_value = 0;
return -EINVAL;
} else {
/* Call the service time-stamp handler. */
*ts_value = svc_desc->get_ts(tid, mpidr, flags);
return 0;
}
}
/*
* This function can be used to dump `ts` value for given `tid`.
* Assumption is that the console is already initialized.
*/
void __pmf_dump_timestamp(unsigned int tid, unsigned long long ts)
{
tf_printf("PMF:cpu %u tid %u ts %llu\n",
plat_my_core_pos(), tid, ts);
}
/*
* This function calculate the address identified by
* `base_addr`, `tid` and `cpuid`.
*/
static inline uintptr_t calc_ts_addr(uintptr_t base_addr,
unsigned int tid,
unsigned int cpuid)
{
assert(cpuid < PLATFORM_CORE_COUNT);
assert(base_addr >= PMF_TIMESTAMP_ARRAY_START);
assert(base_addr < ((PMF_TIMESTAMP_ARRAY_START +
PMF_PERCPU_TIMESTAMP_SIZE) - ((tid & PMF_TID_MASK) *
sizeof(unsigned long long))));
base_addr += ((cpuid * PMF_PERCPU_TIMESTAMP_SIZE) +
((tid & PMF_TID_MASK) * sizeof(unsigned long long)));
return base_addr;
}
/*
* This function stores the `ts` value to the storage identified by
* `base_addr`, `tid` and current cpu id.
* Note: The timestamp addresses are cache line aligned per cpu
* and only the owning CPU would ever write into it.
*/
void __pmf_store_timestamp(uintptr_t base_addr,
unsigned int tid,
unsigned long long ts)
{
unsigned long long *ts_addr = (unsigned long long *)calc_ts_addr(base_addr,
tid, plat_my_core_pos());
*ts_addr = ts;
}
/*
* This is the cached version of `pmf_store_my_timestamp`
* Note: The timestamp addresses are cache line aligned per cpu
* and only the owning CPU would ever write into it.
*/
void __pmf_store_timestamp_with_cache_maint(uintptr_t base_addr,
unsigned int tid,
unsigned long long ts)
{
unsigned long long *ts_addr = (unsigned long long *)calc_ts_addr(base_addr,
tid, plat_my_core_pos());
*ts_addr = ts;
flush_dcache_range((uintptr_t)ts_addr, sizeof(unsigned long long));
}
/*
* This function retrieves the `ts` value from the storage identified by
* `base_addr`, `tid` and `cpuid`.
* Note: The timestamp addresses are cache line aligned per cpu.
*/
unsigned long long __pmf_get_timestamp(uintptr_t base_addr,
unsigned int tid,
unsigned int cpuid,
unsigned int flags)
{
assert(cpuid < PLATFORM_CORE_COUNT);
unsigned long long *ts_addr = (unsigned long long *)calc_ts_addr(base_addr,
tid, cpuid);
if (flags & PMF_CACHE_MAINT)
inv_dcache_range((uintptr_t)ts_addr, sizeof(unsigned long long));
return *ts_addr;
}

91
lib/pmf/pmf_smc.c Normal file
View File

@ -0,0 +1,91 @@
/*
* 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 <pmf.h>
#include <smcc_helpers.h>
/*
* This function is responsible for handling all PMF SMC calls.
*/
uintptr_t pmf_smc_handler(unsigned int smc_fid,
u_register_t x1,
u_register_t x2,
u_register_t x3,
u_register_t x4,
void *cookie,
void *handle,
u_register_t flags)
{
int rc;
unsigned long long ts_value;
if (((smc_fid >> FUNCID_CC_SHIFT) & FUNCID_CC_MASK) == SMC_32) {
x1 = (uint32_t)x1;
x2 = (uint32_t)x2;
x3 = (uint32_t)x3;
switch (smc_fid) {
case PMF_SMC_GET_TIMESTAMP_32:
/*
* Return error code and the captured
* time-stamp to the caller.
* x0 --> error code.
* x1 - x2 --> time-stamp value.
*/
rc = pmf_get_timestamp_smc(x1, x2, x3, &ts_value);
SMC_RET3(handle, rc, (uint32_t)ts_value,
(uint32_t)(ts_value >> 32));
default:
break;
}
} else {
switch (smc_fid) {
case PMF_SMC_GET_TIMESTAMP_64:
/*
* Return error code and the captured
* time-stamp to the caller.
* x0 --> error code.
* x1 --> time-stamp value.
*/
rc = pmf_get_timestamp_smc(x1, x2, x3, &ts_value);
SMC_RET2(handle, rc, ts_value);
default:
break;
}
}
WARN("Unimplemented PMF Call: 0x%x \n", smc_fid);
SMC_RET1(handle, SMC_UNK);
}

View File

@ -59,6 +59,19 @@ static int juno_validate_power_state(unsigned int power_state,
return rc;
}
/*
* Custom `translate_power_state_by_mpidr` handler for Juno. Unlike in the
* `juno_validate_power_state`, we do not down-grade the system power
* domain level request in `power_state` as it will be used to query the
* PSCI_STAT_COUNT/RESIDENCY at the system power domain level.
*/
static int juno_translate_power_state_by_mpidr(u_register_t mpidr,
unsigned int power_state,
psci_power_state_t *output_state)
{
return arm_validate_power_state(power_state, output_state);
}
/*******************************************************************************
* Export the platform handlers via plat_arm_psci_pm_ops. The ARM Standard
* platform will take care of registering the handlers with PSCI.
@ -74,5 +87,6 @@ const plat_psci_ops_t plat_arm_psci_pm_ops = {
.system_reset = css_system_reset,
.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
.get_sys_suspend_power_state = css_get_sys_suspend_power_state,
.translate_power_state_by_mpidr = juno_translate_power_state_by_mpidr
};

View File

@ -82,6 +82,9 @@ ARM_BL31_IN_DRAM := 0
$(eval $(call assert_boolean,ARM_BL31_IN_DRAM))
$(eval $(call add_define,ARM_BL31_IN_DRAM))
# Enable PSCI_STAT_COUNT/RESIDENCY APIs on ARM platforms
ENABLE_PSCI_STAT = 1
PLAT_INCLUDES += -Iinclude/common/tbbr \
-Iinclude/plat/arm/common \
-Iinclude/plat/arm/common/aarch64

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