xref: /rk3399_ARM-atf/lib/pmf/pmf_main.c (revision cf0886e2f11481f55898f745d288c8705e704f53)
1 /*
2  * Copyright (c) 2016-2018, ARM Limited and Contributors. All rights reserved.
3  *
4  * SPDX-License-Identifier: BSD-3-Clause
5  */
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>
14 #include <utils_def.h>
15 
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  ******************************************************************************/
25 
26 IMPORT_SYM(uintptr_t, __PMF_SVC_DESCS_START__,		PMF_SVC_DESCS_START);
27 IMPORT_SYM(uintptr_t, __PMF_SVC_DESCS_END__,		PMF_SVC_DESCS_END);
28 IMPORT_SYM(uintptr_t, __PMF_PERCPU_TIMESTAMP_END__,	PMF_PERCPU_TIMESTAMP_END);
29 IMPORT_SYM(intptr_t,  __PMF_TIMESTAMP_START__,		PMF_TIMESTAMP_ARRAY_START);
30 
31 #define PMF_PERCPU_TIMESTAMP_SIZE	(PMF_PERCPU_TIMESTAMP_END - PMF_TIMESTAMP_ARRAY_START)
32 
33 #define PMF_SVC_DESCS_MAX		10
34 
35 /*
36  * This is used to traverse through registered PMF services.
37  */
38 static pmf_svc_desc_t *pmf_svc_descs;
39 
40 /*
41  * This array is used to store registered PMF services in sorted order.
42  */
43 static 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  */
48 static 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  */
54 int 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  */
116 static 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  */
155 int 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  */
180 void __pmf_dump_timestamp(unsigned int tid, unsigned long long ts)
181 {
182 	printf("PMF:cpu %u	tid %u	ts %llu\n",
183 		plat_my_core_pos(), tid, ts);
184 }
185 
186 /*
187  * This function calculate the address identified by
188  * `base_addr`, `tid` and `cpuid`.
189  */
190 static 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  */
212 void __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  */
226 void __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  */
241 unsigned 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 }
255