1ef69e1eaSDimitris Papastamos /* 2*873d4241Sjohpow01 * 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 21*873d4241Sjohpow01 /* 22*873d4241Sjohpow01 * Get AMU version value from pfr0. 23*873d4241Sjohpow01 * Return values 24*873d4241Sjohpow01 * ID_PFR0_AMU_V1: FEAT_AMUv1 supported (introduced in ARM v8.4) 25*873d4241Sjohpow01 * ID_PFR0_AMU_V1P1: FEAT_AMUv1p1 supported (introduced in ARM v8.6) 26*873d4241Sjohpow01 * ID_PFR0_AMU_NOT_SUPPORTED: not supported 27*873d4241Sjohpow01 */ 28*873d4241Sjohpow01 unsigned int amu_get_version(void) 29ef69e1eaSDimitris Papastamos { 30*873d4241Sjohpow01 return (unsigned int)(read_id_pfr0() >> ID_PFR0_AMU_SHIFT) & 31*873d4241Sjohpow01 ID_PFR0_AMU_MASK; 32c70da546SJoel Hutton } 33c70da546SJoel Hutton 34f3ccf036SAlexei Fedorov #if AMU_GROUP1_NR_COUNTERS 35f3ccf036SAlexei Fedorov /* Check if group 1 counters is implemented */ 36f3ccf036SAlexei Fedorov 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 { 50*873d4241Sjohpow01 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 94*873d4241Sjohpow01 95*873d4241Sjohpow01 /* Initialize FEAT_AMUv1p1 features if present. */ 96*873d4241Sjohpow01 if (amu_get_version() < ID_PFR0_AMU_V1P1) { 97*873d4241Sjohpow01 return; 98*873d4241Sjohpow01 } 99*873d4241Sjohpow01 100*873d4241Sjohpow01 #if AMU_RESTRICT_COUNTERS 101*873d4241Sjohpow01 /* 102*873d4241Sjohpow01 * FEAT_AMUv1p1 adds a register field to restrict access to group 1 103*873d4241Sjohpow01 * counters at all but the highest implemented EL. This is controlled 104*873d4241Sjohpow01 * with the AMU_RESTRICT_COUNTERS compile time flag, when set, system 105*873d4241Sjohpow01 * register reads at lower ELs return zero. Reads from the memory 106*873d4241Sjohpow01 * mapped view are unaffected. 107*873d4241Sjohpow01 */ 108*873d4241Sjohpow01 VERBOSE("AMU group 1 counter access restricted.\n"); 109*873d4241Sjohpow01 write_amcr(read_amcr() | AMCR_CG1RZ_BIT); 110*873d4241Sjohpow01 #else 111*873d4241Sjohpow01 write_amcr(read_amcr() & ~AMCR_CG1RZ_BIT); 112*873d4241Sjohpow01 #endif 113c70da546SJoel Hutton } 114c70da546SJoel Hutton 115c70da546SJoel Hutton /* Read the group 0 counter identified by the given `idx`. */ 116f3ccf036SAlexei Fedorov uint64_t amu_group0_cnt_read(unsigned int idx) 117c70da546SJoel Hutton { 118*873d4241Sjohpow01 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` */ 125f3ccf036SAlexei Fedorov void amu_group0_cnt_write(unsigned int idx, uint64_t val) 126c70da546SJoel Hutton { 127*873d4241Sjohpow01 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` */ 136f3ccf036SAlexei Fedorov uint64_t amu_group1_cnt_read(unsigned int idx) 137c70da546SJoel Hutton { 138*873d4241Sjohpow01 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` */ 146f3ccf036SAlexei Fedorov void amu_group1_cnt_write(unsigned int idx, uint64_t val) 147c70da546SJoel Hutton { 148*873d4241Sjohpow01 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 } 155c70da546SJoel Hutton 156f3ccf036SAlexei Fedorov /* 157f3ccf036SAlexei Fedorov * Program the event type register for the given `idx` with 158f3ccf036SAlexei Fedorov * the event number `val` 159f3ccf036SAlexei Fedorov */ 160f3ccf036SAlexei Fedorov void amu_group1_set_evtype(unsigned int idx, unsigned int val) 161c70da546SJoel Hutton { 162*873d4241Sjohpow01 assert(amu_get_version() != ID_PFR0_AMU_NOT_SUPPORTED); 163f3ccf036SAlexei Fedorov assert(amu_group1_supported()); 164f3ccf036SAlexei Fedorov assert(idx < AMU_GROUP1_NR_COUNTERS); 165c70da546SJoel Hutton 166c70da546SJoel Hutton amu_group1_set_evtype_internal(idx, val); 167c70da546SJoel Hutton isb(); 168ef69e1eaSDimitris Papastamos } 169f3ccf036SAlexei Fedorov #endif /* AMU_GROUP1_NR_COUNTERS */ 170b6eb3932SDimitris Papastamos 171b6eb3932SDimitris Papastamos static void *amu_context_save(const void *arg) 172b6eb3932SDimitris Papastamos { 173f3ccf036SAlexei Fedorov struct amu_ctx *ctx = &amu_ctxs[plat_my_core_pos()]; 174f3ccf036SAlexei Fedorov unsigned int i; 175b6eb3932SDimitris Papastamos 176*873d4241Sjohpow01 if (amu_get_version() == ID_PFR0_AMU_NOT_SUPPORTED) { 177b6eb3932SDimitris Papastamos return (void *)-1; 178f3ccf036SAlexei Fedorov } 179b6eb3932SDimitris Papastamos 180f3ccf036SAlexei Fedorov #if AMU_GROUP1_NR_COUNTERS 181f3ccf036SAlexei Fedorov if (!amu_group1_supported()) { 182f3ccf036SAlexei Fedorov return (void *)-1; 183f3ccf036SAlexei Fedorov } 184f3ccf036SAlexei Fedorov #endif 185f3ccf036SAlexei Fedorov /* Assert that group 0/1 counter configuration is what we expect */ 186f3ccf036SAlexei Fedorov assert(read_amcntenset0_el0() == AMU_GROUP0_COUNTERS_MASK); 187b6eb3932SDimitris Papastamos 188f3ccf036SAlexei Fedorov #if AMU_GROUP1_NR_COUNTERS 189f3ccf036SAlexei Fedorov assert(read_amcntenset1_el0() == AMU_GROUP1_COUNTERS_MASK); 190f3ccf036SAlexei Fedorov #endif 191b6eb3932SDimitris Papastamos /* 192f3ccf036SAlexei Fedorov * Disable group 0/1 counters to avoid other observers like SCP sampling 193b6eb3932SDimitris Papastamos * counter values from the future via the memory mapped view. 194b6eb3932SDimitris Papastamos */ 195b6eb3932SDimitris Papastamos write_amcntenclr0(AMU_GROUP0_COUNTERS_MASK); 196f3ccf036SAlexei Fedorov 197f3ccf036SAlexei Fedorov #if AMU_GROUP1_NR_COUNTERS 198c70da546SJoel Hutton write_amcntenclr1(AMU_GROUP1_COUNTERS_MASK); 199f3ccf036SAlexei Fedorov #endif 200b6eb3932SDimitris Papastamos isb(); 201b6eb3932SDimitris Papastamos 202f3ccf036SAlexei Fedorov /* Save all group 0 counters */ 203f3ccf036SAlexei Fedorov for (i = 0U; i < AMU_GROUP0_NR_COUNTERS; i++) { 204c70da546SJoel Hutton ctx->group0_cnts[i] = amu_group0_cnt_read(i); 205f3ccf036SAlexei Fedorov } 206c70da546SJoel Hutton 207f3ccf036SAlexei Fedorov #if AMU_GROUP1_NR_COUNTERS 208f3ccf036SAlexei Fedorov /* Save group 1 counters */ 209f3ccf036SAlexei Fedorov for (i = 0U; i < AMU_GROUP1_NR_COUNTERS; i++) { 210f3ccf036SAlexei Fedorov if ((AMU_GROUP1_COUNTERS_MASK & (1U << i)) != 0U) { 211c70da546SJoel Hutton ctx->group1_cnts[i] = amu_group1_cnt_read(i); 212f3ccf036SAlexei Fedorov } 213f3ccf036SAlexei Fedorov } 214f3ccf036SAlexei Fedorov #endif 21540daecc1SAntonio Nino Diaz return (void *)0; 216b6eb3932SDimitris Papastamos } 217b6eb3932SDimitris Papastamos 218b6eb3932SDimitris Papastamos static void *amu_context_restore(const void *arg) 219b6eb3932SDimitris Papastamos { 220f3ccf036SAlexei Fedorov struct amu_ctx *ctx = &amu_ctxs[plat_my_core_pos()]; 221f3ccf036SAlexei Fedorov unsigned int i; 222b6eb3932SDimitris Papastamos 223*873d4241Sjohpow01 if (amu_get_version() == ID_PFR0_AMU_NOT_SUPPORTED) { 224b6eb3932SDimitris Papastamos return (void *)-1; 225f3ccf036SAlexei Fedorov } 226b6eb3932SDimitris Papastamos 227f3ccf036SAlexei Fedorov #if AMU_GROUP1_NR_COUNTERS 228f3ccf036SAlexei Fedorov if (!amu_group1_supported()) { 229f3ccf036SAlexei Fedorov return (void *)-1; 230f3ccf036SAlexei Fedorov } 231f3ccf036SAlexei Fedorov #endif 232b6eb3932SDimitris Papastamos /* Counters were disabled in `amu_context_save()` */ 233f3ccf036SAlexei Fedorov assert(read_amcntenset0_el0() == 0U); 234b6eb3932SDimitris Papastamos 235f3ccf036SAlexei Fedorov #if AMU_GROUP1_NR_COUNTERS 236f3ccf036SAlexei Fedorov assert(read_amcntenset1_el0() == 0U); 237f3ccf036SAlexei Fedorov #endif 238f3ccf036SAlexei Fedorov 239f3ccf036SAlexei Fedorov /* Restore all group 0 counters */ 240f3ccf036SAlexei Fedorov for (i = 0U; i < AMU_GROUP0_NR_COUNTERS; i++) { 241c70da546SJoel Hutton amu_group0_cnt_write(i, ctx->group0_cnts[i]); 242f3ccf036SAlexei Fedorov } 243b6eb3932SDimitris Papastamos 244f3ccf036SAlexei Fedorov /* Restore group 0 counter configuration */ 245b6eb3932SDimitris Papastamos write_amcntenset0(AMU_GROUP0_COUNTERS_MASK); 246b6eb3932SDimitris Papastamos 247f3ccf036SAlexei Fedorov #if AMU_GROUP1_NR_COUNTERS 248f3ccf036SAlexei Fedorov /* Restore group 1 counters */ 249f3ccf036SAlexei Fedorov for (i = 0U; i < AMU_GROUP1_NR_COUNTERS; i++) { 250f3ccf036SAlexei Fedorov if ((AMU_GROUP1_COUNTERS_MASK & (1U << i)) != 0U) { 251f3ccf036SAlexei Fedorov amu_group1_cnt_write(i, ctx->group1_cnts[i]); 252f3ccf036SAlexei Fedorov } 253f3ccf036SAlexei Fedorov } 254f3ccf036SAlexei Fedorov 255f3ccf036SAlexei Fedorov /* Restore group 1 counter configuration */ 256c70da546SJoel Hutton write_amcntenset1(AMU_GROUP1_COUNTERS_MASK); 257f3ccf036SAlexei Fedorov #endif 258f3ccf036SAlexei Fedorov 25940daecc1SAntonio Nino Diaz return (void *)0; 260b6eb3932SDimitris Papastamos } 261b6eb3932SDimitris Papastamos 262b6eb3932SDimitris Papastamos SUBSCRIBE_TO_EVENT(psci_suspend_pwrdown_start, amu_context_save); 263b6eb3932SDimitris Papastamos SUBSCRIBE_TO_EVENT(psci_suspend_pwrdown_finish, amu_context_restore); 264