1380559c1SDimitris Papastamos /* 2*f3ccf036SAlexei Fedorov * Copyright (c) 2017-2020, ARM Limited and Contributors. All rights reserved. 3380559c1SDimitris Papastamos * 4380559c1SDimitris Papastamos * SPDX-License-Identifier: BSD-3-Clause 5380559c1SDimitris Papastamos */ 6380559c1SDimitris Papastamos 709d40e0eSAntonio Nino Diaz #include <assert.h> 809d40e0eSAntonio Nino Diaz #include <stdbool.h> 909d40e0eSAntonio Nino Diaz 10380559c1SDimitris Papastamos #include <arch.h> 11380559c1SDimitris Papastamos #include <arch_helpers.h> 12*f3ccf036SAlexei Fedorov 1309d40e0eSAntonio Nino Diaz #include <lib/el3_runtime/pubsub_events.h> 1409d40e0eSAntonio Nino Diaz #include <lib/extensions/amu.h> 1509d40e0eSAntonio Nino Diaz #include <lib/extensions/amu_private.h> 16*f3ccf036SAlexei Fedorov 1709d40e0eSAntonio Nino Diaz #include <plat/common/platform.h> 18380559c1SDimitris Papastamos 19b6eb3932SDimitris Papastamos static struct amu_ctx amu_ctxs[PLATFORM_CORE_COUNT]; 20b6eb3932SDimitris Papastamos 21*f3ccf036SAlexei Fedorov /* Check if AMUv1 for Armv8.4 or 8.6 is implemented */ 2240daecc1SAntonio Nino Diaz bool amu_supported(void) 23380559c1SDimitris Papastamos { 24*f3ccf036SAlexei Fedorov uint64_t features = read_id_aa64pfr0_el1() >> ID_AA64PFR0_AMU_SHIFT; 25380559c1SDimitris Papastamos 26*f3ccf036SAlexei Fedorov features &= ID_AA64PFR0_AMU_MASK; 27*f3ccf036SAlexei Fedorov return ((features == 1U) || (features == 2U)); 280767d50eSDimitris Papastamos } 290767d50eSDimitris Papastamos 30*f3ccf036SAlexei Fedorov #if AMU_GROUP1_NR_COUNTERS 31*f3ccf036SAlexei Fedorov /* Check if group 1 counters is implemented */ 32*f3ccf036SAlexei Fedorov bool amu_group1_supported(void) 33*f3ccf036SAlexei Fedorov { 34*f3ccf036SAlexei Fedorov uint64_t features = read_amcfgr_el0() >> AMCFGR_EL0_NCG_SHIFT; 35*f3ccf036SAlexei Fedorov 36*f3ccf036SAlexei Fedorov return (features & AMCFGR_EL0_NCG_MASK) == 1U; 37*f3ccf036SAlexei Fedorov } 38*f3ccf036SAlexei Fedorov #endif 39*f3ccf036SAlexei Fedorov 400767d50eSDimitris Papastamos /* 410767d50eSDimitris Papastamos * Enable counters. This function is meant to be invoked 420767d50eSDimitris Papastamos * by the context management library before exiting from EL3. 430767d50eSDimitris Papastamos */ 4440daecc1SAntonio Nino Diaz void amu_enable(bool el2_unused) 450767d50eSDimitris Papastamos { 46380559c1SDimitris Papastamos uint64_t v; 47380559c1SDimitris Papastamos 48*f3ccf036SAlexei Fedorov if (!amu_supported()) { 49*f3ccf036SAlexei Fedorov INFO("AMU is not implemented\n"); 500767d50eSDimitris Papastamos return; 51*f3ccf036SAlexei Fedorov } 52*f3ccf036SAlexei Fedorov 53*f3ccf036SAlexei Fedorov #if AMU_GROUP1_NR_COUNTERS 54*f3ccf036SAlexei Fedorov /* Check and set presence of group 1 counters */ 55*f3ccf036SAlexei Fedorov if (!amu_group1_supported()) { 56*f3ccf036SAlexei Fedorov ERROR("AMU Counter Group 1 is not implemented\n"); 57*f3ccf036SAlexei Fedorov panic(); 58*f3ccf036SAlexei Fedorov } 59*f3ccf036SAlexei Fedorov 60*f3ccf036SAlexei Fedorov /* Check number of group 1 counters */ 61*f3ccf036SAlexei Fedorov uint64_t cnt_num = (read_amcgcr_el0() >> AMCGCR_EL0_CG1NC_SHIFT) & 62*f3ccf036SAlexei Fedorov AMCGCR_EL0_CG1NC_MASK; 63*f3ccf036SAlexei Fedorov VERBOSE("%s%llu. %s%u\n", 64*f3ccf036SAlexei Fedorov "Number of AMU Group 1 Counters ", cnt_num, 65*f3ccf036SAlexei Fedorov "Requested number ", AMU_GROUP1_NR_COUNTERS); 66*f3ccf036SAlexei Fedorov 67*f3ccf036SAlexei Fedorov if (cnt_num < AMU_GROUP1_NR_COUNTERS) { 68*f3ccf036SAlexei Fedorov ERROR("%s%llu is less than %s%u\n", 69*f3ccf036SAlexei Fedorov "Number of AMU Group 1 Counters ", cnt_num, 70*f3ccf036SAlexei Fedorov "Requested number ", AMU_GROUP1_NR_COUNTERS); 71*f3ccf036SAlexei Fedorov panic(); 72*f3ccf036SAlexei Fedorov } 73*f3ccf036SAlexei Fedorov #endif 740767d50eSDimitris Papastamos 75380559c1SDimitris Papastamos if (el2_unused) { 76380559c1SDimitris Papastamos /* 77380559c1SDimitris Papastamos * CPTR_EL2.TAM: Set to zero so any accesses to 78380559c1SDimitris Papastamos * the Activity Monitor registers do not trap to EL2. 79380559c1SDimitris Papastamos */ 80380559c1SDimitris Papastamos v = read_cptr_el2(); 81380559c1SDimitris Papastamos v &= ~CPTR_EL2_TAM_BIT; 82380559c1SDimitris Papastamos write_cptr_el2(v); 83380559c1SDimitris Papastamos } 84380559c1SDimitris Papastamos 85380559c1SDimitris Papastamos /* 86380559c1SDimitris Papastamos * CPTR_EL3.TAM: Set to zero so that any accesses to 87380559c1SDimitris Papastamos * the Activity Monitor registers do not trap to EL3. 88380559c1SDimitris Papastamos */ 89380559c1SDimitris Papastamos v = read_cptr_el3(); 90380559c1SDimitris Papastamos v &= ~TAM_BIT; 91380559c1SDimitris Papastamos write_cptr_el3(v); 92380559c1SDimitris Papastamos 93380559c1SDimitris Papastamos /* Enable group 0 counters */ 94380559c1SDimitris Papastamos write_amcntenset0_el0(AMU_GROUP0_COUNTERS_MASK); 95*f3ccf036SAlexei Fedorov 96*f3ccf036SAlexei Fedorov #if AMU_GROUP1_NR_COUNTERS 9759902b7cSDimitris Papastamos /* Enable group 1 counters */ 9859902b7cSDimitris Papastamos write_amcntenset1_el0(AMU_GROUP1_COUNTERS_MASK); 99*f3ccf036SAlexei Fedorov #endif 100380559c1SDimitris Papastamos } 1010767d50eSDimitris Papastamos 1020767d50eSDimitris Papastamos /* Read the group 0 counter identified by the given `idx`. */ 103*f3ccf036SAlexei Fedorov uint64_t amu_group0_cnt_read(unsigned int idx) 1040767d50eSDimitris Papastamos { 10540daecc1SAntonio Nino Diaz assert(amu_supported()); 106*f3ccf036SAlexei Fedorov assert(idx < AMU_GROUP0_NR_COUNTERS); 1070767d50eSDimitris Papastamos 1080767d50eSDimitris Papastamos return amu_group0_cnt_read_internal(idx); 1090767d50eSDimitris Papastamos } 1100767d50eSDimitris Papastamos 111*f3ccf036SAlexei Fedorov /* Write the group 0 counter identified by the given `idx` with `val` */ 112*f3ccf036SAlexei Fedorov void amu_group0_cnt_write(unsigned int idx, uint64_t val) 1130767d50eSDimitris Papastamos { 11440daecc1SAntonio Nino Diaz assert(amu_supported()); 115*f3ccf036SAlexei Fedorov assert(idx < AMU_GROUP0_NR_COUNTERS); 1160767d50eSDimitris Papastamos 1170767d50eSDimitris Papastamos amu_group0_cnt_write_internal(idx, val); 1180767d50eSDimitris Papastamos isb(); 1190767d50eSDimitris Papastamos } 1200767d50eSDimitris Papastamos 121*f3ccf036SAlexei Fedorov #if AMU_GROUP1_NR_COUNTERS 122*f3ccf036SAlexei Fedorov /* Read the group 1 counter identified by the given `idx` */ 123*f3ccf036SAlexei Fedorov uint64_t amu_group1_cnt_read(unsigned int idx) 1240767d50eSDimitris Papastamos { 12540daecc1SAntonio Nino Diaz assert(amu_supported()); 126*f3ccf036SAlexei Fedorov assert(amu_group1_supported()); 127*f3ccf036SAlexei Fedorov assert(idx < AMU_GROUP1_NR_COUNTERS); 1280767d50eSDimitris Papastamos 1290767d50eSDimitris Papastamos return amu_group1_cnt_read_internal(idx); 1300767d50eSDimitris Papastamos } 1310767d50eSDimitris Papastamos 132*f3ccf036SAlexei Fedorov /* Write the group 1 counter identified by the given `idx` with `val` */ 133*f3ccf036SAlexei Fedorov void amu_group1_cnt_write(unsigned int idx, uint64_t val) 1340767d50eSDimitris Papastamos { 13540daecc1SAntonio Nino Diaz assert(amu_supported()); 136*f3ccf036SAlexei Fedorov assert(amu_group1_supported()); 137*f3ccf036SAlexei Fedorov assert(idx < AMU_GROUP1_NR_COUNTERS); 1380767d50eSDimitris Papastamos 1390767d50eSDimitris Papastamos amu_group1_cnt_write_internal(idx, val); 1400767d50eSDimitris Papastamos isb(); 1410767d50eSDimitris Papastamos } 1420767d50eSDimitris Papastamos 1430767d50eSDimitris Papastamos /* 1440767d50eSDimitris Papastamos * Program the event type register for the given `idx` with 145*f3ccf036SAlexei Fedorov * the event number `val` 1460767d50eSDimitris Papastamos */ 147*f3ccf036SAlexei Fedorov void amu_group1_set_evtype(unsigned int idx, unsigned int val) 1480767d50eSDimitris Papastamos { 14940daecc1SAntonio Nino Diaz assert(amu_supported()); 150*f3ccf036SAlexei Fedorov assert(amu_group1_supported()); 151*f3ccf036SAlexei Fedorov assert(idx < AMU_GROUP1_NR_COUNTERS); 1520767d50eSDimitris Papastamos 1530767d50eSDimitris Papastamos amu_group1_set_evtype_internal(idx, val); 1540767d50eSDimitris Papastamos isb(); 155380559c1SDimitris Papastamos } 156*f3ccf036SAlexei Fedorov #endif /* AMU_GROUP1_NR_COUNTERS */ 157b6eb3932SDimitris Papastamos 158b6eb3932SDimitris Papastamos static void *amu_context_save(const void *arg) 159b6eb3932SDimitris Papastamos { 160b6eb3932SDimitris Papastamos struct amu_ctx *ctx = &amu_ctxs[plat_my_core_pos()]; 161*f3ccf036SAlexei Fedorov unsigned int i; 162b6eb3932SDimitris Papastamos 163*f3ccf036SAlexei Fedorov if (!amu_supported()) { 164b6eb3932SDimitris Papastamos return (void *)-1; 165*f3ccf036SAlexei Fedorov } 166b6eb3932SDimitris Papastamos 167*f3ccf036SAlexei Fedorov #if AMU_GROUP1_NR_COUNTERS 168*f3ccf036SAlexei Fedorov if (!amu_group1_supported()) { 169*f3ccf036SAlexei Fedorov return (void *)-1; 170*f3ccf036SAlexei Fedorov } 171*f3ccf036SAlexei Fedorov #endif 172b6eb3932SDimitris Papastamos /* Assert that group 0/1 counter configuration is what we expect */ 173*f3ccf036SAlexei Fedorov assert(read_amcntenset0_el0() == AMU_GROUP0_COUNTERS_MASK); 174b6eb3932SDimitris Papastamos 175*f3ccf036SAlexei Fedorov #if AMU_GROUP1_NR_COUNTERS 176*f3ccf036SAlexei Fedorov assert(read_amcntenset1_el0() == AMU_GROUP1_COUNTERS_MASK); 177*f3ccf036SAlexei Fedorov #endif 178b6eb3932SDimitris Papastamos /* 179b6eb3932SDimitris Papastamos * Disable group 0/1 counters to avoid other observers like SCP sampling 180b6eb3932SDimitris Papastamos * counter values from the future via the memory mapped view. 181b6eb3932SDimitris Papastamos */ 182b6eb3932SDimitris Papastamos write_amcntenclr0_el0(AMU_GROUP0_COUNTERS_MASK); 183*f3ccf036SAlexei Fedorov 184*f3ccf036SAlexei Fedorov #if AMU_GROUP1_NR_COUNTERS 185b6eb3932SDimitris Papastamos write_amcntenclr1_el0(AMU_GROUP1_COUNTERS_MASK); 186*f3ccf036SAlexei Fedorov #endif 187b6eb3932SDimitris Papastamos isb(); 188b6eb3932SDimitris Papastamos 189*f3ccf036SAlexei Fedorov /* Save all group 0 counters */ 190*f3ccf036SAlexei Fedorov for (i = 0U; i < AMU_GROUP0_NR_COUNTERS; i++) { 191b6eb3932SDimitris Papastamos ctx->group0_cnts[i] = amu_group0_cnt_read(i); 192*f3ccf036SAlexei Fedorov } 193b6eb3932SDimitris Papastamos 194*f3ccf036SAlexei Fedorov #if AMU_GROUP1_NR_COUNTERS 195b6eb3932SDimitris Papastamos /* Save group 1 counters */ 196*f3ccf036SAlexei Fedorov for (i = 0U; i < AMU_GROUP1_NR_COUNTERS; i++) { 197*f3ccf036SAlexei Fedorov if ((AMU_GROUP1_COUNTERS_MASK & (1U << i)) != 0U) { 198b6eb3932SDimitris Papastamos ctx->group1_cnts[i] = amu_group1_cnt_read(i); 199*f3ccf036SAlexei Fedorov } 200*f3ccf036SAlexei Fedorov } 201*f3ccf036SAlexei Fedorov #endif 20240daecc1SAntonio Nino Diaz return (void *)0; 203b6eb3932SDimitris Papastamos } 204b6eb3932SDimitris Papastamos 205b6eb3932SDimitris Papastamos static void *amu_context_restore(const void *arg) 206b6eb3932SDimitris Papastamos { 207b6eb3932SDimitris Papastamos struct amu_ctx *ctx = &amu_ctxs[plat_my_core_pos()]; 208*f3ccf036SAlexei Fedorov unsigned int i; 209b6eb3932SDimitris Papastamos 210*f3ccf036SAlexei Fedorov if (!amu_supported()) { 211b6eb3932SDimitris Papastamos return (void *)-1; 212*f3ccf036SAlexei Fedorov } 213b6eb3932SDimitris Papastamos 214*f3ccf036SAlexei Fedorov #if AMU_GROUP1_NR_COUNTERS 215*f3ccf036SAlexei Fedorov if (!amu_group1_supported()) { 216*f3ccf036SAlexei Fedorov return (void *)-1; 217*f3ccf036SAlexei Fedorov } 218*f3ccf036SAlexei Fedorov #endif 219b6eb3932SDimitris Papastamos /* Counters were disabled in `amu_context_save()` */ 220*f3ccf036SAlexei Fedorov assert(read_amcntenset0_el0() == 0U); 221b6eb3932SDimitris Papastamos 222*f3ccf036SAlexei Fedorov #if AMU_GROUP1_NR_COUNTERS 223*f3ccf036SAlexei Fedorov assert(read_amcntenset1_el0() == 0U); 224*f3ccf036SAlexei Fedorov #endif 225b6eb3932SDimitris Papastamos 226*f3ccf036SAlexei Fedorov /* Restore all group 0 counters */ 227*f3ccf036SAlexei Fedorov for (i = 0U; i < AMU_GROUP0_NR_COUNTERS; i++) { 228b6eb3932SDimitris Papastamos amu_group0_cnt_write(i, ctx->group0_cnts[i]); 229*f3ccf036SAlexei Fedorov } 230b6eb3932SDimitris Papastamos 231*f3ccf036SAlexei Fedorov /* Restore group 0 counter configuration */ 232b6eb3932SDimitris Papastamos write_amcntenset0_el0(AMU_GROUP0_COUNTERS_MASK); 233*f3ccf036SAlexei Fedorov 234*f3ccf036SAlexei Fedorov #if AMU_GROUP1_NR_COUNTERS 235*f3ccf036SAlexei Fedorov /* Restore group 1 counters */ 236*f3ccf036SAlexei Fedorov for (i = 0U; i < AMU_GROUP1_NR_COUNTERS; i++) { 237*f3ccf036SAlexei Fedorov if ((AMU_GROUP1_COUNTERS_MASK & (1U << i)) != 0U) { 238*f3ccf036SAlexei Fedorov amu_group1_cnt_write(i, ctx->group1_cnts[i]); 239*f3ccf036SAlexei Fedorov } 240*f3ccf036SAlexei Fedorov } 241*f3ccf036SAlexei Fedorov 242*f3ccf036SAlexei Fedorov /* Restore group 1 counter configuration */ 243b6eb3932SDimitris Papastamos write_amcntenset1_el0(AMU_GROUP1_COUNTERS_MASK); 244*f3ccf036SAlexei Fedorov #endif 245b6eb3932SDimitris Papastamos 24640daecc1SAntonio Nino Diaz return (void *)0; 247b6eb3932SDimitris Papastamos } 248b6eb3932SDimitris Papastamos 249b6eb3932SDimitris Papastamos SUBSCRIBE_TO_EVENT(psci_suspend_pwrdown_start, amu_context_save); 250b6eb3932SDimitris Papastamos SUBSCRIBE_TO_EVENT(psci_suspend_pwrdown_finish, amu_context_restore); 251