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