xref: /rk3399_ARM-atf/lib/extensions/amu/aarch64/amu.c (revision 1fd685a74dd33c9c26a0ec0e82e7a5a378461362)
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>
833b9be6dSChris Kay #include <cdefs.h>
909d40e0eSAntonio Nino Diaz #include <stdbool.h>
1009d40e0eSAntonio Nino Diaz 
11380559c1SDimitris Papastamos #include <arch.h>
12873d4241Sjohpow01 #include <arch_features.h>
13380559c1SDimitris Papastamos #include <arch_helpers.h>
14f3ccf036SAlexei Fedorov 
1509d40e0eSAntonio Nino Diaz #include <lib/el3_runtime/pubsub_events.h>
1609d40e0eSAntonio Nino Diaz #include <lib/extensions/amu.h>
1709d40e0eSAntonio Nino Diaz #include <lib/extensions/amu_private.h>
18f3ccf036SAlexei Fedorov 
1909d40e0eSAntonio Nino Diaz #include <plat/common/platform.h>
20380559c1SDimitris Papastamos 
21b6eb3932SDimitris Papastamos static struct amu_ctx amu_ctxs[PLATFORM_CORE_COUNT];
22b6eb3932SDimitris Papastamos 
2333b9be6dSChris Kay static inline __unused uint64_t read_id_aa64pfr0_el1_amu(void)
24380559c1SDimitris Papastamos {
2533b9be6dSChris Kay 	return (read_id_aa64pfr0_el1() >> ID_AA64PFR0_AMU_SHIFT) &
26873d4241Sjohpow01 		ID_AA64PFR0_AMU_MASK;
270767d50eSDimitris Papastamos }
280767d50eSDimitris Papastamos 
2933b9be6dSChris Kay static inline __unused uint64_t read_hcr_el2_amvoffen(void)
3033b9be6dSChris Kay {
3133b9be6dSChris Kay 	return (read_hcr_el2() & HCR_AMVOFFEN_BIT) >>
3233b9be6dSChris Kay 		HCR_AMVOFFEN_SHIFT;
3333b9be6dSChris Kay }
3433b9be6dSChris Kay 
3533b9be6dSChris Kay static inline __unused void write_cptr_el2_tam(uint64_t value)
3633b9be6dSChris Kay {
3733b9be6dSChris Kay 	write_cptr_el2((read_cptr_el2() & ~CPTR_EL2_TAM_BIT) |
3833b9be6dSChris Kay 		((value << CPTR_EL2_TAM_SHIFT) & CPTR_EL2_TAM_BIT));
3933b9be6dSChris Kay }
4033b9be6dSChris Kay 
4133b9be6dSChris Kay static inline __unused void write_cptr_el3_tam(cpu_context_t *ctx, uint64_t tam)
4233b9be6dSChris Kay {
4333b9be6dSChris Kay 	uint64_t value = read_ctx_reg(get_el3state_ctx(ctx), CTX_CPTR_EL3);
4433b9be6dSChris Kay 
4533b9be6dSChris Kay 	value &= ~TAM_BIT;
4633b9be6dSChris Kay 	value |= (tam << TAM_SHIFT) & TAM_BIT;
4733b9be6dSChris Kay 
4833b9be6dSChris Kay 	write_ctx_reg(get_el3state_ctx(ctx), CTX_CPTR_EL3, value);
4933b9be6dSChris Kay }
5033b9be6dSChris Kay 
5133b9be6dSChris Kay static inline __unused void write_hcr_el2_amvoffen(uint64_t value)
5233b9be6dSChris Kay {
5333b9be6dSChris Kay 	write_hcr_el2((read_hcr_el2() & ~HCR_AMVOFFEN_BIT) |
5433b9be6dSChris Kay 		((value << HCR_AMVOFFEN_SHIFT) & HCR_AMVOFFEN_BIT));
5533b9be6dSChris Kay }
5633b9be6dSChris Kay 
5733b9be6dSChris Kay static inline __unused void write_amcr_el0_cg1rz(uint64_t value)
5833b9be6dSChris Kay {
5933b9be6dSChris Kay 	write_amcr_el0((read_amcr_el0() & ~AMCR_CG1RZ_BIT) |
6033b9be6dSChris Kay 		((value << AMCR_CG1RZ_SHIFT) & AMCR_CG1RZ_BIT));
6133b9be6dSChris Kay }
6233b9be6dSChris Kay 
6333b9be6dSChris Kay static inline __unused uint64_t read_amcfgr_el0_ncg(void)
6433b9be6dSChris Kay {
6533b9be6dSChris Kay 	return (read_amcfgr_el0() >> AMCFGR_EL0_NCG_SHIFT) &
6633b9be6dSChris Kay 		AMCFGR_EL0_NCG_MASK;
6733b9be6dSChris Kay }
6833b9be6dSChris Kay 
6933b9be6dSChris Kay static inline __unused uint64_t read_amcg1idr_el0_voff(void)
7033b9be6dSChris Kay {
7133b9be6dSChris Kay 	return (read_amcg1idr_el0() >> AMCG1IDR_VOFF_SHIFT) &
7233b9be6dSChris Kay 		AMCG1IDR_VOFF_MASK;
7333b9be6dSChris Kay }
7433b9be6dSChris Kay 
7533b9be6dSChris Kay static inline __unused uint64_t read_amcgcr_el0_cg1nc(void)
7633b9be6dSChris Kay {
7733b9be6dSChris Kay 	return (read_amcgcr_el0() >> AMCGCR_EL0_CG1NC_SHIFT) &
7833b9be6dSChris Kay 		AMCGCR_EL0_CG1NC_MASK;
7933b9be6dSChris Kay }
8033b9be6dSChris Kay 
8133b9be6dSChris Kay static inline __unused uint64_t read_amcntenset0_el0_px(void)
8233b9be6dSChris Kay {
8333b9be6dSChris Kay 	return (read_amcntenset0_el0() >> AMCNTENSET0_EL0_Pn_SHIFT) &
8433b9be6dSChris Kay 		AMCNTENSET0_EL0_Pn_MASK;
8533b9be6dSChris Kay }
8633b9be6dSChris Kay 
8733b9be6dSChris Kay static inline __unused uint64_t read_amcntenset1_el0_px(void)
8833b9be6dSChris Kay {
8933b9be6dSChris Kay 	return (read_amcntenset1_el0() >> AMCNTENSET1_EL0_Pn_SHIFT) &
9033b9be6dSChris Kay 		AMCNTENSET1_EL0_Pn_MASK;
9133b9be6dSChris Kay }
9233b9be6dSChris Kay 
9333b9be6dSChris Kay static inline __unused void write_amcntenset0_el0_px(uint64_t px)
9433b9be6dSChris Kay {
9533b9be6dSChris Kay 	uint64_t value = read_amcntenset0_el0();
9633b9be6dSChris Kay 
9733b9be6dSChris Kay 	value &= ~AMCNTENSET0_EL0_Pn_MASK;
9833b9be6dSChris Kay 	value |= (px << AMCNTENSET0_EL0_Pn_SHIFT) & AMCNTENSET0_EL0_Pn_MASK;
9933b9be6dSChris Kay 
10033b9be6dSChris Kay 	write_amcntenset0_el0(value);
10133b9be6dSChris Kay }
10233b9be6dSChris Kay 
10333b9be6dSChris Kay static inline __unused void write_amcntenset1_el0_px(uint64_t px)
10433b9be6dSChris Kay {
10533b9be6dSChris Kay 	uint64_t value = read_amcntenset1_el0();
10633b9be6dSChris Kay 
10733b9be6dSChris Kay 	value &= ~AMCNTENSET1_EL0_Pn_MASK;
10833b9be6dSChris Kay 	value |= (px << AMCNTENSET1_EL0_Pn_SHIFT) & AMCNTENSET1_EL0_Pn_MASK;
10933b9be6dSChris Kay 
11033b9be6dSChris Kay 	write_amcntenset1_el0(value);
11133b9be6dSChris Kay }
11233b9be6dSChris Kay 
11333b9be6dSChris Kay static inline __unused void write_amcntenclr0_el0_px(uint64_t px)
11433b9be6dSChris Kay {
11533b9be6dSChris Kay 	uint64_t value = read_amcntenclr0_el0();
11633b9be6dSChris Kay 
11733b9be6dSChris Kay 	value &= ~AMCNTENCLR0_EL0_Pn_MASK;
11833b9be6dSChris Kay 	value |= (px << AMCNTENCLR0_EL0_Pn_SHIFT) & AMCNTENCLR0_EL0_Pn_MASK;
11933b9be6dSChris Kay 
12033b9be6dSChris Kay 	write_amcntenclr0_el0(value);
12133b9be6dSChris Kay }
12233b9be6dSChris Kay 
12333b9be6dSChris Kay static inline __unused void write_amcntenclr1_el0_px(uint64_t px)
12433b9be6dSChris Kay {
12533b9be6dSChris Kay 	uint64_t value = read_amcntenclr1_el0();
12633b9be6dSChris Kay 
12733b9be6dSChris Kay 	value &= ~AMCNTENCLR1_EL0_Pn_MASK;
12833b9be6dSChris Kay 	value |= (px << AMCNTENCLR1_EL0_Pn_SHIFT) & AMCNTENCLR1_EL0_Pn_MASK;
12933b9be6dSChris Kay 
13033b9be6dSChris Kay 	write_amcntenclr1_el0(value);
13133b9be6dSChris Kay }
13233b9be6dSChris Kay 
13333b9be6dSChris Kay static bool amu_supported(void)
13433b9be6dSChris Kay {
13533b9be6dSChris Kay 	return read_id_aa64pfr0_el1_amu() >= ID_AA64PFR0_AMU_V1;
13633b9be6dSChris Kay }
13733b9be6dSChris Kay 
13833b9be6dSChris Kay static bool amu_v1p1_supported(void)
13933b9be6dSChris Kay {
14033b9be6dSChris Kay 	return read_id_aa64pfr0_el1_amu() >= ID_AA64PFR0_AMU_V1P1;
14133b9be6dSChris Kay }
14233b9be6dSChris Kay 
14333b9be6dSChris Kay #if ENABLE_AMU_AUXILIARY_COUNTERS
144b4b726eaSChris Kay static bool amu_group1_supported(void)
145f3ccf036SAlexei Fedorov {
14633b9be6dSChris Kay 	return read_amcfgr_el0_ncg() > 0U;
147f3ccf036SAlexei Fedorov }
148f3ccf036SAlexei Fedorov #endif
149f3ccf036SAlexei Fedorov 
1500767d50eSDimitris Papastamos /*
1510767d50eSDimitris Papastamos  * Enable counters. This function is meant to be invoked
1520767d50eSDimitris Papastamos  * by the context management library before exiting from EL3.
1530767d50eSDimitris Papastamos  */
15468ac5ed0SArunachalam Ganapathy void amu_enable(bool el2_unused, cpu_context_t *ctx)
1550767d50eSDimitris Papastamos {
15633b9be6dSChris Kay 	if (!amu_supported()) {
1570767d50eSDimitris Papastamos 		return;
158f3ccf036SAlexei Fedorov 	}
159f3ccf036SAlexei Fedorov 
160*1fd685a7SChris Kay #if ENABLE_AMU_AUXILIARY_COUNTERS
161*1fd685a7SChris Kay 	if (AMU_GROUP1_NR_COUNTERS > 0U) {
162f3ccf036SAlexei Fedorov 		/* Check and set presence of group 1 counters */
163f3ccf036SAlexei Fedorov 		if (!amu_group1_supported()) {
164f3ccf036SAlexei Fedorov 			ERROR("AMU Counter Group 1 is not implemented\n");
165f3ccf036SAlexei Fedorov 			panic();
166f3ccf036SAlexei Fedorov 		}
167f3ccf036SAlexei Fedorov 
168f3ccf036SAlexei Fedorov 		/* Check number of group 1 counters */
16933b9be6dSChris Kay 		uint64_t cnt_num = read_amcgcr_el0_cg1nc();
170*1fd685a7SChris Kay 
171f3ccf036SAlexei Fedorov 		VERBOSE("%s%llu. %s%u\n",
172f3ccf036SAlexei Fedorov 			"Number of AMU Group 1 Counters ", cnt_num,
173f3ccf036SAlexei Fedorov 			"Requested number ", AMU_GROUP1_NR_COUNTERS);
174f3ccf036SAlexei Fedorov 
175f3ccf036SAlexei Fedorov 		if (cnt_num < AMU_GROUP1_NR_COUNTERS) {
176f3ccf036SAlexei Fedorov 			ERROR("%s%llu is less than %s%u\n",
177f3ccf036SAlexei Fedorov 			"Number of AMU Group 1 Counters ", cnt_num,
178f3ccf036SAlexei Fedorov 			"Requested number ", AMU_GROUP1_NR_COUNTERS);
179f3ccf036SAlexei Fedorov 			panic();
180f3ccf036SAlexei Fedorov 		}
181*1fd685a7SChris Kay 	}
182f3ccf036SAlexei Fedorov #endif
1830767d50eSDimitris Papastamos 
184380559c1SDimitris Papastamos 	if (el2_unused) {
185380559c1SDimitris Papastamos 		/*
186380559c1SDimitris Papastamos 		 * CPTR_EL2.TAM: Set to zero so any accesses to
187380559c1SDimitris Papastamos 		 * the Activity Monitor registers do not trap to EL2.
188380559c1SDimitris Papastamos 		 */
18933b9be6dSChris Kay 		write_cptr_el2_tam(0U);
190380559c1SDimitris Papastamos 	}
191380559c1SDimitris Papastamos 
192380559c1SDimitris Papastamos 	/*
19368ac5ed0SArunachalam Ganapathy 	 * Retrieve and update the CPTR_EL3 value from the context mentioned
19468ac5ed0SArunachalam Ganapathy 	 * in 'ctx'. Set CPTR_EL3.TAM to zero so that any accesses to
195380559c1SDimitris Papastamos 	 * the Activity Monitor registers do not trap to EL3.
196380559c1SDimitris Papastamos 	 */
19733b9be6dSChris Kay 	write_cptr_el3_tam(ctx, 0U);
198380559c1SDimitris Papastamos 
199380559c1SDimitris Papastamos 	/* Enable group 0 counters */
20033b9be6dSChris Kay 	write_amcntenset0_el0_px(AMU_GROUP0_COUNTERS_MASK);
201f3ccf036SAlexei Fedorov 
202*1fd685a7SChris Kay #if ENABLE_AMU_AUXILIARY_COUNTERS
203*1fd685a7SChris Kay 	if (AMU_GROUP1_NR_COUNTERS > 0U) {
20459902b7cSDimitris Papastamos 		/* Enable group 1 counters */
20533b9be6dSChris Kay 		write_amcntenset1_el0_px(AMU_GROUP1_COUNTERS_MASK);
206*1fd685a7SChris Kay 	}
207f3ccf036SAlexei Fedorov #endif
208873d4241Sjohpow01 
209873d4241Sjohpow01 	/* Initialize FEAT_AMUv1p1 features if present. */
21033b9be6dSChris Kay 	if (!amu_v1p1_supported()) {
211873d4241Sjohpow01 		return;
212873d4241Sjohpow01 	}
213873d4241Sjohpow01 
214873d4241Sjohpow01 	if (el2_unused) {
215873d4241Sjohpow01 		/* Make sure virtual offsets are disabled if EL2 not used. */
21633b9be6dSChris Kay 		write_hcr_el2_amvoffen(0U);
217873d4241Sjohpow01 	}
218873d4241Sjohpow01 
219873d4241Sjohpow01 #if AMU_RESTRICT_COUNTERS
220873d4241Sjohpow01 	/*
221873d4241Sjohpow01 	 * FEAT_AMUv1p1 adds a register field to restrict access to group 1
222873d4241Sjohpow01 	 * counters at all but the highest implemented EL.  This is controlled
223873d4241Sjohpow01 	 * with the AMU_RESTRICT_COUNTERS compile time flag, when set, system
224873d4241Sjohpow01 	 * register reads at lower ELs return zero.  Reads from the memory
225873d4241Sjohpow01 	 * mapped view are unaffected.
226873d4241Sjohpow01 	 */
227873d4241Sjohpow01 	VERBOSE("AMU group 1 counter access restricted.\n");
22833b9be6dSChris Kay 	write_amcr_el0_cg1rz(1U);
229873d4241Sjohpow01 #else
23033b9be6dSChris Kay 	write_amcr_el0_cg1rz(0U);
231873d4241Sjohpow01 #endif
232380559c1SDimitris Papastamos }
2330767d50eSDimitris Papastamos 
2340767d50eSDimitris Papastamos /* Read the group 0 counter identified by the given `idx`. */
235b4b726eaSChris Kay static uint64_t amu_group0_cnt_read(unsigned int idx)
2360767d50eSDimitris Papastamos {
23733b9be6dSChris Kay 	assert(amu_supported());
238f3ccf036SAlexei Fedorov 	assert(idx < AMU_GROUP0_NR_COUNTERS);
2390767d50eSDimitris Papastamos 
2400767d50eSDimitris Papastamos 	return amu_group0_cnt_read_internal(idx);
2410767d50eSDimitris Papastamos }
2420767d50eSDimitris Papastamos 
243f3ccf036SAlexei Fedorov /* Write the group 0 counter identified by the given `idx` with `val` */
244b4b726eaSChris Kay static void amu_group0_cnt_write(unsigned  int idx, uint64_t val)
2450767d50eSDimitris Papastamos {
24633b9be6dSChris Kay 	assert(amu_supported());
247f3ccf036SAlexei Fedorov 	assert(idx < AMU_GROUP0_NR_COUNTERS);
2480767d50eSDimitris Papastamos 
2490767d50eSDimitris Papastamos 	amu_group0_cnt_write_internal(idx, val);
2500767d50eSDimitris Papastamos 	isb();
2510767d50eSDimitris Papastamos }
2520767d50eSDimitris Papastamos 
253873d4241Sjohpow01 /*
254873d4241Sjohpow01  * Read the group 0 offset register for a given index. Index must be 0, 2,
255873d4241Sjohpow01  * or 3, the register for 1 does not exist.
256873d4241Sjohpow01  *
257873d4241Sjohpow01  * Using this function requires FEAT_AMUv1p1 support.
258873d4241Sjohpow01  */
259b4b726eaSChris Kay static uint64_t amu_group0_voffset_read(unsigned int idx)
260873d4241Sjohpow01 {
26133b9be6dSChris Kay 	assert(amu_v1p1_supported());
262873d4241Sjohpow01 	assert(idx < AMU_GROUP0_NR_COUNTERS);
263873d4241Sjohpow01 	assert(idx != 1U);
264873d4241Sjohpow01 
265873d4241Sjohpow01 	return amu_group0_voffset_read_internal(idx);
266873d4241Sjohpow01 }
267873d4241Sjohpow01 
268873d4241Sjohpow01 /*
269873d4241Sjohpow01  * Write the group 0 offset register for a given index. Index must be 0, 2, or
270873d4241Sjohpow01  * 3, the register for 1 does not exist.
271873d4241Sjohpow01  *
272873d4241Sjohpow01  * Using this function requires FEAT_AMUv1p1 support.
273873d4241Sjohpow01  */
274b4b726eaSChris Kay static void amu_group0_voffset_write(unsigned int idx, uint64_t val)
275873d4241Sjohpow01 {
27633b9be6dSChris Kay 	assert(amu_v1p1_supported());
277873d4241Sjohpow01 	assert(idx < AMU_GROUP0_NR_COUNTERS);
278873d4241Sjohpow01 	assert(idx != 1U);
279873d4241Sjohpow01 
280873d4241Sjohpow01 	amu_group0_voffset_write_internal(idx, val);
281873d4241Sjohpow01 	isb();
282873d4241Sjohpow01 }
283873d4241Sjohpow01 
284*1fd685a7SChris Kay #if ENABLE_AMU_AUXILIARY_COUNTERS
285f3ccf036SAlexei Fedorov /* Read the group 1 counter identified by the given `idx` */
286b4b726eaSChris Kay static uint64_t amu_group1_cnt_read(unsigned int idx)
2870767d50eSDimitris Papastamos {
28833b9be6dSChris Kay 	assert(amu_supported());
289f3ccf036SAlexei Fedorov 	assert(amu_group1_supported());
290f3ccf036SAlexei Fedorov 	assert(idx < AMU_GROUP1_NR_COUNTERS);
2910767d50eSDimitris Papastamos 
2920767d50eSDimitris Papastamos 	return amu_group1_cnt_read_internal(idx);
2930767d50eSDimitris Papastamos }
2940767d50eSDimitris Papastamos 
295f3ccf036SAlexei Fedorov /* Write the group 1 counter identified by the given `idx` with `val` */
296b4b726eaSChris Kay static void amu_group1_cnt_write(unsigned int idx, uint64_t val)
2970767d50eSDimitris Papastamos {
29833b9be6dSChris Kay 	assert(amu_supported());
299f3ccf036SAlexei Fedorov 	assert(amu_group1_supported());
300f3ccf036SAlexei Fedorov 	assert(idx < AMU_GROUP1_NR_COUNTERS);
3010767d50eSDimitris Papastamos 
3020767d50eSDimitris Papastamos 	amu_group1_cnt_write_internal(idx, val);
3030767d50eSDimitris Papastamos 	isb();
3040767d50eSDimitris Papastamos }
3050767d50eSDimitris Papastamos 
3060767d50eSDimitris Papastamos /*
307873d4241Sjohpow01  * Read the group 1 offset register for a given index.
308873d4241Sjohpow01  *
309873d4241Sjohpow01  * Using this function requires FEAT_AMUv1p1 support.
310873d4241Sjohpow01  */
311b4b726eaSChris Kay static uint64_t amu_group1_voffset_read(unsigned int idx)
312873d4241Sjohpow01 {
31333b9be6dSChris Kay 	assert(amu_v1p1_supported());
314873d4241Sjohpow01 	assert(amu_group1_supported());
315873d4241Sjohpow01 	assert(idx < AMU_GROUP1_NR_COUNTERS);
31633b9be6dSChris Kay 	assert((read_amcg1idr_el0_voff() & (UINT64_C(1) << idx)) != 0U);
317873d4241Sjohpow01 
318873d4241Sjohpow01 	return amu_group1_voffset_read_internal(idx);
319873d4241Sjohpow01 }
320873d4241Sjohpow01 
321873d4241Sjohpow01 /*
322873d4241Sjohpow01  * Write the group 1 offset register for a given index.
323873d4241Sjohpow01  *
324873d4241Sjohpow01  * Using this function requires FEAT_AMUv1p1 support.
325873d4241Sjohpow01  */
326b4b726eaSChris Kay static void amu_group1_voffset_write(unsigned int idx, uint64_t val)
327873d4241Sjohpow01 {
32833b9be6dSChris Kay 	assert(amu_v1p1_supported());
329873d4241Sjohpow01 	assert(amu_group1_supported());
330873d4241Sjohpow01 	assert(idx < AMU_GROUP1_NR_COUNTERS);
33133b9be6dSChris Kay 	assert((read_amcg1idr_el0_voff() & (UINT64_C(1) << idx)) != 0U);
332873d4241Sjohpow01 
333873d4241Sjohpow01 	amu_group1_voffset_write_internal(idx, val);
334873d4241Sjohpow01 	isb();
335873d4241Sjohpow01 }
336*1fd685a7SChris Kay #endif
337b6eb3932SDimitris Papastamos 
338b6eb3932SDimitris Papastamos static void *amu_context_save(const void *arg)
339b6eb3932SDimitris Papastamos {
340b6eb3932SDimitris Papastamos 	struct amu_ctx *ctx = &amu_ctxs[plat_my_core_pos()];
341f3ccf036SAlexei Fedorov 	unsigned int i;
342b6eb3932SDimitris Papastamos 
34333b9be6dSChris Kay 	if (!amu_supported()) {
344b6eb3932SDimitris Papastamos 		return (void *)-1;
345f3ccf036SAlexei Fedorov 	}
346b6eb3932SDimitris Papastamos 
347*1fd685a7SChris Kay #if ENABLE_AMU_AUXILIARY_COUNTERS
348*1fd685a7SChris Kay 	if (AMU_GROUP1_NR_COUNTERS > 0U) {
349f3ccf036SAlexei Fedorov 		if (!amu_group1_supported()) {
350f3ccf036SAlexei Fedorov 			return (void *)-1;
351f3ccf036SAlexei Fedorov 		}
352*1fd685a7SChris Kay 	}
353f3ccf036SAlexei Fedorov #endif
354*1fd685a7SChris Kay 
355b6eb3932SDimitris Papastamos 	/* Assert that group 0/1 counter configuration is what we expect */
35633b9be6dSChris Kay 	assert(read_amcntenset0_el0_px() == AMU_GROUP0_COUNTERS_MASK);
357b6eb3932SDimitris Papastamos 
358*1fd685a7SChris Kay #if ENABLE_AMU_AUXILIARY_COUNTERS
359*1fd685a7SChris Kay 	if (AMU_GROUP1_NR_COUNTERS > 0U) {
36033b9be6dSChris Kay 		assert(read_amcntenset1_el0_px() == AMU_GROUP1_COUNTERS_MASK);
361*1fd685a7SChris Kay 	}
362f3ccf036SAlexei Fedorov #endif
363*1fd685a7SChris Kay 
364b6eb3932SDimitris Papastamos 	/*
365b6eb3932SDimitris Papastamos 	 * Disable group 0/1 counters to avoid other observers like SCP sampling
366b6eb3932SDimitris Papastamos 	 * counter values from the future via the memory mapped view.
367b6eb3932SDimitris Papastamos 	 */
36833b9be6dSChris Kay 	write_amcntenclr0_el0_px(AMU_GROUP0_COUNTERS_MASK);
369f3ccf036SAlexei Fedorov 
370*1fd685a7SChris Kay #if ENABLE_AMU_AUXILIARY_COUNTERS
371*1fd685a7SChris Kay 	if (AMU_GROUP1_NR_COUNTERS > 0U) {
37233b9be6dSChris Kay 		write_amcntenclr1_el0_px(AMU_GROUP1_COUNTERS_MASK);
373*1fd685a7SChris Kay 	}
374f3ccf036SAlexei Fedorov #endif
375*1fd685a7SChris Kay 
376b6eb3932SDimitris Papastamos 	isb();
377b6eb3932SDimitris Papastamos 
378f3ccf036SAlexei Fedorov 	/* Save all group 0 counters */
379f3ccf036SAlexei Fedorov 	for (i = 0U; i < AMU_GROUP0_NR_COUNTERS; i++) {
380b6eb3932SDimitris Papastamos 		ctx->group0_cnts[i] = amu_group0_cnt_read(i);
381f3ccf036SAlexei Fedorov 	}
382b6eb3932SDimitris Papastamos 
383873d4241Sjohpow01 	/* Save group 0 virtual offsets if supported and enabled. */
38433b9be6dSChris Kay 	if (amu_v1p1_supported() && (read_hcr_el2_amvoffen() != 0U)) {
385873d4241Sjohpow01 		/* Not using a loop because count is fixed and index 1 DNE. */
386873d4241Sjohpow01 		ctx->group0_voffsets[0U] = amu_group0_voffset_read(0U);
387873d4241Sjohpow01 		ctx->group0_voffsets[1U] = amu_group0_voffset_read(2U);
388873d4241Sjohpow01 		ctx->group0_voffsets[2U] = amu_group0_voffset_read(3U);
389873d4241Sjohpow01 	}
390873d4241Sjohpow01 
391*1fd685a7SChris Kay #if ENABLE_AMU_AUXILIARY_COUNTERS
392*1fd685a7SChris Kay 	if (AMU_GROUP1_NR_COUNTERS > 0U) {
393b6eb3932SDimitris Papastamos 		/* Save group 1 counters */
394f3ccf036SAlexei Fedorov 		for (i = 0U; i < AMU_GROUP1_NR_COUNTERS; i++) {
395873d4241Sjohpow01 			if ((AMU_GROUP1_COUNTERS_MASK & (1UL << i)) != 0U) {
396b6eb3932SDimitris Papastamos 				ctx->group1_cnts[i] = amu_group1_cnt_read(i);
397f3ccf036SAlexei Fedorov 			}
398f3ccf036SAlexei Fedorov 		}
399873d4241Sjohpow01 
400873d4241Sjohpow01 		/* Save group 1 virtual offsets if supported and enabled. */
40133b9be6dSChris Kay 		if (amu_v1p1_supported() && (read_hcr_el2_amvoffen() != 0U)) {
40233b9be6dSChris Kay 			uint64_t amcg1idr = read_amcg1idr_el0_voff() &
40333b9be6dSChris Kay 				AMU_GROUP1_COUNTERS_MASK;
404873d4241Sjohpow01 
405873d4241Sjohpow01 			for (i = 0U; i < AMU_GROUP1_NR_COUNTERS; i++) {
406873d4241Sjohpow01 				if (((amcg1idr >> i) & 1ULL) != 0ULL) {
407873d4241Sjohpow01 					ctx->group1_voffsets[i] =
408873d4241Sjohpow01 						amu_group1_voffset_read(i);
409873d4241Sjohpow01 				}
410873d4241Sjohpow01 			}
411873d4241Sjohpow01 		}
412*1fd685a7SChris Kay 	}
413f3ccf036SAlexei Fedorov #endif
414*1fd685a7SChris Kay 
41540daecc1SAntonio Nino Diaz 	return (void *)0;
416b6eb3932SDimitris Papastamos }
417b6eb3932SDimitris Papastamos 
418b6eb3932SDimitris Papastamos static void *amu_context_restore(const void *arg)
419b6eb3932SDimitris Papastamos {
420b6eb3932SDimitris Papastamos 	struct amu_ctx *ctx = &amu_ctxs[plat_my_core_pos()];
421f3ccf036SAlexei Fedorov 	unsigned int i;
422b6eb3932SDimitris Papastamos 
42333b9be6dSChris Kay 	if (!amu_supported()) {
424b6eb3932SDimitris Papastamos 		return (void *)-1;
425f3ccf036SAlexei Fedorov 	}
426b6eb3932SDimitris Papastamos 
427*1fd685a7SChris Kay #if ENABLE_AMU_AUXILIARY_COUNTERS
428*1fd685a7SChris Kay 	if (AMU_GROUP1_NR_COUNTERS > 0U) {
429f3ccf036SAlexei Fedorov 		if (!amu_group1_supported()) {
430f3ccf036SAlexei Fedorov 			return (void *)-1;
431f3ccf036SAlexei Fedorov 		}
432*1fd685a7SChris Kay 	}
433f3ccf036SAlexei Fedorov #endif
434*1fd685a7SChris Kay 
435b6eb3932SDimitris Papastamos 	/* Counters were disabled in `amu_context_save()` */
43633b9be6dSChris Kay 	assert(read_amcntenset0_el0_px() == 0U);
437b6eb3932SDimitris Papastamos 
438*1fd685a7SChris Kay #if ENABLE_AMU_AUXILIARY_COUNTERS
439*1fd685a7SChris Kay 	if (AMU_GROUP1_NR_COUNTERS > 0U) {
44033b9be6dSChris Kay 		assert(read_amcntenset1_el0_px() == 0U);
441*1fd685a7SChris Kay 	}
442f3ccf036SAlexei Fedorov #endif
443b6eb3932SDimitris Papastamos 
444f3ccf036SAlexei Fedorov 	/* Restore all group 0 counters */
445f3ccf036SAlexei Fedorov 	for (i = 0U; i < AMU_GROUP0_NR_COUNTERS; i++) {
446b6eb3932SDimitris Papastamos 		amu_group0_cnt_write(i, ctx->group0_cnts[i]);
447f3ccf036SAlexei Fedorov 	}
448b6eb3932SDimitris Papastamos 
449873d4241Sjohpow01 	/* Restore group 0 virtual offsets if supported and enabled. */
45033b9be6dSChris Kay 	if (amu_v1p1_supported() && (read_hcr_el2_amvoffen() != 0U)) {
451873d4241Sjohpow01 		/* Not using a loop because count is fixed and index 1 DNE. */
452873d4241Sjohpow01 		amu_group0_voffset_write(0U, ctx->group0_voffsets[0U]);
453873d4241Sjohpow01 		amu_group0_voffset_write(2U, ctx->group0_voffsets[1U]);
454873d4241Sjohpow01 		amu_group0_voffset_write(3U, ctx->group0_voffsets[2U]);
455873d4241Sjohpow01 	}
456873d4241Sjohpow01 
457f3ccf036SAlexei Fedorov 	/* Restore group 0 counter configuration */
45833b9be6dSChris Kay 	write_amcntenset0_el0_px(AMU_GROUP0_COUNTERS_MASK);
459f3ccf036SAlexei Fedorov 
460*1fd685a7SChris Kay #if ENABLE_AMU_AUXILIARY_COUNTERS
461*1fd685a7SChris Kay 	if (AMU_GROUP1_NR_COUNTERS > 0U) {
462f3ccf036SAlexei Fedorov 		/* Restore group 1 counters */
463f3ccf036SAlexei Fedorov 		for (i = 0U; i < AMU_GROUP1_NR_COUNTERS; i++) {
464873d4241Sjohpow01 			if ((AMU_GROUP1_COUNTERS_MASK & (1UL << i)) != 0U) {
465f3ccf036SAlexei Fedorov 				amu_group1_cnt_write(i, ctx->group1_cnts[i]);
466f3ccf036SAlexei Fedorov 			}
467f3ccf036SAlexei Fedorov 		}
468f3ccf036SAlexei Fedorov 
469873d4241Sjohpow01 		/* Restore group 1 virtual offsets if supported and enabled. */
47033b9be6dSChris Kay 		if (amu_v1p1_supported() && (read_hcr_el2_amvoffen() != 0U)) {
47133b9be6dSChris Kay 			uint64_t amcg1idr = read_amcg1idr_el0_voff() &
47233b9be6dSChris Kay 				AMU_GROUP1_COUNTERS_MASK;
473873d4241Sjohpow01 
474873d4241Sjohpow01 			for (i = 0U; i < AMU_GROUP1_NR_COUNTERS; i++) {
475873d4241Sjohpow01 				if (((amcg1idr >> i) & 1ULL) != 0ULL) {
476873d4241Sjohpow01 					amu_group1_voffset_write(i,
477873d4241Sjohpow01 						ctx->group1_voffsets[i]);
478873d4241Sjohpow01 				}
479873d4241Sjohpow01 			}
480873d4241Sjohpow01 		}
481873d4241Sjohpow01 
482f3ccf036SAlexei Fedorov 		/* Restore group 1 counter configuration */
48333b9be6dSChris Kay 		write_amcntenset1_el0_px(AMU_GROUP1_COUNTERS_MASK);
484*1fd685a7SChris Kay 	}
485f3ccf036SAlexei Fedorov #endif
486b6eb3932SDimitris Papastamos 
48740daecc1SAntonio Nino Diaz 	return (void *)0;
488b6eb3932SDimitris Papastamos }
489b6eb3932SDimitris Papastamos 
490b6eb3932SDimitris Papastamos SUBSCRIBE_TO_EVENT(psci_suspend_pwrdown_start, amu_context_save);
491b6eb3932SDimitris Papastamos SUBSCRIBE_TO_EVENT(psci_suspend_pwrdown_finish, amu_context_restore);
492