blob: 25513c1915ff75114762c5462232c8af4f6ced71 [file] [log] [blame]
Yatharth Kochar9518d022016-03-11 14:20:19 +00001/*
Joel Hutton5cc3bc82018-03-21 11:40:57 +00002 * Copyright (c) 2016-2018, ARM Limited and Contributors. All rights reserved.
Yatharth Kochar9518d022016-03-11 14:20:19 +00003 *
dp-armfa3cf0b2017-05-03 09:38:09 +01004 * SPDX-License-Identifier: BSD-3-Clause
Yatharth Kochar9518d022016-03-11 14:20:19 +00005 */
6#include <arch.h>
7#include <arch_helpers.h>
8#include <assert.h>
9#include <debug.h>
10#include <errno.h>
11#include <platform.h>
12#include <pmf.h>
13#include <string.h>
Joel Hutton5cc3bc82018-03-21 11:40:57 +000014#include <utils_def.h>
Yatharth Kochar9518d022016-03-11 14:20:19 +000015
16/*******************************************************************************
17 * The 'pmf_svc_descs' array holds the PMF service descriptors exported by
18 * services by placing them in the 'pmf_svc_descs' linker section.
19 * The 'pmf_svc_descs_indices' array holds the index of a descriptor in the
20 * 'pmf_svc_descs' array. The TIF[15:10] bits in the time-stamp id are used
21 * to get an index into the 'pmf_svc_descs_indices' array. This gives the
22 * index of the descriptor in the 'pmf_svc_descs' array which contains the
23 * service function pointers.
24 ******************************************************************************/
Joel Hutton5cc3bc82018-03-21 11:40:57 +000025
26IMPORT_SYM(uintptr_t, __PMF_SVC_DESCS_START__, PMF_SVC_DESCS_START);
27IMPORT_SYM(uintptr_t, __PMF_SVC_DESCS_END__, PMF_SVC_DESCS_END);
Soby Mathew4e28c202018-10-14 08:09:22 +010028IMPORT_SYM(uintptr_t, __PMF_PERCPU_TIMESTAMP_END__, PMF_PERCPU_TIMESTAMP_END);
Joel Hutton5cc3bc82018-03-21 11:40:57 +000029IMPORT_SYM(intptr_t, __PMF_TIMESTAMP_START__, PMF_TIMESTAMP_ARRAY_START);
Soby Mathew4e28c202018-10-14 08:09:22 +010030
31#define PMF_PERCPU_TIMESTAMP_SIZE (PMF_PERCPU_TIMESTAMP_END - PMF_TIMESTAMP_ARRAY_START)
Yatharth Kochar9518d022016-03-11 14:20:19 +000032
33#define PMF_SVC_DESCS_MAX 10
34
35/*
36 * This is used to traverse through registered PMF services.
37 */
38static pmf_svc_desc_t *pmf_svc_descs;
39
40/*
41 * This array is used to store registered PMF services in sorted order.
42 */
43static int pmf_svc_descs_indices[PMF_SVC_DESCS_MAX];
44
45/*
46 * This is used to track total number of successfully registered PMF services.
47 */
48static int pmf_num_services;
49
50/*
51 * This is the main PMF function that initialize registered
52 * PMF services and also sort them in ascending order.
53 */
54int pmf_setup(void)
55{
56 int rc, ii, jj = 0;
57 int pmf_svc_descs_num, temp_val;
58
59 /* If no PMF services are registered then simply bail out */
60 pmf_svc_descs_num = (PMF_SVC_DESCS_END - PMF_SVC_DESCS_START)/
61 sizeof(pmf_svc_desc_t);
62 if (pmf_svc_descs_num == 0)
63 return 0;
64
65 assert(pmf_svc_descs_num < PMF_SVC_DESCS_MAX);
66
67 pmf_svc_descs = (pmf_svc_desc_t *) PMF_SVC_DESCS_START;
68 for (ii = 0; ii < pmf_svc_descs_num; ii++) {
69
70 assert(pmf_svc_descs[ii].get_ts);
71
72 /*
73 * Call the initialization routine for this
74 * PMF service, if it is defined.
75 */
76 if (pmf_svc_descs[ii].init) {
77 rc = pmf_svc_descs[ii].init();
78 if (rc) {
79 WARN("Could not initialize PMF"
80 "service %s - skipping \n",
81 pmf_svc_descs[ii].name);
82 continue;
83 }
84 }
85
86 /* Update the pmf_svc_descs_indices array */
87 pmf_svc_descs_indices[jj++] = ii;
88 }
89
90 pmf_num_services = jj;
91
92 /*
93 * Sort the successfully registered PMF services
94 * according to service ID
95 */
96 for (ii = 1; ii < pmf_num_services; ii++) {
97 for (jj = 0; jj < (pmf_num_services - ii); jj++) {
98 if ((pmf_svc_descs[jj].svc_config & PMF_SVC_ID_MASK) >
99 (pmf_svc_descs[jj + 1].svc_config &
100 PMF_SVC_ID_MASK)) {
101 temp_val = pmf_svc_descs_indices[jj];
102 pmf_svc_descs_indices[jj] =
103 pmf_svc_descs_indices[jj+1];
104 pmf_svc_descs_indices[jj+1] = temp_val;
105 }
106 }
107 }
108
109 return 0;
110}
111
112/*
113 * This function implements binary search to find registered
114 * PMF service based on Service ID provided in `tid` argument.
115 */
116static pmf_svc_desc_t *get_service(unsigned int tid)
117{
118 int low = 0;
119 int mid;
120 int high = pmf_num_services;
121 unsigned int svc_id = tid & PMF_SVC_ID_MASK;
122 int index;
123 unsigned int desc_svc_id;
124
125 if (pmf_num_services == 0)
126 return NULL;
127
128 assert(pmf_svc_descs);
129
130 do {
131 mid = (low + high) / 2;
132 index = pmf_svc_descs_indices[mid];
133
134 desc_svc_id = pmf_svc_descs[index].svc_config & PMF_SVC_ID_MASK;
135 if (svc_id < desc_svc_id)
136 high = mid - 1;
137 if (svc_id > desc_svc_id)
138 low = mid + 1;
139 } while ((svc_id != desc_svc_id) && (low <= high));
140
141 /*
142 * Make sure the Service found supports the tid range.
143 */
144 if ((svc_id == desc_svc_id) && ((tid & PMF_TID_MASK) <
145 (pmf_svc_descs[index].svc_config & PMF_TID_MASK)))
146 return (pmf_svc_desc_t *)&pmf_svc_descs[index];
147
148 return NULL;
149}
150
151/*
152 * This function gets the time-stamp value for the PMF services
153 * registered for SMC interface based on `tid` and `mpidr`.
154 */
155int pmf_get_timestamp_smc(unsigned int tid,
156 u_register_t mpidr,
157 unsigned int flags,
158 unsigned long long *ts_value)
159{
160 pmf_svc_desc_t *svc_desc;
161 assert(ts_value);
162
163 /* Search for registered service. */
164 svc_desc = get_service(tid);
165
166 if ((svc_desc == NULL) || (plat_core_pos_by_mpidr(mpidr) < 0)) {
167 *ts_value = 0;
168 return -EINVAL;
169 } else {
170 /* Call the service time-stamp handler. */
171 *ts_value = svc_desc->get_ts(tid, mpidr, flags);
172 return 0;
173 }
174}
175
176/*
177 * This function can be used to dump `ts` value for given `tid`.
178 * Assumption is that the console is already initialized.
179 */
180void __pmf_dump_timestamp(unsigned int tid, unsigned long long ts)
181{
Antonio Nino Diaz00086e32018-08-16 16:46:06 +0100182 printf("PMF:cpu %u tid %u ts %llu\n",
Yatharth Kochar9518d022016-03-11 14:20:19 +0000183 plat_my_core_pos(), tid, ts);
184}
185
186/*
187 * This function calculate the address identified by
188 * `base_addr`, `tid` and `cpuid`.
189 */
190static inline uintptr_t calc_ts_addr(uintptr_t base_addr,
191 unsigned int tid,
192 unsigned int cpuid)
193{
194 assert(cpuid < PLATFORM_CORE_COUNT);
195 assert(base_addr >= PMF_TIMESTAMP_ARRAY_START);
196 assert(base_addr < ((PMF_TIMESTAMP_ARRAY_START +
197 PMF_PERCPU_TIMESTAMP_SIZE) - ((tid & PMF_TID_MASK) *
198 sizeof(unsigned long long))));
199
200 base_addr += ((cpuid * PMF_PERCPU_TIMESTAMP_SIZE) +
201 ((tid & PMF_TID_MASK) * sizeof(unsigned long long)));
202
203 return base_addr;
204}
205
206/*
207 * This function stores the `ts` value to the storage identified by
208 * `base_addr`, `tid` and current cpu id.
209 * Note: The timestamp addresses are cache line aligned per cpu
210 * and only the owning CPU would ever write into it.
211 */
212void __pmf_store_timestamp(uintptr_t base_addr,
213 unsigned int tid,
214 unsigned long long ts)
215{
216 unsigned long long *ts_addr = (unsigned long long *)calc_ts_addr(base_addr,
217 tid, plat_my_core_pos());
218 *ts_addr = ts;
219}
220
221/*
222 * This is the cached version of `pmf_store_my_timestamp`
223 * Note: The timestamp addresses are cache line aligned per cpu
224 * and only the owning CPU would ever write into it.
225 */
226void __pmf_store_timestamp_with_cache_maint(uintptr_t base_addr,
227 unsigned int tid,
228 unsigned long long ts)
229{
230 unsigned long long *ts_addr = (unsigned long long *)calc_ts_addr(base_addr,
231 tid, plat_my_core_pos());
232 *ts_addr = ts;
233 flush_dcache_range((uintptr_t)ts_addr, sizeof(unsigned long long));
234}
235
236/*
237 * This function retrieves the `ts` value from the storage identified by
238 * `base_addr`, `tid` and `cpuid`.
239 * Note: The timestamp addresses are cache line aligned per cpu.
240 */
241unsigned long long __pmf_get_timestamp(uintptr_t base_addr,
242 unsigned int tid,
243 unsigned int cpuid,
244 unsigned int flags)
245{
246 assert(cpuid < PLATFORM_CORE_COUNT);
247 unsigned long long *ts_addr = (unsigned long long *)calc_ts_addr(base_addr,
248 tid, cpuid);
249
250 if (flags & PMF_CACHE_MAINT)
251 inv_dcache_range((uintptr_t)ts_addr, sizeof(unsigned long long));
252
253 return *ts_addr;
254}