1 /* 2 * Copyright (c) 2016-2017, ARM Limited and Contributors. All rights reserved. 3 * 4 * Redistribution and use in source and binary forms, with or without 5 * modification, are permitted provided that the following conditions are met: 6 * 7 * Redistributions of source code must retain the above copyright notice, this 8 * list of conditions and the following disclaimer. 9 * 10 * Redistributions in binary form must reproduce the above copyright notice, 11 * this list of conditions and the following disclaimer in the documentation 12 * and/or other materials provided with the distribution. 13 * 14 * Neither the name of ARM nor the names of its contributors may be used 15 * to endorse or promote products derived from this software without specific 16 * prior written permission. 17 * 18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 19 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 20 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 21 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 22 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 23 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 24 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 25 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 26 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 27 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 28 * POSSIBILITY OF SUCH DAMAGE. 29 */ 30 31 #include <arch.h> 32 #include <assert.h> 33 #include <platform.h> 34 #include <pmf.h> 35 #include <psci.h> 36 37 #if ENABLE_PSCI_STAT && ENABLE_PMF 38 #pragma weak plat_psci_stat_accounting_start 39 #pragma weak plat_psci_stat_accounting_stop 40 #pragma weak plat_psci_stat_get_residency 41 42 /* Ticks elapsed in one second by a signal of 1 MHz */ 43 #define MHZ_TICKS_PER_SEC 1000000 44 45 /* Following are used as ID's to capture time-stamp */ 46 #define PSCI_STAT_ID_ENTER_LOW_PWR 0 47 #define PSCI_STAT_ID_EXIT_LOW_PWR 1 48 #define PSCI_STAT_TOTAL_IDS 2 49 50 PMF_REGISTER_SERVICE(psci_svc, PMF_PSCI_STAT_SVC_ID, PSCI_STAT_TOTAL_IDS, 51 PMF_STORE_ENABLE) 52 53 /* 54 * This function calculates the stats residency in microseconds, 55 * taking in account the wrap around condition. 56 */ 57 static u_register_t calc_stat_residency(unsigned long long pwrupts, 58 unsigned long long pwrdnts) 59 { 60 /* The divisor to use to convert raw timestamp into microseconds. */ 61 u_register_t residency_div; 62 u_register_t res; 63 64 /* 65 * Calculate divisor so that it can be directly used to 66 * convert time-stamp into microseconds. 67 */ 68 residency_div = read_cntfrq_el0() / MHZ_TICKS_PER_SEC; 69 assert(residency_div); 70 71 if (pwrupts < pwrdnts) 72 res = UINT64_MAX - pwrdnts + pwrupts; 73 else 74 res = pwrupts - pwrdnts; 75 76 return res / residency_div; 77 } 78 79 /* 80 * Capture timestamp before entering a low power state. 81 * No cache maintenance is required when capturing the timestamp. 82 * Cache maintenance may be needed when reading these timestamps. 83 */ 84 void plat_psci_stat_accounting_start( 85 __unused const psci_power_state_t *state_info) 86 { 87 assert(state_info); 88 PMF_CAPTURE_TIMESTAMP(psci_svc, PSCI_STAT_ID_ENTER_LOW_PWR, 89 PMF_NO_CACHE_MAINT); 90 } 91 92 /* 93 * Capture timestamp after exiting a low power state. 94 * No cache maintenance is required when capturing the timestamp. 95 * Cache maintenance may be needed when reading these timestamps. 96 */ 97 void plat_psci_stat_accounting_stop( 98 __unused const psci_power_state_t *state_info) 99 { 100 assert(state_info); 101 PMF_CAPTURE_TIMESTAMP(psci_svc, PSCI_STAT_ID_EXIT_LOW_PWR, 102 PMF_NO_CACHE_MAINT); 103 } 104 105 /* 106 * Calculate the residency for the given level and power state 107 * information. 108 */ 109 u_register_t plat_psci_stat_get_residency(unsigned int lvl, 110 const psci_power_state_t *state_info, 111 int last_cpu_idx) 112 { 113 plat_local_state_t state; 114 unsigned long long pwrup_ts = 0, pwrdn_ts = 0; 115 unsigned int pmf_flags; 116 117 assert(lvl >= PSCI_CPU_PWR_LVL && lvl <= PLAT_MAX_PWR_LVL); 118 assert(state_info); 119 assert(last_cpu_idx >= 0 && last_cpu_idx <= PLATFORM_CORE_COUNT); 120 121 if (lvl == PSCI_CPU_PWR_LVL) 122 assert(last_cpu_idx == plat_my_core_pos()); 123 124 /* 125 * If power down is requested, then timestamp capture will 126 * be with caches OFF. Hence we have to do cache maintenance 127 * when reading the timestamp. 128 */ 129 state = state_info->pwr_domain_state[PSCI_CPU_PWR_LVL]; 130 if (is_local_state_off(state)) { 131 pmf_flags = PMF_CACHE_MAINT; 132 } else { 133 assert(is_local_state_retn(state)); 134 pmf_flags = PMF_NO_CACHE_MAINT; 135 } 136 137 PMF_GET_TIMESTAMP_BY_INDEX(psci_svc, 138 PSCI_STAT_ID_ENTER_LOW_PWR, 139 last_cpu_idx, 140 pmf_flags, 141 pwrdn_ts); 142 143 PMF_GET_TIMESTAMP_BY_INDEX(psci_svc, 144 PSCI_STAT_ID_EXIT_LOW_PWR, 145 plat_my_core_pos(), 146 pmf_flags, 147 pwrup_ts); 148 149 return calc_stat_residency(pwrup_ts, pwrdn_ts); 150 } 151 #endif /* ENABLE_PSCI_STAT && ENABLE_PMF */ 152 153 /* 154 * The PSCI generic code uses this API to let the platform participate in state 155 * coordination during a power management operation. It compares the platform 156 * specific local power states requested by each cpu for a given power domain 157 * and returns the coordinated target power state that the domain should 158 * enter. A platform assigns a number to a local power state. This default 159 * implementation assumes that the platform assigns these numbers in order of 160 * increasing depth of the power state i.e. for two power states X & Y, if X < Y 161 * then X represents a shallower power state than Y. As a result, the 162 * coordinated target local power state for a power domain will be the minimum 163 * of the requested local power states. 164 */ 165 plat_local_state_t plat_get_target_pwr_state(unsigned int lvl, 166 const plat_local_state_t *states, 167 unsigned int ncpu) 168 { 169 plat_local_state_t target = PLAT_MAX_OFF_STATE, temp; 170 171 assert(ncpu); 172 173 do { 174 temp = *states++; 175 if (temp < target) 176 target = temp; 177 } while (--ncpu); 178 179 return target; 180 } 181