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> 833b9be6dSChris Kay #include <cdefs.h> 909d40e0eSAntonio Nino Diaz #include <stdbool.h> 1009d40e0eSAntonio Nino Diaz 11ef69e1eaSDimitris Papastamos #include <arch.h> 12ef69e1eaSDimitris Papastamos #include <arch_helpers.h> 13f3ccf036SAlexei Fedorov 1409d40e0eSAntonio Nino Diaz #include <lib/el3_runtime/pubsub_events.h> 1509d40e0eSAntonio Nino Diaz #include <lib/extensions/amu.h> 1609d40e0eSAntonio Nino Diaz #include <lib/extensions/amu_private.h> 17f3ccf036SAlexei Fedorov 1809d40e0eSAntonio Nino Diaz #include <plat/common/platform.h> 19b6eb3932SDimitris Papastamos 20b6eb3932SDimitris Papastamos static struct amu_ctx amu_ctxs[PLATFORM_CORE_COUNT]; 21ef69e1eaSDimitris Papastamos 2233b9be6dSChris Kay static inline __unused uint32_t read_id_pfr0_amu(void) 23ef69e1eaSDimitris Papastamos { 2433b9be6dSChris Kay return (read_id_pfr0() >> ID_PFR0_AMU_SHIFT) & 25873d4241Sjohpow01 ID_PFR0_AMU_MASK; 26c70da546SJoel Hutton } 27c70da546SJoel Hutton 2833b9be6dSChris Kay static inline __unused void write_hcptr_tam(uint32_t value) 2933b9be6dSChris Kay { 3033b9be6dSChris Kay write_hcptr((read_hcptr() & ~TAM_BIT) | 3133b9be6dSChris Kay ((value << TAM_SHIFT) & TAM_BIT)); 3233b9be6dSChris Kay } 3333b9be6dSChris Kay 3433b9be6dSChris Kay static inline __unused void write_amcr_cg1rz(uint32_t value) 3533b9be6dSChris Kay { 3633b9be6dSChris Kay write_amcr((read_amcr() & ~AMCR_CG1RZ_BIT) | 3733b9be6dSChris Kay ((value << AMCR_CG1RZ_SHIFT) & AMCR_CG1RZ_BIT)); 3833b9be6dSChris Kay } 3933b9be6dSChris Kay 4033b9be6dSChris Kay static inline __unused uint32_t read_amcfgr_ncg(void) 4133b9be6dSChris Kay { 4233b9be6dSChris Kay return (read_amcfgr() >> AMCFGR_NCG_SHIFT) & 4333b9be6dSChris Kay AMCFGR_NCG_MASK; 4433b9be6dSChris Kay } 4533b9be6dSChris Kay 46*81e2ff1fSChris Kay static inline __unused uint32_t read_amcgcr_cg0nc(void) 47*81e2ff1fSChris Kay { 48*81e2ff1fSChris Kay return (read_amcgcr() >> AMCGCR_CG0NC_SHIFT) & 49*81e2ff1fSChris Kay AMCGCR_CG0NC_MASK; 50*81e2ff1fSChris Kay } 51*81e2ff1fSChris Kay 5233b9be6dSChris Kay static inline __unused uint32_t read_amcgcr_cg1nc(void) 5333b9be6dSChris Kay { 5433b9be6dSChris Kay return (read_amcgcr() >> AMCGCR_CG1NC_SHIFT) & 5533b9be6dSChris Kay AMCGCR_CG1NC_MASK; 5633b9be6dSChris Kay } 5733b9be6dSChris Kay 5833b9be6dSChris Kay static inline __unused uint32_t read_amcntenset0_px(void) 5933b9be6dSChris Kay { 6033b9be6dSChris Kay return (read_amcntenset0() >> AMCNTENSET0_Pn_SHIFT) & 6133b9be6dSChris Kay AMCNTENSET0_Pn_MASK; 6233b9be6dSChris Kay } 6333b9be6dSChris Kay 6433b9be6dSChris Kay static inline __unused uint32_t read_amcntenset1_px(void) 6533b9be6dSChris Kay { 6633b9be6dSChris Kay return (read_amcntenset1() >> AMCNTENSET1_Pn_SHIFT) & 6733b9be6dSChris Kay AMCNTENSET1_Pn_MASK; 6833b9be6dSChris Kay } 6933b9be6dSChris Kay 7033b9be6dSChris Kay static inline __unused void write_amcntenset0_px(uint32_t px) 7133b9be6dSChris Kay { 7233b9be6dSChris Kay uint32_t value = read_amcntenset0(); 7333b9be6dSChris Kay 7433b9be6dSChris Kay value &= ~AMCNTENSET0_Pn_MASK; 7533b9be6dSChris Kay value |= (px << AMCNTENSET0_Pn_SHIFT) & 7633b9be6dSChris Kay AMCNTENSET0_Pn_MASK; 7733b9be6dSChris Kay 7833b9be6dSChris Kay write_amcntenset0(value); 7933b9be6dSChris Kay } 8033b9be6dSChris Kay 8133b9be6dSChris Kay static inline __unused void write_amcntenset1_px(uint32_t px) 8233b9be6dSChris Kay { 8333b9be6dSChris Kay uint32_t value = read_amcntenset1(); 8433b9be6dSChris Kay 8533b9be6dSChris Kay value &= ~AMCNTENSET1_Pn_MASK; 8633b9be6dSChris Kay value |= (px << AMCNTENSET1_Pn_SHIFT) & 8733b9be6dSChris Kay AMCNTENSET1_Pn_MASK; 8833b9be6dSChris Kay 8933b9be6dSChris Kay write_amcntenset1(value); 9033b9be6dSChris Kay } 9133b9be6dSChris Kay 9233b9be6dSChris Kay static inline __unused void write_amcntenclr0_px(uint32_t px) 9333b9be6dSChris Kay { 9433b9be6dSChris Kay uint32_t value = read_amcntenclr0(); 9533b9be6dSChris Kay 9633b9be6dSChris Kay value &= ~AMCNTENCLR0_Pn_MASK; 9733b9be6dSChris Kay value |= (px << AMCNTENCLR0_Pn_SHIFT) & AMCNTENCLR0_Pn_MASK; 9833b9be6dSChris Kay 9933b9be6dSChris Kay write_amcntenclr0(value); 10033b9be6dSChris Kay } 10133b9be6dSChris Kay 10233b9be6dSChris Kay static inline __unused void write_amcntenclr1_px(uint32_t px) 10333b9be6dSChris Kay { 10433b9be6dSChris Kay uint32_t value = read_amcntenclr1(); 10533b9be6dSChris Kay 10633b9be6dSChris Kay value &= ~AMCNTENCLR1_Pn_MASK; 10733b9be6dSChris Kay value |= (px << AMCNTENCLR1_Pn_SHIFT) & AMCNTENCLR1_Pn_MASK; 10833b9be6dSChris Kay 10933b9be6dSChris Kay write_amcntenclr1(value); 11033b9be6dSChris Kay } 11133b9be6dSChris Kay 11233b9be6dSChris Kay static bool amu_supported(void) 11333b9be6dSChris Kay { 11433b9be6dSChris Kay return read_id_pfr0_amu() >= ID_PFR0_AMU_V1; 11533b9be6dSChris Kay } 11633b9be6dSChris Kay 11733b9be6dSChris Kay static bool amu_v1p1_supported(void) 11833b9be6dSChris Kay { 11933b9be6dSChris Kay return read_id_pfr0_amu() >= ID_PFR0_AMU_V1P1; 12033b9be6dSChris Kay } 12133b9be6dSChris Kay 12233b9be6dSChris Kay #if ENABLE_AMU_AUXILIARY_COUNTERS 123b4b726eaSChris Kay static bool amu_group1_supported(void) 124f3ccf036SAlexei Fedorov { 12533b9be6dSChris Kay return read_amcfgr_ncg() > 0U; 126f3ccf036SAlexei Fedorov } 127f3ccf036SAlexei Fedorov #endif 128f3ccf036SAlexei Fedorov 129f3ccf036SAlexei Fedorov /* 130f3ccf036SAlexei Fedorov * Enable counters. This function is meant to be invoked 131f3ccf036SAlexei Fedorov * by the context management library before exiting from EL3. 132f3ccf036SAlexei Fedorov */ 13340daecc1SAntonio Nino Diaz void amu_enable(bool el2_unused) 134c70da546SJoel Hutton { 13533b9be6dSChris Kay if (!amu_supported()) { 1360767d50eSDimitris Papastamos return; 137f3ccf036SAlexei Fedorov } 138f3ccf036SAlexei Fedorov 1391fd685a7SChris Kay #if ENABLE_AMU_AUXILIARY_COUNTERS 1401fd685a7SChris Kay if (AMU_GROUP1_NR_COUNTERS > 0U) { 141f3ccf036SAlexei Fedorov /* Check and set presence of group 1 counters */ 142f3ccf036SAlexei Fedorov if (!amu_group1_supported()) { 143f3ccf036SAlexei Fedorov ERROR("AMU Counter Group 1 is not implemented\n"); 144f3ccf036SAlexei Fedorov panic(); 145f3ccf036SAlexei Fedorov } 146f3ccf036SAlexei Fedorov 147f3ccf036SAlexei Fedorov /* Check number of group 1 counters */ 14833b9be6dSChris Kay uint32_t cnt_num = read_amcgcr_cg1nc(); 1491fd685a7SChris Kay 150f3ccf036SAlexei Fedorov VERBOSE("%s%u. %s%u\n", 151f3ccf036SAlexei Fedorov "Number of AMU Group 1 Counters ", cnt_num, 152f3ccf036SAlexei Fedorov "Requested number ", AMU_GROUP1_NR_COUNTERS); 153f3ccf036SAlexei Fedorov 154f3ccf036SAlexei Fedorov if (cnt_num < AMU_GROUP1_NR_COUNTERS) { 155f3ccf036SAlexei Fedorov ERROR("%s%u is less than %s%u\n", 156f3ccf036SAlexei Fedorov "Number of AMU Group 1 Counters ", cnt_num, 157f3ccf036SAlexei Fedorov "Requested number ", AMU_GROUP1_NR_COUNTERS); 158f3ccf036SAlexei Fedorov panic(); 159f3ccf036SAlexei Fedorov } 1601fd685a7SChris Kay } 161f3ccf036SAlexei Fedorov #endif 1620767d50eSDimitris Papastamos 163ef69e1eaSDimitris Papastamos if (el2_unused) { 164ef69e1eaSDimitris Papastamos /* 165ef69e1eaSDimitris Papastamos * Non-secure access from EL0 or EL1 to the Activity Monitor 166ef69e1eaSDimitris Papastamos * registers do not trap to EL2. 167ef69e1eaSDimitris Papastamos */ 16833b9be6dSChris Kay write_hcptr_tam(0U); 169ef69e1eaSDimitris Papastamos } 170ef69e1eaSDimitris Papastamos 171ef69e1eaSDimitris Papastamos /* Enable group 0 counters */ 172*81e2ff1fSChris Kay write_amcntenset0_px((UINT32_C(1) << read_amcgcr_cg0nc()) - 1U); 173c70da546SJoel Hutton 1741fd685a7SChris Kay #if ENABLE_AMU_AUXILIARY_COUNTERS 1751fd685a7SChris Kay if (AMU_GROUP1_NR_COUNTERS > 0U) { 176c70da546SJoel Hutton /* Enable group 1 counters */ 17733b9be6dSChris Kay write_amcntenset1_px(AMU_GROUP1_COUNTERS_MASK); 1781fd685a7SChris Kay } 179f3ccf036SAlexei Fedorov #endif 180873d4241Sjohpow01 181873d4241Sjohpow01 /* Initialize FEAT_AMUv1p1 features if present. */ 18233b9be6dSChris Kay if (!amu_v1p1_supported()) { 183873d4241Sjohpow01 return; 184873d4241Sjohpow01 } 185873d4241Sjohpow01 186873d4241Sjohpow01 #if AMU_RESTRICT_COUNTERS 187873d4241Sjohpow01 /* 188873d4241Sjohpow01 * FEAT_AMUv1p1 adds a register field to restrict access to group 1 189873d4241Sjohpow01 * counters at all but the highest implemented EL. This is controlled 190873d4241Sjohpow01 * with the AMU_RESTRICT_COUNTERS compile time flag, when set, system 191873d4241Sjohpow01 * register reads at lower ELs return zero. Reads from the memory 192873d4241Sjohpow01 * mapped view are unaffected. 193873d4241Sjohpow01 */ 194873d4241Sjohpow01 VERBOSE("AMU group 1 counter access restricted.\n"); 19533b9be6dSChris Kay write_amcr_cg1rz(1U); 196873d4241Sjohpow01 #else 19733b9be6dSChris Kay write_amcr_cg1rz(0U); 198873d4241Sjohpow01 #endif 199c70da546SJoel Hutton } 200c70da546SJoel Hutton 201c70da546SJoel Hutton /* Read the group 0 counter identified by the given `idx`. */ 202b4b726eaSChris Kay static uint64_t amu_group0_cnt_read(unsigned int idx) 203c70da546SJoel Hutton { 20433b9be6dSChris Kay assert(amu_supported()); 205*81e2ff1fSChris Kay assert(idx < read_amcgcr_cg0nc()); 206c70da546SJoel Hutton 207c70da546SJoel Hutton return amu_group0_cnt_read_internal(idx); 208c70da546SJoel Hutton } 209c70da546SJoel Hutton 210f3ccf036SAlexei Fedorov /* Write the group 0 counter identified by the given `idx` with `val` */ 211b4b726eaSChris Kay static void amu_group0_cnt_write(unsigned int idx, uint64_t val) 212c70da546SJoel Hutton { 21333b9be6dSChris Kay assert(amu_supported()); 214*81e2ff1fSChris Kay assert(idx < read_amcgcr_cg0nc()); 215c70da546SJoel Hutton 216c70da546SJoel Hutton amu_group0_cnt_write_internal(idx, val); 217c70da546SJoel Hutton isb(); 218c70da546SJoel Hutton } 219c70da546SJoel Hutton 2201fd685a7SChris Kay #if ENABLE_AMU_AUXILIARY_COUNTERS 221f3ccf036SAlexei Fedorov /* Read the group 1 counter identified by the given `idx` */ 222b4b726eaSChris Kay static uint64_t amu_group1_cnt_read(unsigned int idx) 223c70da546SJoel Hutton { 22433b9be6dSChris Kay assert(amu_supported()); 225f3ccf036SAlexei Fedorov assert(amu_group1_supported()); 226f3ccf036SAlexei Fedorov assert(idx < AMU_GROUP1_NR_COUNTERS); 227c70da546SJoel Hutton 228c70da546SJoel Hutton return amu_group1_cnt_read_internal(idx); 229c70da546SJoel Hutton } 230c70da546SJoel Hutton 231f3ccf036SAlexei Fedorov /* Write the group 1 counter identified by the given `idx` with `val` */ 232b4b726eaSChris Kay static void amu_group1_cnt_write(unsigned int idx, uint64_t val) 233c70da546SJoel Hutton { 23433b9be6dSChris Kay assert(amu_supported()); 235f3ccf036SAlexei Fedorov assert(amu_group1_supported()); 236f3ccf036SAlexei Fedorov assert(idx < AMU_GROUP1_NR_COUNTERS); 237c70da546SJoel Hutton 238c70da546SJoel Hutton amu_group1_cnt_write_internal(idx, val); 239c70da546SJoel Hutton isb(); 240c70da546SJoel Hutton } 2411fd685a7SChris Kay #endif 242b6eb3932SDimitris Papastamos 243b6eb3932SDimitris Papastamos static void *amu_context_save(const void *arg) 244b6eb3932SDimitris Papastamos { 245f3ccf036SAlexei Fedorov struct amu_ctx *ctx = &amu_ctxs[plat_my_core_pos()]; 246f3ccf036SAlexei Fedorov unsigned int i; 247b6eb3932SDimitris Papastamos 24833b9be6dSChris Kay if (!amu_supported()) { 249b6eb3932SDimitris Papastamos return (void *)-1; 250f3ccf036SAlexei Fedorov } 251b6eb3932SDimitris Papastamos 2521fd685a7SChris Kay #if ENABLE_AMU_AUXILIARY_COUNTERS 2531fd685a7SChris Kay if (AMU_GROUP1_NR_COUNTERS > 0U) { 254f3ccf036SAlexei Fedorov if (!amu_group1_supported()) { 255f3ccf036SAlexei Fedorov return (void *)-1; 256f3ccf036SAlexei Fedorov } 2571fd685a7SChris Kay } 258f3ccf036SAlexei Fedorov #endif 2591fd685a7SChris Kay 260f3ccf036SAlexei Fedorov /* Assert that group 0/1 counter configuration is what we expect */ 261*81e2ff1fSChris Kay assert(read_amcntenset0_px() == 262*81e2ff1fSChris Kay ((UINT32_C(1) << read_amcgcr_cg0nc()) - 1U)); 263b6eb3932SDimitris Papastamos 2641fd685a7SChris Kay #if ENABLE_AMU_AUXILIARY_COUNTERS 2651fd685a7SChris Kay if (AMU_GROUP1_NR_COUNTERS > 0U) { 26633b9be6dSChris Kay assert(read_amcntenset1_px() == AMU_GROUP1_COUNTERS_MASK); 2671fd685a7SChris Kay } 268f3ccf036SAlexei Fedorov #endif 269b6eb3932SDimitris Papastamos /* 270f3ccf036SAlexei Fedorov * Disable group 0/1 counters to avoid other observers like SCP sampling 271b6eb3932SDimitris Papastamos * counter values from the future via the memory mapped view. 272b6eb3932SDimitris Papastamos */ 273*81e2ff1fSChris Kay write_amcntenclr0_px((UINT32_C(1) << read_amcgcr_cg0nc()) - 1U); 274f3ccf036SAlexei Fedorov 2751fd685a7SChris Kay #if ENABLE_AMU_AUXILIARY_COUNTERS 2761fd685a7SChris Kay if (AMU_GROUP1_NR_COUNTERS > 0U) { 27733b9be6dSChris Kay write_amcntenclr1_px(AMU_GROUP1_COUNTERS_MASK); 2781fd685a7SChris Kay } 279f3ccf036SAlexei Fedorov #endif 2801fd685a7SChris Kay 281b6eb3932SDimitris Papastamos isb(); 282b6eb3932SDimitris Papastamos 283f3ccf036SAlexei Fedorov /* Save all group 0 counters */ 284*81e2ff1fSChris Kay for (i = 0U; i < read_amcgcr_cg0nc(); i++) { 285c70da546SJoel Hutton ctx->group0_cnts[i] = amu_group0_cnt_read(i); 286f3ccf036SAlexei Fedorov } 287c70da546SJoel Hutton 2881fd685a7SChris Kay #if ENABLE_AMU_AUXILIARY_COUNTERS 2891fd685a7SChris Kay if (AMU_GROUP1_NR_COUNTERS > 0U) { 290f3ccf036SAlexei Fedorov /* Save group 1 counters */ 291f3ccf036SAlexei Fedorov for (i = 0U; i < AMU_GROUP1_NR_COUNTERS; i++) { 292f3ccf036SAlexei Fedorov if ((AMU_GROUP1_COUNTERS_MASK & (1U << i)) != 0U) { 293c70da546SJoel Hutton ctx->group1_cnts[i] = amu_group1_cnt_read(i); 294f3ccf036SAlexei Fedorov } 295f3ccf036SAlexei Fedorov } 2961fd685a7SChris Kay } 297f3ccf036SAlexei Fedorov #endif 2981fd685a7SChris Kay 29940daecc1SAntonio Nino Diaz return (void *)0; 300b6eb3932SDimitris Papastamos } 301b6eb3932SDimitris Papastamos 302b6eb3932SDimitris Papastamos static void *amu_context_restore(const void *arg) 303b6eb3932SDimitris Papastamos { 304f3ccf036SAlexei Fedorov struct amu_ctx *ctx = &amu_ctxs[plat_my_core_pos()]; 305f3ccf036SAlexei Fedorov unsigned int i; 306b6eb3932SDimitris Papastamos 30733b9be6dSChris Kay if (!amu_supported()) { 308b6eb3932SDimitris Papastamos return (void *)-1; 309f3ccf036SAlexei Fedorov } 310b6eb3932SDimitris Papastamos 3111fd685a7SChris Kay #if ENABLE_AMU_AUXILIARY_COUNTERS 3121fd685a7SChris Kay if (AMU_GROUP1_NR_COUNTERS > 0U) { 3131fd685a7SChris Kay if (!amu_group1_supported()) { 314f3ccf036SAlexei Fedorov return (void *)-1; 315f3ccf036SAlexei Fedorov } 3161fd685a7SChris Kay } 317f3ccf036SAlexei Fedorov #endif 3181fd685a7SChris Kay 319b6eb3932SDimitris Papastamos /* Counters were disabled in `amu_context_save()` */ 32033b9be6dSChris Kay assert(read_amcntenset0_px() == 0U); 321b6eb3932SDimitris Papastamos 3221fd685a7SChris Kay #if ENABLE_AMU_AUXILIARY_COUNTERS 3231fd685a7SChris Kay if (AMU_GROUP1_NR_COUNTERS > 0U) { 32433b9be6dSChris Kay assert(read_amcntenset1_px() == 0U); 3251fd685a7SChris Kay } 326f3ccf036SAlexei Fedorov #endif 327f3ccf036SAlexei Fedorov 328f3ccf036SAlexei Fedorov /* Restore all group 0 counters */ 329*81e2ff1fSChris Kay for (i = 0U; i < read_amcgcr_cg0nc(); i++) { 330c70da546SJoel Hutton amu_group0_cnt_write(i, ctx->group0_cnts[i]); 331f3ccf036SAlexei Fedorov } 332b6eb3932SDimitris Papastamos 333f3ccf036SAlexei Fedorov /* Restore group 0 counter configuration */ 334*81e2ff1fSChris Kay write_amcntenset0_px((UINT32_C(1) << read_amcgcr_cg0nc()) - 1U); 335b6eb3932SDimitris Papastamos 3361fd685a7SChris Kay #if ENABLE_AMU_AUXILIARY_COUNTERS 3371fd685a7SChris Kay if (AMU_GROUP1_NR_COUNTERS > 0U) { 338f3ccf036SAlexei Fedorov /* Restore group 1 counters */ 339f3ccf036SAlexei Fedorov for (i = 0U; i < AMU_GROUP1_NR_COUNTERS; i++) { 340f3ccf036SAlexei Fedorov if ((AMU_GROUP1_COUNTERS_MASK & (1U << i)) != 0U) { 341f3ccf036SAlexei Fedorov amu_group1_cnt_write(i, ctx->group1_cnts[i]); 342f3ccf036SAlexei Fedorov } 343f3ccf036SAlexei Fedorov } 344f3ccf036SAlexei Fedorov 345f3ccf036SAlexei Fedorov /* Restore group 1 counter configuration */ 34633b9be6dSChris Kay write_amcntenset1_px(AMU_GROUP1_COUNTERS_MASK); 3471fd685a7SChris Kay } 348f3ccf036SAlexei Fedorov #endif 349f3ccf036SAlexei Fedorov 35040daecc1SAntonio Nino Diaz return (void *)0; 351b6eb3932SDimitris Papastamos } 352b6eb3932SDimitris Papastamos 353b6eb3932SDimitris Papastamos SUBSCRIBE_TO_EVENT(psci_suspend_pwrdown_start, amu_context_save); 354b6eb3932SDimitris Papastamos SUBSCRIBE_TO_EVENT(psci_suspend_pwrdown_finish, amu_context_restore); 355