xref: /rk3399_ARM-atf/lib/extensions/amu/aarch64/amu.c (revision 68ac5ed0493b24e6a0a178171a47db75a31cc423)
1380559c1SDimitris Papastamos /*
2873d4241Sjohpow01  * Copyright (c) 2017-2021, 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>
11873d4241Sjohpow01 #include <arch_features.h>
12380559c1SDimitris Papastamos #include <arch_helpers.h>
13f3ccf036SAlexei Fedorov 
1409d40e0eSAntonio Nino Diaz #include <lib/el3_runtime/pubsub_events.h>
1509d40e0eSAntonio Nino Diaz #include <lib/extensions/amu.h>
1609d40e0eSAntonio Nino Diaz #include <lib/extensions/amu_private.h>
17f3ccf036SAlexei Fedorov 
1809d40e0eSAntonio Nino Diaz #include <plat/common/platform.h>
19380559c1SDimitris Papastamos 
20b6eb3932SDimitris Papastamos static struct amu_ctx amu_ctxs[PLATFORM_CORE_COUNT];
21b6eb3932SDimitris Papastamos 
22873d4241Sjohpow01 /*
23873d4241Sjohpow01  * Get AMU version value from aa64pfr0.
24873d4241Sjohpow01  * Return values
25873d4241Sjohpow01  *   ID_AA64PFR0_AMU_V1: FEAT_AMUv1 supported (introduced in ARM v8.4)
26873d4241Sjohpow01  *   ID_AA64PFR0_AMU_V1P1: FEAT_AMUv1p1 supported (introduced in ARM v8.6)
27873d4241Sjohpow01  *   ID_AA64PFR0_AMU_NOT_SUPPORTED: not supported
28873d4241Sjohpow01  */
29873d4241Sjohpow01 unsigned int amu_get_version(void)
30380559c1SDimitris Papastamos {
31873d4241Sjohpow01 	return (unsigned int)(read_id_aa64pfr0_el1() >> ID_AA64PFR0_AMU_SHIFT) &
32873d4241Sjohpow01 		ID_AA64PFR0_AMU_MASK;
330767d50eSDimitris Papastamos }
340767d50eSDimitris Papastamos 
35f3ccf036SAlexei Fedorov #if AMU_GROUP1_NR_COUNTERS
36f3ccf036SAlexei Fedorov /* Check if group 1 counters is implemented */
37f3ccf036SAlexei Fedorov bool amu_group1_supported(void)
38f3ccf036SAlexei Fedorov {
39f3ccf036SAlexei Fedorov 	uint64_t features = read_amcfgr_el0() >> AMCFGR_EL0_NCG_SHIFT;
40f3ccf036SAlexei Fedorov 
41f3ccf036SAlexei Fedorov 	return (features & AMCFGR_EL0_NCG_MASK) == 1U;
42f3ccf036SAlexei Fedorov }
43f3ccf036SAlexei Fedorov #endif
44f3ccf036SAlexei Fedorov 
450767d50eSDimitris Papastamos /*
460767d50eSDimitris Papastamos  * Enable counters. This function is meant to be invoked
470767d50eSDimitris Papastamos  * by the context management library before exiting from EL3.
480767d50eSDimitris Papastamos  */
49*68ac5ed0SArunachalam Ganapathy void amu_enable(bool el2_unused, cpu_context_t *ctx)
500767d50eSDimitris Papastamos {
51380559c1SDimitris Papastamos 	uint64_t v;
52873d4241Sjohpow01 	unsigned int amu_version = amu_get_version();
53380559c1SDimitris Papastamos 
54873d4241Sjohpow01 	if (amu_version == ID_AA64PFR0_AMU_NOT_SUPPORTED) {
550767d50eSDimitris Papastamos 		return;
56f3ccf036SAlexei Fedorov 	}
57f3ccf036SAlexei Fedorov 
58f3ccf036SAlexei Fedorov #if AMU_GROUP1_NR_COUNTERS
59f3ccf036SAlexei Fedorov 	/* Check and set presence of group 1 counters */
60f3ccf036SAlexei Fedorov 	if (!amu_group1_supported()) {
61f3ccf036SAlexei Fedorov 		ERROR("AMU Counter Group 1 is not implemented\n");
62f3ccf036SAlexei Fedorov 		panic();
63f3ccf036SAlexei Fedorov 	}
64f3ccf036SAlexei Fedorov 
65f3ccf036SAlexei Fedorov 	/* Check number of group 1 counters */
66f3ccf036SAlexei Fedorov 	uint64_t cnt_num = (read_amcgcr_el0() >> AMCGCR_EL0_CG1NC_SHIFT) &
67f3ccf036SAlexei Fedorov 				AMCGCR_EL0_CG1NC_MASK;
68f3ccf036SAlexei Fedorov 	VERBOSE("%s%llu. %s%u\n",
69f3ccf036SAlexei Fedorov 		"Number of AMU Group 1 Counters ", cnt_num,
70f3ccf036SAlexei Fedorov 		"Requested number ", AMU_GROUP1_NR_COUNTERS);
71f3ccf036SAlexei Fedorov 
72f3ccf036SAlexei Fedorov 	if (cnt_num < AMU_GROUP1_NR_COUNTERS) {
73f3ccf036SAlexei Fedorov 		ERROR("%s%llu is less than %s%u\n",
74f3ccf036SAlexei Fedorov 		"Number of AMU Group 1 Counters ", cnt_num,
75f3ccf036SAlexei Fedorov 		"Requested number ", AMU_GROUP1_NR_COUNTERS);
76f3ccf036SAlexei Fedorov 		panic();
77f3ccf036SAlexei Fedorov 	}
78f3ccf036SAlexei Fedorov #endif
790767d50eSDimitris Papastamos 
80380559c1SDimitris Papastamos 	if (el2_unused) {
81380559c1SDimitris Papastamos 		/*
82380559c1SDimitris Papastamos 		 * CPTR_EL2.TAM: Set to zero so any accesses to
83380559c1SDimitris Papastamos 		 * the Activity Monitor registers do not trap to EL2.
84380559c1SDimitris Papastamos 		 */
85380559c1SDimitris Papastamos 		v = read_cptr_el2();
86380559c1SDimitris Papastamos 		v &= ~CPTR_EL2_TAM_BIT;
87380559c1SDimitris Papastamos 		write_cptr_el2(v);
88380559c1SDimitris Papastamos 	}
89380559c1SDimitris Papastamos 
90380559c1SDimitris Papastamos 	/*
91*68ac5ed0SArunachalam Ganapathy 	 * Retrieve and update the CPTR_EL3 value from the context mentioned
92*68ac5ed0SArunachalam Ganapathy 	 * in 'ctx'. Set CPTR_EL3.TAM to zero so that any accesses to
93380559c1SDimitris Papastamos 	 * the Activity Monitor registers do not trap to EL3.
94380559c1SDimitris Papastamos 	 */
95*68ac5ed0SArunachalam Ganapathy 	v = read_ctx_reg(get_el3state_ctx(ctx), CTX_CPTR_EL3);
96380559c1SDimitris Papastamos 	v &= ~TAM_BIT;
97*68ac5ed0SArunachalam Ganapathy 	write_ctx_reg(get_el3state_ctx(ctx), CTX_CPTR_EL3, v);
98380559c1SDimitris Papastamos 
99380559c1SDimitris Papastamos 	/* Enable group 0 counters */
100380559c1SDimitris Papastamos 	write_amcntenset0_el0(AMU_GROUP0_COUNTERS_MASK);
101f3ccf036SAlexei Fedorov 
102f3ccf036SAlexei Fedorov #if AMU_GROUP1_NR_COUNTERS
10359902b7cSDimitris Papastamos 	/* Enable group 1 counters */
10459902b7cSDimitris Papastamos 	write_amcntenset1_el0(AMU_GROUP1_COUNTERS_MASK);
105f3ccf036SAlexei Fedorov #endif
106873d4241Sjohpow01 
107873d4241Sjohpow01 	/* Initialize FEAT_AMUv1p1 features if present. */
108873d4241Sjohpow01 	if (amu_version < ID_AA64PFR0_AMU_V1P1) {
109873d4241Sjohpow01 		return;
110873d4241Sjohpow01 	}
111873d4241Sjohpow01 
112873d4241Sjohpow01 	if (el2_unused) {
113873d4241Sjohpow01 		/* Make sure virtual offsets are disabled if EL2 not used. */
114873d4241Sjohpow01 		write_hcr_el2(read_hcr_el2() & ~HCR_AMVOFFEN_BIT);
115873d4241Sjohpow01 	}
116873d4241Sjohpow01 
117873d4241Sjohpow01 #if AMU_RESTRICT_COUNTERS
118873d4241Sjohpow01 	/*
119873d4241Sjohpow01 	 * FEAT_AMUv1p1 adds a register field to restrict access to group 1
120873d4241Sjohpow01 	 * counters at all but the highest implemented EL.  This is controlled
121873d4241Sjohpow01 	 * with the AMU_RESTRICT_COUNTERS compile time flag, when set, system
122873d4241Sjohpow01 	 * register reads at lower ELs return zero.  Reads from the memory
123873d4241Sjohpow01 	 * mapped view are unaffected.
124873d4241Sjohpow01 	 */
125873d4241Sjohpow01 	VERBOSE("AMU group 1 counter access restricted.\n");
126873d4241Sjohpow01 	write_amcr_el0(read_amcr_el0() | AMCR_CG1RZ_BIT);
127873d4241Sjohpow01 #else
128873d4241Sjohpow01 	write_amcr_el0(read_amcr_el0() & ~AMCR_CG1RZ_BIT);
129873d4241Sjohpow01 #endif
130380559c1SDimitris Papastamos }
1310767d50eSDimitris Papastamos 
1320767d50eSDimitris Papastamos /* Read the group 0 counter identified by the given `idx`. */
133f3ccf036SAlexei Fedorov uint64_t amu_group0_cnt_read(unsigned int idx)
1340767d50eSDimitris Papastamos {
135873d4241Sjohpow01 	assert(amu_get_version() != ID_AA64PFR0_AMU_NOT_SUPPORTED);
136f3ccf036SAlexei Fedorov 	assert(idx < AMU_GROUP0_NR_COUNTERS);
1370767d50eSDimitris Papastamos 
1380767d50eSDimitris Papastamos 	return amu_group0_cnt_read_internal(idx);
1390767d50eSDimitris Papastamos }
1400767d50eSDimitris Papastamos 
141f3ccf036SAlexei Fedorov /* Write the group 0 counter identified by the given `idx` with `val` */
142f3ccf036SAlexei Fedorov void amu_group0_cnt_write(unsigned  int idx, uint64_t val)
1430767d50eSDimitris Papastamos {
144873d4241Sjohpow01 	assert(amu_get_version() != ID_AA64PFR0_AMU_NOT_SUPPORTED);
145f3ccf036SAlexei Fedorov 	assert(idx < AMU_GROUP0_NR_COUNTERS);
1460767d50eSDimitris Papastamos 
1470767d50eSDimitris Papastamos 	amu_group0_cnt_write_internal(idx, val);
1480767d50eSDimitris Papastamos 	isb();
1490767d50eSDimitris Papastamos }
1500767d50eSDimitris Papastamos 
151873d4241Sjohpow01 /*
152873d4241Sjohpow01  * Read the group 0 offset register for a given index. Index must be 0, 2,
153873d4241Sjohpow01  * or 3, the register for 1 does not exist.
154873d4241Sjohpow01  *
155873d4241Sjohpow01  * Using this function requires FEAT_AMUv1p1 support.
156873d4241Sjohpow01  */
157873d4241Sjohpow01 uint64_t amu_group0_voffset_read(unsigned int idx)
158873d4241Sjohpow01 {
159873d4241Sjohpow01 	assert(amu_get_version() >= ID_AA64PFR0_AMU_V1P1);
160873d4241Sjohpow01 	assert(idx < AMU_GROUP0_NR_COUNTERS);
161873d4241Sjohpow01 	assert(idx != 1U);
162873d4241Sjohpow01 
163873d4241Sjohpow01 	return amu_group0_voffset_read_internal(idx);
164873d4241Sjohpow01 }
165873d4241Sjohpow01 
166873d4241Sjohpow01 /*
167873d4241Sjohpow01  * Write the group 0 offset register for a given index. Index must be 0, 2, or
168873d4241Sjohpow01  * 3, the register for 1 does not exist.
169873d4241Sjohpow01  *
170873d4241Sjohpow01  * Using this function requires FEAT_AMUv1p1 support.
171873d4241Sjohpow01  */
172873d4241Sjohpow01 void amu_group0_voffset_write(unsigned int idx, uint64_t val)
173873d4241Sjohpow01 {
174873d4241Sjohpow01 	assert(amu_get_version() >= ID_AA64PFR0_AMU_V1P1);
175873d4241Sjohpow01 	assert(idx < AMU_GROUP0_NR_COUNTERS);
176873d4241Sjohpow01 	assert(idx != 1U);
177873d4241Sjohpow01 
178873d4241Sjohpow01 	amu_group0_voffset_write_internal(idx, val);
179873d4241Sjohpow01 	isb();
180873d4241Sjohpow01 }
181873d4241Sjohpow01 
182f3ccf036SAlexei Fedorov #if AMU_GROUP1_NR_COUNTERS
183f3ccf036SAlexei Fedorov /* Read the group 1 counter identified by the given `idx` */
184f3ccf036SAlexei Fedorov uint64_t amu_group1_cnt_read(unsigned int idx)
1850767d50eSDimitris Papastamos {
186873d4241Sjohpow01 	assert(amu_get_version() != ID_AA64PFR0_AMU_NOT_SUPPORTED);
187f3ccf036SAlexei Fedorov 	assert(amu_group1_supported());
188f3ccf036SAlexei Fedorov 	assert(idx < AMU_GROUP1_NR_COUNTERS);
1890767d50eSDimitris Papastamos 
1900767d50eSDimitris Papastamos 	return amu_group1_cnt_read_internal(idx);
1910767d50eSDimitris Papastamos }
1920767d50eSDimitris Papastamos 
193f3ccf036SAlexei Fedorov /* Write the group 1 counter identified by the given `idx` with `val` */
194f3ccf036SAlexei Fedorov void amu_group1_cnt_write(unsigned int idx, uint64_t val)
1950767d50eSDimitris Papastamos {
196873d4241Sjohpow01 	assert(amu_get_version() != ID_AA64PFR0_AMU_NOT_SUPPORTED);
197f3ccf036SAlexei Fedorov 	assert(amu_group1_supported());
198f3ccf036SAlexei Fedorov 	assert(idx < AMU_GROUP1_NR_COUNTERS);
1990767d50eSDimitris Papastamos 
2000767d50eSDimitris Papastamos 	amu_group1_cnt_write_internal(idx, val);
2010767d50eSDimitris Papastamos 	isb();
2020767d50eSDimitris Papastamos }
2030767d50eSDimitris Papastamos 
2040767d50eSDimitris Papastamos /*
205873d4241Sjohpow01  * Read the group 1 offset register for a given index.
206873d4241Sjohpow01  *
207873d4241Sjohpow01  * Using this function requires FEAT_AMUv1p1 support.
208873d4241Sjohpow01  */
209873d4241Sjohpow01 uint64_t amu_group1_voffset_read(unsigned int idx)
210873d4241Sjohpow01 {
211873d4241Sjohpow01 	assert(amu_get_version() >= ID_AA64PFR0_AMU_V1P1);
212873d4241Sjohpow01 	assert(amu_group1_supported());
213873d4241Sjohpow01 	assert(idx < AMU_GROUP1_NR_COUNTERS);
214873d4241Sjohpow01 	assert(((read_amcg1idr_el0() >> AMCG1IDR_VOFF_SHIFT) &
215873d4241Sjohpow01 		(1ULL << idx)) != 0ULL);
216873d4241Sjohpow01 
217873d4241Sjohpow01 	return amu_group1_voffset_read_internal(idx);
218873d4241Sjohpow01 }
219873d4241Sjohpow01 
220873d4241Sjohpow01 /*
221873d4241Sjohpow01  * Write the group 1 offset register for a given index.
222873d4241Sjohpow01  *
223873d4241Sjohpow01  * Using this function requires FEAT_AMUv1p1 support.
224873d4241Sjohpow01  */
225873d4241Sjohpow01 void amu_group1_voffset_write(unsigned int idx, uint64_t val)
226873d4241Sjohpow01 {
227873d4241Sjohpow01 	assert(amu_get_version() >= ID_AA64PFR0_AMU_V1P1);
228873d4241Sjohpow01 	assert(amu_group1_supported());
229873d4241Sjohpow01 	assert(idx < AMU_GROUP1_NR_COUNTERS);
230873d4241Sjohpow01 	assert(((read_amcg1idr_el0() >> AMCG1IDR_VOFF_SHIFT) &
231873d4241Sjohpow01 		(1ULL << idx)) != 0ULL);
232873d4241Sjohpow01 
233873d4241Sjohpow01 	amu_group1_voffset_write_internal(idx, val);
234873d4241Sjohpow01 	isb();
235873d4241Sjohpow01 }
236873d4241Sjohpow01 
237873d4241Sjohpow01 /*
2380767d50eSDimitris Papastamos  * Program the event type register for the given `idx` with
239f3ccf036SAlexei Fedorov  * the event number `val`
2400767d50eSDimitris Papastamos  */
241f3ccf036SAlexei Fedorov void amu_group1_set_evtype(unsigned int idx, unsigned int val)
2420767d50eSDimitris Papastamos {
243873d4241Sjohpow01 	assert(amu_get_version() != ID_AA64PFR0_AMU_NOT_SUPPORTED);
244f3ccf036SAlexei Fedorov 	assert(amu_group1_supported());
245f3ccf036SAlexei Fedorov 	assert(idx < AMU_GROUP1_NR_COUNTERS);
2460767d50eSDimitris Papastamos 
2470767d50eSDimitris Papastamos 	amu_group1_set_evtype_internal(idx, val);
2480767d50eSDimitris Papastamos 	isb();
249380559c1SDimitris Papastamos }
250f3ccf036SAlexei Fedorov #endif	/* AMU_GROUP1_NR_COUNTERS */
251b6eb3932SDimitris Papastamos 
252b6eb3932SDimitris Papastamos static void *amu_context_save(const void *arg)
253b6eb3932SDimitris Papastamos {
254b6eb3932SDimitris Papastamos 	struct amu_ctx *ctx = &amu_ctxs[plat_my_core_pos()];
255f3ccf036SAlexei Fedorov 	unsigned int i;
256b6eb3932SDimitris Papastamos 
257873d4241Sjohpow01 	if (amu_get_version() == ID_AA64PFR0_AMU_NOT_SUPPORTED) {
258b6eb3932SDimitris Papastamos 		return (void *)-1;
259f3ccf036SAlexei Fedorov 	}
260b6eb3932SDimitris Papastamos 
261f3ccf036SAlexei Fedorov #if AMU_GROUP1_NR_COUNTERS
262f3ccf036SAlexei Fedorov 	if (!amu_group1_supported()) {
263f3ccf036SAlexei Fedorov 		return (void *)-1;
264f3ccf036SAlexei Fedorov 	}
265f3ccf036SAlexei Fedorov #endif
266b6eb3932SDimitris Papastamos 	/* Assert that group 0/1 counter configuration is what we expect */
267f3ccf036SAlexei Fedorov 	assert(read_amcntenset0_el0() == AMU_GROUP0_COUNTERS_MASK);
268b6eb3932SDimitris Papastamos 
269f3ccf036SAlexei Fedorov #if AMU_GROUP1_NR_COUNTERS
270f3ccf036SAlexei Fedorov 	assert(read_amcntenset1_el0() == AMU_GROUP1_COUNTERS_MASK);
271f3ccf036SAlexei Fedorov #endif
272b6eb3932SDimitris Papastamos 	/*
273b6eb3932SDimitris Papastamos 	 * Disable group 0/1 counters to avoid other observers like SCP sampling
274b6eb3932SDimitris Papastamos 	 * counter values from the future via the memory mapped view.
275b6eb3932SDimitris Papastamos 	 */
276b6eb3932SDimitris Papastamos 	write_amcntenclr0_el0(AMU_GROUP0_COUNTERS_MASK);
277f3ccf036SAlexei Fedorov 
278f3ccf036SAlexei Fedorov #if AMU_GROUP1_NR_COUNTERS
279b6eb3932SDimitris Papastamos 	write_amcntenclr1_el0(AMU_GROUP1_COUNTERS_MASK);
280f3ccf036SAlexei Fedorov #endif
281b6eb3932SDimitris Papastamos 	isb();
282b6eb3932SDimitris Papastamos 
283f3ccf036SAlexei Fedorov 	/* Save all group 0 counters */
284f3ccf036SAlexei Fedorov 	for (i = 0U; i < AMU_GROUP0_NR_COUNTERS; i++) {
285b6eb3932SDimitris Papastamos 		ctx->group0_cnts[i] = amu_group0_cnt_read(i);
286f3ccf036SAlexei Fedorov 	}
287b6eb3932SDimitris Papastamos 
288873d4241Sjohpow01 	/* Save group 0 virtual offsets if supported and enabled. */
289873d4241Sjohpow01 	if ((amu_get_version() >= ID_AA64PFR0_AMU_V1P1) &&
290873d4241Sjohpow01 			((read_hcr_el2() & HCR_AMVOFFEN_BIT) != 0ULL)) {
291873d4241Sjohpow01 		/* Not using a loop because count is fixed and index 1 DNE. */
292873d4241Sjohpow01 		ctx->group0_voffsets[0U] = amu_group0_voffset_read(0U);
293873d4241Sjohpow01 		ctx->group0_voffsets[1U] = amu_group0_voffset_read(2U);
294873d4241Sjohpow01 		ctx->group0_voffsets[2U] = amu_group0_voffset_read(3U);
295873d4241Sjohpow01 	}
296873d4241Sjohpow01 
297f3ccf036SAlexei Fedorov #if AMU_GROUP1_NR_COUNTERS
298b6eb3932SDimitris Papastamos 	/* Save group 1 counters */
299f3ccf036SAlexei Fedorov 	for (i = 0U; i < AMU_GROUP1_NR_COUNTERS; i++) {
300873d4241Sjohpow01 		if ((AMU_GROUP1_COUNTERS_MASK & (1UL << i)) != 0U) {
301b6eb3932SDimitris Papastamos 			ctx->group1_cnts[i] = amu_group1_cnt_read(i);
302f3ccf036SAlexei Fedorov 		}
303f3ccf036SAlexei Fedorov 	}
304873d4241Sjohpow01 
305873d4241Sjohpow01 	/* Save group 1 virtual offsets if supported and enabled. */
306873d4241Sjohpow01 	if ((amu_get_version() >= ID_AA64PFR0_AMU_V1P1) &&
307873d4241Sjohpow01 			((read_hcr_el2() & HCR_AMVOFFEN_BIT) != 0ULL)) {
308873d4241Sjohpow01 		u_register_t amcg1idr = read_amcg1idr_el0() >>
309873d4241Sjohpow01 			AMCG1IDR_VOFF_SHIFT;
310873d4241Sjohpow01 		amcg1idr = amcg1idr & AMU_GROUP1_COUNTERS_MASK;
311873d4241Sjohpow01 
312873d4241Sjohpow01 		for (i = 0U; i < AMU_GROUP1_NR_COUNTERS; i++) {
313873d4241Sjohpow01 			if (((amcg1idr >> i) & 1ULL) != 0ULL) {
314873d4241Sjohpow01 				ctx->group1_voffsets[i] =
315873d4241Sjohpow01 					amu_group1_voffset_read(i);
316873d4241Sjohpow01 			}
317873d4241Sjohpow01 		}
318873d4241Sjohpow01 	}
319f3ccf036SAlexei Fedorov #endif
32040daecc1SAntonio Nino Diaz 	return (void *)0;
321b6eb3932SDimitris Papastamos }
322b6eb3932SDimitris Papastamos 
323b6eb3932SDimitris Papastamos static void *amu_context_restore(const void *arg)
324b6eb3932SDimitris Papastamos {
325b6eb3932SDimitris Papastamos 	struct amu_ctx *ctx = &amu_ctxs[plat_my_core_pos()];
326f3ccf036SAlexei Fedorov 	unsigned int i;
327b6eb3932SDimitris Papastamos 
328873d4241Sjohpow01 	if (amu_get_version() == ID_AA64PFR0_AMU_NOT_SUPPORTED) {
329b6eb3932SDimitris Papastamos 		return (void *)-1;
330f3ccf036SAlexei Fedorov 	}
331b6eb3932SDimitris Papastamos 
332f3ccf036SAlexei Fedorov #if AMU_GROUP1_NR_COUNTERS
333f3ccf036SAlexei Fedorov 	if (!amu_group1_supported()) {
334f3ccf036SAlexei Fedorov 		return (void *)-1;
335f3ccf036SAlexei Fedorov 	}
336f3ccf036SAlexei Fedorov #endif
337b6eb3932SDimitris Papastamos 	/* Counters were disabled in `amu_context_save()` */
338f3ccf036SAlexei Fedorov 	assert(read_amcntenset0_el0() == 0U);
339b6eb3932SDimitris Papastamos 
340f3ccf036SAlexei Fedorov #if AMU_GROUP1_NR_COUNTERS
341f3ccf036SAlexei Fedorov 	assert(read_amcntenset1_el0() == 0U);
342f3ccf036SAlexei Fedorov #endif
343b6eb3932SDimitris Papastamos 
344f3ccf036SAlexei Fedorov 	/* Restore all group 0 counters */
345f3ccf036SAlexei Fedorov 	for (i = 0U; i < AMU_GROUP0_NR_COUNTERS; i++) {
346b6eb3932SDimitris Papastamos 		amu_group0_cnt_write(i, ctx->group0_cnts[i]);
347f3ccf036SAlexei Fedorov 	}
348b6eb3932SDimitris Papastamos 
349873d4241Sjohpow01 	/* Restore group 0 virtual offsets if supported and enabled. */
350873d4241Sjohpow01 	if ((amu_get_version() >= ID_AA64PFR0_AMU_V1P1) &&
351873d4241Sjohpow01 			((read_hcr_el2() & HCR_AMVOFFEN_BIT) != 0ULL)) {
352873d4241Sjohpow01 		/* Not using a loop because count is fixed and index 1 DNE. */
353873d4241Sjohpow01 		amu_group0_voffset_write(0U, ctx->group0_voffsets[0U]);
354873d4241Sjohpow01 		amu_group0_voffset_write(2U, ctx->group0_voffsets[1U]);
355873d4241Sjohpow01 		amu_group0_voffset_write(3U, ctx->group0_voffsets[2U]);
356873d4241Sjohpow01 	}
357873d4241Sjohpow01 
358f3ccf036SAlexei Fedorov 	/* Restore group 0 counter configuration */
359b6eb3932SDimitris Papastamos 	write_amcntenset0_el0(AMU_GROUP0_COUNTERS_MASK);
360f3ccf036SAlexei Fedorov 
361f3ccf036SAlexei Fedorov #if AMU_GROUP1_NR_COUNTERS
362f3ccf036SAlexei Fedorov 	/* Restore group 1 counters */
363f3ccf036SAlexei Fedorov 	for (i = 0U; i < AMU_GROUP1_NR_COUNTERS; i++) {
364873d4241Sjohpow01 		if ((AMU_GROUP1_COUNTERS_MASK & (1UL << i)) != 0U) {
365f3ccf036SAlexei Fedorov 			amu_group1_cnt_write(i, ctx->group1_cnts[i]);
366f3ccf036SAlexei Fedorov 		}
367f3ccf036SAlexei Fedorov 	}
368f3ccf036SAlexei Fedorov 
369873d4241Sjohpow01 	/* Restore group 1 virtual offsets if supported and enabled. */
370873d4241Sjohpow01 	if ((amu_get_version() >= ID_AA64PFR0_AMU_V1P1) &&
371873d4241Sjohpow01 			((read_hcr_el2() & HCR_AMVOFFEN_BIT) != 0ULL)) {
372873d4241Sjohpow01 		u_register_t amcg1idr = read_amcg1idr_el0() >>
373873d4241Sjohpow01 			AMCG1IDR_VOFF_SHIFT;
374873d4241Sjohpow01 		amcg1idr = amcg1idr & AMU_GROUP1_COUNTERS_MASK;
375873d4241Sjohpow01 
376873d4241Sjohpow01 		for (i = 0U; i < AMU_GROUP1_NR_COUNTERS; i++) {
377873d4241Sjohpow01 			if (((amcg1idr >> i) & 1ULL) != 0ULL) {
378873d4241Sjohpow01 				amu_group1_voffset_write(i,
379873d4241Sjohpow01 					ctx->group1_voffsets[i]);
380873d4241Sjohpow01 			}
381873d4241Sjohpow01 		}
382873d4241Sjohpow01 	}
383873d4241Sjohpow01 
384f3ccf036SAlexei Fedorov 	/* Restore group 1 counter configuration */
385b6eb3932SDimitris Papastamos 	write_amcntenset1_el0(AMU_GROUP1_COUNTERS_MASK);
386f3ccf036SAlexei Fedorov #endif
387b6eb3932SDimitris Papastamos 
38840daecc1SAntonio Nino Diaz 	return (void *)0;
389b6eb3932SDimitris Papastamos }
390b6eb3932SDimitris Papastamos 
391b6eb3932SDimitris Papastamos SUBSCRIBE_TO_EVENT(psci_suspend_pwrdown_start, amu_context_save);
392b6eb3932SDimitris Papastamos SUBSCRIBE_TO_EVENT(psci_suspend_pwrdown_finish, amu_context_restore);
393