blob: 5d556e5d35743619e171dba0b81e41bc3e6d6bec [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
7#include <amu.h>
Dimitris Papastamos525c37a2017-11-13 09:49:45 +00008#include <amu_private.h>
Dimitris Papastamose08005a2017-10-12 13:02:29 +01009#include <arch.h>
10#include <arch_helpers.h>
Dimitris Papastamos525c37a2017-11-13 09:49:45 +000011#include <assert.h>
Dimitris Papastamoseaf3e6d2017-11-28 13:47:06 +000012#include <platform.h>
13#include <pubsub_events.h>
Dimitris Papastamose08005a2017-10-12 13:02:29 +010014
Dimitris Papastamos525c37a2017-11-13 09:49:45 +000015#define AMU_GROUP0_NR_COUNTERS 4
16
Dimitris Papastamoseaf3e6d2017-11-28 13:47:06 +000017struct amu_ctx {
18 uint64_t group0_cnts[AMU_GROUP0_NR_COUNTERS];
19 uint64_t group1_cnts[AMU_GROUP1_NR_COUNTERS];
20};
21
22static struct amu_ctx amu_ctxs[PLATFORM_CORE_COUNT];
23
Dimitris Papastamos525c37a2017-11-13 09:49:45 +000024int amu_supported(void)
Dimitris Papastamose08005a2017-10-12 13:02:29 +010025{
26 uint64_t features;
27
28 features = read_id_aa64pfr0_el1() >> ID_AA64PFR0_AMU_SHIFT;
Dimitris Papastamos525c37a2017-11-13 09:49:45 +000029 return (features & ID_AA64PFR0_AMU_MASK) == 1;
30}
31
32/*
33 * Enable counters. This function is meant to be invoked
34 * by the context management library before exiting from EL3.
35 */
36void amu_enable(int el2_unused)
37{
38 uint64_t v;
Dimitris Papastamose08005a2017-10-12 13:02:29 +010039
Dimitris Papastamosaaa19852018-02-26 17:56:31 +000040 if (amu_supported() == 0)
Dimitris Papastamos525c37a2017-11-13 09:49:45 +000041 return;
Dimitris Papastamose08005a2017-10-12 13:02:29 +010042
Dimitris Papastamos525c37a2017-11-13 09:49:45 +000043 if (el2_unused) {
Dimitris Papastamose08005a2017-10-12 13:02:29 +010044 /*
Dimitris Papastamos525c37a2017-11-13 09:49:45 +000045 * CPTR_EL2.TAM: Set to zero so any accesses to
46 * the Activity Monitor registers do not trap to EL2.
Dimitris Papastamose08005a2017-10-12 13:02:29 +010047 */
Dimitris Papastamos525c37a2017-11-13 09:49:45 +000048 v = read_cptr_el2();
49 v &= ~CPTR_EL2_TAM_BIT;
50 write_cptr_el2(v);
Dimitris Papastamose08005a2017-10-12 13:02:29 +010051 }
Dimitris Papastamos525c37a2017-11-13 09:49:45 +000052
53 /*
54 * CPTR_EL3.TAM: Set to zero so that any accesses to
55 * the Activity Monitor registers do not trap to EL3.
56 */
57 v = read_cptr_el3();
58 v &= ~TAM_BIT;
59 write_cptr_el3(v);
60
61 /* Enable group 0 counters */
62 write_amcntenset0_el0(AMU_GROUP0_COUNTERS_MASK);
63 /* Enable group 1 counters */
64 write_amcntenset1_el0(AMU_GROUP1_COUNTERS_MASK);
65}
66
67/* Read the group 0 counter identified by the given `idx`. */
68uint64_t amu_group0_cnt_read(int idx)
69{
Dimitris Papastamosaaa19852018-02-26 17:56:31 +000070 assert(amu_supported() != 0);
Dimitris Papastamos525c37a2017-11-13 09:49:45 +000071 assert(idx >= 0 && idx < AMU_GROUP0_NR_COUNTERS);
72
73 return amu_group0_cnt_read_internal(idx);
74}
75
76/* Write the group 0 counter identified by the given `idx` with `val`. */
77void amu_group0_cnt_write(int idx, uint64_t val)
78{
Dimitris Papastamosaaa19852018-02-26 17:56:31 +000079 assert(amu_supported() != 0);
Dimitris Papastamos525c37a2017-11-13 09:49:45 +000080 assert(idx >= 0 && idx < AMU_GROUP0_NR_COUNTERS);
81
82 amu_group0_cnt_write_internal(idx, val);
83 isb();
84}
85
86/* Read the group 1 counter identified by the given `idx`. */
87uint64_t amu_group1_cnt_read(int idx)
88{
Dimitris Papastamosaaa19852018-02-26 17:56:31 +000089 assert(amu_supported() != 0);
Dimitris Papastamos525c37a2017-11-13 09:49:45 +000090 assert(idx >= 0 && idx < AMU_GROUP1_NR_COUNTERS);
91
92 return amu_group1_cnt_read_internal(idx);
93}
94
95/* Write the group 1 counter identified by the given `idx` with `val`. */
96void amu_group1_cnt_write(int idx, uint64_t val)
97{
Dimitris Papastamosaaa19852018-02-26 17:56:31 +000098 assert(amu_supported() != 0);
Dimitris Papastamos525c37a2017-11-13 09:49:45 +000099 assert(idx >= 0 && idx < AMU_GROUP1_NR_COUNTERS);
100
101 amu_group1_cnt_write_internal(idx, val);
102 isb();
103}
104
105/*
106 * Program the event type register for the given `idx` with
107 * the event number `val`.
108 */
109void amu_group1_set_evtype(int idx, unsigned int val)
110{
Dimitris Papastamosaaa19852018-02-26 17:56:31 +0000111 assert(amu_supported() != 0);
Dimitris Papastamos525c37a2017-11-13 09:49:45 +0000112 assert (idx >= 0 && idx < AMU_GROUP1_NR_COUNTERS);
113
114 amu_group1_set_evtype_internal(idx, val);
115 isb();
Dimitris Papastamose08005a2017-10-12 13:02:29 +0100116}
Dimitris Papastamoseaf3e6d2017-11-28 13:47:06 +0000117
118static void *amu_context_save(const void *arg)
119{
120 struct amu_ctx *ctx = &amu_ctxs[plat_my_core_pos()];
121 int i;
122
Dimitris Papastamosaaa19852018-02-26 17:56:31 +0000123 if (amu_supported() == 0)
Dimitris Papastamoseaf3e6d2017-11-28 13:47:06 +0000124 return (void *)-1;
125
126 /* Assert that group 0/1 counter configuration is what we expect */
127 assert(read_amcntenset0_el0() == AMU_GROUP0_COUNTERS_MASK &&
128 read_amcntenset1_el0() == AMU_GROUP1_COUNTERS_MASK);
129
130 assert((sizeof(int) * 8) - __builtin_clz(AMU_GROUP1_COUNTERS_MASK)
131 <= AMU_GROUP1_NR_COUNTERS);
132
133 /*
134 * Disable group 0/1 counters to avoid other observers like SCP sampling
135 * counter values from the future via the memory mapped view.
136 */
137 write_amcntenclr0_el0(AMU_GROUP0_COUNTERS_MASK);
138 write_amcntenclr1_el0(AMU_GROUP1_COUNTERS_MASK);
139 isb();
140
141 /* Save group 0 counters */
142 for (i = 0; i < AMU_GROUP0_NR_COUNTERS; i++)
143 ctx->group0_cnts[i] = amu_group0_cnt_read(i);
144
145 /* Save group 1 counters */
146 for (i = 0; i < AMU_GROUP1_NR_COUNTERS; i++)
147 ctx->group1_cnts[i] = amu_group1_cnt_read(i);
148
149 return 0;
150}
151
152static void *amu_context_restore(const void *arg)
153{
154 struct amu_ctx *ctx = &amu_ctxs[plat_my_core_pos()];
155 int i;
156
Dimitris Papastamosaaa19852018-02-26 17:56:31 +0000157 if (amu_supported() == 0)
Dimitris Papastamoseaf3e6d2017-11-28 13:47:06 +0000158 return (void *)-1;
159
160 /* Counters were disabled in `amu_context_save()` */
161 assert(read_amcntenset0_el0() == 0 && read_amcntenset1_el0() == 0);
162
163 assert((sizeof(int) * 8) - __builtin_clz(AMU_GROUP1_COUNTERS_MASK)
164 <= AMU_GROUP1_NR_COUNTERS);
165
166 /* Restore group 0 counters */
167 for (i = 0; i < AMU_GROUP0_NR_COUNTERS; i++)
168 if (AMU_GROUP0_COUNTERS_MASK & (1U << i))
169 amu_group0_cnt_write(i, ctx->group0_cnts[i]);
170
171 /* Restore group 1 counters */
172 for (i = 0; i < AMU_GROUP1_NR_COUNTERS; i++)
173 if (AMU_GROUP1_COUNTERS_MASK & (1U << i))
174 amu_group1_cnt_write(i, ctx->group1_cnts[i]);
Dimitris Papastamoseaf3e6d2017-11-28 13:47:06 +0000175
176 /* Restore group 0/1 counter configuration */
177 write_amcntenset0_el0(AMU_GROUP0_COUNTERS_MASK);
178 write_amcntenset1_el0(AMU_GROUP1_COUNTERS_MASK);
179
180 return 0;
181}
182
183SUBSCRIBE_TO_EVENT(psci_suspend_pwrdown_start, amu_context_save);
184SUBSCRIBE_TO_EVENT(psci_suspend_pwrdown_finish, amu_context_restore);