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