blob: 85f7007aac42e4db9303c1e96ee5d3a19e3df396 [file] [log] [blame]
Dimitris Papastamose08005a2017-10-12 13:02:29 +01001/*
Dimitris Papastamos7c4a6e62018-01-15 14:52:57 +00002 * Copyright (c) 2017-2018, ARM Limited and Contributors. All rights reserved.
Dimitris Papastamose08005a2017-10-12 13:02:29 +01003 *
4 * SPDX-License-Identifier: BSD-3-Clause
5 */
6
Dimitris Papastamos525c37a2017-11-13 09:49:45 +00007#include <assert.h>
Antonio Nino Diaz033b4bb2018-10-25 16:52:26 +01008#include <stdbool.h>
Dimitris Papastamose08005a2017-10-12 13:02:29 +01009
Antonio Nino Diaze0f90632018-12-14 00:18:21 +000010#include <arch.h>
11#include <arch_helpers.h>
12#include <lib/el3_runtime/pubsub_events.h>
13#include <lib/extensions/amu.h>
14#include <lib/extensions/amu_private.h>
15#include <plat/common/platform.h>
16
Dimitris Papastamos525c37a2017-11-13 09:49:45 +000017#define AMU_GROUP0_NR_COUNTERS 4
18
Dimitris Papastamoseaf3e6d2017-11-28 13:47:06 +000019struct amu_ctx {
20 uint64_t group0_cnts[AMU_GROUP0_NR_COUNTERS];
21 uint64_t group1_cnts[AMU_GROUP1_NR_COUNTERS];
22};
23
24static struct amu_ctx amu_ctxs[PLATFORM_CORE_COUNT];
25
Antonio Nino Diaz033b4bb2018-10-25 16:52:26 +010026bool amu_supported(void)
Dimitris Papastamose08005a2017-10-12 13:02:29 +010027{
28 uint64_t features;
29
30 features = read_id_aa64pfr0_el1() >> ID_AA64PFR0_AMU_SHIFT;
Antonio Nino Diaz033b4bb2018-10-25 16:52:26 +010031 return (features & ID_AA64PFR0_AMU_MASK) == 1U;
Dimitris Papastamos525c37a2017-11-13 09:49:45 +000032}
33
34/*
35 * Enable counters. This function is meant to be invoked
36 * by the context management library before exiting from EL3.
37 */
Antonio Nino Diaz033b4bb2018-10-25 16:52:26 +010038void amu_enable(bool el2_unused)
Dimitris Papastamos525c37a2017-11-13 09:49:45 +000039{
40 uint64_t v;
Dimitris Papastamose08005a2017-10-12 13:02:29 +010041
Antonio Nino Diaz033b4bb2018-10-25 16:52:26 +010042 if (!amu_supported())
Dimitris Papastamos525c37a2017-11-13 09:49:45 +000043 return;
Dimitris Papastamose08005a2017-10-12 13:02:29 +010044
Dimitris Papastamos525c37a2017-11-13 09:49:45 +000045 if (el2_unused) {
Dimitris Papastamose08005a2017-10-12 13:02:29 +010046 /*
Dimitris Papastamos525c37a2017-11-13 09:49:45 +000047 * CPTR_EL2.TAM: Set to zero so any accesses to
48 * the Activity Monitor registers do not trap to EL2.
Dimitris Papastamose08005a2017-10-12 13:02:29 +010049 */
Dimitris Papastamos525c37a2017-11-13 09:49:45 +000050 v = read_cptr_el2();
51 v &= ~CPTR_EL2_TAM_BIT;
52 write_cptr_el2(v);
Dimitris Papastamose08005a2017-10-12 13:02:29 +010053 }
Dimitris Papastamos525c37a2017-11-13 09:49:45 +000054
55 /*
56 * CPTR_EL3.TAM: Set to zero so that any accesses to
57 * the Activity Monitor registers do not trap to EL3.
58 */
59 v = read_cptr_el3();
60 v &= ~TAM_BIT;
61 write_cptr_el3(v);
62
63 /* Enable group 0 counters */
64 write_amcntenset0_el0(AMU_GROUP0_COUNTERS_MASK);
65 /* Enable group 1 counters */
66 write_amcntenset1_el0(AMU_GROUP1_COUNTERS_MASK);
67}
68
69/* Read the group 0 counter identified by the given `idx`. */
70uint64_t amu_group0_cnt_read(int idx)
71{
Antonio Nino Diaz033b4bb2018-10-25 16:52:26 +010072 assert(amu_supported());
73 assert((idx >= 0) && (idx < AMU_GROUP0_NR_COUNTERS));
Dimitris Papastamos525c37a2017-11-13 09:49:45 +000074
75 return amu_group0_cnt_read_internal(idx);
76}
77
78/* Write the group 0 counter identified by the given `idx` with `val`. */
79void amu_group0_cnt_write(int idx, uint64_t val)
80{
Antonio Nino Diaz033b4bb2018-10-25 16:52:26 +010081 assert(amu_supported());
82 assert((idx >= 0) && (idx < AMU_GROUP0_NR_COUNTERS));
Dimitris Papastamos525c37a2017-11-13 09:49:45 +000083
84 amu_group0_cnt_write_internal(idx, val);
85 isb();
86}
87
88/* Read the group 1 counter identified by the given `idx`. */
89uint64_t amu_group1_cnt_read(int idx)
90{
Antonio Nino Diaz033b4bb2018-10-25 16:52:26 +010091 assert(amu_supported());
92 assert((idx >= 0) && (idx < AMU_GROUP1_NR_COUNTERS));
Dimitris Papastamos525c37a2017-11-13 09:49:45 +000093
94 return amu_group1_cnt_read_internal(idx);
95}
96
97/* Write the group 1 counter identified by the given `idx` with `val`. */
98void amu_group1_cnt_write(int idx, uint64_t val)
99{
Antonio Nino Diaz033b4bb2018-10-25 16:52:26 +0100100 assert(amu_supported());
101 assert((idx >= 0) && (idx < AMU_GROUP1_NR_COUNTERS));
Dimitris Papastamos525c37a2017-11-13 09:49:45 +0000102
103 amu_group1_cnt_write_internal(idx, val);
104 isb();
105}
106
107/*
108 * Program the event type register for the given `idx` with
109 * the event number `val`.
110 */
111void amu_group1_set_evtype(int idx, unsigned int val)
112{
Antonio Nino Diaz033b4bb2018-10-25 16:52:26 +0100113 assert(amu_supported());
114 assert((idx >= 0) && (idx < AMU_GROUP1_NR_COUNTERS));
Dimitris Papastamos525c37a2017-11-13 09:49:45 +0000115
116 amu_group1_set_evtype_internal(idx, val);
117 isb();
Dimitris Papastamose08005a2017-10-12 13:02:29 +0100118}
Dimitris Papastamoseaf3e6d2017-11-28 13:47:06 +0000119
120static void *amu_context_save(const void *arg)
121{
122 struct amu_ctx *ctx = &amu_ctxs[plat_my_core_pos()];
123 int i;
124
Antonio Nino Diaz033b4bb2018-10-25 16:52:26 +0100125 if (!amu_supported())
Dimitris Papastamoseaf3e6d2017-11-28 13:47:06 +0000126 return (void *)-1;
127
128 /* Assert that group 0/1 counter configuration is what we expect */
Antonio Nino Diaz033b4bb2018-10-25 16:52:26 +0100129 assert((read_amcntenset0_el0() == AMU_GROUP0_COUNTERS_MASK) &&
130 (read_amcntenset1_el0() == AMU_GROUP1_COUNTERS_MASK));
Dimitris Papastamoseaf3e6d2017-11-28 13:47:06 +0000131
Antonio Nino Diaz033b4bb2018-10-25 16:52:26 +0100132 assert(((sizeof(int) * 8) - __builtin_clz(AMU_GROUP1_COUNTERS_MASK))
Dimitris Papastamoseaf3e6d2017-11-28 13:47:06 +0000133 <= AMU_GROUP1_NR_COUNTERS);
134
135 /*
136 * Disable group 0/1 counters to avoid other observers like SCP sampling
137 * counter values from the future via the memory mapped view.
138 */
139 write_amcntenclr0_el0(AMU_GROUP0_COUNTERS_MASK);
140 write_amcntenclr1_el0(AMU_GROUP1_COUNTERS_MASK);
141 isb();
142
143 /* Save group 0 counters */
144 for (i = 0; i < AMU_GROUP0_NR_COUNTERS; i++)
145 ctx->group0_cnts[i] = amu_group0_cnt_read(i);
146
147 /* Save group 1 counters */
148 for (i = 0; i < AMU_GROUP1_NR_COUNTERS; i++)
149 ctx->group1_cnts[i] = amu_group1_cnt_read(i);
150
Antonio Nino Diaz033b4bb2018-10-25 16:52:26 +0100151 return (void *)0;
Dimitris Papastamoseaf3e6d2017-11-28 13:47:06 +0000152}
153
154static void *amu_context_restore(const void *arg)
155{
156 struct amu_ctx *ctx = &amu_ctxs[plat_my_core_pos()];
157 int i;
158
Antonio Nino Diaz033b4bb2018-10-25 16:52:26 +0100159 if (!amu_supported())
Dimitris Papastamoseaf3e6d2017-11-28 13:47:06 +0000160 return (void *)-1;
161
162 /* Counters were disabled in `amu_context_save()` */
Antonio Nino Diaz033b4bb2018-10-25 16:52:26 +0100163 assert((read_amcntenset0_el0() == 0U) && (read_amcntenset1_el0() == 0U));
Dimitris Papastamoseaf3e6d2017-11-28 13:47:06 +0000164
Antonio Nino Diaz033b4bb2018-10-25 16:52:26 +0100165 assert(((sizeof(int) * 8U) - __builtin_clz(AMU_GROUP1_COUNTERS_MASK))
Dimitris Papastamoseaf3e6d2017-11-28 13:47:06 +0000166 <= AMU_GROUP1_NR_COUNTERS);
167
168 /* Restore group 0 counters */
169 for (i = 0; i < AMU_GROUP0_NR_COUNTERS; i++)
Antonio Nino Diaz033b4bb2018-10-25 16:52:26 +0100170 if ((AMU_GROUP0_COUNTERS_MASK & (1U << i)) != 0U)
Dimitris Papastamoseaf3e6d2017-11-28 13:47:06 +0000171 amu_group0_cnt_write(i, ctx->group0_cnts[i]);
172
173 /* Restore group 1 counters */
174 for (i = 0; i < AMU_GROUP1_NR_COUNTERS; i++)
Antonio Nino Diaz033b4bb2018-10-25 16:52:26 +0100175 if ((AMU_GROUP1_COUNTERS_MASK & (1U << i)) != 0U)
Dimitris Papastamoseaf3e6d2017-11-28 13:47:06 +0000176 amu_group1_cnt_write(i, ctx->group1_cnts[i]);
Dimitris Papastamoseaf3e6d2017-11-28 13:47:06 +0000177
178 /* Restore group 0/1 counter configuration */
179 write_amcntenset0_el0(AMU_GROUP0_COUNTERS_MASK);
180 write_amcntenset1_el0(AMU_GROUP1_COUNTERS_MASK);
181
Antonio Nino Diaz033b4bb2018-10-25 16:52:26 +0100182 return (void *)0;
Dimitris Papastamoseaf3e6d2017-11-28 13:47:06 +0000183}
184
185SUBSCRIBE_TO_EVENT(psci_suspend_pwrdown_start, amu_context_save);
186SUBSCRIBE_TO_EVENT(psci_suspend_pwrdown_finish, amu_context_restore);