xref: /rk3399_ARM-atf/lib/pmf/pmf_main.c (revision a31d8983f42153b0448103bdd47e1f4c9c093765)
1 /*
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  ******************************************************************************/
48 extern uintptr_t __PMF_SVC_DESCS_START__;
49 extern 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__))
52 extern void *__PERCPU_TIMESTAMP_SIZE__;
53 #define PMF_PERCPU_TIMESTAMP_SIZE	((uintptr_t)&__PERCPU_TIMESTAMP_SIZE__)
54 extern uintptr_t __PMF_TIMESTAMP_START__;
55 #define PMF_TIMESTAMP_ARRAY_START	((uintptr_t)&__PMF_TIMESTAMP_START__)
56 extern 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  */
64 static pmf_svc_desc_t *pmf_svc_descs;
65 
66 /*
67  * This array is used to store registered PMF services in sorted order.
68  */
69 static 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  */
74 static 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  */
80 int 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  */
142 static 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  */
181 int 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  */
206 void __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  */
216 static 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  */
238 void __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  */
252 void __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  */
267 unsigned 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 }
281