Merge pull request #652 from soby-mathew/sm/pmf_psci_stat
Introduce PMF and implement PSCI STAT APIs
This commit is contained in:
commit
10b93d7975
14
Makefile
14
Makefile
|
@ -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
|
||||
################################################################################
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
10
bl31/bl31.mk
10
bl31/bl31.mk
|
@ -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
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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)
|
||||
----------------------------------------------
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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;
|
||||
|
||||
/*******************************************************************************
|
||||
|
|
|
@ -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__ */
|
|
@ -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__ */
|
|
@ -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;
|
||||
}
|
|
@ -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);
|
||||
}
|
|
@ -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
|
||||
};
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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__ */
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
|
@ -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
|
||||
|
|
Loading…
Reference in New Issue