1380559c1SDimitris Papastamos /* 2c6cc9ac3SDimitris 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 7*09d40e0eSAntonio Nino Diaz #include <assert.h> 8*09d40e0eSAntonio Nino Diaz #include <stdbool.h> 9*09d40e0eSAntonio Nino Diaz 10380559c1SDimitris Papastamos #include <arch.h> 11380559c1SDimitris Papastamos #include <arch_helpers.h> 12*09d40e0eSAntonio Nino Diaz #include <lib/el3_runtime/pubsub_events.h> 13*09d40e0eSAntonio Nino Diaz #include <lib/extensions/amu.h> 14*09d40e0eSAntonio Nino Diaz #include <lib/extensions/amu_private.h> 15*09d40e0eSAntonio Nino Diaz #include <plat/common/platform.h> 16380559c1SDimitris Papastamos 170767d50eSDimitris Papastamos #define AMU_GROUP0_NR_COUNTERS 4 180767d50eSDimitris Papastamos 19b6eb3932SDimitris Papastamos struct amu_ctx { 20b6eb3932SDimitris Papastamos uint64_t group0_cnts[AMU_GROUP0_NR_COUNTERS]; 21b6eb3932SDimitris Papastamos uint64_t group1_cnts[AMU_GROUP1_NR_COUNTERS]; 22b6eb3932SDimitris Papastamos }; 23b6eb3932SDimitris Papastamos 24b6eb3932SDimitris Papastamos static struct amu_ctx amu_ctxs[PLATFORM_CORE_COUNT]; 25b6eb3932SDimitris Papastamos 2640daecc1SAntonio Nino Diaz bool amu_supported(void) 27380559c1SDimitris Papastamos { 28380559c1SDimitris Papastamos uint64_t features; 29380559c1SDimitris Papastamos 30380559c1SDimitris Papastamos features = read_id_aa64pfr0_el1() >> ID_AA64PFR0_AMU_SHIFT; 3140daecc1SAntonio Nino Diaz return (features & ID_AA64PFR0_AMU_MASK) == 1U; 320767d50eSDimitris Papastamos } 330767d50eSDimitris Papastamos 340767d50eSDimitris Papastamos /* 350767d50eSDimitris Papastamos * Enable counters. This function is meant to be invoked 360767d50eSDimitris Papastamos * by the context management library before exiting from EL3. 370767d50eSDimitris Papastamos */ 3840daecc1SAntonio Nino Diaz void amu_enable(bool el2_unused) 390767d50eSDimitris Papastamos { 40380559c1SDimitris Papastamos uint64_t v; 41380559c1SDimitris Papastamos 4240daecc1SAntonio Nino Diaz if (!amu_supported()) 430767d50eSDimitris Papastamos return; 440767d50eSDimitris Papastamos 45380559c1SDimitris Papastamos if (el2_unused) { 46380559c1SDimitris Papastamos /* 47380559c1SDimitris Papastamos * CPTR_EL2.TAM: Set to zero so any accesses to 48380559c1SDimitris Papastamos * the Activity Monitor registers do not trap to EL2. 49380559c1SDimitris Papastamos */ 50380559c1SDimitris Papastamos v = read_cptr_el2(); 51380559c1SDimitris Papastamos v &= ~CPTR_EL2_TAM_BIT; 52380559c1SDimitris Papastamos write_cptr_el2(v); 53380559c1SDimitris Papastamos } 54380559c1SDimitris Papastamos 55380559c1SDimitris Papastamos /* 56380559c1SDimitris Papastamos * CPTR_EL3.TAM: Set to zero so that any accesses to 57380559c1SDimitris Papastamos * the Activity Monitor registers do not trap to EL3. 58380559c1SDimitris Papastamos */ 59380559c1SDimitris Papastamos v = read_cptr_el3(); 60380559c1SDimitris Papastamos v &= ~TAM_BIT; 61380559c1SDimitris Papastamos write_cptr_el3(v); 62380559c1SDimitris Papastamos 63380559c1SDimitris Papastamos /* Enable group 0 counters */ 64380559c1SDimitris Papastamos write_amcntenset0_el0(AMU_GROUP0_COUNTERS_MASK); 6559902b7cSDimitris Papastamos /* Enable group 1 counters */ 6659902b7cSDimitris Papastamos write_amcntenset1_el0(AMU_GROUP1_COUNTERS_MASK); 67380559c1SDimitris Papastamos } 680767d50eSDimitris Papastamos 690767d50eSDimitris Papastamos /* Read the group 0 counter identified by the given `idx`. */ 700767d50eSDimitris Papastamos uint64_t amu_group0_cnt_read(int idx) 710767d50eSDimitris Papastamos { 7240daecc1SAntonio Nino Diaz assert(amu_supported()); 7340daecc1SAntonio Nino Diaz assert((idx >= 0) && (idx < AMU_GROUP0_NR_COUNTERS)); 740767d50eSDimitris Papastamos 750767d50eSDimitris Papastamos return amu_group0_cnt_read_internal(idx); 760767d50eSDimitris Papastamos } 770767d50eSDimitris Papastamos 780767d50eSDimitris Papastamos /* Write the group 0 counter identified by the given `idx` with `val`. */ 790767d50eSDimitris Papastamos void amu_group0_cnt_write(int idx, uint64_t val) 800767d50eSDimitris Papastamos { 8140daecc1SAntonio Nino Diaz assert(amu_supported()); 8240daecc1SAntonio Nino Diaz assert((idx >= 0) && (idx < AMU_GROUP0_NR_COUNTERS)); 830767d50eSDimitris Papastamos 840767d50eSDimitris Papastamos amu_group0_cnt_write_internal(idx, val); 850767d50eSDimitris Papastamos isb(); 860767d50eSDimitris Papastamos } 870767d50eSDimitris Papastamos 880767d50eSDimitris Papastamos /* Read the group 1 counter identified by the given `idx`. */ 890767d50eSDimitris Papastamos uint64_t amu_group1_cnt_read(int idx) 900767d50eSDimitris Papastamos { 9140daecc1SAntonio Nino Diaz assert(amu_supported()); 9240daecc1SAntonio Nino Diaz assert((idx >= 0) && (idx < AMU_GROUP1_NR_COUNTERS)); 930767d50eSDimitris Papastamos 940767d50eSDimitris Papastamos return amu_group1_cnt_read_internal(idx); 950767d50eSDimitris Papastamos } 960767d50eSDimitris Papastamos 970767d50eSDimitris Papastamos /* Write the group 1 counter identified by the given `idx` with `val`. */ 980767d50eSDimitris Papastamos void amu_group1_cnt_write(int idx, uint64_t val) 990767d50eSDimitris Papastamos { 10040daecc1SAntonio Nino Diaz assert(amu_supported()); 10140daecc1SAntonio Nino Diaz assert((idx >= 0) && (idx < AMU_GROUP1_NR_COUNTERS)); 1020767d50eSDimitris Papastamos 1030767d50eSDimitris Papastamos amu_group1_cnt_write_internal(idx, val); 1040767d50eSDimitris Papastamos isb(); 1050767d50eSDimitris Papastamos } 1060767d50eSDimitris Papastamos 1070767d50eSDimitris Papastamos /* 1080767d50eSDimitris Papastamos * Program the event type register for the given `idx` with 1090767d50eSDimitris Papastamos * the event number `val`. 1100767d50eSDimitris Papastamos */ 1110767d50eSDimitris Papastamos void amu_group1_set_evtype(int idx, unsigned int val) 1120767d50eSDimitris Papastamos { 11340daecc1SAntonio Nino Diaz assert(amu_supported()); 11440daecc1SAntonio Nino Diaz assert((idx >= 0) && (idx < AMU_GROUP1_NR_COUNTERS)); 1150767d50eSDimitris Papastamos 1160767d50eSDimitris Papastamos amu_group1_set_evtype_internal(idx, val); 1170767d50eSDimitris Papastamos isb(); 118380559c1SDimitris Papastamos } 119b6eb3932SDimitris Papastamos 120b6eb3932SDimitris Papastamos static void *amu_context_save(const void *arg) 121b6eb3932SDimitris Papastamos { 122b6eb3932SDimitris Papastamos struct amu_ctx *ctx = &amu_ctxs[plat_my_core_pos()]; 123b6eb3932SDimitris Papastamos int i; 124b6eb3932SDimitris Papastamos 12540daecc1SAntonio Nino Diaz if (!amu_supported()) 126b6eb3932SDimitris Papastamos return (void *)-1; 127b6eb3932SDimitris Papastamos 128b6eb3932SDimitris Papastamos /* Assert that group 0/1 counter configuration is what we expect */ 12940daecc1SAntonio Nino Diaz assert((read_amcntenset0_el0() == AMU_GROUP0_COUNTERS_MASK) && 13040daecc1SAntonio Nino Diaz (read_amcntenset1_el0() == AMU_GROUP1_COUNTERS_MASK)); 131b6eb3932SDimitris Papastamos 13240daecc1SAntonio Nino Diaz assert(((sizeof(int) * 8) - __builtin_clz(AMU_GROUP1_COUNTERS_MASK)) 133b6eb3932SDimitris Papastamos <= AMU_GROUP1_NR_COUNTERS); 134b6eb3932SDimitris Papastamos 135b6eb3932SDimitris Papastamos /* 136b6eb3932SDimitris Papastamos * Disable group 0/1 counters to avoid other observers like SCP sampling 137b6eb3932SDimitris Papastamos * counter values from the future via the memory mapped view. 138b6eb3932SDimitris Papastamos */ 139b6eb3932SDimitris Papastamos write_amcntenclr0_el0(AMU_GROUP0_COUNTERS_MASK); 140b6eb3932SDimitris Papastamos write_amcntenclr1_el0(AMU_GROUP1_COUNTERS_MASK); 141b6eb3932SDimitris Papastamos isb(); 142b6eb3932SDimitris Papastamos 143b6eb3932SDimitris Papastamos /* Save group 0 counters */ 144b6eb3932SDimitris Papastamos for (i = 0; i < AMU_GROUP0_NR_COUNTERS; i++) 145b6eb3932SDimitris Papastamos ctx->group0_cnts[i] = amu_group0_cnt_read(i); 146b6eb3932SDimitris Papastamos 147b6eb3932SDimitris Papastamos /* Save group 1 counters */ 148b6eb3932SDimitris Papastamos for (i = 0; i < AMU_GROUP1_NR_COUNTERS; i++) 149b6eb3932SDimitris Papastamos ctx->group1_cnts[i] = amu_group1_cnt_read(i); 150b6eb3932SDimitris Papastamos 15140daecc1SAntonio Nino Diaz return (void *)0; 152b6eb3932SDimitris Papastamos } 153b6eb3932SDimitris Papastamos 154b6eb3932SDimitris Papastamos static void *amu_context_restore(const void *arg) 155b6eb3932SDimitris Papastamos { 156b6eb3932SDimitris Papastamos struct amu_ctx *ctx = &amu_ctxs[plat_my_core_pos()]; 157b6eb3932SDimitris Papastamos int i; 158b6eb3932SDimitris Papastamos 15940daecc1SAntonio Nino Diaz if (!amu_supported()) 160b6eb3932SDimitris Papastamos return (void *)-1; 161b6eb3932SDimitris Papastamos 162b6eb3932SDimitris Papastamos /* Counters were disabled in `amu_context_save()` */ 16340daecc1SAntonio Nino Diaz assert((read_amcntenset0_el0() == 0U) && (read_amcntenset1_el0() == 0U)); 164b6eb3932SDimitris Papastamos 16540daecc1SAntonio Nino Diaz assert(((sizeof(int) * 8U) - __builtin_clz(AMU_GROUP1_COUNTERS_MASK)) 166b6eb3932SDimitris Papastamos <= AMU_GROUP1_NR_COUNTERS); 167b6eb3932SDimitris Papastamos 168b6eb3932SDimitris Papastamos /* Restore group 0 counters */ 169b6eb3932SDimitris Papastamos for (i = 0; i < AMU_GROUP0_NR_COUNTERS; i++) 17040daecc1SAntonio Nino Diaz if ((AMU_GROUP0_COUNTERS_MASK & (1U << i)) != 0U) 171b6eb3932SDimitris Papastamos amu_group0_cnt_write(i, ctx->group0_cnts[i]); 172b6eb3932SDimitris Papastamos 173b6eb3932SDimitris Papastamos /* Restore group 1 counters */ 174b6eb3932SDimitris Papastamos for (i = 0; i < AMU_GROUP1_NR_COUNTERS; i++) 17540daecc1SAntonio Nino Diaz if ((AMU_GROUP1_COUNTERS_MASK & (1U << i)) != 0U) 176b6eb3932SDimitris Papastamos amu_group1_cnt_write(i, ctx->group1_cnts[i]); 177b6eb3932SDimitris Papastamos 178b6eb3932SDimitris Papastamos /* Restore group 0/1 counter configuration */ 179b6eb3932SDimitris Papastamos write_amcntenset0_el0(AMU_GROUP0_COUNTERS_MASK); 180b6eb3932SDimitris Papastamos write_amcntenset1_el0(AMU_GROUP1_COUNTERS_MASK); 181b6eb3932SDimitris Papastamos 18240daecc1SAntonio Nino Diaz return (void *)0; 183b6eb3932SDimitris Papastamos } 184b6eb3932SDimitris Papastamos 185b6eb3932SDimitris Papastamos SUBSCRIBE_TO_EVENT(psci_suspend_pwrdown_start, amu_context_save); 186b6eb3932SDimitris Papastamos SUBSCRIBE_TO_EVENT(psci_suspend_pwrdown_finish, amu_context_restore); 187