blob: a756d5e403aafb155765cd5550ce365f42c990d8 [file] [log] [blame]
Soby Mathew0d786072016-03-24 16:56:29 +00001/*
Antonio Nino Diaz6f3ccc52018-07-20 09:17:26 +01002 * Copyright (c) 2016-2018, ARM Limited and Contributors. All rights reserved.
Soby Mathew0d786072016-03-24 16:56:29 +00003 *
dp-armfa3cf0b2017-05-03 09:38:09 +01004 * SPDX-License-Identifier: BSD-3-Clause
Soby Mathew0d786072016-03-24 16:56:29 +00005 */
6
Soby Mathew0d786072016-03-24 16:56:29 +00007#include <assert.h>
Antonio Nino Diaze0f90632018-12-14 00:18:21 +00008
9#include <arch.h>
10#include <lib/pmf/pmf.h>
11#include <lib/psci/psci.h>
12#include <plat/common/platform.h>
Soby Mathew0d786072016-03-24 16:56:29 +000013
dp-arm66abfbe2017-01-31 13:01:04 +000014#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 */
Antonio Nino Diaz6f3ccc52018-07-20 09:17:26 +010020#define MHZ_TICKS_PER_SEC 1000000U
dp-arm66abfbe2017-01-31 13:01:04 +000021
Soby Mathew8336f682017-10-16 15:19:31 +010022/* Maximum time-stamp value read from architectural counters */
Julius Werner8e0ef0f2019-07-09 14:02:43 -070023#ifdef __aarch64__
Soby Mathew8336f682017-10-16 15:19:31 +010024#define MAX_TS UINT64_MAX
Julius Werner8e0ef0f2019-07-09 14:02:43 -070025#else
26#define MAX_TS UINT32_MAX
Soby Mathew8336f682017-10-16 15:19:31 +010027#endif
28
dp-arm66abfbe2017-01-31 13:01:04 +000029/* 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
Madhukar Pappireddya091d042020-01-06 14:42:30 -060034PMF_DECLARE_CAPTURE_TIMESTAMP(psci_svc)
35PMF_DECLARE_GET_TIMESTAMP(psci_svc)
dp-arm66abfbe2017-01-31 13:01:04 +000036PMF_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 */
43static 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;
Antonio Nino Diaz6f3ccc52018-07-20 09:17:26 +010055 assert(residency_div > 0U);
dp-arm66abfbe2017-01-31 13:01:04 +000056
57 if (pwrupts < pwrdnts)
Soby Mathew8336f682017-10-16 15:19:31 +010058 res = MAX_TS - pwrdnts + pwrupts;
dp-arm66abfbe2017-01-31 13:01:04 +000059 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 */
70void plat_psci_stat_accounting_start(
71 __unused const psci_power_state_t *state_info)
72{
Antonio Nino Diazfec756f2018-07-18 16:24:16 +010073 assert(state_info != NULL);
dp-arm66abfbe2017-01-31 13:01:04 +000074 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 */
83void plat_psci_stat_accounting_stop(
84 __unused const psci_power_state_t *state_info)
85{
Antonio Nino Diazfec756f2018-07-18 16:24:16 +010086 assert(state_info != NULL);
dp-arm66abfbe2017-01-31 13:01:04 +000087 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 */
95u_register_t plat_psci_stat_get_residency(unsigned int lvl,
96 const psci_power_state_t *state_info,
97 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
Antonio Nino Diazfec756f2018-07-18 16:24:16 +0100103 assert((lvl >= PSCI_CPU_PWR_LVL) && (lvl <= PLAT_MAX_PWR_LVL));
104 assert(state_info != NULL);
105 assert(last_cpu_idx <= PLATFORM_CORE_COUNT);
dp-arm66abfbe2017-01-31 13:01:04 +0000106
107 if (lvl == PSCI_CPU_PWR_LVL)
Antonio Nino Diazfec756f2018-07-18 16:24:16 +0100108 assert((unsigned int)last_cpu_idx == plat_my_core_pos());
dp-arm66abfbe2017-01-31 13:01:04 +0000109
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];
Antonio Nino Diazfec756f2018-07-18 16:24:16 +0100116 if (is_local_state_off(state) != 0) {
dp-arm66abfbe2017-01-31 13:01:04 +0000117 pmf_flags = PMF_CACHE_MAINT;
118 } else {
Antonio Nino Diazfec756f2018-07-18 16:24:16 +0100119 assert(is_local_state_retn(state) == 1);
dp-arm66abfbe2017-01-31 13:01:04 +0000120 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
Soby Mathew0d786072016-03-24 16:56:29 +0000139/*
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 */
151plat_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;
Antonio Nino Diazfec756f2018-07-18 16:24:16 +0100156 const plat_local_state_t *st = states;
157 unsigned int n = ncpu;
Soby Mathew0d786072016-03-24 16:56:29 +0000158
Antonio Nino Diazfec756f2018-07-18 16:24:16 +0100159 assert(ncpu > 0U);
Soby Mathew0d786072016-03-24 16:56:29 +0000160
161 do {
Antonio Nino Diazfec756f2018-07-18 16:24:16 +0100162 temp = *st;
163 st++;
Soby Mathew0d786072016-03-24 16:56:29 +0000164 if (temp < target)
165 target = temp;
Antonio Nino Diazfec756f2018-07-18 16:24:16 +0100166 n--;
167 } while (n > 0U);
Soby Mathew0d786072016-03-24 16:56:29 +0000168
169 return target;
170}