1ef69e1eaSDimitris Papastamos /* 2*f3ccf036SAlexei Fedorov * Copyright (c) 2017-2020, ARM Limited and Contributors. All rights reserved. 3ef69e1eaSDimitris Papastamos * 4ef69e1eaSDimitris Papastamos * SPDX-License-Identifier: BSD-3-Clause 5ef69e1eaSDimitris Papastamos */ 6ef69e1eaSDimitris Papastamos 7*f3ccf036SAlexei Fedorov #include <assert.h> 809d40e0eSAntonio Nino Diaz #include <stdbool.h> 909d40e0eSAntonio Nino Diaz 10ef69e1eaSDimitris Papastamos #include <arch.h> 11ef69e1eaSDimitris 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> 18b6eb3932SDimitris Papastamos 19b6eb3932SDimitris Papastamos static struct amu_ctx amu_ctxs[PLATFORM_CORE_COUNT]; 20ef69e1eaSDimitris Papastamos 21*f3ccf036SAlexei Fedorov /* Check if AMUv1 for Armv8.4 or 8.6 is implemented */ 2240daecc1SAntonio Nino Diaz bool amu_supported(void) 23ef69e1eaSDimitris Papastamos { 24*f3ccf036SAlexei Fedorov uint32_t features = read_id_pfr0() >> ID_PFR0_AMU_SHIFT; 25ef69e1eaSDimitris Papastamos 26*f3ccf036SAlexei Fedorov features &= ID_PFR0_AMU_MASK; 27*f3ccf036SAlexei Fedorov return ((features == 1U) || (features == 2U)); 28c70da546SJoel Hutton } 29c70da546SJoel Hutton 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 uint32_t features = read_amcfgr() >> AMCFGR_NCG_SHIFT; 35*f3ccf036SAlexei Fedorov 36*f3ccf036SAlexei Fedorov return (features & AMCFGR_NCG_MASK) == 1U; 37*f3ccf036SAlexei Fedorov } 38*f3ccf036SAlexei Fedorov #endif 39*f3ccf036SAlexei Fedorov 40*f3ccf036SAlexei Fedorov /* 41*f3ccf036SAlexei Fedorov * Enable counters. This function is meant to be invoked 42*f3ccf036SAlexei Fedorov * by the context management library before exiting from EL3. 43*f3ccf036SAlexei Fedorov */ 4440daecc1SAntonio Nino Diaz void amu_enable(bool el2_unused) 45c70da546SJoel Hutton { 46*f3ccf036SAlexei Fedorov if (!amu_supported()) { 47*f3ccf036SAlexei Fedorov INFO("AMU is not implemented\n"); 480767d50eSDimitris Papastamos return; 49*f3ccf036SAlexei Fedorov } 50*f3ccf036SAlexei Fedorov 51*f3ccf036SAlexei Fedorov #if AMU_GROUP1_NR_COUNTERS 52*f3ccf036SAlexei Fedorov /* Check and set presence of group 1 counters */ 53*f3ccf036SAlexei Fedorov if (!amu_group1_supported()) { 54*f3ccf036SAlexei Fedorov ERROR("AMU Counter Group 1 is not implemented\n"); 55*f3ccf036SAlexei Fedorov panic(); 56*f3ccf036SAlexei Fedorov } 57*f3ccf036SAlexei Fedorov 58*f3ccf036SAlexei Fedorov /* Check number of group 1 counters */ 59*f3ccf036SAlexei Fedorov uint32_t cnt_num = (read_amcgcr() >> AMCGCR_CG1NC_SHIFT) & 60*f3ccf036SAlexei Fedorov AMCGCR_CG1NC_MASK; 61*f3ccf036SAlexei Fedorov VERBOSE("%s%u. %s%u\n", 62*f3ccf036SAlexei Fedorov "Number of AMU Group 1 Counters ", cnt_num, 63*f3ccf036SAlexei Fedorov "Requested number ", AMU_GROUP1_NR_COUNTERS); 64*f3ccf036SAlexei Fedorov 65*f3ccf036SAlexei Fedorov if (cnt_num < AMU_GROUP1_NR_COUNTERS) { 66*f3ccf036SAlexei Fedorov ERROR("%s%u is less than %s%u\n", 67*f3ccf036SAlexei Fedorov "Number of AMU Group 1 Counters ", cnt_num, 68*f3ccf036SAlexei Fedorov "Requested number ", AMU_GROUP1_NR_COUNTERS); 69*f3ccf036SAlexei Fedorov panic(); 70*f3ccf036SAlexei Fedorov } 71*f3ccf036SAlexei Fedorov #endif 720767d50eSDimitris Papastamos 73ef69e1eaSDimitris Papastamos if (el2_unused) { 74ef69e1eaSDimitris Papastamos uint64_t v; 75ef69e1eaSDimitris Papastamos /* 76ef69e1eaSDimitris Papastamos * Non-secure access from EL0 or EL1 to the Activity Monitor 77ef69e1eaSDimitris Papastamos * registers do not trap to EL2. 78ef69e1eaSDimitris Papastamos */ 79ef69e1eaSDimitris Papastamos v = read_hcptr(); 80ef69e1eaSDimitris Papastamos v &= ~TAM_BIT; 81ef69e1eaSDimitris Papastamos write_hcptr(v); 82ef69e1eaSDimitris Papastamos } 83ef69e1eaSDimitris Papastamos 84ef69e1eaSDimitris Papastamos /* Enable group 0 counters */ 85ef69e1eaSDimitris Papastamos write_amcntenset0(AMU_GROUP0_COUNTERS_MASK); 86c70da546SJoel Hutton 87*f3ccf036SAlexei Fedorov #if AMU_GROUP1_NR_COUNTERS 88c70da546SJoel Hutton /* Enable group 1 counters */ 89c70da546SJoel Hutton write_amcntenset1(AMU_GROUP1_COUNTERS_MASK); 90*f3ccf036SAlexei Fedorov #endif 91c70da546SJoel Hutton } 92c70da546SJoel Hutton 93c70da546SJoel Hutton /* Read the group 0 counter identified by the given `idx`. */ 94*f3ccf036SAlexei Fedorov uint64_t amu_group0_cnt_read(unsigned int idx) 95c70da546SJoel Hutton { 9640daecc1SAntonio Nino Diaz assert(amu_supported()); 97*f3ccf036SAlexei Fedorov assert(idx < AMU_GROUP0_NR_COUNTERS); 98c70da546SJoel Hutton 99c70da546SJoel Hutton return amu_group0_cnt_read_internal(idx); 100c70da546SJoel Hutton } 101c70da546SJoel Hutton 102*f3ccf036SAlexei Fedorov /* Write the group 0 counter identified by the given `idx` with `val` */ 103*f3ccf036SAlexei Fedorov void amu_group0_cnt_write(unsigned int idx, uint64_t val) 104c70da546SJoel Hutton { 10540daecc1SAntonio Nino Diaz assert(amu_supported()); 106*f3ccf036SAlexei Fedorov assert(idx < AMU_GROUP0_NR_COUNTERS); 107c70da546SJoel Hutton 108c70da546SJoel Hutton amu_group0_cnt_write_internal(idx, val); 109c70da546SJoel Hutton isb(); 110c70da546SJoel Hutton } 111c70da546SJoel Hutton 112*f3ccf036SAlexei Fedorov #if AMU_GROUP1_NR_COUNTERS 113*f3ccf036SAlexei Fedorov /* Read the group 1 counter identified by the given `idx` */ 114*f3ccf036SAlexei Fedorov uint64_t amu_group1_cnt_read(unsigned int idx) 115c70da546SJoel Hutton { 11640daecc1SAntonio Nino Diaz assert(amu_supported()); 117*f3ccf036SAlexei Fedorov assert(amu_group1_supported()); 118*f3ccf036SAlexei Fedorov assert(idx < AMU_GROUP1_NR_COUNTERS); 119c70da546SJoel Hutton 120c70da546SJoel Hutton return amu_group1_cnt_read_internal(idx); 121c70da546SJoel Hutton } 122c70da546SJoel Hutton 123*f3ccf036SAlexei Fedorov /* Write the group 1 counter identified by the given `idx` with `val` */ 124*f3ccf036SAlexei Fedorov void amu_group1_cnt_write(unsigned int idx, uint64_t val) 125c70da546SJoel Hutton { 12640daecc1SAntonio Nino Diaz assert(amu_supported()); 127*f3ccf036SAlexei Fedorov assert(amu_group1_supported()); 128*f3ccf036SAlexei Fedorov assert(idx < AMU_GROUP1_NR_COUNTERS); 129c70da546SJoel Hutton 130c70da546SJoel Hutton amu_group1_cnt_write_internal(idx, val); 131c70da546SJoel Hutton isb(); 132c70da546SJoel Hutton } 133c70da546SJoel Hutton 134*f3ccf036SAlexei Fedorov /* 135*f3ccf036SAlexei Fedorov * Program the event type register for the given `idx` with 136*f3ccf036SAlexei Fedorov * the event number `val` 137*f3ccf036SAlexei Fedorov */ 138*f3ccf036SAlexei Fedorov void amu_group1_set_evtype(unsigned int idx, unsigned int val) 139c70da546SJoel Hutton { 14040daecc1SAntonio Nino Diaz assert(amu_supported()); 141*f3ccf036SAlexei Fedorov assert(amu_group1_supported()); 142*f3ccf036SAlexei Fedorov assert(idx < AMU_GROUP1_NR_COUNTERS); 143c70da546SJoel Hutton 144c70da546SJoel Hutton amu_group1_set_evtype_internal(idx, val); 145c70da546SJoel Hutton isb(); 146ef69e1eaSDimitris Papastamos } 147*f3ccf036SAlexei Fedorov #endif /* AMU_GROUP1_NR_COUNTERS */ 148b6eb3932SDimitris Papastamos 149b6eb3932SDimitris Papastamos static void *amu_context_save(const void *arg) 150b6eb3932SDimitris Papastamos { 151*f3ccf036SAlexei Fedorov struct amu_ctx *ctx = &amu_ctxs[plat_my_core_pos()]; 152*f3ccf036SAlexei Fedorov unsigned int i; 153b6eb3932SDimitris Papastamos 154*f3ccf036SAlexei Fedorov if (!amu_supported()) { 155b6eb3932SDimitris Papastamos return (void *)-1; 156*f3ccf036SAlexei Fedorov } 157b6eb3932SDimitris Papastamos 158*f3ccf036SAlexei Fedorov #if AMU_GROUP1_NR_COUNTERS 159*f3ccf036SAlexei Fedorov if (!amu_group1_supported()) { 160*f3ccf036SAlexei Fedorov return (void *)-1; 161*f3ccf036SAlexei Fedorov } 162*f3ccf036SAlexei Fedorov #endif 163*f3ccf036SAlexei Fedorov /* Assert that group 0/1 counter configuration is what we expect */ 164*f3ccf036SAlexei Fedorov assert(read_amcntenset0_el0() == AMU_GROUP0_COUNTERS_MASK); 165b6eb3932SDimitris Papastamos 166*f3ccf036SAlexei Fedorov #if AMU_GROUP1_NR_COUNTERS 167*f3ccf036SAlexei Fedorov assert(read_amcntenset1_el0() == AMU_GROUP1_COUNTERS_MASK); 168*f3ccf036SAlexei Fedorov #endif 169b6eb3932SDimitris Papastamos /* 170*f3ccf036SAlexei Fedorov * Disable group 0/1 counters to avoid other observers like SCP sampling 171b6eb3932SDimitris Papastamos * counter values from the future via the memory mapped view. 172b6eb3932SDimitris Papastamos */ 173b6eb3932SDimitris Papastamos write_amcntenclr0(AMU_GROUP0_COUNTERS_MASK); 174*f3ccf036SAlexei Fedorov 175*f3ccf036SAlexei Fedorov #if AMU_GROUP1_NR_COUNTERS 176c70da546SJoel Hutton write_amcntenclr1(AMU_GROUP1_COUNTERS_MASK); 177*f3ccf036SAlexei Fedorov #endif 178b6eb3932SDimitris Papastamos isb(); 179b6eb3932SDimitris Papastamos 180*f3ccf036SAlexei Fedorov /* Save all group 0 counters */ 181*f3ccf036SAlexei Fedorov for (i = 0U; i < AMU_GROUP0_NR_COUNTERS; i++) { 182c70da546SJoel Hutton ctx->group0_cnts[i] = amu_group0_cnt_read(i); 183*f3ccf036SAlexei Fedorov } 184c70da546SJoel Hutton 185*f3ccf036SAlexei Fedorov #if AMU_GROUP1_NR_COUNTERS 186*f3ccf036SAlexei Fedorov /* Save group 1 counters */ 187*f3ccf036SAlexei Fedorov for (i = 0U; i < AMU_GROUP1_NR_COUNTERS; i++) { 188*f3ccf036SAlexei Fedorov if ((AMU_GROUP1_COUNTERS_MASK & (1U << i)) != 0U) { 189c70da546SJoel Hutton ctx->group1_cnts[i] = amu_group1_cnt_read(i); 190*f3ccf036SAlexei Fedorov } 191*f3ccf036SAlexei Fedorov } 192*f3ccf036SAlexei Fedorov #endif 19340daecc1SAntonio Nino Diaz return (void *)0; 194b6eb3932SDimitris Papastamos } 195b6eb3932SDimitris Papastamos 196b6eb3932SDimitris Papastamos static void *amu_context_restore(const void *arg) 197b6eb3932SDimitris Papastamos { 198*f3ccf036SAlexei Fedorov struct amu_ctx *ctx = &amu_ctxs[plat_my_core_pos()]; 199*f3ccf036SAlexei Fedorov unsigned int i; 200b6eb3932SDimitris Papastamos 201*f3ccf036SAlexei Fedorov if (!amu_supported()) { 202b6eb3932SDimitris Papastamos return (void *)-1; 203*f3ccf036SAlexei Fedorov } 204b6eb3932SDimitris Papastamos 205*f3ccf036SAlexei Fedorov #if AMU_GROUP1_NR_COUNTERS 206*f3ccf036SAlexei Fedorov if (!amu_group1_supported()) { 207*f3ccf036SAlexei Fedorov return (void *)-1; 208*f3ccf036SAlexei Fedorov } 209*f3ccf036SAlexei Fedorov #endif 210b6eb3932SDimitris Papastamos /* Counters were disabled in `amu_context_save()` */ 211*f3ccf036SAlexei Fedorov assert(read_amcntenset0_el0() == 0U); 212b6eb3932SDimitris Papastamos 213*f3ccf036SAlexei Fedorov #if AMU_GROUP1_NR_COUNTERS 214*f3ccf036SAlexei Fedorov assert(read_amcntenset1_el0() == 0U); 215*f3ccf036SAlexei Fedorov #endif 216*f3ccf036SAlexei Fedorov 217*f3ccf036SAlexei Fedorov /* Restore all group 0 counters */ 218*f3ccf036SAlexei Fedorov for (i = 0U; i < AMU_GROUP0_NR_COUNTERS; i++) { 219c70da546SJoel Hutton amu_group0_cnt_write(i, ctx->group0_cnts[i]); 220*f3ccf036SAlexei Fedorov } 221b6eb3932SDimitris Papastamos 222*f3ccf036SAlexei Fedorov /* Restore group 0 counter configuration */ 223b6eb3932SDimitris Papastamos write_amcntenset0(AMU_GROUP0_COUNTERS_MASK); 224b6eb3932SDimitris Papastamos 225*f3ccf036SAlexei Fedorov #if AMU_GROUP1_NR_COUNTERS 226*f3ccf036SAlexei Fedorov /* Restore group 1 counters */ 227*f3ccf036SAlexei Fedorov for (i = 0U; i < AMU_GROUP1_NR_COUNTERS; i++) { 228*f3ccf036SAlexei Fedorov if ((AMU_GROUP1_COUNTERS_MASK & (1U << i)) != 0U) { 229*f3ccf036SAlexei Fedorov amu_group1_cnt_write(i, ctx->group1_cnts[i]); 230*f3ccf036SAlexei Fedorov } 231*f3ccf036SAlexei Fedorov } 232*f3ccf036SAlexei Fedorov 233*f3ccf036SAlexei Fedorov /* Restore group 1 counter configuration */ 234c70da546SJoel Hutton write_amcntenset1(AMU_GROUP1_COUNTERS_MASK); 235*f3ccf036SAlexei Fedorov #endif 236*f3ccf036SAlexei Fedorov 23740daecc1SAntonio Nino Diaz return (void *)0; 238b6eb3932SDimitris Papastamos } 239b6eb3932SDimitris Papastamos 240b6eb3932SDimitris Papastamos SUBSCRIBE_TO_EVENT(psci_suspend_pwrdown_start, amu_context_save); 241b6eb3932SDimitris Papastamos SUBSCRIBE_TO_EVENT(psci_suspend_pwrdown_finish, amu_context_restore); 242