1ef69e1eaSDimitris Papastamos /* 2873d4241Sjohpow01 * Copyright (c) 2017-2021, ARM Limited and Contributors. All rights reserved. 3ef69e1eaSDimitris Papastamos * 4ef69e1eaSDimitris Papastamos * SPDX-License-Identifier: BSD-3-Clause 5ef69e1eaSDimitris Papastamos */ 6ef69e1eaSDimitris Papastamos 7f3ccf036SAlexei Fedorov #include <assert.h> 809d40e0eSAntonio Nino Diaz #include <stdbool.h> 909d40e0eSAntonio Nino Diaz 10ef69e1eaSDimitris Papastamos #include <arch.h> 11ef69e1eaSDimitris Papastamos #include <arch_helpers.h> 12f3ccf036SAlexei 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> 16f3ccf036SAlexei Fedorov 1709d40e0eSAntonio Nino Diaz #include <plat/common/platform.h> 18b6eb3932SDimitris Papastamos 19b6eb3932SDimitris Papastamos static struct amu_ctx amu_ctxs[PLATFORM_CORE_COUNT]; 20ef69e1eaSDimitris Papastamos 21873d4241Sjohpow01 /* 22873d4241Sjohpow01 * Get AMU version value from pfr0. 23873d4241Sjohpow01 * Return values 24873d4241Sjohpow01 * ID_PFR0_AMU_V1: FEAT_AMUv1 supported (introduced in ARM v8.4) 25873d4241Sjohpow01 * ID_PFR0_AMU_V1P1: FEAT_AMUv1p1 supported (introduced in ARM v8.6) 26873d4241Sjohpow01 * ID_PFR0_AMU_NOT_SUPPORTED: not supported 27873d4241Sjohpow01 */ 28*b4b726eaSChris Kay static unsigned int amu_get_version(void) 29ef69e1eaSDimitris Papastamos { 30873d4241Sjohpow01 return (unsigned int)(read_id_pfr0() >> ID_PFR0_AMU_SHIFT) & 31873d4241Sjohpow01 ID_PFR0_AMU_MASK; 32c70da546SJoel Hutton } 33c70da546SJoel Hutton 34f3ccf036SAlexei Fedorov #if AMU_GROUP1_NR_COUNTERS 35f3ccf036SAlexei Fedorov /* Check if group 1 counters is implemented */ 36*b4b726eaSChris Kay static bool amu_group1_supported(void) 37f3ccf036SAlexei Fedorov { 38f3ccf036SAlexei Fedorov uint32_t features = read_amcfgr() >> AMCFGR_NCG_SHIFT; 39f3ccf036SAlexei Fedorov 40f3ccf036SAlexei Fedorov return (features & AMCFGR_NCG_MASK) == 1U; 41f3ccf036SAlexei Fedorov } 42f3ccf036SAlexei Fedorov #endif 43f3ccf036SAlexei Fedorov 44f3ccf036SAlexei Fedorov /* 45f3ccf036SAlexei Fedorov * Enable counters. This function is meant to be invoked 46f3ccf036SAlexei Fedorov * by the context management library before exiting from EL3. 47f3ccf036SAlexei Fedorov */ 4840daecc1SAntonio Nino Diaz void amu_enable(bool el2_unused) 49c70da546SJoel Hutton { 50873d4241Sjohpow01 if (amu_get_version() == ID_PFR0_AMU_NOT_SUPPORTED) { 510767d50eSDimitris Papastamos return; 52f3ccf036SAlexei Fedorov } 53f3ccf036SAlexei Fedorov 54f3ccf036SAlexei Fedorov #if AMU_GROUP1_NR_COUNTERS 55f3ccf036SAlexei Fedorov /* Check and set presence of group 1 counters */ 56f3ccf036SAlexei Fedorov if (!amu_group1_supported()) { 57f3ccf036SAlexei Fedorov ERROR("AMU Counter Group 1 is not implemented\n"); 58f3ccf036SAlexei Fedorov panic(); 59f3ccf036SAlexei Fedorov } 60f3ccf036SAlexei Fedorov 61f3ccf036SAlexei Fedorov /* Check number of group 1 counters */ 62f3ccf036SAlexei Fedorov uint32_t cnt_num = (read_amcgcr() >> AMCGCR_CG1NC_SHIFT) & 63f3ccf036SAlexei Fedorov AMCGCR_CG1NC_MASK; 64f3ccf036SAlexei Fedorov VERBOSE("%s%u. %s%u\n", 65f3ccf036SAlexei Fedorov "Number of AMU Group 1 Counters ", cnt_num, 66f3ccf036SAlexei Fedorov "Requested number ", AMU_GROUP1_NR_COUNTERS); 67f3ccf036SAlexei Fedorov 68f3ccf036SAlexei Fedorov if (cnt_num < AMU_GROUP1_NR_COUNTERS) { 69f3ccf036SAlexei Fedorov ERROR("%s%u is less than %s%u\n", 70f3ccf036SAlexei Fedorov "Number of AMU Group 1 Counters ", cnt_num, 71f3ccf036SAlexei Fedorov "Requested number ", AMU_GROUP1_NR_COUNTERS); 72f3ccf036SAlexei Fedorov panic(); 73f3ccf036SAlexei Fedorov } 74f3ccf036SAlexei Fedorov #endif 750767d50eSDimitris Papastamos 76ef69e1eaSDimitris Papastamos if (el2_unused) { 77ef69e1eaSDimitris Papastamos uint64_t v; 78ef69e1eaSDimitris Papastamos /* 79ef69e1eaSDimitris Papastamos * Non-secure access from EL0 or EL1 to the Activity Monitor 80ef69e1eaSDimitris Papastamos * registers do not trap to EL2. 81ef69e1eaSDimitris Papastamos */ 82ef69e1eaSDimitris Papastamos v = read_hcptr(); 83ef69e1eaSDimitris Papastamos v &= ~TAM_BIT; 84ef69e1eaSDimitris Papastamos write_hcptr(v); 85ef69e1eaSDimitris Papastamos } 86ef69e1eaSDimitris Papastamos 87ef69e1eaSDimitris Papastamos /* Enable group 0 counters */ 88ef69e1eaSDimitris Papastamos write_amcntenset0(AMU_GROUP0_COUNTERS_MASK); 89c70da546SJoel Hutton 90f3ccf036SAlexei Fedorov #if AMU_GROUP1_NR_COUNTERS 91c70da546SJoel Hutton /* Enable group 1 counters */ 92c70da546SJoel Hutton write_amcntenset1(AMU_GROUP1_COUNTERS_MASK); 93f3ccf036SAlexei Fedorov #endif 94873d4241Sjohpow01 95873d4241Sjohpow01 /* Initialize FEAT_AMUv1p1 features if present. */ 96873d4241Sjohpow01 if (amu_get_version() < ID_PFR0_AMU_V1P1) { 97873d4241Sjohpow01 return; 98873d4241Sjohpow01 } 99873d4241Sjohpow01 100873d4241Sjohpow01 #if AMU_RESTRICT_COUNTERS 101873d4241Sjohpow01 /* 102873d4241Sjohpow01 * FEAT_AMUv1p1 adds a register field to restrict access to group 1 103873d4241Sjohpow01 * counters at all but the highest implemented EL. This is controlled 104873d4241Sjohpow01 * with the AMU_RESTRICT_COUNTERS compile time flag, when set, system 105873d4241Sjohpow01 * register reads at lower ELs return zero. Reads from the memory 106873d4241Sjohpow01 * mapped view are unaffected. 107873d4241Sjohpow01 */ 108873d4241Sjohpow01 VERBOSE("AMU group 1 counter access restricted.\n"); 109873d4241Sjohpow01 write_amcr(read_amcr() | AMCR_CG1RZ_BIT); 110873d4241Sjohpow01 #else 111873d4241Sjohpow01 write_amcr(read_amcr() & ~AMCR_CG1RZ_BIT); 112873d4241Sjohpow01 #endif 113c70da546SJoel Hutton } 114c70da546SJoel Hutton 115c70da546SJoel Hutton /* Read the group 0 counter identified by the given `idx`. */ 116*b4b726eaSChris Kay static uint64_t amu_group0_cnt_read(unsigned int idx) 117c70da546SJoel Hutton { 118873d4241Sjohpow01 assert(amu_get_version() != ID_PFR0_AMU_NOT_SUPPORTED); 119f3ccf036SAlexei Fedorov assert(idx < AMU_GROUP0_NR_COUNTERS); 120c70da546SJoel Hutton 121c70da546SJoel Hutton return amu_group0_cnt_read_internal(idx); 122c70da546SJoel Hutton } 123c70da546SJoel Hutton 124f3ccf036SAlexei Fedorov /* Write the group 0 counter identified by the given `idx` with `val` */ 125*b4b726eaSChris Kay static void amu_group0_cnt_write(unsigned int idx, uint64_t val) 126c70da546SJoel Hutton { 127873d4241Sjohpow01 assert(amu_get_version() != ID_PFR0_AMU_NOT_SUPPORTED); 128f3ccf036SAlexei Fedorov assert(idx < AMU_GROUP0_NR_COUNTERS); 129c70da546SJoel Hutton 130c70da546SJoel Hutton amu_group0_cnt_write_internal(idx, val); 131c70da546SJoel Hutton isb(); 132c70da546SJoel Hutton } 133c70da546SJoel Hutton 134f3ccf036SAlexei Fedorov #if AMU_GROUP1_NR_COUNTERS 135f3ccf036SAlexei Fedorov /* Read the group 1 counter identified by the given `idx` */ 136*b4b726eaSChris Kay static uint64_t amu_group1_cnt_read(unsigned int idx) 137c70da546SJoel Hutton { 138873d4241Sjohpow01 assert(amu_get_version() != ID_PFR0_AMU_NOT_SUPPORTED); 139f3ccf036SAlexei Fedorov assert(amu_group1_supported()); 140f3ccf036SAlexei Fedorov assert(idx < AMU_GROUP1_NR_COUNTERS); 141c70da546SJoel Hutton 142c70da546SJoel Hutton return amu_group1_cnt_read_internal(idx); 143c70da546SJoel Hutton } 144c70da546SJoel Hutton 145f3ccf036SAlexei Fedorov /* Write the group 1 counter identified by the given `idx` with `val` */ 146*b4b726eaSChris Kay static void amu_group1_cnt_write(unsigned int idx, uint64_t val) 147c70da546SJoel Hutton { 148873d4241Sjohpow01 assert(amu_get_version() != ID_PFR0_AMU_NOT_SUPPORTED); 149f3ccf036SAlexei Fedorov assert(amu_group1_supported()); 150f3ccf036SAlexei Fedorov assert(idx < AMU_GROUP1_NR_COUNTERS); 151c70da546SJoel Hutton 152c70da546SJoel Hutton amu_group1_cnt_write_internal(idx, val); 153c70da546SJoel Hutton isb(); 154c70da546SJoel Hutton } 155f3ccf036SAlexei Fedorov #endif /* AMU_GROUP1_NR_COUNTERS */ 156b6eb3932SDimitris Papastamos 157b6eb3932SDimitris Papastamos static void *amu_context_save(const void *arg) 158b6eb3932SDimitris Papastamos { 159f3ccf036SAlexei Fedorov struct amu_ctx *ctx = &amu_ctxs[plat_my_core_pos()]; 160f3ccf036SAlexei Fedorov unsigned int i; 161b6eb3932SDimitris Papastamos 162873d4241Sjohpow01 if (amu_get_version() == ID_PFR0_AMU_NOT_SUPPORTED) { 163b6eb3932SDimitris Papastamos return (void *)-1; 164f3ccf036SAlexei Fedorov } 165b6eb3932SDimitris Papastamos 166f3ccf036SAlexei Fedorov #if AMU_GROUP1_NR_COUNTERS 167f3ccf036SAlexei Fedorov if (!amu_group1_supported()) { 168f3ccf036SAlexei Fedorov return (void *)-1; 169f3ccf036SAlexei Fedorov } 170f3ccf036SAlexei Fedorov #endif 171f3ccf036SAlexei Fedorov /* Assert that group 0/1 counter configuration is what we expect */ 172f3ccf036SAlexei Fedorov assert(read_amcntenset0_el0() == AMU_GROUP0_COUNTERS_MASK); 173b6eb3932SDimitris Papastamos 174f3ccf036SAlexei Fedorov #if AMU_GROUP1_NR_COUNTERS 175f3ccf036SAlexei Fedorov assert(read_amcntenset1_el0() == AMU_GROUP1_COUNTERS_MASK); 176f3ccf036SAlexei Fedorov #endif 177b6eb3932SDimitris Papastamos /* 178f3ccf036SAlexei Fedorov * Disable group 0/1 counters to avoid other observers like SCP sampling 179b6eb3932SDimitris Papastamos * counter values from the future via the memory mapped view. 180b6eb3932SDimitris Papastamos */ 181b6eb3932SDimitris Papastamos write_amcntenclr0(AMU_GROUP0_COUNTERS_MASK); 182f3ccf036SAlexei Fedorov 183f3ccf036SAlexei Fedorov #if AMU_GROUP1_NR_COUNTERS 184c70da546SJoel Hutton write_amcntenclr1(AMU_GROUP1_COUNTERS_MASK); 185f3ccf036SAlexei Fedorov #endif 186b6eb3932SDimitris Papastamos isb(); 187b6eb3932SDimitris Papastamos 188f3ccf036SAlexei Fedorov /* Save all group 0 counters */ 189f3ccf036SAlexei Fedorov for (i = 0U; i < AMU_GROUP0_NR_COUNTERS; i++) { 190c70da546SJoel Hutton ctx->group0_cnts[i] = amu_group0_cnt_read(i); 191f3ccf036SAlexei Fedorov } 192c70da546SJoel Hutton 193f3ccf036SAlexei Fedorov #if AMU_GROUP1_NR_COUNTERS 194f3ccf036SAlexei Fedorov /* Save group 1 counters */ 195f3ccf036SAlexei Fedorov for (i = 0U; i < AMU_GROUP1_NR_COUNTERS; i++) { 196f3ccf036SAlexei Fedorov if ((AMU_GROUP1_COUNTERS_MASK & (1U << i)) != 0U) { 197c70da546SJoel Hutton ctx->group1_cnts[i] = amu_group1_cnt_read(i); 198f3ccf036SAlexei Fedorov } 199f3ccf036SAlexei Fedorov } 200f3ccf036SAlexei Fedorov #endif 20140daecc1SAntonio Nino Diaz return (void *)0; 202b6eb3932SDimitris Papastamos } 203b6eb3932SDimitris Papastamos 204b6eb3932SDimitris Papastamos static void *amu_context_restore(const void *arg) 205b6eb3932SDimitris Papastamos { 206f3ccf036SAlexei Fedorov struct amu_ctx *ctx = &amu_ctxs[plat_my_core_pos()]; 207f3ccf036SAlexei Fedorov unsigned int i; 208b6eb3932SDimitris Papastamos 209873d4241Sjohpow01 if (amu_get_version() == ID_PFR0_AMU_NOT_SUPPORTED) { 210b6eb3932SDimitris Papastamos return (void *)-1; 211f3ccf036SAlexei Fedorov } 212b6eb3932SDimitris Papastamos 213f3ccf036SAlexei Fedorov #if AMU_GROUP1_NR_COUNTERS 214f3ccf036SAlexei Fedorov if (!amu_group1_supported()) { 215f3ccf036SAlexei Fedorov return (void *)-1; 216f3ccf036SAlexei Fedorov } 217f3ccf036SAlexei Fedorov #endif 218b6eb3932SDimitris Papastamos /* Counters were disabled in `amu_context_save()` */ 219f3ccf036SAlexei Fedorov assert(read_amcntenset0_el0() == 0U); 220b6eb3932SDimitris Papastamos 221f3ccf036SAlexei Fedorov #if AMU_GROUP1_NR_COUNTERS 222f3ccf036SAlexei Fedorov assert(read_amcntenset1_el0() == 0U); 223f3ccf036SAlexei Fedorov #endif 224f3ccf036SAlexei Fedorov 225f3ccf036SAlexei Fedorov /* Restore all group 0 counters */ 226f3ccf036SAlexei Fedorov for (i = 0U; i < AMU_GROUP0_NR_COUNTERS; i++) { 227c70da546SJoel Hutton amu_group0_cnt_write(i, ctx->group0_cnts[i]); 228f3ccf036SAlexei Fedorov } 229b6eb3932SDimitris Papastamos 230f3ccf036SAlexei Fedorov /* Restore group 0 counter configuration */ 231b6eb3932SDimitris Papastamos write_amcntenset0(AMU_GROUP0_COUNTERS_MASK); 232b6eb3932SDimitris Papastamos 233f3ccf036SAlexei Fedorov #if AMU_GROUP1_NR_COUNTERS 234f3ccf036SAlexei Fedorov /* Restore group 1 counters */ 235f3ccf036SAlexei Fedorov for (i = 0U; i < AMU_GROUP1_NR_COUNTERS; i++) { 236f3ccf036SAlexei Fedorov if ((AMU_GROUP1_COUNTERS_MASK & (1U << i)) != 0U) { 237f3ccf036SAlexei Fedorov amu_group1_cnt_write(i, ctx->group1_cnts[i]); 238f3ccf036SAlexei Fedorov } 239f3ccf036SAlexei Fedorov } 240f3ccf036SAlexei Fedorov 241f3ccf036SAlexei Fedorov /* Restore group 1 counter configuration */ 242c70da546SJoel Hutton write_amcntenset1(AMU_GROUP1_COUNTERS_MASK); 243f3ccf036SAlexei Fedorov #endif 244f3ccf036SAlexei Fedorov 24540daecc1SAntonio Nino Diaz return (void *)0; 246b6eb3932SDimitris Papastamos } 247b6eb3932SDimitris Papastamos 248b6eb3932SDimitris Papastamos SUBSCRIBE_TO_EVENT(psci_suspend_pwrdown_start, amu_context_save); 249b6eb3932SDimitris Papastamos SUBSCRIBE_TO_EVENT(psci_suspend_pwrdown_finish, amu_context_restore); 250