xref: /rk3399_ARM-atf/lib/extensions/amu/aarch64/amu.c (revision b6eb39327c5b009bc0bbecdb6867d8bf7c05f2fd)
1380559c1SDimitris Papastamos /*
2380559c1SDimitris Papastamos  * Copyright (c) 2017, ARM Limited and Contributors. All rights reserved.
3380559c1SDimitris Papastamos  *
4380559c1SDimitris Papastamos  * SPDX-License-Identifier: BSD-3-Clause
5380559c1SDimitris Papastamos  */
6380559c1SDimitris Papastamos 
7380559c1SDimitris Papastamos #include <amu.h>
80767d50eSDimitris Papastamos #include <amu_private.h>
9380559c1SDimitris Papastamos #include <arch.h>
10380559c1SDimitris Papastamos #include <arch_helpers.h>
110767d50eSDimitris Papastamos #include <assert.h>
120767d50eSDimitris Papastamos #include <debug.h>
13*b6eb3932SDimitris Papastamos #include <platform.h>
14*b6eb3932SDimitris Papastamos #include <pubsub_events.h>
15380559c1SDimitris Papastamos 
160767d50eSDimitris Papastamos #define AMU_GROUP0_NR_COUNTERS	4
170767d50eSDimitris Papastamos 
18*b6eb3932SDimitris Papastamos struct amu_ctx {
19*b6eb3932SDimitris Papastamos 	uint64_t group0_cnts[AMU_GROUP0_NR_COUNTERS];
20*b6eb3932SDimitris Papastamos 	uint64_t group1_cnts[AMU_GROUP1_NR_COUNTERS];
21*b6eb3932SDimitris Papastamos };
22*b6eb3932SDimitris Papastamos 
23*b6eb3932SDimitris Papastamos static struct amu_ctx amu_ctxs[PLATFORM_CORE_COUNT];
24*b6eb3932SDimitris Papastamos 
250767d50eSDimitris Papastamos int amu_supported(void)
26380559c1SDimitris Papastamos {
27380559c1SDimitris Papastamos 	uint64_t features;
28380559c1SDimitris Papastamos 
29380559c1SDimitris Papastamos 	features = read_id_aa64pfr0_el1() >> ID_AA64PFR0_AMU_SHIFT;
300767d50eSDimitris Papastamos 	return (features & ID_AA64PFR0_AMU_MASK) == 1;
310767d50eSDimitris Papastamos }
320767d50eSDimitris Papastamos 
330767d50eSDimitris Papastamos /*
340767d50eSDimitris Papastamos  * Enable counters.  This function is meant to be invoked
350767d50eSDimitris Papastamos  * by the context management library before exiting from EL3.
360767d50eSDimitris Papastamos  */
370767d50eSDimitris Papastamos void amu_enable(int el2_unused)
380767d50eSDimitris Papastamos {
39380559c1SDimitris Papastamos 	uint64_t v;
40380559c1SDimitris Papastamos 
410767d50eSDimitris Papastamos 	if (!amu_supported()) {
420767d50eSDimitris Papastamos 		WARN("Cannot enable AMU - not supported\n");
430767d50eSDimitris Papastamos 		return;
440767d50eSDimitris Papastamos 	}
450767d50eSDimitris Papastamos 
46380559c1SDimitris Papastamos 	if (el2_unused) {
47380559c1SDimitris Papastamos 		/*
48380559c1SDimitris Papastamos 		 * CPTR_EL2.TAM: Set to zero so any accesses to
49380559c1SDimitris Papastamos 		 * the Activity Monitor registers do not trap to EL2.
50380559c1SDimitris Papastamos 		 */
51380559c1SDimitris Papastamos 		v = read_cptr_el2();
52380559c1SDimitris Papastamos 		v &= ~CPTR_EL2_TAM_BIT;
53380559c1SDimitris Papastamos 		write_cptr_el2(v);
54380559c1SDimitris Papastamos 	}
55380559c1SDimitris Papastamos 
56380559c1SDimitris Papastamos 	/*
57380559c1SDimitris Papastamos 	 * CPTR_EL3.TAM: Set to zero so that any accesses to
58380559c1SDimitris Papastamos 	 * the Activity Monitor registers do not trap to EL3.
59380559c1SDimitris Papastamos 	 */
60380559c1SDimitris Papastamos 	v = read_cptr_el3();
61380559c1SDimitris Papastamos 	v &= ~TAM_BIT;
62380559c1SDimitris Papastamos 	write_cptr_el3(v);
63380559c1SDimitris Papastamos 
64380559c1SDimitris Papastamos 	/* Enable group 0 counters */
65380559c1SDimitris Papastamos 	write_amcntenset0_el0(AMU_GROUP0_COUNTERS_MASK);
6659902b7cSDimitris Papastamos 	/* Enable group 1 counters */
6759902b7cSDimitris Papastamos 	write_amcntenset1_el0(AMU_GROUP1_COUNTERS_MASK);
68380559c1SDimitris Papastamos }
690767d50eSDimitris Papastamos 
700767d50eSDimitris Papastamos /* Read the group 0 counter identified by the given `idx`. */
710767d50eSDimitris Papastamos uint64_t amu_group0_cnt_read(int idx)
720767d50eSDimitris Papastamos {
730767d50eSDimitris Papastamos 	assert(amu_supported());
740767d50eSDimitris Papastamos 	assert(idx >= 0 && idx < AMU_GROUP0_NR_COUNTERS);
750767d50eSDimitris Papastamos 
760767d50eSDimitris Papastamos 	return amu_group0_cnt_read_internal(idx);
770767d50eSDimitris Papastamos }
780767d50eSDimitris Papastamos 
790767d50eSDimitris Papastamos /* Write the group 0 counter identified by the given `idx` with `val`. */
800767d50eSDimitris Papastamos void amu_group0_cnt_write(int idx, uint64_t val)
810767d50eSDimitris Papastamos {
820767d50eSDimitris Papastamos 	assert(amu_supported());
830767d50eSDimitris Papastamos 	assert(idx >= 0 && idx < AMU_GROUP0_NR_COUNTERS);
840767d50eSDimitris Papastamos 
850767d50eSDimitris Papastamos 	amu_group0_cnt_write_internal(idx, val);
860767d50eSDimitris Papastamos 	isb();
870767d50eSDimitris Papastamos }
880767d50eSDimitris Papastamos 
890767d50eSDimitris Papastamos /* Read the group 1 counter identified by the given `idx`. */
900767d50eSDimitris Papastamos uint64_t amu_group1_cnt_read(int idx)
910767d50eSDimitris Papastamos {
920767d50eSDimitris Papastamos 	assert(amu_supported());
930767d50eSDimitris Papastamos 	assert(idx >= 0 && idx < AMU_GROUP1_NR_COUNTERS);
940767d50eSDimitris Papastamos 
950767d50eSDimitris Papastamos 	return amu_group1_cnt_read_internal(idx);
960767d50eSDimitris Papastamos }
970767d50eSDimitris Papastamos 
980767d50eSDimitris Papastamos /* Write the group 1 counter identified by the given `idx` with `val`. */
990767d50eSDimitris Papastamos void amu_group1_cnt_write(int idx, uint64_t val)
1000767d50eSDimitris Papastamos {
1010767d50eSDimitris Papastamos 	assert(amu_supported());
1020767d50eSDimitris Papastamos 	assert(idx >= 0 && idx < AMU_GROUP1_NR_COUNTERS);
1030767d50eSDimitris Papastamos 
1040767d50eSDimitris Papastamos 	amu_group1_cnt_write_internal(idx, val);
1050767d50eSDimitris Papastamos 	isb();
1060767d50eSDimitris Papastamos }
1070767d50eSDimitris Papastamos 
1080767d50eSDimitris Papastamos /*
1090767d50eSDimitris Papastamos  * Program the event type register for the given `idx` with
1100767d50eSDimitris Papastamos  * the event number `val`.
1110767d50eSDimitris Papastamos  */
1120767d50eSDimitris Papastamos void amu_group1_set_evtype(int idx, unsigned int val)
1130767d50eSDimitris Papastamos {
1140767d50eSDimitris Papastamos 	assert(amu_supported());
1150767d50eSDimitris Papastamos 	assert (idx >= 0 && idx < AMU_GROUP1_NR_COUNTERS);
1160767d50eSDimitris Papastamos 
1170767d50eSDimitris Papastamos 	amu_group1_set_evtype_internal(idx, val);
1180767d50eSDimitris Papastamos 	isb();
119380559c1SDimitris Papastamos }
120*b6eb3932SDimitris Papastamos 
121*b6eb3932SDimitris Papastamos static void *amu_context_save(const void *arg)
122*b6eb3932SDimitris Papastamos {
123*b6eb3932SDimitris Papastamos 	struct amu_ctx *ctx = &amu_ctxs[plat_my_core_pos()];
124*b6eb3932SDimitris Papastamos 	int i;
125*b6eb3932SDimitris Papastamos 
126*b6eb3932SDimitris Papastamos 	if (!amu_supported())
127*b6eb3932SDimitris Papastamos 		return (void *)-1;
128*b6eb3932SDimitris Papastamos 
129*b6eb3932SDimitris Papastamos 	/* Assert that group 0/1 counter configuration is what we expect */
130*b6eb3932SDimitris Papastamos 	assert(read_amcntenset0_el0() == AMU_GROUP0_COUNTERS_MASK &&
131*b6eb3932SDimitris Papastamos 	       read_amcntenset1_el0() == AMU_GROUP1_COUNTERS_MASK);
132*b6eb3932SDimitris Papastamos 
133*b6eb3932SDimitris Papastamos 	assert((sizeof(int) * 8) - __builtin_clz(AMU_GROUP1_COUNTERS_MASK)
134*b6eb3932SDimitris Papastamos 		<= AMU_GROUP1_NR_COUNTERS);
135*b6eb3932SDimitris Papastamos 
136*b6eb3932SDimitris Papastamos 	/*
137*b6eb3932SDimitris Papastamos 	 * Disable group 0/1 counters to avoid other observers like SCP sampling
138*b6eb3932SDimitris Papastamos 	 * counter values from the future via the memory mapped view.
139*b6eb3932SDimitris Papastamos 	 */
140*b6eb3932SDimitris Papastamos 	write_amcntenclr0_el0(AMU_GROUP0_COUNTERS_MASK);
141*b6eb3932SDimitris Papastamos 	write_amcntenclr1_el0(AMU_GROUP1_COUNTERS_MASK);
142*b6eb3932SDimitris Papastamos 	isb();
143*b6eb3932SDimitris Papastamos 
144*b6eb3932SDimitris Papastamos 	/* Save group 0 counters */
145*b6eb3932SDimitris Papastamos 	for (i = 0; i < AMU_GROUP0_NR_COUNTERS; i++)
146*b6eb3932SDimitris Papastamos 		ctx->group0_cnts[i] = amu_group0_cnt_read(i);
147*b6eb3932SDimitris Papastamos 
148*b6eb3932SDimitris Papastamos 	/* Save group 1 counters */
149*b6eb3932SDimitris Papastamos 	for (i = 0; i < AMU_GROUP1_NR_COUNTERS; i++)
150*b6eb3932SDimitris Papastamos 		ctx->group1_cnts[i] = amu_group1_cnt_read(i);
151*b6eb3932SDimitris Papastamos 
152*b6eb3932SDimitris Papastamos 	return 0;
153*b6eb3932SDimitris Papastamos }
154*b6eb3932SDimitris Papastamos 
155*b6eb3932SDimitris Papastamos static void *amu_context_restore(const void *arg)
156*b6eb3932SDimitris Papastamos {
157*b6eb3932SDimitris Papastamos 	struct amu_ctx *ctx = &amu_ctxs[plat_my_core_pos()];
158*b6eb3932SDimitris Papastamos 	int i;
159*b6eb3932SDimitris Papastamos 
160*b6eb3932SDimitris Papastamos 	if (!amu_supported())
161*b6eb3932SDimitris Papastamos 		return (void *)-1;
162*b6eb3932SDimitris Papastamos 
163*b6eb3932SDimitris Papastamos 	/* Counters were disabled in `amu_context_save()` */
164*b6eb3932SDimitris Papastamos 	assert(read_amcntenset0_el0() == 0 && read_amcntenset1_el0() == 0);
165*b6eb3932SDimitris Papastamos 
166*b6eb3932SDimitris Papastamos 	assert((sizeof(int) * 8) - __builtin_clz(AMU_GROUP1_COUNTERS_MASK)
167*b6eb3932SDimitris Papastamos 		<= AMU_GROUP1_NR_COUNTERS);
168*b6eb3932SDimitris Papastamos 
169*b6eb3932SDimitris Papastamos 	/* Restore group 0 counters */
170*b6eb3932SDimitris Papastamos 	for (i = 0; i < AMU_GROUP0_NR_COUNTERS; i++)
171*b6eb3932SDimitris Papastamos 		if (AMU_GROUP0_COUNTERS_MASK & (1U << i))
172*b6eb3932SDimitris Papastamos 			amu_group0_cnt_write(i, ctx->group0_cnts[i]);
173*b6eb3932SDimitris Papastamos 
174*b6eb3932SDimitris Papastamos 	/* Restore group 1 counters */
175*b6eb3932SDimitris Papastamos 	for (i = 0; i < AMU_GROUP1_NR_COUNTERS; i++)
176*b6eb3932SDimitris Papastamos 		if (AMU_GROUP1_COUNTERS_MASK & (1U << i))
177*b6eb3932SDimitris Papastamos 			amu_group1_cnt_write(i, ctx->group1_cnts[i]);
178*b6eb3932SDimitris Papastamos 	isb();
179*b6eb3932SDimitris Papastamos 
180*b6eb3932SDimitris Papastamos 	/* Restore group 0/1 counter configuration */
181*b6eb3932SDimitris Papastamos 	write_amcntenset0_el0(AMU_GROUP0_COUNTERS_MASK);
182*b6eb3932SDimitris Papastamos 	write_amcntenset1_el0(AMU_GROUP1_COUNTERS_MASK);
183*b6eb3932SDimitris Papastamos 
184*b6eb3932SDimitris Papastamos 	return 0;
185*b6eb3932SDimitris Papastamos }
186*b6eb3932SDimitris Papastamos 
187*b6eb3932SDimitris Papastamos SUBSCRIBE_TO_EVENT(psci_suspend_pwrdown_start, amu_context_save);
188*b6eb3932SDimitris Papastamos SUBSCRIBE_TO_EVENT(psci_suspend_pwrdown_finish, amu_context_restore);
189