1 /* 2 * Copyright (c) 2017-2018, ARM Limited and Contributors. All rights reserved. 3 * 4 * SPDX-License-Identifier: BSD-3-Clause 5 */ 6 7 #include <stdbool.h> 8 9 #include <arch.h> 10 #include <arch_helpers.h> 11 #include <lib/el3_runtime/pubsub_events.h> 12 #include <lib/extensions/amu.h> 13 #include <lib/extensions/amu_private.h> 14 #include <plat/common/platform.h> 15 16 #define AMU_GROUP0_NR_COUNTERS 4 17 18 struct amu_ctx { 19 uint64_t group0_cnts[AMU_GROUP0_NR_COUNTERS]; 20 uint64_t group1_cnts[AMU_GROUP1_NR_COUNTERS]; 21 }; 22 23 static struct amu_ctx amu_ctxs[PLATFORM_CORE_COUNT]; 24 25 bool amu_supported(void) 26 { 27 uint64_t features; 28 29 features = read_id_pfr0() >> ID_PFR0_AMU_SHIFT; 30 return (features & ID_PFR0_AMU_MASK) == 1U; 31 } 32 33 void amu_enable(bool el2_unused) 34 { 35 if (!amu_supported()) 36 return; 37 38 if (el2_unused) { 39 uint64_t v; 40 /* 41 * Non-secure access from EL0 or EL1 to the Activity Monitor 42 * registers do not trap to EL2. 43 */ 44 v = read_hcptr(); 45 v &= ~TAM_BIT; 46 write_hcptr(v); 47 } 48 49 /* Enable group 0 counters */ 50 write_amcntenset0(AMU_GROUP0_COUNTERS_MASK); 51 52 /* Enable group 1 counters */ 53 write_amcntenset1(AMU_GROUP1_COUNTERS_MASK); 54 } 55 56 /* Read the group 0 counter identified by the given `idx`. */ 57 uint64_t amu_group0_cnt_read(int idx) 58 { 59 assert(amu_supported()); 60 assert((idx >= 0) && (idx < AMU_GROUP0_NR_COUNTERS)); 61 62 return amu_group0_cnt_read_internal(idx); 63 } 64 65 /* Write the group 0 counter identified by the given `idx` with `val`. */ 66 void amu_group0_cnt_write(int idx, uint64_t val) 67 { 68 assert(amu_supported()); 69 assert((idx >= 0) && (idx < AMU_GROUP0_NR_COUNTERS)); 70 71 amu_group0_cnt_write_internal(idx, val); 72 isb(); 73 } 74 75 /* Read the group 1 counter identified by the given `idx`. */ 76 uint64_t amu_group1_cnt_read(int idx) 77 { 78 assert(amu_supported()); 79 assert((idx >= 0) && (idx < AMU_GROUP1_NR_COUNTERS)); 80 81 return amu_group1_cnt_read_internal(idx); 82 } 83 84 /* Write the group 1 counter identified by the given `idx` with `val`. */ 85 void amu_group1_cnt_write(int idx, uint64_t val) 86 { 87 assert(amu_supported()); 88 assert((idx >= 0) && (idx < AMU_GROUP1_NR_COUNTERS)); 89 90 amu_group1_cnt_write_internal(idx, val); 91 isb(); 92 } 93 94 void amu_group1_set_evtype(int idx, unsigned int val) 95 { 96 assert(amu_supported()); 97 assert((idx >= 0) && (idx < AMU_GROUP1_NR_COUNTERS)); 98 99 amu_group1_set_evtype_internal(idx, val); 100 isb(); 101 } 102 103 static void *amu_context_save(const void *arg) 104 { 105 struct amu_ctx *ctx; 106 int i; 107 108 if (!amu_supported()) 109 return (void *)-1; 110 111 ctx = &amu_ctxs[plat_my_core_pos()]; 112 113 /* Assert that group 0 counter configuration is what we expect */ 114 assert(read_amcntenset0() == AMU_GROUP0_COUNTERS_MASK && 115 read_amcntenset1() == AMU_GROUP1_COUNTERS_MASK); 116 117 /* 118 * Disable group 0 counters to avoid other observers like SCP sampling 119 * counter values from the future via the memory mapped view. 120 */ 121 write_amcntenclr0(AMU_GROUP0_COUNTERS_MASK); 122 write_amcntenclr1(AMU_GROUP1_COUNTERS_MASK); 123 isb(); 124 125 for (i = 0; i < AMU_GROUP0_NR_COUNTERS; i++) 126 ctx->group0_cnts[i] = amu_group0_cnt_read(i); 127 128 for (i = 0; i < AMU_GROUP1_NR_COUNTERS; i++) 129 ctx->group1_cnts[i] = amu_group1_cnt_read(i); 130 131 return (void *)0; 132 } 133 134 static void *amu_context_restore(const void *arg) 135 { 136 struct amu_ctx *ctx; 137 int i; 138 139 if (!amu_supported()) 140 return (void *)-1; 141 142 ctx = &amu_ctxs[plat_my_core_pos()]; 143 144 /* Counters were disabled in `amu_context_save()` */ 145 assert((read_amcntenset0() == 0U) && (read_amcntenset1() == 0U)); 146 147 /* Restore group 0 counters */ 148 for (i = 0; i < AMU_GROUP0_NR_COUNTERS; i++) 149 amu_group0_cnt_write(i, ctx->group0_cnts[i]); 150 for (i = 0; i < AMU_GROUP1_NR_COUNTERS; i++) 151 amu_group1_cnt_write(i, ctx->group1_cnts[i]); 152 153 /* Enable group 0 counters */ 154 write_amcntenset0(AMU_GROUP0_COUNTERS_MASK); 155 156 /* Enable group 1 counters */ 157 write_amcntenset1(AMU_GROUP1_COUNTERS_MASK); 158 return (void *)0; 159 } 160 161 SUBSCRIBE_TO_EVENT(psci_suspend_pwrdown_start, amu_context_save); 162 SUBSCRIBE_TO_EVENT(psci_suspend_pwrdown_finish, amu_context_restore); 163