xref: /rk3399_ARM-atf/lib/extensions/amu/aarch64/amu.c (revision b4b726ea868359cf683c07337b69fe91a2a6929a)
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  */
29*b4b726eaSChris Kay static 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 */
37*b4b726eaSChris Kay static 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  */
4968ac5ed0SArunachalam 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 	/*
9168ac5ed0SArunachalam Ganapathy 	 * Retrieve and update the CPTR_EL3 value from the context mentioned
9268ac5ed0SArunachalam 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 	 */
9568ac5ed0SArunachalam Ganapathy 	v = read_ctx_reg(get_el3state_ctx(ctx), CTX_CPTR_EL3);
96380559c1SDimitris Papastamos 	v &= ~TAM_BIT;
9768ac5ed0SArunachalam 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`. */
133*b4b726eaSChris Kay static 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` */
142*b4b726eaSChris Kay static 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  */
157*b4b726eaSChris Kay static 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  */
172*b4b726eaSChris Kay static 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` */
184*b4b726eaSChris Kay static 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` */
194*b4b726eaSChris Kay static 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  */
209*b4b726eaSChris Kay static 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  */
225*b4b726eaSChris Kay static 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 }
236f3ccf036SAlexei Fedorov #endif	/* AMU_GROUP1_NR_COUNTERS */
237b6eb3932SDimitris Papastamos 
238b6eb3932SDimitris Papastamos static void *amu_context_save(const void *arg)
239b6eb3932SDimitris Papastamos {
240b6eb3932SDimitris Papastamos 	struct amu_ctx *ctx = &amu_ctxs[plat_my_core_pos()];
241f3ccf036SAlexei Fedorov 	unsigned int i;
242b6eb3932SDimitris Papastamos 
243873d4241Sjohpow01 	if (amu_get_version() == ID_AA64PFR0_AMU_NOT_SUPPORTED) {
244b6eb3932SDimitris Papastamos 		return (void *)-1;
245f3ccf036SAlexei Fedorov 	}
246b6eb3932SDimitris Papastamos 
247f3ccf036SAlexei Fedorov #if AMU_GROUP1_NR_COUNTERS
248f3ccf036SAlexei Fedorov 	if (!amu_group1_supported()) {
249f3ccf036SAlexei Fedorov 		return (void *)-1;
250f3ccf036SAlexei Fedorov 	}
251f3ccf036SAlexei Fedorov #endif
252b6eb3932SDimitris Papastamos 	/* Assert that group 0/1 counter configuration is what we expect */
253f3ccf036SAlexei Fedorov 	assert(read_amcntenset0_el0() == AMU_GROUP0_COUNTERS_MASK);
254b6eb3932SDimitris Papastamos 
255f3ccf036SAlexei Fedorov #if AMU_GROUP1_NR_COUNTERS
256f3ccf036SAlexei Fedorov 	assert(read_amcntenset1_el0() == AMU_GROUP1_COUNTERS_MASK);
257f3ccf036SAlexei Fedorov #endif
258b6eb3932SDimitris Papastamos 	/*
259b6eb3932SDimitris Papastamos 	 * Disable group 0/1 counters to avoid other observers like SCP sampling
260b6eb3932SDimitris Papastamos 	 * counter values from the future via the memory mapped view.
261b6eb3932SDimitris Papastamos 	 */
262b6eb3932SDimitris Papastamos 	write_amcntenclr0_el0(AMU_GROUP0_COUNTERS_MASK);
263f3ccf036SAlexei Fedorov 
264f3ccf036SAlexei Fedorov #if AMU_GROUP1_NR_COUNTERS
265b6eb3932SDimitris Papastamos 	write_amcntenclr1_el0(AMU_GROUP1_COUNTERS_MASK);
266f3ccf036SAlexei Fedorov #endif
267b6eb3932SDimitris Papastamos 	isb();
268b6eb3932SDimitris Papastamos 
269f3ccf036SAlexei Fedorov 	/* Save all group 0 counters */
270f3ccf036SAlexei Fedorov 	for (i = 0U; i < AMU_GROUP0_NR_COUNTERS; i++) {
271b6eb3932SDimitris Papastamos 		ctx->group0_cnts[i] = amu_group0_cnt_read(i);
272f3ccf036SAlexei Fedorov 	}
273b6eb3932SDimitris Papastamos 
274873d4241Sjohpow01 	/* Save group 0 virtual offsets if supported and enabled. */
275873d4241Sjohpow01 	if ((amu_get_version() >= ID_AA64PFR0_AMU_V1P1) &&
276873d4241Sjohpow01 			((read_hcr_el2() & HCR_AMVOFFEN_BIT) != 0ULL)) {
277873d4241Sjohpow01 		/* Not using a loop because count is fixed and index 1 DNE. */
278873d4241Sjohpow01 		ctx->group0_voffsets[0U] = amu_group0_voffset_read(0U);
279873d4241Sjohpow01 		ctx->group0_voffsets[1U] = amu_group0_voffset_read(2U);
280873d4241Sjohpow01 		ctx->group0_voffsets[2U] = amu_group0_voffset_read(3U);
281873d4241Sjohpow01 	}
282873d4241Sjohpow01 
283f3ccf036SAlexei Fedorov #if AMU_GROUP1_NR_COUNTERS
284b6eb3932SDimitris Papastamos 	/* Save group 1 counters */
285f3ccf036SAlexei Fedorov 	for (i = 0U; i < AMU_GROUP1_NR_COUNTERS; i++) {
286873d4241Sjohpow01 		if ((AMU_GROUP1_COUNTERS_MASK & (1UL << i)) != 0U) {
287b6eb3932SDimitris Papastamos 			ctx->group1_cnts[i] = amu_group1_cnt_read(i);
288f3ccf036SAlexei Fedorov 		}
289f3ccf036SAlexei Fedorov 	}
290873d4241Sjohpow01 
291873d4241Sjohpow01 	/* Save group 1 virtual offsets if supported and enabled. */
292873d4241Sjohpow01 	if ((amu_get_version() >= ID_AA64PFR0_AMU_V1P1) &&
293873d4241Sjohpow01 			((read_hcr_el2() & HCR_AMVOFFEN_BIT) != 0ULL)) {
294873d4241Sjohpow01 		u_register_t amcg1idr = read_amcg1idr_el0() >>
295873d4241Sjohpow01 			AMCG1IDR_VOFF_SHIFT;
296873d4241Sjohpow01 		amcg1idr = amcg1idr & AMU_GROUP1_COUNTERS_MASK;
297873d4241Sjohpow01 
298873d4241Sjohpow01 		for (i = 0U; i < AMU_GROUP1_NR_COUNTERS; i++) {
299873d4241Sjohpow01 			if (((amcg1idr >> i) & 1ULL) != 0ULL) {
300873d4241Sjohpow01 				ctx->group1_voffsets[i] =
301873d4241Sjohpow01 					amu_group1_voffset_read(i);
302873d4241Sjohpow01 			}
303873d4241Sjohpow01 		}
304873d4241Sjohpow01 	}
305f3ccf036SAlexei Fedorov #endif
30640daecc1SAntonio Nino Diaz 	return (void *)0;
307b6eb3932SDimitris Papastamos }
308b6eb3932SDimitris Papastamos 
309b6eb3932SDimitris Papastamos static void *amu_context_restore(const void *arg)
310b6eb3932SDimitris Papastamos {
311b6eb3932SDimitris Papastamos 	struct amu_ctx *ctx = &amu_ctxs[plat_my_core_pos()];
312f3ccf036SAlexei Fedorov 	unsigned int i;
313b6eb3932SDimitris Papastamos 
314873d4241Sjohpow01 	if (amu_get_version() == ID_AA64PFR0_AMU_NOT_SUPPORTED) {
315b6eb3932SDimitris Papastamos 		return (void *)-1;
316f3ccf036SAlexei Fedorov 	}
317b6eb3932SDimitris Papastamos 
318f3ccf036SAlexei Fedorov #if AMU_GROUP1_NR_COUNTERS
319f3ccf036SAlexei Fedorov 	if (!amu_group1_supported()) {
320f3ccf036SAlexei Fedorov 		return (void *)-1;
321f3ccf036SAlexei Fedorov 	}
322f3ccf036SAlexei Fedorov #endif
323b6eb3932SDimitris Papastamos 	/* Counters were disabled in `amu_context_save()` */
324f3ccf036SAlexei Fedorov 	assert(read_amcntenset0_el0() == 0U);
325b6eb3932SDimitris Papastamos 
326f3ccf036SAlexei Fedorov #if AMU_GROUP1_NR_COUNTERS
327f3ccf036SAlexei Fedorov 	assert(read_amcntenset1_el0() == 0U);
328f3ccf036SAlexei Fedorov #endif
329b6eb3932SDimitris Papastamos 
330f3ccf036SAlexei Fedorov 	/* Restore all group 0 counters */
331f3ccf036SAlexei Fedorov 	for (i = 0U; i < AMU_GROUP0_NR_COUNTERS; i++) {
332b6eb3932SDimitris Papastamos 		amu_group0_cnt_write(i, ctx->group0_cnts[i]);
333f3ccf036SAlexei Fedorov 	}
334b6eb3932SDimitris Papastamos 
335873d4241Sjohpow01 	/* Restore group 0 virtual offsets if supported and enabled. */
336873d4241Sjohpow01 	if ((amu_get_version() >= ID_AA64PFR0_AMU_V1P1) &&
337873d4241Sjohpow01 			((read_hcr_el2() & HCR_AMVOFFEN_BIT) != 0ULL)) {
338873d4241Sjohpow01 		/* Not using a loop because count is fixed and index 1 DNE. */
339873d4241Sjohpow01 		amu_group0_voffset_write(0U, ctx->group0_voffsets[0U]);
340873d4241Sjohpow01 		amu_group0_voffset_write(2U, ctx->group0_voffsets[1U]);
341873d4241Sjohpow01 		amu_group0_voffset_write(3U, ctx->group0_voffsets[2U]);
342873d4241Sjohpow01 	}
343873d4241Sjohpow01 
344f3ccf036SAlexei Fedorov 	/* Restore group 0 counter configuration */
345b6eb3932SDimitris Papastamos 	write_amcntenset0_el0(AMU_GROUP0_COUNTERS_MASK);
346f3ccf036SAlexei Fedorov 
347f3ccf036SAlexei Fedorov #if AMU_GROUP1_NR_COUNTERS
348f3ccf036SAlexei Fedorov 	/* Restore group 1 counters */
349f3ccf036SAlexei Fedorov 	for (i = 0U; i < AMU_GROUP1_NR_COUNTERS; i++) {
350873d4241Sjohpow01 		if ((AMU_GROUP1_COUNTERS_MASK & (1UL << i)) != 0U) {
351f3ccf036SAlexei Fedorov 			amu_group1_cnt_write(i, ctx->group1_cnts[i]);
352f3ccf036SAlexei Fedorov 		}
353f3ccf036SAlexei Fedorov 	}
354f3ccf036SAlexei Fedorov 
355873d4241Sjohpow01 	/* Restore group 1 virtual offsets if supported and enabled. */
356873d4241Sjohpow01 	if ((amu_get_version() >= ID_AA64PFR0_AMU_V1P1) &&
357873d4241Sjohpow01 			((read_hcr_el2() & HCR_AMVOFFEN_BIT) != 0ULL)) {
358873d4241Sjohpow01 		u_register_t amcg1idr = read_amcg1idr_el0() >>
359873d4241Sjohpow01 			AMCG1IDR_VOFF_SHIFT;
360873d4241Sjohpow01 		amcg1idr = amcg1idr & AMU_GROUP1_COUNTERS_MASK;
361873d4241Sjohpow01 
362873d4241Sjohpow01 		for (i = 0U; i < AMU_GROUP1_NR_COUNTERS; i++) {
363873d4241Sjohpow01 			if (((amcg1idr >> i) & 1ULL) != 0ULL) {
364873d4241Sjohpow01 				amu_group1_voffset_write(i,
365873d4241Sjohpow01 					ctx->group1_voffsets[i]);
366873d4241Sjohpow01 			}
367873d4241Sjohpow01 		}
368873d4241Sjohpow01 	}
369873d4241Sjohpow01 
370f3ccf036SAlexei Fedorov 	/* Restore group 1 counter configuration */
371b6eb3932SDimitris Papastamos 	write_amcntenset1_el0(AMU_GROUP1_COUNTERS_MASK);
372f3ccf036SAlexei Fedorov #endif
373b6eb3932SDimitris Papastamos 
37440daecc1SAntonio Nino Diaz 	return (void *)0;
375b6eb3932SDimitris Papastamos }
376b6eb3932SDimitris Papastamos 
377b6eb3932SDimitris Papastamos SUBSCRIBE_TO_EVENT(psci_suspend_pwrdown_start, amu_context_save);
378b6eb3932SDimitris Papastamos SUBSCRIBE_TO_EVENT(psci_suspend_pwrdown_finish, amu_context_restore);
379