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 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> 14*40daecc1SAntonio Nino Diaz #include <stdbool.h> 15380559c1SDimitris Papastamos 160767d50eSDimitris Papastamos #define AMU_GROUP0_NR_COUNTERS 4 170767d50eSDimitris Papastamos 18b6eb3932SDimitris Papastamos struct amu_ctx { 19b6eb3932SDimitris Papastamos uint64_t group0_cnts[AMU_GROUP0_NR_COUNTERS]; 20b6eb3932SDimitris Papastamos uint64_t group1_cnts[AMU_GROUP1_NR_COUNTERS]; 21b6eb3932SDimitris Papastamos }; 22b6eb3932SDimitris Papastamos 23b6eb3932SDimitris Papastamos static struct amu_ctx amu_ctxs[PLATFORM_CORE_COUNT]; 24b6eb3932SDimitris Papastamos 25*40daecc1SAntonio Nino Diaz bool amu_supported(void) 26380559c1SDimitris Papastamos { 27380559c1SDimitris Papastamos uint64_t features; 28380559c1SDimitris Papastamos 29380559c1SDimitris Papastamos features = read_id_aa64pfr0_el1() >> ID_AA64PFR0_AMU_SHIFT; 30*40daecc1SAntonio Nino Diaz return (features & ID_AA64PFR0_AMU_MASK) == 1U; 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 */ 37*40daecc1SAntonio Nino Diaz void amu_enable(bool el2_unused) 380767d50eSDimitris Papastamos { 39380559c1SDimitris Papastamos uint64_t v; 40380559c1SDimitris Papastamos 41*40daecc1SAntonio Nino Diaz if (!amu_supported()) 420767d50eSDimitris Papastamos return; 430767d50eSDimitris Papastamos 44380559c1SDimitris Papastamos if (el2_unused) { 45380559c1SDimitris Papastamos /* 46380559c1SDimitris Papastamos * CPTR_EL2.TAM: Set to zero so any accesses to 47380559c1SDimitris Papastamos * the Activity Monitor registers do not trap to EL2. 48380559c1SDimitris Papastamos */ 49380559c1SDimitris Papastamos v = read_cptr_el2(); 50380559c1SDimitris Papastamos v &= ~CPTR_EL2_TAM_BIT; 51380559c1SDimitris Papastamos write_cptr_el2(v); 52380559c1SDimitris Papastamos } 53380559c1SDimitris Papastamos 54380559c1SDimitris Papastamos /* 55380559c1SDimitris Papastamos * CPTR_EL3.TAM: Set to zero so that any accesses to 56380559c1SDimitris Papastamos * the Activity Monitor registers do not trap to EL3. 57380559c1SDimitris Papastamos */ 58380559c1SDimitris Papastamos v = read_cptr_el3(); 59380559c1SDimitris Papastamos v &= ~TAM_BIT; 60380559c1SDimitris Papastamos write_cptr_el3(v); 61380559c1SDimitris Papastamos 62380559c1SDimitris Papastamos /* Enable group 0 counters */ 63380559c1SDimitris Papastamos write_amcntenset0_el0(AMU_GROUP0_COUNTERS_MASK); 6459902b7cSDimitris Papastamos /* Enable group 1 counters */ 6559902b7cSDimitris Papastamos write_amcntenset1_el0(AMU_GROUP1_COUNTERS_MASK); 66380559c1SDimitris Papastamos } 670767d50eSDimitris Papastamos 680767d50eSDimitris Papastamos /* Read the group 0 counter identified by the given `idx`. */ 690767d50eSDimitris Papastamos uint64_t amu_group0_cnt_read(int idx) 700767d50eSDimitris Papastamos { 71*40daecc1SAntonio Nino Diaz assert(amu_supported()); 72*40daecc1SAntonio Nino Diaz assert((idx >= 0) && (idx < AMU_GROUP0_NR_COUNTERS)); 730767d50eSDimitris Papastamos 740767d50eSDimitris Papastamos return amu_group0_cnt_read_internal(idx); 750767d50eSDimitris Papastamos } 760767d50eSDimitris Papastamos 770767d50eSDimitris Papastamos /* Write the group 0 counter identified by the given `idx` with `val`. */ 780767d50eSDimitris Papastamos void amu_group0_cnt_write(int idx, uint64_t val) 790767d50eSDimitris Papastamos { 80*40daecc1SAntonio Nino Diaz assert(amu_supported()); 81*40daecc1SAntonio Nino Diaz assert((idx >= 0) && (idx < AMU_GROUP0_NR_COUNTERS)); 820767d50eSDimitris Papastamos 830767d50eSDimitris Papastamos amu_group0_cnt_write_internal(idx, val); 840767d50eSDimitris Papastamos isb(); 850767d50eSDimitris Papastamos } 860767d50eSDimitris Papastamos 870767d50eSDimitris Papastamos /* Read the group 1 counter identified by the given `idx`. */ 880767d50eSDimitris Papastamos uint64_t amu_group1_cnt_read(int idx) 890767d50eSDimitris Papastamos { 90*40daecc1SAntonio Nino Diaz assert(amu_supported()); 91*40daecc1SAntonio Nino Diaz assert((idx >= 0) && (idx < AMU_GROUP1_NR_COUNTERS)); 920767d50eSDimitris Papastamos 930767d50eSDimitris Papastamos return amu_group1_cnt_read_internal(idx); 940767d50eSDimitris Papastamos } 950767d50eSDimitris Papastamos 960767d50eSDimitris Papastamos /* Write the group 1 counter identified by the given `idx` with `val`. */ 970767d50eSDimitris Papastamos void amu_group1_cnt_write(int idx, uint64_t val) 980767d50eSDimitris Papastamos { 99*40daecc1SAntonio Nino Diaz assert(amu_supported()); 100*40daecc1SAntonio Nino Diaz assert((idx >= 0) && (idx < AMU_GROUP1_NR_COUNTERS)); 1010767d50eSDimitris Papastamos 1020767d50eSDimitris Papastamos amu_group1_cnt_write_internal(idx, val); 1030767d50eSDimitris Papastamos isb(); 1040767d50eSDimitris Papastamos } 1050767d50eSDimitris Papastamos 1060767d50eSDimitris Papastamos /* 1070767d50eSDimitris Papastamos * Program the event type register for the given `idx` with 1080767d50eSDimitris Papastamos * the event number `val`. 1090767d50eSDimitris Papastamos */ 1100767d50eSDimitris Papastamos void amu_group1_set_evtype(int idx, unsigned int val) 1110767d50eSDimitris Papastamos { 112*40daecc1SAntonio Nino Diaz assert(amu_supported()); 113*40daecc1SAntonio Nino Diaz assert((idx >= 0) && (idx < AMU_GROUP1_NR_COUNTERS)); 1140767d50eSDimitris Papastamos 1150767d50eSDimitris Papastamos amu_group1_set_evtype_internal(idx, val); 1160767d50eSDimitris Papastamos isb(); 117380559c1SDimitris Papastamos } 118b6eb3932SDimitris Papastamos 119b6eb3932SDimitris Papastamos static void *amu_context_save(const void *arg) 120b6eb3932SDimitris Papastamos { 121b6eb3932SDimitris Papastamos struct amu_ctx *ctx = &amu_ctxs[plat_my_core_pos()]; 122b6eb3932SDimitris Papastamos int i; 123b6eb3932SDimitris Papastamos 124*40daecc1SAntonio Nino Diaz if (!amu_supported()) 125b6eb3932SDimitris Papastamos return (void *)-1; 126b6eb3932SDimitris Papastamos 127b6eb3932SDimitris Papastamos /* Assert that group 0/1 counter configuration is what we expect */ 128*40daecc1SAntonio Nino Diaz assert((read_amcntenset0_el0() == AMU_GROUP0_COUNTERS_MASK) && 129*40daecc1SAntonio Nino Diaz (read_amcntenset1_el0() == AMU_GROUP1_COUNTERS_MASK)); 130b6eb3932SDimitris Papastamos 131*40daecc1SAntonio Nino Diaz assert(((sizeof(int) * 8) - __builtin_clz(AMU_GROUP1_COUNTERS_MASK)) 132b6eb3932SDimitris Papastamos <= AMU_GROUP1_NR_COUNTERS); 133b6eb3932SDimitris Papastamos 134b6eb3932SDimitris Papastamos /* 135b6eb3932SDimitris Papastamos * Disable group 0/1 counters to avoid other observers like SCP sampling 136b6eb3932SDimitris Papastamos * counter values from the future via the memory mapped view. 137b6eb3932SDimitris Papastamos */ 138b6eb3932SDimitris Papastamos write_amcntenclr0_el0(AMU_GROUP0_COUNTERS_MASK); 139b6eb3932SDimitris Papastamos write_amcntenclr1_el0(AMU_GROUP1_COUNTERS_MASK); 140b6eb3932SDimitris Papastamos isb(); 141b6eb3932SDimitris Papastamos 142b6eb3932SDimitris Papastamos /* Save group 0 counters */ 143b6eb3932SDimitris Papastamos for (i = 0; i < AMU_GROUP0_NR_COUNTERS; i++) 144b6eb3932SDimitris Papastamos ctx->group0_cnts[i] = amu_group0_cnt_read(i); 145b6eb3932SDimitris Papastamos 146b6eb3932SDimitris Papastamos /* Save group 1 counters */ 147b6eb3932SDimitris Papastamos for (i = 0; i < AMU_GROUP1_NR_COUNTERS; i++) 148b6eb3932SDimitris Papastamos ctx->group1_cnts[i] = amu_group1_cnt_read(i); 149b6eb3932SDimitris Papastamos 150*40daecc1SAntonio Nino Diaz return (void *)0; 151b6eb3932SDimitris Papastamos } 152b6eb3932SDimitris Papastamos 153b6eb3932SDimitris Papastamos static void *amu_context_restore(const void *arg) 154b6eb3932SDimitris Papastamos { 155b6eb3932SDimitris Papastamos struct amu_ctx *ctx = &amu_ctxs[plat_my_core_pos()]; 156b6eb3932SDimitris Papastamos int i; 157b6eb3932SDimitris Papastamos 158*40daecc1SAntonio Nino Diaz if (!amu_supported()) 159b6eb3932SDimitris Papastamos return (void *)-1; 160b6eb3932SDimitris Papastamos 161b6eb3932SDimitris Papastamos /* Counters were disabled in `amu_context_save()` */ 162*40daecc1SAntonio Nino Diaz assert((read_amcntenset0_el0() == 0U) && (read_amcntenset1_el0() == 0U)); 163b6eb3932SDimitris Papastamos 164*40daecc1SAntonio Nino Diaz assert(((sizeof(int) * 8U) - __builtin_clz(AMU_GROUP1_COUNTERS_MASK)) 165b6eb3932SDimitris Papastamos <= AMU_GROUP1_NR_COUNTERS); 166b6eb3932SDimitris Papastamos 167b6eb3932SDimitris Papastamos /* Restore group 0 counters */ 168b6eb3932SDimitris Papastamos for (i = 0; i < AMU_GROUP0_NR_COUNTERS; i++) 169*40daecc1SAntonio Nino Diaz if ((AMU_GROUP0_COUNTERS_MASK & (1U << i)) != 0U) 170b6eb3932SDimitris Papastamos amu_group0_cnt_write(i, ctx->group0_cnts[i]); 171b6eb3932SDimitris Papastamos 172b6eb3932SDimitris Papastamos /* Restore group 1 counters */ 173b6eb3932SDimitris Papastamos for (i = 0; i < AMU_GROUP1_NR_COUNTERS; i++) 174*40daecc1SAntonio Nino Diaz if ((AMU_GROUP1_COUNTERS_MASK & (1U << i)) != 0U) 175b6eb3932SDimitris Papastamos amu_group1_cnt_write(i, ctx->group1_cnts[i]); 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 181*40daecc1SAntonio Nino Diaz return (void *)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