1380559c1SDimitris Papastamos /* 2*c6cc9ac3SDimitris Papastamos * Copyright (c) 2017-2018, ARM Limited and Contributors. All rights reserved. 3380559c1SDimitris Papastamos * 4380559c1SDimitris Papastamos * SPDX-License-Identifier: BSD-3-Clause 5380559c1SDimitris Papastamos */ 6380559c1SDimitris Papastamos 7380559c1SDimitris Papastamos #include <amu.h> 80767d50eSDimitris Papastamos #include <amu_private.h> 9380559c1SDimitris Papastamos #include <arch.h> 10380559c1SDimitris Papastamos #include <arch_helpers.h> 110767d50eSDimitris Papastamos #include <assert.h> 12b6eb3932SDimitris Papastamos #include <platform.h> 13b6eb3932SDimitris Papastamos #include <pubsub_events.h> 14380559c1SDimitris Papastamos 150767d50eSDimitris Papastamos #define AMU_GROUP0_NR_COUNTERS 4 160767d50eSDimitris Papastamos 17b6eb3932SDimitris Papastamos struct amu_ctx { 18b6eb3932SDimitris Papastamos uint64_t group0_cnts[AMU_GROUP0_NR_COUNTERS]; 19b6eb3932SDimitris Papastamos uint64_t group1_cnts[AMU_GROUP1_NR_COUNTERS]; 20b6eb3932SDimitris Papastamos }; 21b6eb3932SDimitris Papastamos 22b6eb3932SDimitris Papastamos static struct amu_ctx amu_ctxs[PLATFORM_CORE_COUNT]; 23b6eb3932SDimitris Papastamos 240767d50eSDimitris Papastamos int amu_supported(void) 25380559c1SDimitris Papastamos { 26380559c1SDimitris Papastamos uint64_t features; 27380559c1SDimitris Papastamos 28380559c1SDimitris Papastamos features = read_id_aa64pfr0_el1() >> ID_AA64PFR0_AMU_SHIFT; 290767d50eSDimitris Papastamos return (features & ID_AA64PFR0_AMU_MASK) == 1; 300767d50eSDimitris Papastamos } 310767d50eSDimitris Papastamos 320767d50eSDimitris Papastamos /* 330767d50eSDimitris Papastamos * Enable counters. This function is meant to be invoked 340767d50eSDimitris Papastamos * by the context management library before exiting from EL3. 350767d50eSDimitris Papastamos */ 360767d50eSDimitris Papastamos void amu_enable(int el2_unused) 370767d50eSDimitris Papastamos { 38380559c1SDimitris Papastamos uint64_t v; 39380559c1SDimitris Papastamos 40*c6cc9ac3SDimitris Papastamos if (!amu_supported()) 410767d50eSDimitris Papastamos return; 420767d50eSDimitris Papastamos 43380559c1SDimitris Papastamos if (el2_unused) { 44380559c1SDimitris Papastamos /* 45380559c1SDimitris Papastamos * CPTR_EL2.TAM: Set to zero so any accesses to 46380559c1SDimitris Papastamos * the Activity Monitor registers do not trap to EL2. 47380559c1SDimitris Papastamos */ 48380559c1SDimitris Papastamos v = read_cptr_el2(); 49380559c1SDimitris Papastamos v &= ~CPTR_EL2_TAM_BIT; 50380559c1SDimitris Papastamos write_cptr_el2(v); 51380559c1SDimitris Papastamos } 52380559c1SDimitris Papastamos 53380559c1SDimitris Papastamos /* 54380559c1SDimitris Papastamos * CPTR_EL3.TAM: Set to zero so that any accesses to 55380559c1SDimitris Papastamos * the Activity Monitor registers do not trap to EL3. 56380559c1SDimitris Papastamos */ 57380559c1SDimitris Papastamos v = read_cptr_el3(); 58380559c1SDimitris Papastamos v &= ~TAM_BIT; 59380559c1SDimitris Papastamos write_cptr_el3(v); 60380559c1SDimitris Papastamos 61380559c1SDimitris Papastamos /* Enable group 0 counters */ 62380559c1SDimitris Papastamos write_amcntenset0_el0(AMU_GROUP0_COUNTERS_MASK); 6359902b7cSDimitris Papastamos /* Enable group 1 counters */ 6459902b7cSDimitris Papastamos write_amcntenset1_el0(AMU_GROUP1_COUNTERS_MASK); 65380559c1SDimitris Papastamos } 660767d50eSDimitris Papastamos 670767d50eSDimitris Papastamos /* Read the group 0 counter identified by the given `idx`. */ 680767d50eSDimitris Papastamos uint64_t amu_group0_cnt_read(int idx) 690767d50eSDimitris Papastamos { 700767d50eSDimitris Papastamos assert(amu_supported()); 710767d50eSDimitris Papastamos assert(idx >= 0 && idx < AMU_GROUP0_NR_COUNTERS); 720767d50eSDimitris Papastamos 730767d50eSDimitris Papastamos return amu_group0_cnt_read_internal(idx); 740767d50eSDimitris Papastamos } 750767d50eSDimitris Papastamos 760767d50eSDimitris Papastamos /* Write the group 0 counter identified by the given `idx` with `val`. */ 770767d50eSDimitris Papastamos void amu_group0_cnt_write(int idx, uint64_t val) 780767d50eSDimitris Papastamos { 790767d50eSDimitris Papastamos assert(amu_supported()); 800767d50eSDimitris Papastamos assert(idx >= 0 && idx < AMU_GROUP0_NR_COUNTERS); 810767d50eSDimitris Papastamos 820767d50eSDimitris Papastamos amu_group0_cnt_write_internal(idx, val); 830767d50eSDimitris Papastamos isb(); 840767d50eSDimitris Papastamos } 850767d50eSDimitris Papastamos 860767d50eSDimitris Papastamos /* Read the group 1 counter identified by the given `idx`. */ 870767d50eSDimitris Papastamos uint64_t amu_group1_cnt_read(int idx) 880767d50eSDimitris Papastamos { 890767d50eSDimitris Papastamos assert(amu_supported()); 900767d50eSDimitris Papastamos assert(idx >= 0 && idx < AMU_GROUP1_NR_COUNTERS); 910767d50eSDimitris Papastamos 920767d50eSDimitris Papastamos return amu_group1_cnt_read_internal(idx); 930767d50eSDimitris Papastamos } 940767d50eSDimitris Papastamos 950767d50eSDimitris Papastamos /* Write the group 1 counter identified by the given `idx` with `val`. */ 960767d50eSDimitris Papastamos void amu_group1_cnt_write(int idx, uint64_t val) 970767d50eSDimitris Papastamos { 980767d50eSDimitris Papastamos assert(amu_supported()); 990767d50eSDimitris Papastamos assert(idx >= 0 && idx < AMU_GROUP1_NR_COUNTERS); 1000767d50eSDimitris Papastamos 1010767d50eSDimitris Papastamos amu_group1_cnt_write_internal(idx, val); 1020767d50eSDimitris Papastamos isb(); 1030767d50eSDimitris Papastamos } 1040767d50eSDimitris Papastamos 1050767d50eSDimitris Papastamos /* 1060767d50eSDimitris Papastamos * Program the event type register for the given `idx` with 1070767d50eSDimitris Papastamos * the event number `val`. 1080767d50eSDimitris Papastamos */ 1090767d50eSDimitris Papastamos void amu_group1_set_evtype(int idx, unsigned int val) 1100767d50eSDimitris Papastamos { 1110767d50eSDimitris Papastamos assert(amu_supported()); 1120767d50eSDimitris Papastamos assert (idx >= 0 && idx < AMU_GROUP1_NR_COUNTERS); 1130767d50eSDimitris Papastamos 1140767d50eSDimitris Papastamos amu_group1_set_evtype_internal(idx, val); 1150767d50eSDimitris Papastamos isb(); 116380559c1SDimitris Papastamos } 117b6eb3932SDimitris Papastamos 118b6eb3932SDimitris Papastamos static void *amu_context_save(const void *arg) 119b6eb3932SDimitris Papastamos { 120b6eb3932SDimitris Papastamos struct amu_ctx *ctx = &amu_ctxs[plat_my_core_pos()]; 121b6eb3932SDimitris Papastamos int i; 122b6eb3932SDimitris Papastamos 123b6eb3932SDimitris Papastamos if (!amu_supported()) 124b6eb3932SDimitris Papastamos return (void *)-1; 125b6eb3932SDimitris Papastamos 126b6eb3932SDimitris Papastamos /* Assert that group 0/1 counter configuration is what we expect */ 127b6eb3932SDimitris Papastamos assert(read_amcntenset0_el0() == AMU_GROUP0_COUNTERS_MASK && 128b6eb3932SDimitris Papastamos read_amcntenset1_el0() == AMU_GROUP1_COUNTERS_MASK); 129b6eb3932SDimitris Papastamos 130b6eb3932SDimitris Papastamos assert((sizeof(int) * 8) - __builtin_clz(AMU_GROUP1_COUNTERS_MASK) 131b6eb3932SDimitris Papastamos <= AMU_GROUP1_NR_COUNTERS); 132b6eb3932SDimitris Papastamos 133b6eb3932SDimitris Papastamos /* 134b6eb3932SDimitris Papastamos * Disable group 0/1 counters to avoid other observers like SCP sampling 135b6eb3932SDimitris Papastamos * counter values from the future via the memory mapped view. 136b6eb3932SDimitris Papastamos */ 137b6eb3932SDimitris Papastamos write_amcntenclr0_el0(AMU_GROUP0_COUNTERS_MASK); 138b6eb3932SDimitris Papastamos write_amcntenclr1_el0(AMU_GROUP1_COUNTERS_MASK); 139b6eb3932SDimitris Papastamos isb(); 140b6eb3932SDimitris Papastamos 141b6eb3932SDimitris Papastamos /* Save group 0 counters */ 142b6eb3932SDimitris Papastamos for (i = 0; i < AMU_GROUP0_NR_COUNTERS; i++) 143b6eb3932SDimitris Papastamos ctx->group0_cnts[i] = amu_group0_cnt_read(i); 144b6eb3932SDimitris Papastamos 145b6eb3932SDimitris Papastamos /* Save group 1 counters */ 146b6eb3932SDimitris Papastamos for (i = 0; i < AMU_GROUP1_NR_COUNTERS; i++) 147b6eb3932SDimitris Papastamos ctx->group1_cnts[i] = amu_group1_cnt_read(i); 148b6eb3932SDimitris Papastamos 149b6eb3932SDimitris Papastamos return 0; 150b6eb3932SDimitris Papastamos } 151b6eb3932SDimitris Papastamos 152b6eb3932SDimitris Papastamos static void *amu_context_restore(const void *arg) 153b6eb3932SDimitris Papastamos { 154b6eb3932SDimitris Papastamos struct amu_ctx *ctx = &amu_ctxs[plat_my_core_pos()]; 155b6eb3932SDimitris Papastamos int i; 156b6eb3932SDimitris Papastamos 157b6eb3932SDimitris Papastamos if (!amu_supported()) 158b6eb3932SDimitris Papastamos return (void *)-1; 159b6eb3932SDimitris Papastamos 160b6eb3932SDimitris Papastamos /* Counters were disabled in `amu_context_save()` */ 161b6eb3932SDimitris Papastamos assert(read_amcntenset0_el0() == 0 && read_amcntenset1_el0() == 0); 162b6eb3932SDimitris Papastamos 163b6eb3932SDimitris Papastamos assert((sizeof(int) * 8) - __builtin_clz(AMU_GROUP1_COUNTERS_MASK) 164b6eb3932SDimitris Papastamos <= AMU_GROUP1_NR_COUNTERS); 165b6eb3932SDimitris Papastamos 166b6eb3932SDimitris Papastamos /* Restore group 0 counters */ 167b6eb3932SDimitris Papastamos for (i = 0; i < AMU_GROUP0_NR_COUNTERS; i++) 168b6eb3932SDimitris Papastamos if (AMU_GROUP0_COUNTERS_MASK & (1U << i)) 169b6eb3932SDimitris Papastamos amu_group0_cnt_write(i, ctx->group0_cnts[i]); 170b6eb3932SDimitris Papastamos 171b6eb3932SDimitris Papastamos /* Restore group 1 counters */ 172b6eb3932SDimitris Papastamos for (i = 0; i < AMU_GROUP1_NR_COUNTERS; i++) 173b6eb3932SDimitris Papastamos if (AMU_GROUP1_COUNTERS_MASK & (1U << i)) 174b6eb3932SDimitris Papastamos amu_group1_cnt_write(i, ctx->group1_cnts[i]); 175b6eb3932SDimitris Papastamos isb(); 176b6eb3932SDimitris Papastamos 177b6eb3932SDimitris Papastamos /* Restore group 0/1 counter configuration */ 178b6eb3932SDimitris Papastamos write_amcntenset0_el0(AMU_GROUP0_COUNTERS_MASK); 179b6eb3932SDimitris Papastamos write_amcntenset1_el0(AMU_GROUP1_COUNTERS_MASK); 180b6eb3932SDimitris Papastamos 181b6eb3932SDimitris Papastamos return 0; 182b6eb3932SDimitris Papastamos } 183b6eb3932SDimitris Papastamos 184b6eb3932SDimitris Papastamos SUBSCRIBE_TO_EVENT(psci_suspend_pwrdown_start, amu_context_save); 185b6eb3932SDimitris Papastamos SUBSCRIBE_TO_EVENT(psci_suspend_pwrdown_finish, amu_context_restore); 186