1380559c1SDimitris Papastamos /* 2380559c1SDimitris Papastamos * Copyright (c) 2017, 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> 120767d50eSDimitris Papastamos #include <debug.h> 13*b6eb3932SDimitris Papastamos #include <platform.h> 14*b6eb3932SDimitris Papastamos #include <pubsub_events.h> 15380559c1SDimitris Papastamos 160767d50eSDimitris Papastamos #define AMU_GROUP0_NR_COUNTERS 4 170767d50eSDimitris Papastamos 18*b6eb3932SDimitris Papastamos struct amu_ctx { 19*b6eb3932SDimitris Papastamos uint64_t group0_cnts[AMU_GROUP0_NR_COUNTERS]; 20*b6eb3932SDimitris Papastamos uint64_t group1_cnts[AMU_GROUP1_NR_COUNTERS]; 21*b6eb3932SDimitris Papastamos }; 22*b6eb3932SDimitris Papastamos 23*b6eb3932SDimitris Papastamos static struct amu_ctx amu_ctxs[PLATFORM_CORE_COUNT]; 24*b6eb3932SDimitris Papastamos 250767d50eSDimitris Papastamos int amu_supported(void) 26380559c1SDimitris Papastamos { 27380559c1SDimitris Papastamos uint64_t features; 28380559c1SDimitris Papastamos 29380559c1SDimitris Papastamos features = read_id_aa64pfr0_el1() >> ID_AA64PFR0_AMU_SHIFT; 300767d50eSDimitris Papastamos return (features & ID_AA64PFR0_AMU_MASK) == 1; 310767d50eSDimitris Papastamos } 320767d50eSDimitris Papastamos 330767d50eSDimitris Papastamos /* 340767d50eSDimitris Papastamos * Enable counters. This function is meant to be invoked 350767d50eSDimitris Papastamos * by the context management library before exiting from EL3. 360767d50eSDimitris Papastamos */ 370767d50eSDimitris Papastamos void amu_enable(int el2_unused) 380767d50eSDimitris Papastamos { 39380559c1SDimitris Papastamos uint64_t v; 40380559c1SDimitris Papastamos 410767d50eSDimitris Papastamos if (!amu_supported()) { 420767d50eSDimitris Papastamos WARN("Cannot enable AMU - not supported\n"); 430767d50eSDimitris Papastamos return; 440767d50eSDimitris Papastamos } 450767d50eSDimitris Papastamos 46380559c1SDimitris Papastamos if (el2_unused) { 47380559c1SDimitris Papastamos /* 48380559c1SDimitris Papastamos * CPTR_EL2.TAM: Set to zero so any accesses to 49380559c1SDimitris Papastamos * the Activity Monitor registers do not trap to EL2. 50380559c1SDimitris Papastamos */ 51380559c1SDimitris Papastamos v = read_cptr_el2(); 52380559c1SDimitris Papastamos v &= ~CPTR_EL2_TAM_BIT; 53380559c1SDimitris Papastamos write_cptr_el2(v); 54380559c1SDimitris Papastamos } 55380559c1SDimitris Papastamos 56380559c1SDimitris Papastamos /* 57380559c1SDimitris Papastamos * CPTR_EL3.TAM: Set to zero so that any accesses to 58380559c1SDimitris Papastamos * the Activity Monitor registers do not trap to EL3. 59380559c1SDimitris Papastamos */ 60380559c1SDimitris Papastamos v = read_cptr_el3(); 61380559c1SDimitris Papastamos v &= ~TAM_BIT; 62380559c1SDimitris Papastamos write_cptr_el3(v); 63380559c1SDimitris Papastamos 64380559c1SDimitris Papastamos /* Enable group 0 counters */ 65380559c1SDimitris Papastamos write_amcntenset0_el0(AMU_GROUP0_COUNTERS_MASK); 6659902b7cSDimitris Papastamos /* Enable group 1 counters */ 6759902b7cSDimitris Papastamos write_amcntenset1_el0(AMU_GROUP1_COUNTERS_MASK); 68380559c1SDimitris Papastamos } 690767d50eSDimitris Papastamos 700767d50eSDimitris Papastamos /* Read the group 0 counter identified by the given `idx`. */ 710767d50eSDimitris Papastamos uint64_t amu_group0_cnt_read(int idx) 720767d50eSDimitris Papastamos { 730767d50eSDimitris Papastamos assert(amu_supported()); 740767d50eSDimitris Papastamos assert(idx >= 0 && idx < AMU_GROUP0_NR_COUNTERS); 750767d50eSDimitris Papastamos 760767d50eSDimitris Papastamos return amu_group0_cnt_read_internal(idx); 770767d50eSDimitris Papastamos } 780767d50eSDimitris Papastamos 790767d50eSDimitris Papastamos /* Write the group 0 counter identified by the given `idx` with `val`. */ 800767d50eSDimitris Papastamos void amu_group0_cnt_write(int idx, uint64_t val) 810767d50eSDimitris Papastamos { 820767d50eSDimitris Papastamos assert(amu_supported()); 830767d50eSDimitris Papastamos assert(idx >= 0 && idx < AMU_GROUP0_NR_COUNTERS); 840767d50eSDimitris Papastamos 850767d50eSDimitris Papastamos amu_group0_cnt_write_internal(idx, val); 860767d50eSDimitris Papastamos isb(); 870767d50eSDimitris Papastamos } 880767d50eSDimitris Papastamos 890767d50eSDimitris Papastamos /* Read the group 1 counter identified by the given `idx`. */ 900767d50eSDimitris Papastamos uint64_t amu_group1_cnt_read(int idx) 910767d50eSDimitris Papastamos { 920767d50eSDimitris Papastamos assert(amu_supported()); 930767d50eSDimitris Papastamos assert(idx >= 0 && idx < AMU_GROUP1_NR_COUNTERS); 940767d50eSDimitris Papastamos 950767d50eSDimitris Papastamos return amu_group1_cnt_read_internal(idx); 960767d50eSDimitris Papastamos } 970767d50eSDimitris Papastamos 980767d50eSDimitris Papastamos /* Write the group 1 counter identified by the given `idx` with `val`. */ 990767d50eSDimitris Papastamos void amu_group1_cnt_write(int idx, uint64_t val) 1000767d50eSDimitris Papastamos { 1010767d50eSDimitris Papastamos assert(amu_supported()); 1020767d50eSDimitris Papastamos assert(idx >= 0 && idx < AMU_GROUP1_NR_COUNTERS); 1030767d50eSDimitris Papastamos 1040767d50eSDimitris Papastamos amu_group1_cnt_write_internal(idx, val); 1050767d50eSDimitris Papastamos isb(); 1060767d50eSDimitris Papastamos } 1070767d50eSDimitris Papastamos 1080767d50eSDimitris Papastamos /* 1090767d50eSDimitris Papastamos * Program the event type register for the given `idx` with 1100767d50eSDimitris Papastamos * the event number `val`. 1110767d50eSDimitris Papastamos */ 1120767d50eSDimitris Papastamos void amu_group1_set_evtype(int idx, unsigned int val) 1130767d50eSDimitris Papastamos { 1140767d50eSDimitris Papastamos assert(amu_supported()); 1150767d50eSDimitris Papastamos assert (idx >= 0 && idx < AMU_GROUP1_NR_COUNTERS); 1160767d50eSDimitris Papastamos 1170767d50eSDimitris Papastamos amu_group1_set_evtype_internal(idx, val); 1180767d50eSDimitris Papastamos isb(); 119380559c1SDimitris Papastamos } 120*b6eb3932SDimitris Papastamos 121*b6eb3932SDimitris Papastamos static void *amu_context_save(const void *arg) 122*b6eb3932SDimitris Papastamos { 123*b6eb3932SDimitris Papastamos struct amu_ctx *ctx = &amu_ctxs[plat_my_core_pos()]; 124*b6eb3932SDimitris Papastamos int i; 125*b6eb3932SDimitris Papastamos 126*b6eb3932SDimitris Papastamos if (!amu_supported()) 127*b6eb3932SDimitris Papastamos return (void *)-1; 128*b6eb3932SDimitris Papastamos 129*b6eb3932SDimitris Papastamos /* Assert that group 0/1 counter configuration is what we expect */ 130*b6eb3932SDimitris Papastamos assert(read_amcntenset0_el0() == AMU_GROUP0_COUNTERS_MASK && 131*b6eb3932SDimitris Papastamos read_amcntenset1_el0() == AMU_GROUP1_COUNTERS_MASK); 132*b6eb3932SDimitris Papastamos 133*b6eb3932SDimitris Papastamos assert((sizeof(int) * 8) - __builtin_clz(AMU_GROUP1_COUNTERS_MASK) 134*b6eb3932SDimitris Papastamos <= AMU_GROUP1_NR_COUNTERS); 135*b6eb3932SDimitris Papastamos 136*b6eb3932SDimitris Papastamos /* 137*b6eb3932SDimitris Papastamos * Disable group 0/1 counters to avoid other observers like SCP sampling 138*b6eb3932SDimitris Papastamos * counter values from the future via the memory mapped view. 139*b6eb3932SDimitris Papastamos */ 140*b6eb3932SDimitris Papastamos write_amcntenclr0_el0(AMU_GROUP0_COUNTERS_MASK); 141*b6eb3932SDimitris Papastamos write_amcntenclr1_el0(AMU_GROUP1_COUNTERS_MASK); 142*b6eb3932SDimitris Papastamos isb(); 143*b6eb3932SDimitris Papastamos 144*b6eb3932SDimitris Papastamos /* Save group 0 counters */ 145*b6eb3932SDimitris Papastamos for (i = 0; i < AMU_GROUP0_NR_COUNTERS; i++) 146*b6eb3932SDimitris Papastamos ctx->group0_cnts[i] = amu_group0_cnt_read(i); 147*b6eb3932SDimitris Papastamos 148*b6eb3932SDimitris Papastamos /* Save group 1 counters */ 149*b6eb3932SDimitris Papastamos for (i = 0; i < AMU_GROUP1_NR_COUNTERS; i++) 150*b6eb3932SDimitris Papastamos ctx->group1_cnts[i] = amu_group1_cnt_read(i); 151*b6eb3932SDimitris Papastamos 152*b6eb3932SDimitris Papastamos return 0; 153*b6eb3932SDimitris Papastamos } 154*b6eb3932SDimitris Papastamos 155*b6eb3932SDimitris Papastamos static void *amu_context_restore(const void *arg) 156*b6eb3932SDimitris Papastamos { 157*b6eb3932SDimitris Papastamos struct amu_ctx *ctx = &amu_ctxs[plat_my_core_pos()]; 158*b6eb3932SDimitris Papastamos int i; 159*b6eb3932SDimitris Papastamos 160*b6eb3932SDimitris Papastamos if (!amu_supported()) 161*b6eb3932SDimitris Papastamos return (void *)-1; 162*b6eb3932SDimitris Papastamos 163*b6eb3932SDimitris Papastamos /* Counters were disabled in `amu_context_save()` */ 164*b6eb3932SDimitris Papastamos assert(read_amcntenset0_el0() == 0 && read_amcntenset1_el0() == 0); 165*b6eb3932SDimitris Papastamos 166*b6eb3932SDimitris Papastamos assert((sizeof(int) * 8) - __builtin_clz(AMU_GROUP1_COUNTERS_MASK) 167*b6eb3932SDimitris Papastamos <= AMU_GROUP1_NR_COUNTERS); 168*b6eb3932SDimitris Papastamos 169*b6eb3932SDimitris Papastamos /* Restore group 0 counters */ 170*b6eb3932SDimitris Papastamos for (i = 0; i < AMU_GROUP0_NR_COUNTERS; i++) 171*b6eb3932SDimitris Papastamos if (AMU_GROUP0_COUNTERS_MASK & (1U << i)) 172*b6eb3932SDimitris Papastamos amu_group0_cnt_write(i, ctx->group0_cnts[i]); 173*b6eb3932SDimitris Papastamos 174*b6eb3932SDimitris Papastamos /* Restore group 1 counters */ 175*b6eb3932SDimitris Papastamos for (i = 0; i < AMU_GROUP1_NR_COUNTERS; i++) 176*b6eb3932SDimitris Papastamos if (AMU_GROUP1_COUNTERS_MASK & (1U << i)) 177*b6eb3932SDimitris Papastamos amu_group1_cnt_write(i, ctx->group1_cnts[i]); 178*b6eb3932SDimitris Papastamos isb(); 179*b6eb3932SDimitris Papastamos 180*b6eb3932SDimitris Papastamos /* Restore group 0/1 counter configuration */ 181*b6eb3932SDimitris Papastamos write_amcntenset0_el0(AMU_GROUP0_COUNTERS_MASK); 182*b6eb3932SDimitris Papastamos write_amcntenset1_el0(AMU_GROUP1_COUNTERS_MASK); 183*b6eb3932SDimitris Papastamos 184*b6eb3932SDimitris Papastamos return 0; 185*b6eb3932SDimitris Papastamos } 186*b6eb3932SDimitris Papastamos 187*b6eb3932SDimitris Papastamos SUBSCRIBE_TO_EVENT(psci_suspend_pwrdown_start, amu_context_save); 188*b6eb3932SDimitris Papastamos SUBSCRIBE_TO_EVENT(psci_suspend_pwrdown_finish, amu_context_restore); 189