PSCI: Decouple PSCI stat residency calculation from PMF
authordp-arm <dimitris.papastamos@arm.com>
Tue, 31 Jan 2017 13:01:04 +0000 (13:01 +0000)
committerdp-arm <dimitris.papastamos@arm.com>
Mon, 13 Feb 2017 14:33:06 +0000 (14:33 +0000)
This patch introduces the following three platform interfaces:

* void plat_psci_stat_accounting_start(const psci_power_state_t *state_info)

  This is an optional hook that platforms can implement in order
  to perform accounting before entering a low power state.  This
  typically involves capturing a timestamp.

* void plat_psci_stat_accounting_stop(const psci_power_state_t *state_info)

  This is an optional hook that platforms can implement in order
  to perform accounting after exiting from a low power state.  This
  typically involves capturing a timestamp.

* u_register_t plat_psci_stat_get_residency(unsigned int lvl,
const psci_power_state_t *state_info,
unsigned int last_cpu_index)

  This is an optional hook that platforms can implement in order
  to calculate the PSCI stat residency.

If any of these interfaces are overridden by the platform, it is
recommended that all of them are.

By default `ENABLE_PSCI_STAT` is disabled.  If `ENABLE_PSCI_STAT`
is set but `ENABLE_PMF` is not set then an alternative PSCI stat
collection backend must be provided.  If both are set, then default
weak definitions of these functions are provided, using PMF to
calculate the residency.

NOTE: Previously, platforms did not have to explicitly set
`ENABLE_PMF` since this was automatically done by the top-level
Makefile.

Change-Id: I17b47804dea68c77bc284df15ee1ccd66bc4b79b
Signed-off-by: dp-arm <dimitris.papastamos@arm.com>
12 files changed:
Makefile
docs/porting-guide.md
docs/user-guide.md
include/plat/common/platform.h
lib/psci/psci_common.c
lib/psci/psci_main.c
lib/psci/psci_off.c
lib/psci/psci_private.h
lib/psci/psci_stat.c
lib/psci/psci_suspend.c
plat/arm/common/arm_common.mk
plat/common/plat_psci_common.c

index 9e148fbde51c474aa7fdf37321cf60ece65e24aa..9a080d3eab1c541f3207afa9b697a15976ed2a92 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -346,11 +346,6 @@ 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
-
 ifneq (${FIP_ALIGN},0)
 FIP_ARGS += --align ${FIP_ALIGN}
 endif
index e8486f12793d6e152ba4bdd8ee7ed001696db436..a5e59667a95ac4b6cc010b51b0f2c3a4158e63f6 100644 (file)
@@ -1707,10 +1707,56 @@ level could enter. It depends on the `validate_power_state()` handler to
 convert the power-state parameter (possibly encoding a composite power state)
 passed in a PSCI `CPU_SUSPEND` call to this representation.
 
-The following functions must be implemented to initialize PSCI functionality in
-the ARM Trusted Firmware.
+The following functions form part of platform port of PSCI functionality.
 
 
+### Function : plat_psci_stat_accounting_start() [optional]
+
+    Argument : const psci_power_state_t *
+    Return   : void
+
+This is an optional hook that platforms can implement for residency statistics
+accounting before entering a low power state.  The `pwr_domain_state` field of
+`state_info` (first argument) can be inspected if stat accounting is done
+differently at CPU level versus higher levels.  As an example, if the element at
+index 0 (CPU power level) in the `pwr_domain_state` array indicates a power down
+state, special hardware logic may be programmed in order to keep track of the
+residency statistics.  For higher levels (array indices > 0), the residency
+statistics could be tracked in software using PMF.  If `ENABLE_PMF` is set, the
+default implementation will use PMF to capture timestamps.
+
+### Function : plat_psci_stat_accounting_stop() [optional]
+
+    Argument : const psci_power_state_t *
+    Return   : void
+
+This is an optional hook that platforms can implement for residency statistics
+accounting after exiting from a low power state.  The `pwr_domain_state` field
+of `state_info` (first argument) can be inspected if stat accounting is done
+differently at CPU level versus higher levels.  As an example, if the element at
+index 0 (CPU power level) in the `pwr_domain_state` array indicates a power down
+state, special hardware logic may be programmed in order to keep track of the
+residency statistics.  For higher levels (array indices > 0), the residency
+statistics could be tracked in software using PMF.  If `ENABLE_PMF` is set, the
+default implementation will use PMF to capture timestamps.
+
+### Function : plat_psci_stat_get_residency() [optional]
+
+    Argument : unsigned int, const psci_power_state_t *, int
+    Return   : u_register_t
+
+This is an optional interface that is is invoked after resuming from a low power
+state and provides the time spent resident in that low power state by the power
+domain at a particular power domain level.  When a CPU wakes up from suspend,
+all its parent power domain levels are also woken up.  The generic PSCI code
+invokes this function for each parent power domain that is resumed and it
+identified by the `lvl` (first argument) parameter.  The `state_info` (second
+argument) describes the low power state that the power domain has resumed from.
+The current CPU is the first CPU in the power domain to resume from the low
+power state and the `last_cpu_idx` (third parameter) is the index of the last
+CPU in the power domain to suspend and may be needed to calculate the residency
+for that power domain.
+
 ### Function : plat_get_target_pwr_state() [optional]
 
     Argument : unsigned int, const plat_local_state_t *, unsigned int
index ebdb5a259854fb0b49cdddf233a2942a48376e7a..265061c45357cc797269c867587857c58aa81b60 100644 (file)
@@ -274,8 +274,9 @@ performed.
 
 *   `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.
+     In the absence of an alternate stat collection backend, `ENABLE_PMF` must
+     be enabled. If `ENABLE_PMF` is set, the residency statistics are tracked in
+     software.
 
 *   `ENABLE_RUNTIME_INSTRUMENTATION`: Boolean option to enable runtime
     instrumentation which injects timestamp collection points into
index f904292b04088a85f59064bc7c42dbd4c3dbcc47..73bb6431fb8b2a8e98d1ac2616070ebb978a61bb 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2013-2016, ARM Limited and Contributors. All rights reserved.
+ * Copyright (c) 2013-2017, 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:
@@ -254,6 +254,11 @@ const unsigned char *plat_get_power_domain_tree_desc(void);
 /*******************************************************************************
  * Optional PSCI functions (BL31).
  ******************************************************************************/
+void plat_psci_stat_accounting_start(const psci_power_state_t *state_info);
+void plat_psci_stat_accounting_stop(const psci_power_state_t *state_info);
+u_register_t plat_psci_stat_get_residency(unsigned int lvl,
+                       const psci_power_state_t *state_info,
+                       int last_cpu_index);
 plat_local_state_t plat_get_target_pwr_state(unsigned int lvl,
                        const plat_local_state_t *states,
                        unsigned int ncpu);
index 68cdd6eb12f7cb34c4dcaf92804c66bc5fe17c9b..822329e654ac6d49314ff5b4ab7ea516d7ae066c 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2013-2016, ARM Limited and Contributors. All rights reserved.
+ * Copyright (c) 2013-2017, 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:
@@ -760,13 +760,7 @@ void psci_warmboot_entrypoint(void)
                                      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);
+       plat_psci_stat_accounting_stop(&state_info);
 #endif
 
        psci_get_target_local_pwr_states(end_pwrlvl, &state_info);
@@ -801,7 +795,7 @@ void psci_warmboot_entrypoint(void)
         * 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);
+       psci_stats_update_pwr_up(end_pwrlvl, &state_info);
 #endif
 
        /*
index 0a3a60ace0541967fbad0627ec04fb830b76c7b3..5e166b52dd0c5165a15bf1cb19b264674d356c82 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2013-2016, ARM Limited and Contributors. All rights reserved.
+ * Copyright (c) 2013-2017, 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:
@@ -117,13 +117,7 @@ int psci_cpu_suspend(unsigned int power_state,
                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);
+               plat_psci_stat_accounting_start(&state_info);
 #endif
 
 #if ENABLE_RUNTIME_INSTRUMENTATION
@@ -144,13 +138,10 @@ int psci_cpu_suspend(unsigned int power_state,
 #endif
 
 #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);
+               plat_psci_stat_accounting_stop(&state_info);
 
                /* Update PSCI stats */
-               psci_stats_update_pwr_up(PSCI_CPU_PWR_LVL, &state_info,
-                       PMF_NO_CACHE_MAINT);
+               psci_stats_update_pwr_up(PSCI_CPU_PWR_LVL, &state_info);
 #endif
 
                return PSCI_E_SUCCESS;
index 897bf319af12eba21a3b2653bbaddd38c8d9d02b..394aaa3b155ec69ec560a3a5b5a3b1db155beefd 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2013-2016, ARM Limited and Contributors. All rights reserved.
+ * Copyright (c) 2013-2017, 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:
@@ -137,13 +137,7 @@ 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);
+       plat_psci_stat_accounting_start(&state_info);
 #endif
 
 exit:
index 781b3b5265c946dd0adee33a0a2140998acf5e67..ca8291e482004ff741816ce873bbb77aca7e835d 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2013-2016, ARM Limited and Contributors. All rights reserved.
+ * Copyright (c) 2013-2017, 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:
@@ -35,7 +35,6 @@
 #include <bakery_lock.h>
 #include <bl_common.h>
 #include <cpu_data.h>
-#include <pmf.h>
 #include <psci.h>
 #include <spinlock.h>
 
 #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
@@ -246,8 +236,7 @@ void __dead2 psci_system_reset(void);
 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);
+                       const psci_power_state_t *state_info);
 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,
index ecbe592b8c5b0d833cf5cdeb9b18c0ab54c1599f..d8034a5d7eeff112112370b7d603de4b8c7531bc 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2016, ARM Limited and Contributors. All rights reserved.
+ * Copyright (c) 2016-2017, 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:
@@ -38,9 +38,6 @@
 #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;
@@ -62,27 +59,6 @@ static psci_stat_t psci_cpu_stat[PLATFORM_CORE_COUNT]
 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
@@ -150,44 +126,23 @@ void psci_stats_update_pwr_down(unsigned int end_pwrlvl,
  * 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)
+                       const psci_power_state_t *state_info)
 {
        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);
+       /* Call into platform interface to calculate residency. */
+       residency = plat_psci_stat_get_residency(PSCI_CPU_PWR_LVL,
+           state_info, cpu_idx);
 
        /* Update CPU stats. */
        psci_cpu_stat[cpu_idx][stat_idx].residency += residency;
@@ -207,10 +162,9 @@ void psci_stats_update_pwr_up(unsigned int end_pwrlvl,
 
                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);
+               /* Call into platform interface to calculate residency. */
+               residency = plat_psci_stat_get_residency(lvl, state_info,
+                   last_cpu_in_non_cpu_pd[parent_idx]);
 
                /* Initialize back to reset value */
                last_cpu_in_non_cpu_pd[parent_idx] = -1;
@@ -218,9 +172,6 @@ void psci_stats_update_pwr_up(unsigned int end_pwrlvl,
                /* 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++;
index dc2ab7748b54f74f3e2b6fdc03c5989529d4d0dd..5c8c3e26a8feca6d720e97e555147a0f0aebabc4 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2013-2016, ARM Limited and Contributors. All rights reserved.
+ * Copyright (c) 2013-2017, 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:
@@ -211,13 +211,7 @@ 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);
+       plat_psci_stat_accounting_start(state_info);
 #endif
 
 exit:
index c2f28f983654f563351885bc68f87f35e29c1f51..4628a43d07042f11bcdec24415b23d2f6661a20b 100644 (file)
@@ -1,5 +1,5 @@
 #
-# Copyright (c) 2015-2016, ARM Limited and Contributors. All rights reserved.
+# Copyright (c) 2015-2017, 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:
@@ -92,6 +92,7 @@ $(eval $(call add_define,ARM_BL31_IN_DRAM))
 
 # Enable PSCI_STAT_COUNT/RESIDENCY APIs on ARM platforms
 ENABLE_PSCI_STAT               :=      1
+ENABLE_PMF                     :=      1
 
 # On ARM platforms, separate the code and read-only data sections to allow
 # mapping the former as executable and the latter as execute-never.
index 3eb6886edb56229deda335fd71ff09d9a4b9d55a..0e00faaf5e803f15c5d72a5f1a6f54c96281ce47 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2016, ARM Limited and Contributors. All rights reserved.
+ * Copyright (c) 2016-2017, 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:
 #include <arch.h>
 #include <assert.h>
 #include <platform.h>
+#include <pmf.h>
 #include <psci.h>
 
+#if ENABLE_PSCI_STAT && ENABLE_PMF
+#pragma weak plat_psci_stat_accounting_start
+#pragma weak plat_psci_stat_accounting_stop
+#pragma weak plat_psci_stat_get_residency
+
+/* Ticks elapsed in one second by a signal of 1 MHz */
+#define MHZ_TICKS_PER_SEC 1000000
+
+/* 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
+
+PMF_REGISTER_SERVICE(psci_svc, PMF_PSCI_STAT_SVC_ID, PSCI_STAT_TOTAL_IDS,
+       PMF_STORE_ENABLE)
+
+/*
+ * This function calculates the stats residency in microseconds,
+ * taking in account the wrap around condition.
+ */
+static u_register_t calc_stat_residency(unsigned long long pwrupts,
+       unsigned long long pwrdnts)
+{
+       /* The divisor to use to convert raw timestamp into microseconds. */
+       u_register_t residency_div;
+       u_register_t res;
+
+       /*
+        * 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);
+
+       if (pwrupts < pwrdnts)
+               res = UINT64_MAX - pwrdnts + pwrupts;
+       else
+               res = pwrupts - pwrdnts;
+
+       return res / residency_div;
+}
+
+/*
+ * Capture timestamp before entering a low power state.
+ * No cache maintenance is required when capturing the timestamp.
+ * Cache maintenance may be needed when reading these timestamps.
+ */
+void plat_psci_stat_accounting_start(
+       __unused const psci_power_state_t *state_info)
+{
+       assert(state_info);
+       PMF_CAPTURE_TIMESTAMP(psci_svc, PSCI_STAT_ID_ENTER_LOW_PWR,
+               PMF_NO_CACHE_MAINT);
+}
+
+/*
+ * Capture timestamp after exiting a low power state.
+ * No cache maintenance is required when capturing the timestamp.
+ * Cache maintenance may be needed when reading these timestamps.
+ */
+void plat_psci_stat_accounting_stop(
+       __unused const psci_power_state_t *state_info)
+{
+       assert(state_info);
+       PMF_CAPTURE_TIMESTAMP(psci_svc, PSCI_STAT_ID_EXIT_LOW_PWR,
+               PMF_NO_CACHE_MAINT);
+}
+
+/*
+ * Calculate the residency for the given level and power state
+ * information.
+ */
+u_register_t plat_psci_stat_get_residency(unsigned int lvl,
+       const psci_power_state_t *state_info,
+       int last_cpu_idx)
+{
+       plat_local_state_t state;
+       unsigned long long pwrup_ts = 0, pwrdn_ts = 0;
+       unsigned int pmf_flags;
+
+       assert(lvl >= PSCI_CPU_PWR_LVL && lvl <= PLAT_MAX_PWR_LVL);
+       assert(state_info);
+       assert(last_cpu_idx >= 0 && last_cpu_idx <= PLATFORM_CORE_COUNT);
+
+       if (lvl == PSCI_CPU_PWR_LVL)
+               assert(last_cpu_idx == plat_my_core_pos());
+
+       /*
+        * If power down is requested, then timestamp capture will
+        * be with caches OFF.  Hence we have to do cache maintenance
+        * when reading the timestamp.
+        */
+       state = state_info->pwr_domain_state[PSCI_CPU_PWR_LVL];
+       if (is_local_state_off(state)) {
+               pmf_flags = PMF_CACHE_MAINT;
+       } else {
+               assert(is_local_state_retn(state));
+               pmf_flags = PMF_NO_CACHE_MAINT;
+       }
+
+       PMF_GET_TIMESTAMP_BY_INDEX(psci_svc,
+               PSCI_STAT_ID_ENTER_LOW_PWR,
+               last_cpu_idx,
+               pmf_flags,
+               pwrdn_ts);
+
+       PMF_GET_TIMESTAMP_BY_INDEX(psci_svc,
+               PSCI_STAT_ID_EXIT_LOW_PWR,
+               plat_my_core_pos(),
+               pmf_flags,
+               pwrup_ts);
+
+       return calc_stat_residency(pwrup_ts, pwrdn_ts);
+}
+#endif /* ENABLE_PSCI_STAT && ENABLE_PMF */
+
 /*
  * The PSCI generic code uses this API to let the platform participate in state
  * coordination during a power management operation. It compares the platform