1 /* 2 * Copyright (c) 2016-2019, ARM Limited and Contributors. All rights reserved. 3 * 4 * SPDX-License-Identifier: BSD-3-Clause 5 */ 6 7 #include <assert.h> 8 9 #include <arch.h> 10 #include <lib/pmf/pmf.h> 11 #include <lib/psci/psci.h> 12 #include <plat/common/platform.h> 13 14 #if ENABLE_PSCI_STAT && ENABLE_PMF 15 #pragma weak plat_psci_stat_accounting_start 16 #pragma weak plat_psci_stat_accounting_stop 17 #pragma weak plat_psci_stat_get_residency 18 19 /* Ticks elapsed in one second by a signal of 1 MHz */ 20 #define MHZ_TICKS_PER_SEC 1000000U 21 22 /* Maximum time-stamp value read from architectural counters */ 23 #ifdef __aarch64__ 24 #define MAX_TS UINT64_MAX 25 #else 26 #define MAX_TS UINT32_MAX 27 #endif 28 29 /* Following are used as ID's to capture time-stamp */ 30 #define PSCI_STAT_ID_ENTER_LOW_PWR 0 31 #define PSCI_STAT_ID_EXIT_LOW_PWR 1 32 #define PSCI_STAT_TOTAL_IDS 2 33 34 PMF_DECLARE_CAPTURE_TIMESTAMP(psci_svc) 35 PMF_DECLARE_GET_TIMESTAMP(psci_svc) 36 PMF_REGISTER_SERVICE(psci_svc, PMF_PSCI_STAT_SVC_ID, PSCI_STAT_TOTAL_IDS, 37 PMF_STORE_ENABLE) 38 39 /* 40 * This function calculates the stats residency in microseconds, 41 * taking in account the wrap around condition. 42 */ 43 static u_register_t calc_stat_residency(unsigned long long pwrupts, 44 unsigned long long pwrdnts) 45 { 46 /* The divisor to use to convert raw timestamp into microseconds. */ 47 u_register_t residency_div; 48 u_register_t res; 49 50 /* 51 * Calculate divisor so that it can be directly used to 52 * convert time-stamp into microseconds. 53 */ 54 residency_div = read_cntfrq_el0() / MHZ_TICKS_PER_SEC; 55 assert(residency_div > 0U); 56 57 if (pwrupts < pwrdnts) 58 res = MAX_TS - pwrdnts + pwrupts; 59 else 60 res = pwrupts - pwrdnts; 61 62 return res / residency_div; 63 } 64 65 /* 66 * Capture timestamp before entering a low power state. 67 * No cache maintenance is required when capturing the timestamp. 68 * Cache maintenance may be needed when reading these timestamps. 69 */ 70 void plat_psci_stat_accounting_start( 71 __unused const psci_power_state_t *state_info) 72 { 73 assert(state_info != NULL); 74 PMF_CAPTURE_TIMESTAMP(psci_svc, PSCI_STAT_ID_ENTER_LOW_PWR, 75 PMF_NO_CACHE_MAINT); 76 } 77 78 /* 79 * Capture timestamp after exiting a low power state. 80 * No cache maintenance is required when capturing the timestamp. 81 * Cache maintenance may be needed when reading these timestamps. 82 */ 83 void plat_psci_stat_accounting_stop( 84 __unused const psci_power_state_t *state_info) 85 { 86 assert(state_info != NULL); 87 PMF_CAPTURE_TIMESTAMP(psci_svc, PSCI_STAT_ID_EXIT_LOW_PWR, 88 PMF_NO_CACHE_MAINT); 89 } 90 91 /* 92 * Calculate the residency for the given level and power state 93 * information. 94 */ 95 u_register_t plat_psci_stat_get_residency(unsigned int lvl, 96 const psci_power_state_t *state_info, 97 unsigned int last_cpu_idx) 98 { 99 plat_local_state_t state; 100 unsigned long long pwrup_ts = 0, pwrdn_ts = 0; 101 unsigned int pmf_flags; 102 103 assert((lvl >= PSCI_CPU_PWR_LVL) && (lvl <= PLAT_MAX_PWR_LVL)); 104 assert(state_info != NULL); 105 assert(last_cpu_idx <= PLATFORM_CORE_COUNT); 106 107 if (lvl == PSCI_CPU_PWR_LVL) 108 assert(last_cpu_idx == plat_my_core_pos()); 109 110 /* 111 * If power down is requested, then timestamp capture will 112 * be with caches OFF. Hence we have to do cache maintenance 113 * when reading the timestamp. 114 */ 115 state = state_info->pwr_domain_state[PSCI_CPU_PWR_LVL]; 116 if (is_local_state_off(state) != 0) { 117 pmf_flags = PMF_CACHE_MAINT; 118 } else { 119 assert(is_local_state_retn(state) == 1); 120 pmf_flags = PMF_NO_CACHE_MAINT; 121 } 122 123 PMF_GET_TIMESTAMP_BY_INDEX(psci_svc, 124 PSCI_STAT_ID_ENTER_LOW_PWR, 125 last_cpu_idx, 126 pmf_flags, 127 pwrdn_ts); 128 129 PMF_GET_TIMESTAMP_BY_INDEX(psci_svc, 130 PSCI_STAT_ID_EXIT_LOW_PWR, 131 plat_my_core_pos(), 132 pmf_flags, 133 pwrup_ts); 134 135 return calc_stat_residency(pwrup_ts, pwrdn_ts); 136 } 137 #endif /* ENABLE_PSCI_STAT && ENABLE_PMF */ 138 139 /* 140 * The PSCI generic code uses this API to let the platform participate in state 141 * coordination during a power management operation. It compares the platform 142 * specific local power states requested by each cpu for a given power domain 143 * and returns the coordinated target power state that the domain should 144 * enter. A platform assigns a number to a local power state. This default 145 * implementation assumes that the platform assigns these numbers in order of 146 * increasing depth of the power state i.e. for two power states X & Y, if X < Y 147 * then X represents a shallower power state than Y. As a result, the 148 * coordinated target local power state for a power domain will be the minimum 149 * of the requested local power states. 150 */ 151 plat_local_state_t plat_get_target_pwr_state(unsigned int lvl, 152 const plat_local_state_t *states, 153 unsigned int ncpu) 154 { 155 plat_local_state_t target = PLAT_MAX_OFF_STATE, temp; 156 const plat_local_state_t *st = states; 157 unsigned int n = ncpu; 158 159 assert(ncpu > 0U); 160 161 do { 162 temp = *st; 163 st++; 164 if (temp < target) 165 target = temp; 166 n--; 167 } while (n > 0U); 168 169 return target; 170 } 171