xref: /rk3399_ARM-atf/lib/extensions/amu/aarch64/amu.c (revision e747a59be4ab8e9fa6edc7f4fb04478cd0f823c2)
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 
11*e747a59bSChris Kay #include "../amu_private.h"
12380559c1SDimitris Papastamos #include <arch.h>
13873d4241Sjohpow01 #include <arch_features.h>
14380559c1SDimitris Papastamos #include <arch_helpers.h>
1509d40e0eSAntonio Nino Diaz #include <lib/el3_runtime/pubsub_events.h>
1609d40e0eSAntonio Nino Diaz #include <lib/extensions/amu.h>
17f3ccf036SAlexei Fedorov 
1809d40e0eSAntonio Nino Diaz #include <plat/common/platform.h>
19380559c1SDimitris Papastamos 
20*e747a59bSChris Kay struct amu_ctx {
21*e747a59bSChris Kay 	uint64_t group0_cnts[AMU_GROUP0_MAX_COUNTERS];
22*e747a59bSChris Kay #if ENABLE_AMU_AUXILIARY_COUNTERS
23*e747a59bSChris Kay 	uint64_t group1_cnts[AMU_GROUP1_MAX_COUNTERS];
24*e747a59bSChris Kay #endif
25*e747a59bSChris Kay 
26*e747a59bSChris Kay 	/* Architected event counter 1 does not have an offset register */
27*e747a59bSChris Kay 	uint64_t group0_voffsets[AMU_GROUP0_MAX_COUNTERS - 1U];
28*e747a59bSChris Kay #if ENABLE_AMU_AUXILIARY_COUNTERS
29*e747a59bSChris Kay 	uint64_t group1_voffsets[AMU_GROUP1_MAX_COUNTERS];
30*e747a59bSChris Kay #endif
31*e747a59bSChris Kay 
32*e747a59bSChris Kay 	uint16_t group0_enable;
33*e747a59bSChris Kay #if ENABLE_AMU_AUXILIARY_COUNTERS
34*e747a59bSChris Kay 	uint16_t group1_enable;
35*e747a59bSChris Kay #endif
36*e747a59bSChris Kay };
37*e747a59bSChris Kay 
38*e747a59bSChris Kay static struct amu_ctx amu_ctxs_[PLATFORM_CORE_COUNT];
39*e747a59bSChris Kay 
40*e747a59bSChris Kay CASSERT((sizeof(amu_ctxs_[0].group0_enable) * CHAR_BIT) <= AMU_GROUP0_MAX_COUNTERS,
41*e747a59bSChris Kay 	amu_ctx_group0_enable_cannot_represent_all_group0_counters);
42*e747a59bSChris Kay 
43*e747a59bSChris Kay #if ENABLE_AMU_AUXILIARY_COUNTERS
44*e747a59bSChris Kay CASSERT((sizeof(amu_ctxs_[0].group1_enable) * CHAR_BIT) <= AMU_GROUP1_MAX_COUNTERS,
45*e747a59bSChris Kay 	amu_ctx_group1_enable_cannot_represent_all_group1_counters);
46*e747a59bSChris Kay #endif
47b6eb3932SDimitris Papastamos 
4833b9be6dSChris Kay static inline __unused uint64_t read_id_aa64pfr0_el1_amu(void)
49380559c1SDimitris Papastamos {
5033b9be6dSChris Kay 	return (read_id_aa64pfr0_el1() >> ID_AA64PFR0_AMU_SHIFT) &
51873d4241Sjohpow01 		ID_AA64PFR0_AMU_MASK;
520767d50eSDimitris Papastamos }
530767d50eSDimitris Papastamos 
5433b9be6dSChris Kay static inline __unused uint64_t read_hcr_el2_amvoffen(void)
5533b9be6dSChris Kay {
5633b9be6dSChris Kay 	return (read_hcr_el2() & HCR_AMVOFFEN_BIT) >>
5733b9be6dSChris Kay 		HCR_AMVOFFEN_SHIFT;
5833b9be6dSChris Kay }
5933b9be6dSChris Kay 
6033b9be6dSChris Kay static inline __unused void write_cptr_el2_tam(uint64_t value)
6133b9be6dSChris Kay {
6233b9be6dSChris Kay 	write_cptr_el2((read_cptr_el2() & ~CPTR_EL2_TAM_BIT) |
6333b9be6dSChris Kay 		((value << CPTR_EL2_TAM_SHIFT) & CPTR_EL2_TAM_BIT));
6433b9be6dSChris Kay }
6533b9be6dSChris Kay 
6633b9be6dSChris Kay static inline __unused void write_cptr_el3_tam(cpu_context_t *ctx, uint64_t tam)
6733b9be6dSChris Kay {
6833b9be6dSChris Kay 	uint64_t value = read_ctx_reg(get_el3state_ctx(ctx), CTX_CPTR_EL3);
6933b9be6dSChris Kay 
7033b9be6dSChris Kay 	value &= ~TAM_BIT;
7133b9be6dSChris Kay 	value |= (tam << TAM_SHIFT) & TAM_BIT;
7233b9be6dSChris Kay 
7333b9be6dSChris Kay 	write_ctx_reg(get_el3state_ctx(ctx), CTX_CPTR_EL3, value);
7433b9be6dSChris Kay }
7533b9be6dSChris Kay 
7633b9be6dSChris Kay static inline __unused void write_hcr_el2_amvoffen(uint64_t value)
7733b9be6dSChris Kay {
7833b9be6dSChris Kay 	write_hcr_el2((read_hcr_el2() & ~HCR_AMVOFFEN_BIT) |
7933b9be6dSChris Kay 		((value << HCR_AMVOFFEN_SHIFT) & HCR_AMVOFFEN_BIT));
8033b9be6dSChris Kay }
8133b9be6dSChris Kay 
8233b9be6dSChris Kay static inline __unused void write_amcr_el0_cg1rz(uint64_t value)
8333b9be6dSChris Kay {
8433b9be6dSChris Kay 	write_amcr_el0((read_amcr_el0() & ~AMCR_CG1RZ_BIT) |
8533b9be6dSChris Kay 		((value << AMCR_CG1RZ_SHIFT) & AMCR_CG1RZ_BIT));
8633b9be6dSChris Kay }
8733b9be6dSChris Kay 
8833b9be6dSChris Kay static inline __unused uint64_t read_amcfgr_el0_ncg(void)
8933b9be6dSChris Kay {
9033b9be6dSChris Kay 	return (read_amcfgr_el0() >> AMCFGR_EL0_NCG_SHIFT) &
9133b9be6dSChris Kay 		AMCFGR_EL0_NCG_MASK;
9233b9be6dSChris Kay }
9333b9be6dSChris Kay 
94*e747a59bSChris Kay static inline __unused uint64_t read_amcgcr_el0_cg0nc(void)
9581e2ff1fSChris Kay {
9681e2ff1fSChris Kay 	return (read_amcgcr_el0() >> AMCGCR_EL0_CG0NC_SHIFT) &
9781e2ff1fSChris Kay 		AMCGCR_EL0_CG0NC_MASK;
9881e2ff1fSChris Kay }
9981e2ff1fSChris Kay 
10033b9be6dSChris Kay static inline __unused uint64_t read_amcg1idr_el0_voff(void)
10133b9be6dSChris Kay {
10233b9be6dSChris Kay 	return (read_amcg1idr_el0() >> AMCG1IDR_VOFF_SHIFT) &
10333b9be6dSChris Kay 		AMCG1IDR_VOFF_MASK;
10433b9be6dSChris Kay }
10533b9be6dSChris Kay 
10633b9be6dSChris Kay static inline __unused uint64_t read_amcgcr_el0_cg1nc(void)
10733b9be6dSChris Kay {
10833b9be6dSChris Kay 	return (read_amcgcr_el0() >> AMCGCR_EL0_CG1NC_SHIFT) &
10933b9be6dSChris Kay 		AMCGCR_EL0_CG1NC_MASK;
11033b9be6dSChris Kay }
11133b9be6dSChris Kay 
11233b9be6dSChris Kay static inline __unused uint64_t read_amcntenset0_el0_px(void)
11333b9be6dSChris Kay {
11433b9be6dSChris Kay 	return (read_amcntenset0_el0() >> AMCNTENSET0_EL0_Pn_SHIFT) &
11533b9be6dSChris Kay 		AMCNTENSET0_EL0_Pn_MASK;
11633b9be6dSChris Kay }
11733b9be6dSChris Kay 
11833b9be6dSChris Kay static inline __unused uint64_t read_amcntenset1_el0_px(void)
11933b9be6dSChris Kay {
12033b9be6dSChris Kay 	return (read_amcntenset1_el0() >> AMCNTENSET1_EL0_Pn_SHIFT) &
12133b9be6dSChris Kay 		AMCNTENSET1_EL0_Pn_MASK;
12233b9be6dSChris Kay }
12333b9be6dSChris Kay 
12433b9be6dSChris Kay static inline __unused void write_amcntenset0_el0_px(uint64_t px)
12533b9be6dSChris Kay {
12633b9be6dSChris Kay 	uint64_t value = read_amcntenset0_el0();
12733b9be6dSChris Kay 
12833b9be6dSChris Kay 	value &= ~AMCNTENSET0_EL0_Pn_MASK;
12933b9be6dSChris Kay 	value |= (px << AMCNTENSET0_EL0_Pn_SHIFT) & AMCNTENSET0_EL0_Pn_MASK;
13033b9be6dSChris Kay 
13133b9be6dSChris Kay 	write_amcntenset0_el0(value);
13233b9be6dSChris Kay }
13333b9be6dSChris Kay 
13433b9be6dSChris Kay static inline __unused void write_amcntenset1_el0_px(uint64_t px)
13533b9be6dSChris Kay {
13633b9be6dSChris Kay 	uint64_t value = read_amcntenset1_el0();
13733b9be6dSChris Kay 
13833b9be6dSChris Kay 	value &= ~AMCNTENSET1_EL0_Pn_MASK;
13933b9be6dSChris Kay 	value |= (px << AMCNTENSET1_EL0_Pn_SHIFT) & AMCNTENSET1_EL0_Pn_MASK;
14033b9be6dSChris Kay 
14133b9be6dSChris Kay 	write_amcntenset1_el0(value);
14233b9be6dSChris Kay }
14333b9be6dSChris Kay 
14433b9be6dSChris Kay static inline __unused void write_amcntenclr0_el0_px(uint64_t px)
14533b9be6dSChris Kay {
14633b9be6dSChris Kay 	uint64_t value = read_amcntenclr0_el0();
14733b9be6dSChris Kay 
14833b9be6dSChris Kay 	value &= ~AMCNTENCLR0_EL0_Pn_MASK;
14933b9be6dSChris Kay 	value |= (px << AMCNTENCLR0_EL0_Pn_SHIFT) & AMCNTENCLR0_EL0_Pn_MASK;
15033b9be6dSChris Kay 
15133b9be6dSChris Kay 	write_amcntenclr0_el0(value);
15233b9be6dSChris Kay }
15333b9be6dSChris Kay 
15433b9be6dSChris Kay static inline __unused void write_amcntenclr1_el0_px(uint64_t px)
15533b9be6dSChris Kay {
15633b9be6dSChris Kay 	uint64_t value = read_amcntenclr1_el0();
15733b9be6dSChris Kay 
15833b9be6dSChris Kay 	value &= ~AMCNTENCLR1_EL0_Pn_MASK;
15933b9be6dSChris Kay 	value |= (px << AMCNTENCLR1_EL0_Pn_SHIFT) & AMCNTENCLR1_EL0_Pn_MASK;
16033b9be6dSChris Kay 
16133b9be6dSChris Kay 	write_amcntenclr1_el0(value);
16233b9be6dSChris Kay }
16333b9be6dSChris Kay 
164*e747a59bSChris Kay static __unused bool amu_supported(void)
16533b9be6dSChris Kay {
16633b9be6dSChris Kay 	return read_id_aa64pfr0_el1_amu() >= ID_AA64PFR0_AMU_V1;
16733b9be6dSChris Kay }
16833b9be6dSChris Kay 
169*e747a59bSChris Kay static __unused bool amu_v1p1_supported(void)
17033b9be6dSChris Kay {
17133b9be6dSChris Kay 	return read_id_aa64pfr0_el1_amu() >= ID_AA64PFR0_AMU_V1P1;
17233b9be6dSChris Kay }
17333b9be6dSChris Kay 
17433b9be6dSChris Kay #if ENABLE_AMU_AUXILIARY_COUNTERS
175*e747a59bSChris Kay static __unused bool amu_group1_supported(void)
176f3ccf036SAlexei Fedorov {
17733b9be6dSChris Kay 	return read_amcfgr_el0_ncg() > 0U;
178f3ccf036SAlexei Fedorov }
179f3ccf036SAlexei Fedorov #endif
180f3ccf036SAlexei Fedorov 
1810767d50eSDimitris Papastamos /*
182*e747a59bSChris Kay  * Enable counters. This function is meant to be invoked by the context
183*e747a59bSChris Kay  * management library before exiting from EL3.
1840767d50eSDimitris Papastamos  */
18568ac5ed0SArunachalam Ganapathy void amu_enable(bool el2_unused, cpu_context_t *ctx)
1860767d50eSDimitris Papastamos {
187*e747a59bSChris Kay 	uint64_t id_aa64pfr0_el1_amu;		/* AMU version */
188*e747a59bSChris Kay 
189*e747a59bSChris Kay 	uint64_t amcfgr_el0_ncg;		/* Number of counter groups */
190*e747a59bSChris Kay 	uint64_t amcgcr_el0_cg0nc;		/* Number of group 0 counters */
191*e747a59bSChris Kay 
192*e747a59bSChris Kay 	uint64_t amcntenset0_el0_px = 0x0;	/* Group 0 enable mask */
193*e747a59bSChris Kay 	uint64_t amcntenset1_el0_px = 0x0;	/* Group 1 enable mask */
194*e747a59bSChris Kay 
195*e747a59bSChris Kay 	id_aa64pfr0_el1_amu = read_id_aa64pfr0_el1_amu();
196*e747a59bSChris Kay 	if (id_aa64pfr0_el1_amu == ID_AA64PFR0_AMU_NOT_SUPPORTED) {
197*e747a59bSChris Kay 		/*
198*e747a59bSChris Kay 		 * If the AMU is unsupported, nothing needs to be done.
199*e747a59bSChris Kay 		 */
200*e747a59bSChris Kay 
2010767d50eSDimitris Papastamos 		return;
202f3ccf036SAlexei Fedorov 	}
203f3ccf036SAlexei Fedorov 
204380559c1SDimitris Papastamos 	if (el2_unused) {
205380559c1SDimitris Papastamos 		/*
206*e747a59bSChris Kay 		 * CPTR_EL2.TAM: Set to zero so any accesses to the Activity
207*e747a59bSChris Kay 		 * Monitor registers do not trap to EL2.
208380559c1SDimitris Papastamos 		 */
20933b9be6dSChris Kay 		write_cptr_el2_tam(0U);
210380559c1SDimitris Papastamos 	}
211380559c1SDimitris Papastamos 
212380559c1SDimitris Papastamos 	/*
21368ac5ed0SArunachalam Ganapathy 	 * Retrieve and update the CPTR_EL3 value from the context mentioned
21468ac5ed0SArunachalam Ganapathy 	 * in 'ctx'. Set CPTR_EL3.TAM to zero so that any accesses to
215380559c1SDimitris Papastamos 	 * the Activity Monitor registers do not trap to EL3.
216380559c1SDimitris Papastamos 	 */
21733b9be6dSChris Kay 	write_cptr_el3_tam(ctx, 0U);
218380559c1SDimitris Papastamos 
219*e747a59bSChris Kay 	/*
220*e747a59bSChris Kay 	 * Retrieve the number of architected counters. All of these counters
221*e747a59bSChris Kay 	 * are enabled by default.
222*e747a59bSChris Kay 	 */
223f3ccf036SAlexei Fedorov 
224*e747a59bSChris Kay 	amcgcr_el0_cg0nc = read_amcgcr_el0_cg0nc();
225*e747a59bSChris Kay 	amcntenset0_el0_px = (UINT64_C(1) << (amcgcr_el0_cg0nc)) - 1U;
226*e747a59bSChris Kay 
227*e747a59bSChris Kay 	assert(amcgcr_el0_cg0nc <= AMU_AMCGCR_CG0NC_MAX);
228*e747a59bSChris Kay 
229*e747a59bSChris Kay 	/*
230*e747a59bSChris Kay 	 * Enable the requested counters.
231*e747a59bSChris Kay 	 */
232*e747a59bSChris Kay 
233*e747a59bSChris Kay 	write_amcntenset0_el0_px(amcntenset0_el0_px);
234*e747a59bSChris Kay 
235*e747a59bSChris Kay 	amcfgr_el0_ncg = read_amcfgr_el0_ncg();
236*e747a59bSChris Kay 	if (amcfgr_el0_ncg > 0U) {
237*e747a59bSChris Kay 		write_amcntenset1_el0_px(amcntenset1_el0_px);
2381fd685a7SChris Kay 	}
239873d4241Sjohpow01 
240873d4241Sjohpow01 	/* Initialize FEAT_AMUv1p1 features if present. */
241*e747a59bSChris Kay 	if (id_aa64pfr0_el1_amu >= ID_AA64PFR0_AMU_V1P1) {
242873d4241Sjohpow01 		return;
243873d4241Sjohpow01 	}
244873d4241Sjohpow01 
245873d4241Sjohpow01 	if (el2_unused) {
246873d4241Sjohpow01 		/* Make sure virtual offsets are disabled if EL2 not used. */
24733b9be6dSChris Kay 		write_hcr_el2_amvoffen(0U);
248873d4241Sjohpow01 	}
249873d4241Sjohpow01 
250873d4241Sjohpow01 #if AMU_RESTRICT_COUNTERS
251873d4241Sjohpow01 	/*
252873d4241Sjohpow01 	 * FEAT_AMUv1p1 adds a register field to restrict access to group 1
253873d4241Sjohpow01 	 * counters at all but the highest implemented EL.  This is controlled
254873d4241Sjohpow01 	 * with the AMU_RESTRICT_COUNTERS compile time flag, when set, system
255873d4241Sjohpow01 	 * register reads at lower ELs return zero.  Reads from the memory
256873d4241Sjohpow01 	 * mapped view are unaffected.
257873d4241Sjohpow01 	 */
258873d4241Sjohpow01 	VERBOSE("AMU group 1 counter access restricted.\n");
25933b9be6dSChris Kay 	write_amcr_el0_cg1rz(1U);
260873d4241Sjohpow01 #else
26133b9be6dSChris Kay 	write_amcr_el0_cg1rz(0U);
262873d4241Sjohpow01 #endif
263380559c1SDimitris Papastamos }
2640767d50eSDimitris Papastamos 
2650767d50eSDimitris Papastamos /* Read the group 0 counter identified by the given `idx`. */
266b4b726eaSChris Kay static uint64_t amu_group0_cnt_read(unsigned int idx)
2670767d50eSDimitris Papastamos {
26833b9be6dSChris Kay 	assert(amu_supported());
26981e2ff1fSChris Kay 	assert(idx < read_amcgcr_el0_cg0nc());
2700767d50eSDimitris Papastamos 
2710767d50eSDimitris Papastamos 	return amu_group0_cnt_read_internal(idx);
2720767d50eSDimitris Papastamos }
2730767d50eSDimitris Papastamos 
274f3ccf036SAlexei Fedorov /* Write the group 0 counter identified by the given `idx` with `val` */
275b4b726eaSChris Kay static void amu_group0_cnt_write(unsigned  int idx, uint64_t val)
2760767d50eSDimitris Papastamos {
27733b9be6dSChris Kay 	assert(amu_supported());
27881e2ff1fSChris Kay 	assert(idx < read_amcgcr_el0_cg0nc());
2790767d50eSDimitris Papastamos 
2800767d50eSDimitris Papastamos 	amu_group0_cnt_write_internal(idx, val);
2810767d50eSDimitris Papastamos 	isb();
2820767d50eSDimitris Papastamos }
2830767d50eSDimitris Papastamos 
284873d4241Sjohpow01 /*
285*e747a59bSChris Kay  * Unlike with auxiliary counters, we cannot detect at runtime whether an
286*e747a59bSChris Kay  * architected counter supports a virtual offset. These are instead fixed
287*e747a59bSChris Kay  * according to FEAT_AMUv1p1, but this switch will need to be updated if later
288*e747a59bSChris Kay  * revisions of FEAT_AMU add additional architected counters.
289*e747a59bSChris Kay  */
290*e747a59bSChris Kay static bool amu_group0_voffset_supported(uint64_t idx)
291*e747a59bSChris Kay {
292*e747a59bSChris Kay 	switch (idx) {
293*e747a59bSChris Kay 	case 0U:
294*e747a59bSChris Kay 	case 2U:
295*e747a59bSChris Kay 	case 3U:
296*e747a59bSChris Kay 		return true;
297*e747a59bSChris Kay 
298*e747a59bSChris Kay 	case 1U:
299*e747a59bSChris Kay 		return false;
300*e747a59bSChris Kay 
301*e747a59bSChris Kay 	default:
302*e747a59bSChris Kay 		ERROR("AMU: can't set up virtual offset for unknown "
303*e747a59bSChris Kay 		      "architected counter %llu!\n", idx);
304*e747a59bSChris Kay 
305*e747a59bSChris Kay 		panic();
306*e747a59bSChris Kay 	}
307*e747a59bSChris Kay }
308*e747a59bSChris Kay 
309*e747a59bSChris Kay /*
310873d4241Sjohpow01  * Read the group 0 offset register for a given index. Index must be 0, 2,
311873d4241Sjohpow01  * or 3, the register for 1 does not exist.
312873d4241Sjohpow01  *
313873d4241Sjohpow01  * Using this function requires FEAT_AMUv1p1 support.
314873d4241Sjohpow01  */
315b4b726eaSChris Kay static uint64_t amu_group0_voffset_read(unsigned int idx)
316873d4241Sjohpow01 {
31733b9be6dSChris Kay 	assert(amu_v1p1_supported());
31881e2ff1fSChris Kay 	assert(idx < read_amcgcr_el0_cg0nc());
319873d4241Sjohpow01 	assert(idx != 1U);
320873d4241Sjohpow01 
321873d4241Sjohpow01 	return amu_group0_voffset_read_internal(idx);
322873d4241Sjohpow01 }
323873d4241Sjohpow01 
324873d4241Sjohpow01 /*
325873d4241Sjohpow01  * Write the group 0 offset register for a given index. Index must be 0, 2, or
326873d4241Sjohpow01  * 3, the register for 1 does not exist.
327873d4241Sjohpow01  *
328873d4241Sjohpow01  * Using this function requires FEAT_AMUv1p1 support.
329873d4241Sjohpow01  */
330b4b726eaSChris Kay static void amu_group0_voffset_write(unsigned int idx, uint64_t val)
331873d4241Sjohpow01 {
33233b9be6dSChris Kay 	assert(amu_v1p1_supported());
33381e2ff1fSChris Kay 	assert(idx < read_amcgcr_el0_cg0nc());
334873d4241Sjohpow01 	assert(idx != 1U);
335873d4241Sjohpow01 
336873d4241Sjohpow01 	amu_group0_voffset_write_internal(idx, val);
337873d4241Sjohpow01 	isb();
338873d4241Sjohpow01 }
339873d4241Sjohpow01 
3401fd685a7SChris Kay #if ENABLE_AMU_AUXILIARY_COUNTERS
341f3ccf036SAlexei Fedorov /* Read the group 1 counter identified by the given `idx` */
342b4b726eaSChris Kay static uint64_t amu_group1_cnt_read(unsigned int idx)
3430767d50eSDimitris Papastamos {
34433b9be6dSChris Kay 	assert(amu_supported());
345f3ccf036SAlexei Fedorov 	assert(amu_group1_supported());
34631d3cc25SChris Kay 	assert(idx < read_amcgcr_el0_cg1nc());
3470767d50eSDimitris Papastamos 
3480767d50eSDimitris Papastamos 	return amu_group1_cnt_read_internal(idx);
3490767d50eSDimitris Papastamos }
3500767d50eSDimitris Papastamos 
351f3ccf036SAlexei Fedorov /* Write the group 1 counter identified by the given `idx` with `val` */
352b4b726eaSChris Kay static void amu_group1_cnt_write(unsigned int idx, uint64_t val)
3530767d50eSDimitris Papastamos {
35433b9be6dSChris Kay 	assert(amu_supported());
355f3ccf036SAlexei Fedorov 	assert(amu_group1_supported());
35631d3cc25SChris Kay 	assert(idx < read_amcgcr_el0_cg1nc());
3570767d50eSDimitris Papastamos 
3580767d50eSDimitris Papastamos 	amu_group1_cnt_write_internal(idx, val);
3590767d50eSDimitris Papastamos 	isb();
3600767d50eSDimitris Papastamos }
3610767d50eSDimitris Papastamos 
3620767d50eSDimitris Papastamos /*
363873d4241Sjohpow01  * Read the group 1 offset register for a given index.
364873d4241Sjohpow01  *
365873d4241Sjohpow01  * Using this function requires FEAT_AMUv1p1 support.
366873d4241Sjohpow01  */
367b4b726eaSChris Kay static uint64_t amu_group1_voffset_read(unsigned int idx)
368873d4241Sjohpow01 {
36933b9be6dSChris Kay 	assert(amu_v1p1_supported());
370873d4241Sjohpow01 	assert(amu_group1_supported());
37131d3cc25SChris Kay 	assert(idx < read_amcgcr_el0_cg1nc());
37233b9be6dSChris Kay 	assert((read_amcg1idr_el0_voff() & (UINT64_C(1) << idx)) != 0U);
373873d4241Sjohpow01 
374873d4241Sjohpow01 	return amu_group1_voffset_read_internal(idx);
375873d4241Sjohpow01 }
376873d4241Sjohpow01 
377873d4241Sjohpow01 /*
378873d4241Sjohpow01  * Write the group 1 offset register for a given index.
379873d4241Sjohpow01  *
380873d4241Sjohpow01  * Using this function requires FEAT_AMUv1p1 support.
381873d4241Sjohpow01  */
382b4b726eaSChris Kay static void amu_group1_voffset_write(unsigned int idx, uint64_t val)
383873d4241Sjohpow01 {
38433b9be6dSChris Kay 	assert(amu_v1p1_supported());
385873d4241Sjohpow01 	assert(amu_group1_supported());
38631d3cc25SChris Kay 	assert(idx < read_amcgcr_el0_cg1nc());
38733b9be6dSChris Kay 	assert((read_amcg1idr_el0_voff() & (UINT64_C(1) << idx)) != 0U);
388873d4241Sjohpow01 
389873d4241Sjohpow01 	amu_group1_voffset_write_internal(idx, val);
390873d4241Sjohpow01 	isb();
391873d4241Sjohpow01 }
3921fd685a7SChris Kay #endif
393b6eb3932SDimitris Papastamos 
394b6eb3932SDimitris Papastamos static void *amu_context_save(const void *arg)
395b6eb3932SDimitris Papastamos {
396*e747a59bSChris Kay 	uint64_t i, j;
397b6eb3932SDimitris Papastamos 
398*e747a59bSChris Kay 	unsigned int core_pos;
399*e747a59bSChris Kay 	struct amu_ctx *ctx;
400b6eb3932SDimitris Papastamos 
401*e747a59bSChris Kay 	uint64_t id_aa64pfr0_el1_amu;	/* AMU version */
402*e747a59bSChris Kay 	uint64_t hcr_el2_amvoffen;	/* AMU virtual offsets enabled */
403*e747a59bSChris Kay 	uint64_t amcgcr_el0_cg0nc;	/* Number of group 0 counters */
404b6eb3932SDimitris Papastamos 
4051fd685a7SChris Kay #if ENABLE_AMU_AUXILIARY_COUNTERS
406*e747a59bSChris Kay 	uint64_t amcg1idr_el0_voff;	/* Auxiliary counters with virtual offsets */
407*e747a59bSChris Kay 	uint64_t amcfgr_el0_ncg;	/* Number of counter groups */
408*e747a59bSChris Kay 	uint64_t amcgcr_el0_cg1nc;	/* Number of group 1 counters */
409*e747a59bSChris Kay #endif
410*e747a59bSChris Kay 
411*e747a59bSChris Kay 	id_aa64pfr0_el1_amu = read_id_aa64pfr0_el1_amu();
412*e747a59bSChris Kay 	if (id_aa64pfr0_el1_amu == ID_AA64PFR0_AMU_NOT_SUPPORTED) {
413*e747a59bSChris Kay 		return (void *)0;
414*e747a59bSChris Kay 	}
415*e747a59bSChris Kay 
416*e747a59bSChris Kay 	core_pos = plat_my_core_pos();
417*e747a59bSChris Kay 	ctx = &amu_ctxs_[core_pos];
418*e747a59bSChris Kay 
419*e747a59bSChris Kay 	amcgcr_el0_cg0nc = read_amcgcr_el0_cg0nc();
420*e747a59bSChris Kay 	hcr_el2_amvoffen = (id_aa64pfr0_el1_amu >= ID_AA64PFR0_AMU_V1P1) ?
421*e747a59bSChris Kay 		read_hcr_el2_amvoffen() : 0U;
422*e747a59bSChris Kay 
423*e747a59bSChris Kay #if ENABLE_AMU_AUXILIARY_COUNTERS
424*e747a59bSChris Kay 	amcfgr_el0_ncg = read_amcfgr_el0_ncg();
425*e747a59bSChris Kay 	amcgcr_el0_cg1nc = (amcfgr_el0_ncg > 0U) ? read_amcgcr_el0_cg1nc() : 0U;
426*e747a59bSChris Kay 	amcg1idr_el0_voff = (hcr_el2_amvoffen != 0U) ? read_amcg1idr_el0_voff() : 0U;
427*e747a59bSChris Kay #endif
428*e747a59bSChris Kay 
429*e747a59bSChris Kay 	/*
430*e747a59bSChris Kay 	 * Disable all AMU counters.
431*e747a59bSChris Kay 	 */
432*e747a59bSChris Kay 
433*e747a59bSChris Kay 	ctx->group0_enable = read_amcntenset0_el0_px();
434*e747a59bSChris Kay 	write_amcntenclr0_el0_px(ctx->group0_enable);
435*e747a59bSChris Kay 
436*e747a59bSChris Kay #if ENABLE_AMU_AUXILIARY_COUNTERS
437*e747a59bSChris Kay 	if (amcfgr_el0_ncg > 0U) {
438*e747a59bSChris Kay 		ctx->group1_enable = read_amcntenset1_el0_px();
439*e747a59bSChris Kay 		write_amcntenclr1_el0_px(ctx->group1_enable);
4401fd685a7SChris Kay 	}
441f3ccf036SAlexei Fedorov #endif
4421fd685a7SChris Kay 
443b6eb3932SDimitris Papastamos 	/*
444*e747a59bSChris Kay 	 * Save the counters to the local context.
445b6eb3932SDimitris Papastamos 	 */
446f3ccf036SAlexei Fedorov 
447*e747a59bSChris Kay 	isb(); /* Ensure counters have been stopped */
4481fd685a7SChris Kay 
449*e747a59bSChris Kay 	for (i = 0U; i < amcgcr_el0_cg0nc; i++) {
450b6eb3932SDimitris Papastamos 		ctx->group0_cnts[i] = amu_group0_cnt_read(i);
451f3ccf036SAlexei Fedorov 	}
452b6eb3932SDimitris Papastamos 
453*e747a59bSChris Kay #if ENABLE_AMU_AUXILIARY_COUNTERS
454*e747a59bSChris Kay 	for (i = 0U; i < amcgcr_el0_cg1nc; i++) {
455*e747a59bSChris Kay 		ctx->group1_cnts[i] = amu_group1_cnt_read(i);
456*e747a59bSChris Kay 	}
457*e747a59bSChris Kay #endif
458*e747a59bSChris Kay 
459*e747a59bSChris Kay 	/*
460*e747a59bSChris Kay 	 * Save virtual offsets for counters that offer them.
461*e747a59bSChris Kay 	 */
462*e747a59bSChris Kay 
463*e747a59bSChris Kay 	if (hcr_el2_amvoffen != 0U) {
464*e747a59bSChris Kay 		for (i = 0U, j = 0U; i < amcgcr_el0_cg0nc; i++) {
465*e747a59bSChris Kay 			if (!amu_group0_voffset_supported(i)) {
466*e747a59bSChris Kay 				continue; /* No virtual offset */
467*e747a59bSChris Kay 			}
468*e747a59bSChris Kay 
469*e747a59bSChris Kay 			ctx->group0_voffsets[j++] = amu_group0_voffset_read(i);
470873d4241Sjohpow01 		}
471873d4241Sjohpow01 
4721fd685a7SChris Kay #if ENABLE_AMU_AUXILIARY_COUNTERS
473*e747a59bSChris Kay 		for (i = 0U, j = 0U; i < amcgcr_el0_cg1nc; i++) {
474*e747a59bSChris Kay 			if ((amcg1idr_el0_voff >> i) & 1U) {
475*e747a59bSChris Kay 				continue; /* No virtual offset */
476f3ccf036SAlexei Fedorov 			}
477873d4241Sjohpow01 
478*e747a59bSChris Kay 			ctx->group1_voffsets[j++] = amu_group1_voffset_read(i);
4791fd685a7SChris Kay 		}
480f3ccf036SAlexei Fedorov #endif
481*e747a59bSChris Kay 	}
4821fd685a7SChris Kay 
48340daecc1SAntonio Nino Diaz 	return (void *)0;
484b6eb3932SDimitris Papastamos }
485b6eb3932SDimitris Papastamos 
486b6eb3932SDimitris Papastamos static void *amu_context_restore(const void *arg)
487b6eb3932SDimitris Papastamos {
488*e747a59bSChris Kay 	uint64_t i, j;
489b6eb3932SDimitris Papastamos 
490*e747a59bSChris Kay 	unsigned int core_pos;
491*e747a59bSChris Kay 	struct amu_ctx *ctx;
492b6eb3932SDimitris Papastamos 
493*e747a59bSChris Kay 	uint64_t id_aa64pfr0_el1_amu;	/* AMU version */
494*e747a59bSChris Kay 
495*e747a59bSChris Kay 	uint64_t hcr_el2_amvoffen;	/* AMU virtual offsets enabled */
496*e747a59bSChris Kay 
497*e747a59bSChris Kay 	uint64_t amcfgr_el0_ncg;	/* Number of counter groups */
498*e747a59bSChris Kay 	uint64_t amcgcr_el0_cg0nc;	/* Number of group 0 counters */
499b6eb3932SDimitris Papastamos 
5001fd685a7SChris Kay #if ENABLE_AMU_AUXILIARY_COUNTERS
501*e747a59bSChris Kay 	uint64_t amcgcr_el0_cg1nc;	/* Number of group 1 counters */
502*e747a59bSChris Kay 	uint64_t amcg1idr_el0_voff;	/* Auxiliary counters with virtual offsets */
503f3ccf036SAlexei Fedorov #endif
504b6eb3932SDimitris Papastamos 
505*e747a59bSChris Kay 	id_aa64pfr0_el1_amu = read_id_aa64pfr0_el1_amu();
506*e747a59bSChris Kay 	if (id_aa64pfr0_el1_amu == ID_AA64PFR0_AMU_NOT_SUPPORTED) {
507*e747a59bSChris Kay 		return (void *)0;
508*e747a59bSChris Kay 	}
509*e747a59bSChris Kay 
510*e747a59bSChris Kay 	core_pos = plat_my_core_pos();
511*e747a59bSChris Kay 	ctx = &amu_ctxs_[core_pos];
512*e747a59bSChris Kay 
513*e747a59bSChris Kay 	amcfgr_el0_ncg = read_amcfgr_el0_ncg();
514*e747a59bSChris Kay 	amcgcr_el0_cg0nc = read_amcgcr_el0_cg0nc();
515*e747a59bSChris Kay 
516*e747a59bSChris Kay 	hcr_el2_amvoffen = (id_aa64pfr0_el1_amu >= ID_AA64PFR0_AMU_V1P1) ?
517*e747a59bSChris Kay 		read_hcr_el2_amvoffen() : 0U;
518*e747a59bSChris Kay 
519*e747a59bSChris Kay #if ENABLE_AMU_AUXILIARY_COUNTERS
520*e747a59bSChris Kay 	amcgcr_el0_cg1nc = (amcfgr_el0_ncg > 0U) ? read_amcgcr_el0_cg1nc() : 0U;
521*e747a59bSChris Kay 	amcg1idr_el0_voff = (hcr_el2_amvoffen != 0U) ? read_amcg1idr_el0_voff() : 0U;
522*e747a59bSChris Kay #endif
523*e747a59bSChris Kay 
524*e747a59bSChris Kay 	/*
525*e747a59bSChris Kay 	 * Sanity check that all counters were disabled when the context was
526*e747a59bSChris Kay 	 * previously saved.
527*e747a59bSChris Kay 	 */
528*e747a59bSChris Kay 
529*e747a59bSChris Kay 	assert(read_amcntenset0_el0_px() == 0U);
530*e747a59bSChris Kay 
531*e747a59bSChris Kay 	if (amcfgr_el0_ncg > 0U) {
532*e747a59bSChris Kay 		assert(read_amcntenset1_el0_px() == 0U);
533*e747a59bSChris Kay 	}
534*e747a59bSChris Kay 
535*e747a59bSChris Kay 	/*
536*e747a59bSChris Kay 	 * Restore the counter values from the local context.
537*e747a59bSChris Kay 	 */
538*e747a59bSChris Kay 
539*e747a59bSChris Kay 	for (i = 0U; i < amcgcr_el0_cg0nc; i++) {
540b6eb3932SDimitris Papastamos 		amu_group0_cnt_write(i, ctx->group0_cnts[i]);
541f3ccf036SAlexei Fedorov 	}
542b6eb3932SDimitris Papastamos 
5431fd685a7SChris Kay #if ENABLE_AMU_AUXILIARY_COUNTERS
544*e747a59bSChris Kay 	for (i = 0U; i < amcgcr_el0_cg1nc; i++) {
545f3ccf036SAlexei Fedorov 		amu_group1_cnt_write(i, ctx->group1_cnts[i]);
546f3ccf036SAlexei Fedorov 	}
547*e747a59bSChris Kay #endif
548*e747a59bSChris Kay 
549*e747a59bSChris Kay 	/*
550*e747a59bSChris Kay 	 * Restore virtual offsets for counters that offer them.
551*e747a59bSChris Kay 	 */
552*e747a59bSChris Kay 
553*e747a59bSChris Kay 	if (hcr_el2_amvoffen != 0U) {
554*e747a59bSChris Kay 		for (i = 0U, j = 0U; i < amcgcr_el0_cg0nc; i++) {
555*e747a59bSChris Kay 			if (!amu_group0_voffset_supported(i)) {
556*e747a59bSChris Kay 				continue; /* No virtual offset */
557f3ccf036SAlexei Fedorov 			}
558f3ccf036SAlexei Fedorov 
559*e747a59bSChris Kay 			amu_group0_voffset_write(i, ctx->group0_voffsets[j++]);
560873d4241Sjohpow01 		}
561873d4241Sjohpow01 
562*e747a59bSChris Kay #if ENABLE_AMU_AUXILIARY_COUNTERS
563*e747a59bSChris Kay 		for (i = 0U, j = 0U; i < amcgcr_el0_cg1nc; i++) {
564*e747a59bSChris Kay 			if ((amcg1idr_el0_voff >> i) & 1U) {
565*e747a59bSChris Kay 				continue; /* No virtual offset */
566*e747a59bSChris Kay 			}
567*e747a59bSChris Kay 
568*e747a59bSChris Kay 			amu_group1_voffset_write(i, ctx->group1_voffsets[j++]);
569*e747a59bSChris Kay 		}
570*e747a59bSChris Kay #endif
571*e747a59bSChris Kay 	}
572*e747a59bSChris Kay 
573*e747a59bSChris Kay 	/*
574*e747a59bSChris Kay 	 * Re-enable counters that were disabled during context save.
575*e747a59bSChris Kay 	 */
576*e747a59bSChris Kay 
577*e747a59bSChris Kay 	write_amcntenset0_el0_px(ctx->group0_enable);
578*e747a59bSChris Kay 
579*e747a59bSChris Kay #if ENABLE_AMU_AUXILIARY_COUNTERS
580*e747a59bSChris Kay 	if (amcfgr_el0_ncg > 0) {
581*e747a59bSChris Kay 		write_amcntenset1_el0_px(ctx->group1_enable);
5821fd685a7SChris Kay 	}
583f3ccf036SAlexei Fedorov #endif
584b6eb3932SDimitris Papastamos 
58540daecc1SAntonio Nino Diaz 	return (void *)0;
586b6eb3932SDimitris Papastamos }
587b6eb3932SDimitris Papastamos 
588b6eb3932SDimitris Papastamos SUBSCRIBE_TO_EVENT(psci_suspend_pwrdown_start, amu_context_save);
589b6eb3932SDimitris Papastamos SUBSCRIBE_TO_EVENT(psci_suspend_pwrdown_finish, amu_context_restore);
590