xref: /rk3399_ARM-atf/lib/extensions/amu/aarch64/amu.c (revision f3ccf036ecb1ae16287817833ebb07a26dcc0230)
1380559c1SDimitris Papastamos /*
2*f3ccf036SAlexei Fedorov  * Copyright (c) 2017-2020, 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>
809d40e0eSAntonio Nino Diaz #include <stdbool.h>
909d40e0eSAntonio Nino Diaz 
10380559c1SDimitris Papastamos #include <arch.h>
11380559c1SDimitris Papastamos #include <arch_helpers.h>
12*f3ccf036SAlexei Fedorov 
1309d40e0eSAntonio Nino Diaz #include <lib/el3_runtime/pubsub_events.h>
1409d40e0eSAntonio Nino Diaz #include <lib/extensions/amu.h>
1509d40e0eSAntonio Nino Diaz #include <lib/extensions/amu_private.h>
16*f3ccf036SAlexei Fedorov 
1709d40e0eSAntonio Nino Diaz #include <plat/common/platform.h>
18380559c1SDimitris Papastamos 
19b6eb3932SDimitris Papastamos static struct amu_ctx amu_ctxs[PLATFORM_CORE_COUNT];
20b6eb3932SDimitris Papastamos 
21*f3ccf036SAlexei Fedorov /* Check if AMUv1 for Armv8.4 or 8.6 is implemented */
2240daecc1SAntonio Nino Diaz bool amu_supported(void)
23380559c1SDimitris Papastamos {
24*f3ccf036SAlexei Fedorov 	uint64_t features = read_id_aa64pfr0_el1() >> ID_AA64PFR0_AMU_SHIFT;
25380559c1SDimitris Papastamos 
26*f3ccf036SAlexei Fedorov 	features &= ID_AA64PFR0_AMU_MASK;
27*f3ccf036SAlexei Fedorov 	return ((features == 1U) || (features == 2U));
280767d50eSDimitris Papastamos }
290767d50eSDimitris Papastamos 
30*f3ccf036SAlexei Fedorov #if AMU_GROUP1_NR_COUNTERS
31*f3ccf036SAlexei Fedorov /* Check if group 1 counters is implemented */
32*f3ccf036SAlexei Fedorov bool amu_group1_supported(void)
33*f3ccf036SAlexei Fedorov {
34*f3ccf036SAlexei Fedorov 	uint64_t features = read_amcfgr_el0() >> AMCFGR_EL0_NCG_SHIFT;
35*f3ccf036SAlexei Fedorov 
36*f3ccf036SAlexei Fedorov 	return (features & AMCFGR_EL0_NCG_MASK) == 1U;
37*f3ccf036SAlexei Fedorov }
38*f3ccf036SAlexei Fedorov #endif
39*f3ccf036SAlexei Fedorov 
400767d50eSDimitris Papastamos /*
410767d50eSDimitris Papastamos  * Enable counters. This function is meant to be invoked
420767d50eSDimitris Papastamos  * by the context management library before exiting from EL3.
430767d50eSDimitris Papastamos  */
4440daecc1SAntonio Nino Diaz void amu_enable(bool el2_unused)
450767d50eSDimitris Papastamos {
46380559c1SDimitris Papastamos 	uint64_t v;
47380559c1SDimitris Papastamos 
48*f3ccf036SAlexei Fedorov 	if (!amu_supported()) {
49*f3ccf036SAlexei Fedorov 		INFO("AMU is not implemented\n");
500767d50eSDimitris Papastamos 		return;
51*f3ccf036SAlexei Fedorov 	}
52*f3ccf036SAlexei Fedorov 
53*f3ccf036SAlexei Fedorov #if AMU_GROUP1_NR_COUNTERS
54*f3ccf036SAlexei Fedorov 	/* Check and set presence of group 1 counters */
55*f3ccf036SAlexei Fedorov 	if (!amu_group1_supported()) {
56*f3ccf036SAlexei Fedorov 		ERROR("AMU Counter Group 1 is not implemented\n");
57*f3ccf036SAlexei Fedorov 		panic();
58*f3ccf036SAlexei Fedorov 	}
59*f3ccf036SAlexei Fedorov 
60*f3ccf036SAlexei Fedorov 	/* Check number of group 1 counters */
61*f3ccf036SAlexei Fedorov 	uint64_t cnt_num = (read_amcgcr_el0() >> AMCGCR_EL0_CG1NC_SHIFT) &
62*f3ccf036SAlexei Fedorov 				AMCGCR_EL0_CG1NC_MASK;
63*f3ccf036SAlexei Fedorov 	VERBOSE("%s%llu. %s%u\n",
64*f3ccf036SAlexei Fedorov 		"Number of AMU Group 1 Counters ", cnt_num,
65*f3ccf036SAlexei Fedorov 		"Requested number ", AMU_GROUP1_NR_COUNTERS);
66*f3ccf036SAlexei Fedorov 
67*f3ccf036SAlexei Fedorov 	if (cnt_num < AMU_GROUP1_NR_COUNTERS) {
68*f3ccf036SAlexei Fedorov 		ERROR("%s%llu is less than %s%u\n",
69*f3ccf036SAlexei Fedorov 		"Number of AMU Group 1 Counters ", cnt_num,
70*f3ccf036SAlexei Fedorov 		"Requested number ", AMU_GROUP1_NR_COUNTERS);
71*f3ccf036SAlexei Fedorov 		panic();
72*f3ccf036SAlexei Fedorov 	}
73*f3ccf036SAlexei Fedorov #endif
740767d50eSDimitris Papastamos 
75380559c1SDimitris Papastamos 	if (el2_unused) {
76380559c1SDimitris Papastamos 		/*
77380559c1SDimitris Papastamos 		 * CPTR_EL2.TAM: Set to zero so any accesses to
78380559c1SDimitris Papastamos 		 * the Activity Monitor registers do not trap to EL2.
79380559c1SDimitris Papastamos 		 */
80380559c1SDimitris Papastamos 		v = read_cptr_el2();
81380559c1SDimitris Papastamos 		v &= ~CPTR_EL2_TAM_BIT;
82380559c1SDimitris Papastamos 		write_cptr_el2(v);
83380559c1SDimitris Papastamos 	}
84380559c1SDimitris Papastamos 
85380559c1SDimitris Papastamos 	/*
86380559c1SDimitris Papastamos 	 * CPTR_EL3.TAM: Set to zero so that any accesses to
87380559c1SDimitris Papastamos 	 * the Activity Monitor registers do not trap to EL3.
88380559c1SDimitris Papastamos 	 */
89380559c1SDimitris Papastamos 	v = read_cptr_el3();
90380559c1SDimitris Papastamos 	v &= ~TAM_BIT;
91380559c1SDimitris Papastamos 	write_cptr_el3(v);
92380559c1SDimitris Papastamos 
93380559c1SDimitris Papastamos 	/* Enable group 0 counters */
94380559c1SDimitris Papastamos 	write_amcntenset0_el0(AMU_GROUP0_COUNTERS_MASK);
95*f3ccf036SAlexei Fedorov 
96*f3ccf036SAlexei Fedorov #if AMU_GROUP1_NR_COUNTERS
9759902b7cSDimitris Papastamos 	/* Enable group 1 counters */
9859902b7cSDimitris Papastamos 	write_amcntenset1_el0(AMU_GROUP1_COUNTERS_MASK);
99*f3ccf036SAlexei Fedorov #endif
100380559c1SDimitris Papastamos }
1010767d50eSDimitris Papastamos 
1020767d50eSDimitris Papastamos /* Read the group 0 counter identified by the given `idx`. */
103*f3ccf036SAlexei Fedorov uint64_t amu_group0_cnt_read(unsigned int idx)
1040767d50eSDimitris Papastamos {
10540daecc1SAntonio Nino Diaz 	assert(amu_supported());
106*f3ccf036SAlexei Fedorov 	assert(idx < AMU_GROUP0_NR_COUNTERS);
1070767d50eSDimitris Papastamos 
1080767d50eSDimitris Papastamos 	return amu_group0_cnt_read_internal(idx);
1090767d50eSDimitris Papastamos }
1100767d50eSDimitris Papastamos 
111*f3ccf036SAlexei Fedorov /* Write the group 0 counter identified by the given `idx` with `val` */
112*f3ccf036SAlexei Fedorov void amu_group0_cnt_write(unsigned  int idx, uint64_t val)
1130767d50eSDimitris Papastamos {
11440daecc1SAntonio Nino Diaz 	assert(amu_supported());
115*f3ccf036SAlexei Fedorov 	assert(idx < AMU_GROUP0_NR_COUNTERS);
1160767d50eSDimitris Papastamos 
1170767d50eSDimitris Papastamos 	amu_group0_cnt_write_internal(idx, val);
1180767d50eSDimitris Papastamos 	isb();
1190767d50eSDimitris Papastamos }
1200767d50eSDimitris Papastamos 
121*f3ccf036SAlexei Fedorov #if AMU_GROUP1_NR_COUNTERS
122*f3ccf036SAlexei Fedorov /* Read the group 1 counter identified by the given `idx` */
123*f3ccf036SAlexei Fedorov uint64_t amu_group1_cnt_read(unsigned  int idx)
1240767d50eSDimitris Papastamos {
12540daecc1SAntonio Nino Diaz 	assert(amu_supported());
126*f3ccf036SAlexei Fedorov 	assert(amu_group1_supported());
127*f3ccf036SAlexei Fedorov 	assert(idx < AMU_GROUP1_NR_COUNTERS);
1280767d50eSDimitris Papastamos 
1290767d50eSDimitris Papastamos 	return amu_group1_cnt_read_internal(idx);
1300767d50eSDimitris Papastamos }
1310767d50eSDimitris Papastamos 
132*f3ccf036SAlexei Fedorov /* Write the group 1 counter identified by the given `idx` with `val` */
133*f3ccf036SAlexei Fedorov void amu_group1_cnt_write(unsigned  int idx, uint64_t val)
1340767d50eSDimitris Papastamos {
13540daecc1SAntonio Nino Diaz 	assert(amu_supported());
136*f3ccf036SAlexei Fedorov 	assert(amu_group1_supported());
137*f3ccf036SAlexei Fedorov 	assert(idx < AMU_GROUP1_NR_COUNTERS);
1380767d50eSDimitris Papastamos 
1390767d50eSDimitris Papastamos 	amu_group1_cnt_write_internal(idx, val);
1400767d50eSDimitris Papastamos 	isb();
1410767d50eSDimitris Papastamos }
1420767d50eSDimitris Papastamos 
1430767d50eSDimitris Papastamos /*
1440767d50eSDimitris Papastamos  * Program the event type register for the given `idx` with
145*f3ccf036SAlexei Fedorov  * the event number `val`
1460767d50eSDimitris Papastamos  */
147*f3ccf036SAlexei Fedorov void amu_group1_set_evtype(unsigned int idx, unsigned int val)
1480767d50eSDimitris Papastamos {
14940daecc1SAntonio Nino Diaz 	assert(amu_supported());
150*f3ccf036SAlexei Fedorov 	assert(amu_group1_supported());
151*f3ccf036SAlexei Fedorov 	assert(idx < AMU_GROUP1_NR_COUNTERS);
1520767d50eSDimitris Papastamos 
1530767d50eSDimitris Papastamos 	amu_group1_set_evtype_internal(idx, val);
1540767d50eSDimitris Papastamos 	isb();
155380559c1SDimitris Papastamos }
156*f3ccf036SAlexei Fedorov #endif	/* AMU_GROUP1_NR_COUNTERS */
157b6eb3932SDimitris Papastamos 
158b6eb3932SDimitris Papastamos static void *amu_context_save(const void *arg)
159b6eb3932SDimitris Papastamos {
160b6eb3932SDimitris Papastamos 	struct amu_ctx *ctx = &amu_ctxs[plat_my_core_pos()];
161*f3ccf036SAlexei Fedorov 	unsigned int i;
162b6eb3932SDimitris Papastamos 
163*f3ccf036SAlexei Fedorov 	if (!amu_supported()) {
164b6eb3932SDimitris Papastamos 		return (void *)-1;
165*f3ccf036SAlexei Fedorov 	}
166b6eb3932SDimitris Papastamos 
167*f3ccf036SAlexei Fedorov #if AMU_GROUP1_NR_COUNTERS
168*f3ccf036SAlexei Fedorov 	if (!amu_group1_supported()) {
169*f3ccf036SAlexei Fedorov 		return (void *)-1;
170*f3ccf036SAlexei Fedorov 	}
171*f3ccf036SAlexei Fedorov #endif
172b6eb3932SDimitris Papastamos 	/* Assert that group 0/1 counter configuration is what we expect */
173*f3ccf036SAlexei Fedorov 	assert(read_amcntenset0_el0() == AMU_GROUP0_COUNTERS_MASK);
174b6eb3932SDimitris Papastamos 
175*f3ccf036SAlexei Fedorov #if AMU_GROUP1_NR_COUNTERS
176*f3ccf036SAlexei Fedorov 	assert(read_amcntenset1_el0() == AMU_GROUP1_COUNTERS_MASK);
177*f3ccf036SAlexei Fedorov #endif
178b6eb3932SDimitris Papastamos 	/*
179b6eb3932SDimitris Papastamos 	 * Disable group 0/1 counters to avoid other observers like SCP sampling
180b6eb3932SDimitris Papastamos 	 * counter values from the future via the memory mapped view.
181b6eb3932SDimitris Papastamos 	 */
182b6eb3932SDimitris Papastamos 	write_amcntenclr0_el0(AMU_GROUP0_COUNTERS_MASK);
183*f3ccf036SAlexei Fedorov 
184*f3ccf036SAlexei Fedorov #if AMU_GROUP1_NR_COUNTERS
185b6eb3932SDimitris Papastamos 	write_amcntenclr1_el0(AMU_GROUP1_COUNTERS_MASK);
186*f3ccf036SAlexei Fedorov #endif
187b6eb3932SDimitris Papastamos 	isb();
188b6eb3932SDimitris Papastamos 
189*f3ccf036SAlexei Fedorov 	/* Save all group 0 counters */
190*f3ccf036SAlexei Fedorov 	for (i = 0U; i < AMU_GROUP0_NR_COUNTERS; i++) {
191b6eb3932SDimitris Papastamos 		ctx->group0_cnts[i] = amu_group0_cnt_read(i);
192*f3ccf036SAlexei Fedorov 	}
193b6eb3932SDimitris Papastamos 
194*f3ccf036SAlexei Fedorov #if AMU_GROUP1_NR_COUNTERS
195b6eb3932SDimitris Papastamos 	/* Save group 1 counters */
196*f3ccf036SAlexei Fedorov 	for (i = 0U; i < AMU_GROUP1_NR_COUNTERS; i++) {
197*f3ccf036SAlexei Fedorov 		if ((AMU_GROUP1_COUNTERS_MASK & (1U << i)) != 0U) {
198b6eb3932SDimitris Papastamos 			ctx->group1_cnts[i] = amu_group1_cnt_read(i);
199*f3ccf036SAlexei Fedorov 		}
200*f3ccf036SAlexei Fedorov 	}
201*f3ccf036SAlexei Fedorov #endif
20240daecc1SAntonio Nino Diaz 	return (void *)0;
203b6eb3932SDimitris Papastamos }
204b6eb3932SDimitris Papastamos 
205b6eb3932SDimitris Papastamos static void *amu_context_restore(const void *arg)
206b6eb3932SDimitris Papastamos {
207b6eb3932SDimitris Papastamos 	struct amu_ctx *ctx = &amu_ctxs[plat_my_core_pos()];
208*f3ccf036SAlexei Fedorov 	unsigned int i;
209b6eb3932SDimitris Papastamos 
210*f3ccf036SAlexei Fedorov 	if (!amu_supported()) {
211b6eb3932SDimitris Papastamos 		return (void *)-1;
212*f3ccf036SAlexei Fedorov 	}
213b6eb3932SDimitris Papastamos 
214*f3ccf036SAlexei Fedorov #if AMU_GROUP1_NR_COUNTERS
215*f3ccf036SAlexei Fedorov 	if (!amu_group1_supported()) {
216*f3ccf036SAlexei Fedorov 		return (void *)-1;
217*f3ccf036SAlexei Fedorov 	}
218*f3ccf036SAlexei Fedorov #endif
219b6eb3932SDimitris Papastamos 	/* Counters were disabled in `amu_context_save()` */
220*f3ccf036SAlexei Fedorov 	assert(read_amcntenset0_el0() == 0U);
221b6eb3932SDimitris Papastamos 
222*f3ccf036SAlexei Fedorov #if AMU_GROUP1_NR_COUNTERS
223*f3ccf036SAlexei Fedorov 	assert(read_amcntenset1_el0() == 0U);
224*f3ccf036SAlexei Fedorov #endif
225b6eb3932SDimitris Papastamos 
226*f3ccf036SAlexei Fedorov 	/* Restore all group 0 counters */
227*f3ccf036SAlexei Fedorov 	for (i = 0U; i < AMU_GROUP0_NR_COUNTERS; i++) {
228b6eb3932SDimitris Papastamos 		amu_group0_cnt_write(i, ctx->group0_cnts[i]);
229*f3ccf036SAlexei Fedorov 	}
230b6eb3932SDimitris Papastamos 
231*f3ccf036SAlexei Fedorov 	/* Restore group 0 counter configuration */
232b6eb3932SDimitris Papastamos 	write_amcntenset0_el0(AMU_GROUP0_COUNTERS_MASK);
233*f3ccf036SAlexei Fedorov 
234*f3ccf036SAlexei Fedorov #if AMU_GROUP1_NR_COUNTERS
235*f3ccf036SAlexei Fedorov 	/* Restore group 1 counters */
236*f3ccf036SAlexei Fedorov 	for (i = 0U; i < AMU_GROUP1_NR_COUNTERS; i++) {
237*f3ccf036SAlexei Fedorov 		if ((AMU_GROUP1_COUNTERS_MASK & (1U << i)) != 0U) {
238*f3ccf036SAlexei Fedorov 			amu_group1_cnt_write(i, ctx->group1_cnts[i]);
239*f3ccf036SAlexei Fedorov 		}
240*f3ccf036SAlexei Fedorov 	}
241*f3ccf036SAlexei Fedorov 
242*f3ccf036SAlexei Fedorov 	/* Restore group 1 counter configuration */
243b6eb3932SDimitris Papastamos 	write_amcntenset1_el0(AMU_GROUP1_COUNTERS_MASK);
244*f3ccf036SAlexei Fedorov #endif
245b6eb3932SDimitris Papastamos 
24640daecc1SAntonio Nino Diaz 	return (void *)0;
247b6eb3932SDimitris Papastamos }
248b6eb3932SDimitris Papastamos 
249b6eb3932SDimitris Papastamos SUBSCRIBE_TO_EVENT(psci_suspend_pwrdown_start, amu_context_save);
250b6eb3932SDimitris Papastamos SUBSCRIBE_TO_EVENT(psci_suspend_pwrdown_finish, amu_context_restore);
251