1380559c1SDimitris Papastamos /* 2873d4241Sjohpow01 * Copyright (c) 2017-2021, ARM Limited and Contributors. All rights reserved. 3380559c1SDimitris Papastamos * 4380559c1SDimitris Papastamos * SPDX-License-Identifier: BSD-3-Clause 5380559c1SDimitris Papastamos */ 6380559c1SDimitris Papastamos 709d40e0eSAntonio Nino Diaz #include <assert.h> 833b9be6dSChris Kay #include <cdefs.h> 909d40e0eSAntonio Nino Diaz #include <stdbool.h> 1009d40e0eSAntonio Nino Diaz 11*e747a59bSChris Kay #include "../amu_private.h" 12380559c1SDimitris Papastamos #include <arch.h> 13873d4241Sjohpow01 #include <arch_features.h> 14380559c1SDimitris Papastamos #include <arch_helpers.h> 1509d40e0eSAntonio Nino Diaz #include <lib/el3_runtime/pubsub_events.h> 1609d40e0eSAntonio Nino Diaz #include <lib/extensions/amu.h> 17f3ccf036SAlexei Fedorov 1809d40e0eSAntonio Nino Diaz #include <plat/common/platform.h> 19380559c1SDimitris Papastamos 20*e747a59bSChris Kay struct amu_ctx { 21*e747a59bSChris Kay uint64_t group0_cnts[AMU_GROUP0_MAX_COUNTERS]; 22*e747a59bSChris Kay #if ENABLE_AMU_AUXILIARY_COUNTERS 23*e747a59bSChris Kay uint64_t group1_cnts[AMU_GROUP1_MAX_COUNTERS]; 24*e747a59bSChris Kay #endif 25*e747a59bSChris Kay 26*e747a59bSChris Kay /* Architected event counter 1 does not have an offset register */ 27*e747a59bSChris Kay uint64_t group0_voffsets[AMU_GROUP0_MAX_COUNTERS - 1U]; 28*e747a59bSChris Kay #if ENABLE_AMU_AUXILIARY_COUNTERS 29*e747a59bSChris Kay uint64_t group1_voffsets[AMU_GROUP1_MAX_COUNTERS]; 30*e747a59bSChris Kay #endif 31*e747a59bSChris Kay 32*e747a59bSChris Kay uint16_t group0_enable; 33*e747a59bSChris Kay #if ENABLE_AMU_AUXILIARY_COUNTERS 34*e747a59bSChris Kay uint16_t group1_enable; 35*e747a59bSChris Kay #endif 36*e747a59bSChris Kay }; 37*e747a59bSChris Kay 38*e747a59bSChris Kay static struct amu_ctx amu_ctxs_[PLATFORM_CORE_COUNT]; 39*e747a59bSChris Kay 40*e747a59bSChris Kay CASSERT((sizeof(amu_ctxs_[0].group0_enable) * CHAR_BIT) <= AMU_GROUP0_MAX_COUNTERS, 41*e747a59bSChris Kay amu_ctx_group0_enable_cannot_represent_all_group0_counters); 42*e747a59bSChris Kay 43*e747a59bSChris Kay #if ENABLE_AMU_AUXILIARY_COUNTERS 44*e747a59bSChris Kay CASSERT((sizeof(amu_ctxs_[0].group1_enable) * CHAR_BIT) <= AMU_GROUP1_MAX_COUNTERS, 45*e747a59bSChris Kay amu_ctx_group1_enable_cannot_represent_all_group1_counters); 46*e747a59bSChris Kay #endif 47b6eb3932SDimitris Papastamos 4833b9be6dSChris Kay static inline __unused uint64_t read_id_aa64pfr0_el1_amu(void) 49380559c1SDimitris Papastamos { 5033b9be6dSChris Kay return (read_id_aa64pfr0_el1() >> ID_AA64PFR0_AMU_SHIFT) & 51873d4241Sjohpow01 ID_AA64PFR0_AMU_MASK; 520767d50eSDimitris Papastamos } 530767d50eSDimitris Papastamos 5433b9be6dSChris Kay static inline __unused uint64_t read_hcr_el2_amvoffen(void) 5533b9be6dSChris Kay { 5633b9be6dSChris Kay return (read_hcr_el2() & HCR_AMVOFFEN_BIT) >> 5733b9be6dSChris Kay HCR_AMVOFFEN_SHIFT; 5833b9be6dSChris Kay } 5933b9be6dSChris Kay 6033b9be6dSChris Kay static inline __unused void write_cptr_el2_tam(uint64_t value) 6133b9be6dSChris Kay { 6233b9be6dSChris Kay write_cptr_el2((read_cptr_el2() & ~CPTR_EL2_TAM_BIT) | 6333b9be6dSChris Kay ((value << CPTR_EL2_TAM_SHIFT) & CPTR_EL2_TAM_BIT)); 6433b9be6dSChris Kay } 6533b9be6dSChris Kay 6633b9be6dSChris Kay static inline __unused void write_cptr_el3_tam(cpu_context_t *ctx, uint64_t tam) 6733b9be6dSChris Kay { 6833b9be6dSChris Kay uint64_t value = read_ctx_reg(get_el3state_ctx(ctx), CTX_CPTR_EL3); 6933b9be6dSChris Kay 7033b9be6dSChris Kay value &= ~TAM_BIT; 7133b9be6dSChris Kay value |= (tam << TAM_SHIFT) & TAM_BIT; 7233b9be6dSChris Kay 7333b9be6dSChris Kay write_ctx_reg(get_el3state_ctx(ctx), CTX_CPTR_EL3, value); 7433b9be6dSChris Kay } 7533b9be6dSChris Kay 7633b9be6dSChris Kay static inline __unused void write_hcr_el2_amvoffen(uint64_t value) 7733b9be6dSChris Kay { 7833b9be6dSChris Kay write_hcr_el2((read_hcr_el2() & ~HCR_AMVOFFEN_BIT) | 7933b9be6dSChris Kay ((value << HCR_AMVOFFEN_SHIFT) & HCR_AMVOFFEN_BIT)); 8033b9be6dSChris Kay } 8133b9be6dSChris Kay 8233b9be6dSChris Kay static inline __unused void write_amcr_el0_cg1rz(uint64_t value) 8333b9be6dSChris Kay { 8433b9be6dSChris Kay write_amcr_el0((read_amcr_el0() & ~AMCR_CG1RZ_BIT) | 8533b9be6dSChris Kay ((value << AMCR_CG1RZ_SHIFT) & AMCR_CG1RZ_BIT)); 8633b9be6dSChris Kay } 8733b9be6dSChris Kay 8833b9be6dSChris Kay static inline __unused uint64_t read_amcfgr_el0_ncg(void) 8933b9be6dSChris Kay { 9033b9be6dSChris Kay return (read_amcfgr_el0() >> AMCFGR_EL0_NCG_SHIFT) & 9133b9be6dSChris Kay AMCFGR_EL0_NCG_MASK; 9233b9be6dSChris Kay } 9333b9be6dSChris Kay 94*e747a59bSChris Kay static inline __unused uint64_t read_amcgcr_el0_cg0nc(void) 9581e2ff1fSChris Kay { 9681e2ff1fSChris Kay return (read_amcgcr_el0() >> AMCGCR_EL0_CG0NC_SHIFT) & 9781e2ff1fSChris Kay AMCGCR_EL0_CG0NC_MASK; 9881e2ff1fSChris Kay } 9981e2ff1fSChris Kay 10033b9be6dSChris Kay static inline __unused uint64_t read_amcg1idr_el0_voff(void) 10133b9be6dSChris Kay { 10233b9be6dSChris Kay return (read_amcg1idr_el0() >> AMCG1IDR_VOFF_SHIFT) & 10333b9be6dSChris Kay AMCG1IDR_VOFF_MASK; 10433b9be6dSChris Kay } 10533b9be6dSChris Kay 10633b9be6dSChris Kay static inline __unused uint64_t read_amcgcr_el0_cg1nc(void) 10733b9be6dSChris Kay { 10833b9be6dSChris Kay return (read_amcgcr_el0() >> AMCGCR_EL0_CG1NC_SHIFT) & 10933b9be6dSChris Kay AMCGCR_EL0_CG1NC_MASK; 11033b9be6dSChris Kay } 11133b9be6dSChris Kay 11233b9be6dSChris Kay static inline __unused uint64_t read_amcntenset0_el0_px(void) 11333b9be6dSChris Kay { 11433b9be6dSChris Kay return (read_amcntenset0_el0() >> AMCNTENSET0_EL0_Pn_SHIFT) & 11533b9be6dSChris Kay AMCNTENSET0_EL0_Pn_MASK; 11633b9be6dSChris Kay } 11733b9be6dSChris Kay 11833b9be6dSChris Kay static inline __unused uint64_t read_amcntenset1_el0_px(void) 11933b9be6dSChris Kay { 12033b9be6dSChris Kay return (read_amcntenset1_el0() >> AMCNTENSET1_EL0_Pn_SHIFT) & 12133b9be6dSChris Kay AMCNTENSET1_EL0_Pn_MASK; 12233b9be6dSChris Kay } 12333b9be6dSChris Kay 12433b9be6dSChris Kay static inline __unused void write_amcntenset0_el0_px(uint64_t px) 12533b9be6dSChris Kay { 12633b9be6dSChris Kay uint64_t value = read_amcntenset0_el0(); 12733b9be6dSChris Kay 12833b9be6dSChris Kay value &= ~AMCNTENSET0_EL0_Pn_MASK; 12933b9be6dSChris Kay value |= (px << AMCNTENSET0_EL0_Pn_SHIFT) & AMCNTENSET0_EL0_Pn_MASK; 13033b9be6dSChris Kay 13133b9be6dSChris Kay write_amcntenset0_el0(value); 13233b9be6dSChris Kay } 13333b9be6dSChris Kay 13433b9be6dSChris Kay static inline __unused void write_amcntenset1_el0_px(uint64_t px) 13533b9be6dSChris Kay { 13633b9be6dSChris Kay uint64_t value = read_amcntenset1_el0(); 13733b9be6dSChris Kay 13833b9be6dSChris Kay value &= ~AMCNTENSET1_EL0_Pn_MASK; 13933b9be6dSChris Kay value |= (px << AMCNTENSET1_EL0_Pn_SHIFT) & AMCNTENSET1_EL0_Pn_MASK; 14033b9be6dSChris Kay 14133b9be6dSChris Kay write_amcntenset1_el0(value); 14233b9be6dSChris Kay } 14333b9be6dSChris Kay 14433b9be6dSChris Kay static inline __unused void write_amcntenclr0_el0_px(uint64_t px) 14533b9be6dSChris Kay { 14633b9be6dSChris Kay uint64_t value = read_amcntenclr0_el0(); 14733b9be6dSChris Kay 14833b9be6dSChris Kay value &= ~AMCNTENCLR0_EL0_Pn_MASK; 14933b9be6dSChris Kay value |= (px << AMCNTENCLR0_EL0_Pn_SHIFT) & AMCNTENCLR0_EL0_Pn_MASK; 15033b9be6dSChris Kay 15133b9be6dSChris Kay write_amcntenclr0_el0(value); 15233b9be6dSChris Kay } 15333b9be6dSChris Kay 15433b9be6dSChris Kay static inline __unused void write_amcntenclr1_el0_px(uint64_t px) 15533b9be6dSChris Kay { 15633b9be6dSChris Kay uint64_t value = read_amcntenclr1_el0(); 15733b9be6dSChris Kay 15833b9be6dSChris Kay value &= ~AMCNTENCLR1_EL0_Pn_MASK; 15933b9be6dSChris Kay value |= (px << AMCNTENCLR1_EL0_Pn_SHIFT) & AMCNTENCLR1_EL0_Pn_MASK; 16033b9be6dSChris Kay 16133b9be6dSChris Kay write_amcntenclr1_el0(value); 16233b9be6dSChris Kay } 16333b9be6dSChris Kay 164*e747a59bSChris Kay static __unused bool amu_supported(void) 16533b9be6dSChris Kay { 16633b9be6dSChris Kay return read_id_aa64pfr0_el1_amu() >= ID_AA64PFR0_AMU_V1; 16733b9be6dSChris Kay } 16833b9be6dSChris Kay 169*e747a59bSChris Kay static __unused bool amu_v1p1_supported(void) 17033b9be6dSChris Kay { 17133b9be6dSChris Kay return read_id_aa64pfr0_el1_amu() >= ID_AA64PFR0_AMU_V1P1; 17233b9be6dSChris Kay } 17333b9be6dSChris Kay 17433b9be6dSChris Kay #if ENABLE_AMU_AUXILIARY_COUNTERS 175*e747a59bSChris Kay static __unused bool amu_group1_supported(void) 176f3ccf036SAlexei Fedorov { 17733b9be6dSChris Kay return read_amcfgr_el0_ncg() > 0U; 178f3ccf036SAlexei Fedorov } 179f3ccf036SAlexei Fedorov #endif 180f3ccf036SAlexei Fedorov 1810767d50eSDimitris Papastamos /* 182*e747a59bSChris Kay * Enable counters. This function is meant to be invoked by the context 183*e747a59bSChris Kay * management library before exiting from EL3. 1840767d50eSDimitris Papastamos */ 18568ac5ed0SArunachalam Ganapathy void amu_enable(bool el2_unused, cpu_context_t *ctx) 1860767d50eSDimitris Papastamos { 187*e747a59bSChris Kay uint64_t id_aa64pfr0_el1_amu; /* AMU version */ 188*e747a59bSChris Kay 189*e747a59bSChris Kay uint64_t amcfgr_el0_ncg; /* Number of counter groups */ 190*e747a59bSChris Kay uint64_t amcgcr_el0_cg0nc; /* Number of group 0 counters */ 191*e747a59bSChris Kay 192*e747a59bSChris Kay uint64_t amcntenset0_el0_px = 0x0; /* Group 0 enable mask */ 193*e747a59bSChris Kay uint64_t amcntenset1_el0_px = 0x0; /* Group 1 enable mask */ 194*e747a59bSChris Kay 195*e747a59bSChris Kay id_aa64pfr0_el1_amu = read_id_aa64pfr0_el1_amu(); 196*e747a59bSChris Kay if (id_aa64pfr0_el1_amu == ID_AA64PFR0_AMU_NOT_SUPPORTED) { 197*e747a59bSChris Kay /* 198*e747a59bSChris Kay * If the AMU is unsupported, nothing needs to be done. 199*e747a59bSChris Kay */ 200*e747a59bSChris Kay 2010767d50eSDimitris Papastamos return; 202f3ccf036SAlexei Fedorov } 203f3ccf036SAlexei Fedorov 204380559c1SDimitris Papastamos if (el2_unused) { 205380559c1SDimitris Papastamos /* 206*e747a59bSChris Kay * CPTR_EL2.TAM: Set to zero so any accesses to the Activity 207*e747a59bSChris Kay * Monitor registers do not trap to EL2. 208380559c1SDimitris Papastamos */ 20933b9be6dSChris Kay write_cptr_el2_tam(0U); 210380559c1SDimitris Papastamos } 211380559c1SDimitris Papastamos 212380559c1SDimitris Papastamos /* 21368ac5ed0SArunachalam Ganapathy * Retrieve and update the CPTR_EL3 value from the context mentioned 21468ac5ed0SArunachalam Ganapathy * in 'ctx'. Set CPTR_EL3.TAM to zero so that any accesses to 215380559c1SDimitris Papastamos * the Activity Monitor registers do not trap to EL3. 216380559c1SDimitris Papastamos */ 21733b9be6dSChris Kay write_cptr_el3_tam(ctx, 0U); 218380559c1SDimitris Papastamos 219*e747a59bSChris Kay /* 220*e747a59bSChris Kay * Retrieve the number of architected counters. All of these counters 221*e747a59bSChris Kay * are enabled by default. 222*e747a59bSChris Kay */ 223f3ccf036SAlexei Fedorov 224*e747a59bSChris Kay amcgcr_el0_cg0nc = read_amcgcr_el0_cg0nc(); 225*e747a59bSChris Kay amcntenset0_el0_px = (UINT64_C(1) << (amcgcr_el0_cg0nc)) - 1U; 226*e747a59bSChris Kay 227*e747a59bSChris Kay assert(amcgcr_el0_cg0nc <= AMU_AMCGCR_CG0NC_MAX); 228*e747a59bSChris Kay 229*e747a59bSChris Kay /* 230*e747a59bSChris Kay * Enable the requested counters. 231*e747a59bSChris Kay */ 232*e747a59bSChris Kay 233*e747a59bSChris Kay write_amcntenset0_el0_px(amcntenset0_el0_px); 234*e747a59bSChris Kay 235*e747a59bSChris Kay amcfgr_el0_ncg = read_amcfgr_el0_ncg(); 236*e747a59bSChris Kay if (amcfgr_el0_ncg > 0U) { 237*e747a59bSChris Kay write_amcntenset1_el0_px(amcntenset1_el0_px); 2381fd685a7SChris Kay } 239873d4241Sjohpow01 240873d4241Sjohpow01 /* Initialize FEAT_AMUv1p1 features if present. */ 241*e747a59bSChris Kay if (id_aa64pfr0_el1_amu >= ID_AA64PFR0_AMU_V1P1) { 242873d4241Sjohpow01 return; 243873d4241Sjohpow01 } 244873d4241Sjohpow01 245873d4241Sjohpow01 if (el2_unused) { 246873d4241Sjohpow01 /* Make sure virtual offsets are disabled if EL2 not used. */ 24733b9be6dSChris Kay write_hcr_el2_amvoffen(0U); 248873d4241Sjohpow01 } 249873d4241Sjohpow01 250873d4241Sjohpow01 #if AMU_RESTRICT_COUNTERS 251873d4241Sjohpow01 /* 252873d4241Sjohpow01 * FEAT_AMUv1p1 adds a register field to restrict access to group 1 253873d4241Sjohpow01 * counters at all but the highest implemented EL. This is controlled 254873d4241Sjohpow01 * with the AMU_RESTRICT_COUNTERS compile time flag, when set, system 255873d4241Sjohpow01 * register reads at lower ELs return zero. Reads from the memory 256873d4241Sjohpow01 * mapped view are unaffected. 257873d4241Sjohpow01 */ 258873d4241Sjohpow01 VERBOSE("AMU group 1 counter access restricted.\n"); 25933b9be6dSChris Kay write_amcr_el0_cg1rz(1U); 260873d4241Sjohpow01 #else 26133b9be6dSChris Kay write_amcr_el0_cg1rz(0U); 262873d4241Sjohpow01 #endif 263380559c1SDimitris Papastamos } 2640767d50eSDimitris Papastamos 2650767d50eSDimitris Papastamos /* Read the group 0 counter identified by the given `idx`. */ 266b4b726eaSChris Kay static uint64_t amu_group0_cnt_read(unsigned int idx) 2670767d50eSDimitris Papastamos { 26833b9be6dSChris Kay assert(amu_supported()); 26981e2ff1fSChris Kay assert(idx < read_amcgcr_el0_cg0nc()); 2700767d50eSDimitris Papastamos 2710767d50eSDimitris Papastamos return amu_group0_cnt_read_internal(idx); 2720767d50eSDimitris Papastamos } 2730767d50eSDimitris Papastamos 274f3ccf036SAlexei Fedorov /* Write the group 0 counter identified by the given `idx` with `val` */ 275b4b726eaSChris Kay static void amu_group0_cnt_write(unsigned int idx, uint64_t val) 2760767d50eSDimitris Papastamos { 27733b9be6dSChris Kay assert(amu_supported()); 27881e2ff1fSChris Kay assert(idx < read_amcgcr_el0_cg0nc()); 2790767d50eSDimitris Papastamos 2800767d50eSDimitris Papastamos amu_group0_cnt_write_internal(idx, val); 2810767d50eSDimitris Papastamos isb(); 2820767d50eSDimitris Papastamos } 2830767d50eSDimitris Papastamos 284873d4241Sjohpow01 /* 285*e747a59bSChris Kay * Unlike with auxiliary counters, we cannot detect at runtime whether an 286*e747a59bSChris Kay * architected counter supports a virtual offset. These are instead fixed 287*e747a59bSChris Kay * according to FEAT_AMUv1p1, but this switch will need to be updated if later 288*e747a59bSChris Kay * revisions of FEAT_AMU add additional architected counters. 289*e747a59bSChris Kay */ 290*e747a59bSChris Kay static bool amu_group0_voffset_supported(uint64_t idx) 291*e747a59bSChris Kay { 292*e747a59bSChris Kay switch (idx) { 293*e747a59bSChris Kay case 0U: 294*e747a59bSChris Kay case 2U: 295*e747a59bSChris Kay case 3U: 296*e747a59bSChris Kay return true; 297*e747a59bSChris Kay 298*e747a59bSChris Kay case 1U: 299*e747a59bSChris Kay return false; 300*e747a59bSChris Kay 301*e747a59bSChris Kay default: 302*e747a59bSChris Kay ERROR("AMU: can't set up virtual offset for unknown " 303*e747a59bSChris Kay "architected counter %llu!\n", idx); 304*e747a59bSChris Kay 305*e747a59bSChris Kay panic(); 306*e747a59bSChris Kay } 307*e747a59bSChris Kay } 308*e747a59bSChris Kay 309*e747a59bSChris Kay /* 310873d4241Sjohpow01 * Read the group 0 offset register for a given index. Index must be 0, 2, 311873d4241Sjohpow01 * or 3, the register for 1 does not exist. 312873d4241Sjohpow01 * 313873d4241Sjohpow01 * Using this function requires FEAT_AMUv1p1 support. 314873d4241Sjohpow01 */ 315b4b726eaSChris Kay static uint64_t amu_group0_voffset_read(unsigned int idx) 316873d4241Sjohpow01 { 31733b9be6dSChris Kay assert(amu_v1p1_supported()); 31881e2ff1fSChris Kay assert(idx < read_amcgcr_el0_cg0nc()); 319873d4241Sjohpow01 assert(idx != 1U); 320873d4241Sjohpow01 321873d4241Sjohpow01 return amu_group0_voffset_read_internal(idx); 322873d4241Sjohpow01 } 323873d4241Sjohpow01 324873d4241Sjohpow01 /* 325873d4241Sjohpow01 * Write the group 0 offset register for a given index. Index must be 0, 2, or 326873d4241Sjohpow01 * 3, the register for 1 does not exist. 327873d4241Sjohpow01 * 328873d4241Sjohpow01 * Using this function requires FEAT_AMUv1p1 support. 329873d4241Sjohpow01 */ 330b4b726eaSChris Kay static void amu_group0_voffset_write(unsigned int idx, uint64_t val) 331873d4241Sjohpow01 { 33233b9be6dSChris Kay assert(amu_v1p1_supported()); 33381e2ff1fSChris Kay assert(idx < read_amcgcr_el0_cg0nc()); 334873d4241Sjohpow01 assert(idx != 1U); 335873d4241Sjohpow01 336873d4241Sjohpow01 amu_group0_voffset_write_internal(idx, val); 337873d4241Sjohpow01 isb(); 338873d4241Sjohpow01 } 339873d4241Sjohpow01 3401fd685a7SChris Kay #if ENABLE_AMU_AUXILIARY_COUNTERS 341f3ccf036SAlexei Fedorov /* Read the group 1 counter identified by the given `idx` */ 342b4b726eaSChris Kay static uint64_t amu_group1_cnt_read(unsigned int idx) 3430767d50eSDimitris Papastamos { 34433b9be6dSChris Kay assert(amu_supported()); 345f3ccf036SAlexei Fedorov assert(amu_group1_supported()); 34631d3cc25SChris Kay assert(idx < read_amcgcr_el0_cg1nc()); 3470767d50eSDimitris Papastamos 3480767d50eSDimitris Papastamos return amu_group1_cnt_read_internal(idx); 3490767d50eSDimitris Papastamos } 3500767d50eSDimitris Papastamos 351f3ccf036SAlexei Fedorov /* Write the group 1 counter identified by the given `idx` with `val` */ 352b4b726eaSChris Kay static void amu_group1_cnt_write(unsigned int idx, uint64_t val) 3530767d50eSDimitris Papastamos { 35433b9be6dSChris Kay assert(amu_supported()); 355f3ccf036SAlexei Fedorov assert(amu_group1_supported()); 35631d3cc25SChris Kay assert(idx < read_amcgcr_el0_cg1nc()); 3570767d50eSDimitris Papastamos 3580767d50eSDimitris Papastamos amu_group1_cnt_write_internal(idx, val); 3590767d50eSDimitris Papastamos isb(); 3600767d50eSDimitris Papastamos } 3610767d50eSDimitris Papastamos 3620767d50eSDimitris Papastamos /* 363873d4241Sjohpow01 * Read the group 1 offset register for a given index. 364873d4241Sjohpow01 * 365873d4241Sjohpow01 * Using this function requires FEAT_AMUv1p1 support. 366873d4241Sjohpow01 */ 367b4b726eaSChris Kay static uint64_t amu_group1_voffset_read(unsigned int idx) 368873d4241Sjohpow01 { 36933b9be6dSChris Kay assert(amu_v1p1_supported()); 370873d4241Sjohpow01 assert(amu_group1_supported()); 37131d3cc25SChris Kay assert(idx < read_amcgcr_el0_cg1nc()); 37233b9be6dSChris Kay assert((read_amcg1idr_el0_voff() & (UINT64_C(1) << idx)) != 0U); 373873d4241Sjohpow01 374873d4241Sjohpow01 return amu_group1_voffset_read_internal(idx); 375873d4241Sjohpow01 } 376873d4241Sjohpow01 377873d4241Sjohpow01 /* 378873d4241Sjohpow01 * Write the group 1 offset register for a given index. 379873d4241Sjohpow01 * 380873d4241Sjohpow01 * Using this function requires FEAT_AMUv1p1 support. 381873d4241Sjohpow01 */ 382b4b726eaSChris Kay static void amu_group1_voffset_write(unsigned int idx, uint64_t val) 383873d4241Sjohpow01 { 38433b9be6dSChris Kay assert(amu_v1p1_supported()); 385873d4241Sjohpow01 assert(amu_group1_supported()); 38631d3cc25SChris Kay assert(idx < read_amcgcr_el0_cg1nc()); 38733b9be6dSChris Kay assert((read_amcg1idr_el0_voff() & (UINT64_C(1) << idx)) != 0U); 388873d4241Sjohpow01 389873d4241Sjohpow01 amu_group1_voffset_write_internal(idx, val); 390873d4241Sjohpow01 isb(); 391873d4241Sjohpow01 } 3921fd685a7SChris Kay #endif 393b6eb3932SDimitris Papastamos 394b6eb3932SDimitris Papastamos static void *amu_context_save(const void *arg) 395b6eb3932SDimitris Papastamos { 396*e747a59bSChris Kay uint64_t i, j; 397b6eb3932SDimitris Papastamos 398*e747a59bSChris Kay unsigned int core_pos; 399*e747a59bSChris Kay struct amu_ctx *ctx; 400b6eb3932SDimitris Papastamos 401*e747a59bSChris Kay uint64_t id_aa64pfr0_el1_amu; /* AMU version */ 402*e747a59bSChris Kay uint64_t hcr_el2_amvoffen; /* AMU virtual offsets enabled */ 403*e747a59bSChris Kay uint64_t amcgcr_el0_cg0nc; /* Number of group 0 counters */ 404b6eb3932SDimitris Papastamos 4051fd685a7SChris Kay #if ENABLE_AMU_AUXILIARY_COUNTERS 406*e747a59bSChris Kay uint64_t amcg1idr_el0_voff; /* Auxiliary counters with virtual offsets */ 407*e747a59bSChris Kay uint64_t amcfgr_el0_ncg; /* Number of counter groups */ 408*e747a59bSChris Kay uint64_t amcgcr_el0_cg1nc; /* Number of group 1 counters */ 409*e747a59bSChris Kay #endif 410*e747a59bSChris Kay 411*e747a59bSChris Kay id_aa64pfr0_el1_amu = read_id_aa64pfr0_el1_amu(); 412*e747a59bSChris Kay if (id_aa64pfr0_el1_amu == ID_AA64PFR0_AMU_NOT_SUPPORTED) { 413*e747a59bSChris Kay return (void *)0; 414*e747a59bSChris Kay } 415*e747a59bSChris Kay 416*e747a59bSChris Kay core_pos = plat_my_core_pos(); 417*e747a59bSChris Kay ctx = &amu_ctxs_[core_pos]; 418*e747a59bSChris Kay 419*e747a59bSChris Kay amcgcr_el0_cg0nc = read_amcgcr_el0_cg0nc(); 420*e747a59bSChris Kay hcr_el2_amvoffen = (id_aa64pfr0_el1_amu >= ID_AA64PFR0_AMU_V1P1) ? 421*e747a59bSChris Kay read_hcr_el2_amvoffen() : 0U; 422*e747a59bSChris Kay 423*e747a59bSChris Kay #if ENABLE_AMU_AUXILIARY_COUNTERS 424*e747a59bSChris Kay amcfgr_el0_ncg = read_amcfgr_el0_ncg(); 425*e747a59bSChris Kay amcgcr_el0_cg1nc = (amcfgr_el0_ncg > 0U) ? read_amcgcr_el0_cg1nc() : 0U; 426*e747a59bSChris Kay amcg1idr_el0_voff = (hcr_el2_amvoffen != 0U) ? read_amcg1idr_el0_voff() : 0U; 427*e747a59bSChris Kay #endif 428*e747a59bSChris Kay 429*e747a59bSChris Kay /* 430*e747a59bSChris Kay * Disable all AMU counters. 431*e747a59bSChris Kay */ 432*e747a59bSChris Kay 433*e747a59bSChris Kay ctx->group0_enable = read_amcntenset0_el0_px(); 434*e747a59bSChris Kay write_amcntenclr0_el0_px(ctx->group0_enable); 435*e747a59bSChris Kay 436*e747a59bSChris Kay #if ENABLE_AMU_AUXILIARY_COUNTERS 437*e747a59bSChris Kay if (amcfgr_el0_ncg > 0U) { 438*e747a59bSChris Kay ctx->group1_enable = read_amcntenset1_el0_px(); 439*e747a59bSChris Kay write_amcntenclr1_el0_px(ctx->group1_enable); 4401fd685a7SChris Kay } 441f3ccf036SAlexei Fedorov #endif 4421fd685a7SChris Kay 443b6eb3932SDimitris Papastamos /* 444*e747a59bSChris Kay * Save the counters to the local context. 445b6eb3932SDimitris Papastamos */ 446f3ccf036SAlexei Fedorov 447*e747a59bSChris Kay isb(); /* Ensure counters have been stopped */ 4481fd685a7SChris Kay 449*e747a59bSChris Kay for (i = 0U; i < amcgcr_el0_cg0nc; i++) { 450b6eb3932SDimitris Papastamos ctx->group0_cnts[i] = amu_group0_cnt_read(i); 451f3ccf036SAlexei Fedorov } 452b6eb3932SDimitris Papastamos 453*e747a59bSChris Kay #if ENABLE_AMU_AUXILIARY_COUNTERS 454*e747a59bSChris Kay for (i = 0U; i < amcgcr_el0_cg1nc; i++) { 455*e747a59bSChris Kay ctx->group1_cnts[i] = amu_group1_cnt_read(i); 456*e747a59bSChris Kay } 457*e747a59bSChris Kay #endif 458*e747a59bSChris Kay 459*e747a59bSChris Kay /* 460*e747a59bSChris Kay * Save virtual offsets for counters that offer them. 461*e747a59bSChris Kay */ 462*e747a59bSChris Kay 463*e747a59bSChris Kay if (hcr_el2_amvoffen != 0U) { 464*e747a59bSChris Kay for (i = 0U, j = 0U; i < amcgcr_el0_cg0nc; i++) { 465*e747a59bSChris Kay if (!amu_group0_voffset_supported(i)) { 466*e747a59bSChris Kay continue; /* No virtual offset */ 467*e747a59bSChris Kay } 468*e747a59bSChris Kay 469*e747a59bSChris Kay ctx->group0_voffsets[j++] = amu_group0_voffset_read(i); 470873d4241Sjohpow01 } 471873d4241Sjohpow01 4721fd685a7SChris Kay #if ENABLE_AMU_AUXILIARY_COUNTERS 473*e747a59bSChris Kay for (i = 0U, j = 0U; i < amcgcr_el0_cg1nc; i++) { 474*e747a59bSChris Kay if ((amcg1idr_el0_voff >> i) & 1U) { 475*e747a59bSChris Kay continue; /* No virtual offset */ 476f3ccf036SAlexei Fedorov } 477873d4241Sjohpow01 478*e747a59bSChris Kay ctx->group1_voffsets[j++] = amu_group1_voffset_read(i); 4791fd685a7SChris Kay } 480f3ccf036SAlexei Fedorov #endif 481*e747a59bSChris Kay } 4821fd685a7SChris Kay 48340daecc1SAntonio Nino Diaz return (void *)0; 484b6eb3932SDimitris Papastamos } 485b6eb3932SDimitris Papastamos 486b6eb3932SDimitris Papastamos static void *amu_context_restore(const void *arg) 487b6eb3932SDimitris Papastamos { 488*e747a59bSChris Kay uint64_t i, j; 489b6eb3932SDimitris Papastamos 490*e747a59bSChris Kay unsigned int core_pos; 491*e747a59bSChris Kay struct amu_ctx *ctx; 492b6eb3932SDimitris Papastamos 493*e747a59bSChris Kay uint64_t id_aa64pfr0_el1_amu; /* AMU version */ 494*e747a59bSChris Kay 495*e747a59bSChris Kay uint64_t hcr_el2_amvoffen; /* AMU virtual offsets enabled */ 496*e747a59bSChris Kay 497*e747a59bSChris Kay uint64_t amcfgr_el0_ncg; /* Number of counter groups */ 498*e747a59bSChris Kay uint64_t amcgcr_el0_cg0nc; /* Number of group 0 counters */ 499b6eb3932SDimitris Papastamos 5001fd685a7SChris Kay #if ENABLE_AMU_AUXILIARY_COUNTERS 501*e747a59bSChris Kay uint64_t amcgcr_el0_cg1nc; /* Number of group 1 counters */ 502*e747a59bSChris Kay uint64_t amcg1idr_el0_voff; /* Auxiliary counters with virtual offsets */ 503f3ccf036SAlexei Fedorov #endif 504b6eb3932SDimitris Papastamos 505*e747a59bSChris Kay id_aa64pfr0_el1_amu = read_id_aa64pfr0_el1_amu(); 506*e747a59bSChris Kay if (id_aa64pfr0_el1_amu == ID_AA64PFR0_AMU_NOT_SUPPORTED) { 507*e747a59bSChris Kay return (void *)0; 508*e747a59bSChris Kay } 509*e747a59bSChris Kay 510*e747a59bSChris Kay core_pos = plat_my_core_pos(); 511*e747a59bSChris Kay ctx = &amu_ctxs_[core_pos]; 512*e747a59bSChris Kay 513*e747a59bSChris Kay amcfgr_el0_ncg = read_amcfgr_el0_ncg(); 514*e747a59bSChris Kay amcgcr_el0_cg0nc = read_amcgcr_el0_cg0nc(); 515*e747a59bSChris Kay 516*e747a59bSChris Kay hcr_el2_amvoffen = (id_aa64pfr0_el1_amu >= ID_AA64PFR0_AMU_V1P1) ? 517*e747a59bSChris Kay read_hcr_el2_amvoffen() : 0U; 518*e747a59bSChris Kay 519*e747a59bSChris Kay #if ENABLE_AMU_AUXILIARY_COUNTERS 520*e747a59bSChris Kay amcgcr_el0_cg1nc = (amcfgr_el0_ncg > 0U) ? read_amcgcr_el0_cg1nc() : 0U; 521*e747a59bSChris Kay amcg1idr_el0_voff = (hcr_el2_amvoffen != 0U) ? read_amcg1idr_el0_voff() : 0U; 522*e747a59bSChris Kay #endif 523*e747a59bSChris Kay 524*e747a59bSChris Kay /* 525*e747a59bSChris Kay * Sanity check that all counters were disabled when the context was 526*e747a59bSChris Kay * previously saved. 527*e747a59bSChris Kay */ 528*e747a59bSChris Kay 529*e747a59bSChris Kay assert(read_amcntenset0_el0_px() == 0U); 530*e747a59bSChris Kay 531*e747a59bSChris Kay if (amcfgr_el0_ncg > 0U) { 532*e747a59bSChris Kay assert(read_amcntenset1_el0_px() == 0U); 533*e747a59bSChris Kay } 534*e747a59bSChris Kay 535*e747a59bSChris Kay /* 536*e747a59bSChris Kay * Restore the counter values from the local context. 537*e747a59bSChris Kay */ 538*e747a59bSChris Kay 539*e747a59bSChris Kay for (i = 0U; i < amcgcr_el0_cg0nc; i++) { 540b6eb3932SDimitris Papastamos amu_group0_cnt_write(i, ctx->group0_cnts[i]); 541f3ccf036SAlexei Fedorov } 542b6eb3932SDimitris Papastamos 5431fd685a7SChris Kay #if ENABLE_AMU_AUXILIARY_COUNTERS 544*e747a59bSChris Kay for (i = 0U; i < amcgcr_el0_cg1nc; i++) { 545f3ccf036SAlexei Fedorov amu_group1_cnt_write(i, ctx->group1_cnts[i]); 546f3ccf036SAlexei Fedorov } 547*e747a59bSChris Kay #endif 548*e747a59bSChris Kay 549*e747a59bSChris Kay /* 550*e747a59bSChris Kay * Restore virtual offsets for counters that offer them. 551*e747a59bSChris Kay */ 552*e747a59bSChris Kay 553*e747a59bSChris Kay if (hcr_el2_amvoffen != 0U) { 554*e747a59bSChris Kay for (i = 0U, j = 0U; i < amcgcr_el0_cg0nc; i++) { 555*e747a59bSChris Kay if (!amu_group0_voffset_supported(i)) { 556*e747a59bSChris Kay continue; /* No virtual offset */ 557f3ccf036SAlexei Fedorov } 558f3ccf036SAlexei Fedorov 559*e747a59bSChris Kay amu_group0_voffset_write(i, ctx->group0_voffsets[j++]); 560873d4241Sjohpow01 } 561873d4241Sjohpow01 562*e747a59bSChris Kay #if ENABLE_AMU_AUXILIARY_COUNTERS 563*e747a59bSChris Kay for (i = 0U, j = 0U; i < amcgcr_el0_cg1nc; i++) { 564*e747a59bSChris Kay if ((amcg1idr_el0_voff >> i) & 1U) { 565*e747a59bSChris Kay continue; /* No virtual offset */ 566*e747a59bSChris Kay } 567*e747a59bSChris Kay 568*e747a59bSChris Kay amu_group1_voffset_write(i, ctx->group1_voffsets[j++]); 569*e747a59bSChris Kay } 570*e747a59bSChris Kay #endif 571*e747a59bSChris Kay } 572*e747a59bSChris Kay 573*e747a59bSChris Kay /* 574*e747a59bSChris Kay * Re-enable counters that were disabled during context save. 575*e747a59bSChris Kay */ 576*e747a59bSChris Kay 577*e747a59bSChris Kay write_amcntenset0_el0_px(ctx->group0_enable); 578*e747a59bSChris Kay 579*e747a59bSChris Kay #if ENABLE_AMU_AUXILIARY_COUNTERS 580*e747a59bSChris Kay if (amcfgr_el0_ncg > 0) { 581*e747a59bSChris Kay write_amcntenset1_el0_px(ctx->group1_enable); 5821fd685a7SChris Kay } 583f3ccf036SAlexei Fedorov #endif 584b6eb3932SDimitris Papastamos 58540daecc1SAntonio Nino Diaz return (void *)0; 586b6eb3932SDimitris Papastamos } 587b6eb3932SDimitris Papastamos 588b6eb3932SDimitris Papastamos SUBSCRIBE_TO_EVENT(psci_suspend_pwrdown_start, amu_context_save); 589b6eb3932SDimitris Papastamos SUBSCRIBE_TO_EVENT(psci_suspend_pwrdown_finish, amu_context_restore); 590