feat(dsu): save/restore DSU PMU register

Adds driver support to preserve DSU PMU register values over a DSU
power cycle. This driver needs to be enabled by the platforms that
support DSU and also need it's PMU registers to be preserved

Change-Id: I7fc68a3d7d99ee369379aa5cd114fffc763fc0d2
Signed-off-by: Arvind Ram Prakash <arvind.ramprakash@arm.com>
diff --git a/Makefile b/Makefile
index 57e27ca..0ecd794 100644
--- a/Makefile
+++ b/Makefile
@@ -1207,6 +1207,7 @@
 	INIT_UNUSED_NS_EL2	\
 	PLATFORM_REPORT_CTX_MEM_USE \
 	EARLY_CONSOLE \
+	PRESERVE_DSU_PMU_REGS \
 )))
 
 # Numeric_Flags
@@ -1405,6 +1406,7 @@
 	INIT_UNUSED_NS_EL2	\
 	PLATFORM_REPORT_CTX_MEM_USE \
 	EARLY_CONSOLE \
+	PRESERVE_DSU_PMU_REGS \
 )))
 
 ifeq (${PLATFORM_REPORT_CTX_MEM_USE}, 1)
diff --git a/docs/getting_started/build-options.rst b/docs/getting_started/build-options.rst
index 2892dc6..6ab95f9 100644
--- a/docs/getting_started/build-options.rst
+++ b/docs/getting_started/build-options.rst
@@ -768,6 +768,11 @@
    ``EL3_PAYLOAD_BASE``. If both are defined, ``EL3_PAYLOAD_BASE`` has priority
    over ``PRELOADED_BL33_BASE``.
 
+-  ``PRESERVE_DSU_PMU_REGS``: This options when enabled allows the platform to
+   save/restore the DynamIQ Shared Unit's(DSU) Performance Monitoring Unit(PMU)
+   registers when the cluster goes through a power cycle. This is disabled by
+   default and platforms that require this feature have to enable them.
+
 -  ``PROGRAMMABLE_RESET_ADDRESS``: This option indicates whether the reset
    vector address can be programmed or is fixed on the platform. It can take
    either 0 (fixed) or 1 (programmable). Default is 0. If the platform has a
diff --git a/drivers/arm/css/dsu/dsu.c b/drivers/arm/css/dsu/dsu.c
new file mode 100644
index 0000000..f0e8df1
--- /dev/null
+++ b/drivers/arm/css/dsu/dsu.c
@@ -0,0 +1,135 @@
+/*
+ * Copyright (c) 2024, Arm Limited. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#include <assert.h>
+
+#include <arch.h>
+#include <arch_helpers.h>
+#include <common/bl_common.h>
+#include <common/debug.h>
+#include <drivers/arm/css/dsu.h>
+
+#include <plat/arm/common/plat_arm.h>
+#include <plat/common/platform.h>
+
+/*
+ * Context structure that saves the state of DSU PMU registers
+ */
+cluster_pmu_state_t cluster_pmu_context[PLAT_ARM_CLUSTER_COUNT];
+
+/****************************************************************************
+ * This function, save_dsu_pmu_state, is designed to save the
+ * current state of the Performance Monitoring Unit (PMU) for a cluster.
+ *
+ * The function performs the following operations:
+ * 1. Saves the current values of several PMU registers
+ *    (CLUSTERPMCR_EL1, CLUSTERPMCNTENSET_EL1, CLUSTERPMCCNTR_EL1,
+ *    CLUSTERPMOVSSET_EL1, and CLUSTERPMSELR_EL1) into the cluster_pmu_state
+ *    structure.
+ *
+ * 2. Disables the PMU event counting by
+ *    clearing the E bit in the clusterpmcr_el1 register.
+ *
+ * 3. Iterates over the available PMU counters as
+ *    determined by the read_cluster_eventctr_num() function.
+ *    For each counter, it:
+ *    a. Selects the counter by writing its index to CLUSTERPMSELR_EL1.
+ *    b. Reads the current counter value (event count) and
+ *       the event type being counted from CLUSTERPMXEVCNTR_EL1 and
+ *       CLUSTERPMXEVTYPER_EL1 registers, respectively.
+ *
+ * This function is useful for preserving the DynamIQ Shared Unit's (DSU)
+ * PMU registers over a power cycle.
+ ***************************************************************************/
+
+void save_dsu_pmu_state(cluster_pmu_state_t *cluster_pmu_state)
+{
+	unsigned int idx = 0U;
+	unsigned int cluster_eventctr_num = read_cluster_eventctr_num();
+
+	assert(cluster_pmu_state != 0);
+
+	save_pmu_reg(cluster_pmu_state, clusterpmcr);
+
+	write_clusterpmcr(cluster_pmu_state->clusterpmcr &
+			~(CLUSTERPMCR_E_BIT));
+
+	save_pmu_reg(cluster_pmu_state, clusterpmcntenset);
+
+	save_pmu_reg(cluster_pmu_state, clusterpmccntr);
+
+	save_pmu_reg(cluster_pmu_state, clusterpmovsset);
+
+	save_pmu_reg(cluster_pmu_state, clusterpmselr);
+
+	for (idx = 0U ; idx < cluster_eventctr_num ; idx++) {
+		write_clusterpmselr(idx);
+		cluster_pmu_state->counter_val[idx] = read_clusterpmxevcntr();
+		cluster_pmu_state->counter_type[idx] = read_clusterpmxevtyper();
+	}
+}
+
+void cluster_off_dsu_pmu_context_save(void)
+{
+	unsigned int cluster_pos;
+
+	cluster_pos = (unsigned int) plat_cluster_id_by_mpidr(read_mpidr_el1());
+
+	save_dsu_pmu_state(&cluster_pmu_context[cluster_pos]);
+}
+
+/*****************************************************************************
+ * This function, restore_dsu_pmu_state, restores the state of the
+ * Performance Monitoring Unit (PMU) from a previously saved state.
+ *
+ * The function performs the following operations:
+ * 1. Restores the CLUSTERPMCR_EL1 register with the
+ *    saved value from the cluster_pmu_state structure.
+ * 2. Iterates over the available PMU counters as determined
+ *    by the read_cluster_eventctr_num() function. For each counter, it:
+ *    a. Selects the counter by writing its index to CLUSTERPMSELR_EL1.
+ *    b. Restores the counter value (event count) and the event type to
+ *       CLUSTERPMXEVCNTR_EL1 and CLUSTERPMXEVTYPER_EL1 registers, respectively
+ * 3. Restores several other PMU registers (CLUSTERPMSELR_EL1,
+ *    CLUSTERPMOVSCLR_EL1, CLUSTERPMOVSSET_EL1, CLUSTERPMCCNTR_EL1,
+ *    and CLUSTERPMCNTENSET_EL1) with their saved values.
+ *
+ *****************************************************************************/
+void restore_dsu_pmu_state(cluster_pmu_state_t *cluster_pmu_state)
+{
+	unsigned int idx = 0U;
+	unsigned int cluster_eventctr_num = read_cluster_eventctr_num();
+
+	assert(cluster_pmu_state != 0);
+
+	for (idx = 0U ; idx < cluster_eventctr_num ; idx++) {
+		write_clusterpmselr(idx);
+		write_clusterpmxevcntr(cluster_pmu_state->counter_val[idx]);
+		write_clusterpmxevtyper(cluster_pmu_state->counter_type[idx]);
+	}
+
+	restore_pmu_reg(cluster_pmu_state, clusterpmselr);
+
+	write_clusterpmovsclr(~(uint32_t)cluster_pmu_state->clusterpmovsset);
+
+	restore_pmu_reg(cluster_pmu_state, clusterpmovsset);
+
+	restore_pmu_reg(cluster_pmu_state, clusterpmccntr);
+
+	restore_pmu_reg(cluster_pmu_state, clusterpmcntenset);
+
+	write_clusterpmcr(cluster_pmu_state->clusterpmcr);
+}
+
+void cluster_on_dsu_pmu_context_restore(void)
+{
+	unsigned int cluster_pos;
+
+	cluster_pos = (unsigned int) plat_cluster_id_by_mpidr(read_mpidr_el1());
+
+	restore_dsu_pmu_state(&cluster_pmu_context[cluster_pos]);
+}
+
diff --git a/include/arch/aarch32/arch.h b/include/arch/aarch32/arch.h
index 73b2d76..d32ead4 100644
--- a/include/arch/aarch32/arch.h
+++ b/include/arch/aarch32/arch.h
@@ -795,7 +795,21 @@
 /*******************************************************************************
  * Definitions for DynamicIQ Shared Unit registers
  ******************************************************************************/
-#define CLUSTERPWRDN	p15, 0, c15, c3, 6
+#define CLUSTERPWRDN		p15, 0, c15, c3, 6
+#define CLUSTERPMCR		p15, 0, c15, c5, 0
+#define CLUSTERPMCNTENSET	p15, 0, c15, c5, 1
+#define CLUSTERPMCCNTR		p15, 0, c15, c6, 0
+#define CLUSTERPMOVSSET		p15, 0, c15, c5, 3
+#define CLUSTERPMOVSCLR		p15, 0, c15, c5, 4
+#define CLUSTERPMSELR		p15, 0, c15, c5, 5
+#define CLUSTERPMXEVTYPER	p15, 0,	c15, c6, 1
+#define CLUSTERPMXEVCNTR	p15, 0, c15, c6, 2
+
+/* CLUSTERPMCR register definitions */
+#define CLUSTERPMCR_E_BIT	BIT(0)
+#define CLUSTERPMCR_N_SHIFT	U(11)
+#define CLUSTERPMCR_N_MASK	U(0x1f)
+
 
 /* CLUSTERPWRDN register definitions */
 #define DSU_CLUSTER_PWR_OFF	0
diff --git a/include/arch/aarch32/arch_helpers.h b/include/arch/aarch32/arch_helpers.h
index 3244d3b..adc96ae 100644
--- a/include/arch/aarch32/arch_helpers.h
+++ b/include/arch/aarch32/arch_helpers.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2016-2021, ARM Limited and Contributors. All rights reserved.
+ * Copyright (c) 2016-2024, ARM Limited and Contributors. All rights reserved.
  * Portions copyright (c) 2021-2022, ProvenRun S.A.S. All rights reserved.
  *
  * SPDX-License-Identifier: BSD-3-Clause
@@ -354,6 +354,14 @@
  * DynamIQ Shared Unit power management
  */
 DEFINE_COPROCR_RW_FUNCS(clusterpwrdn, CLUSTERPWRDN)
+DEFINE_COPROCR_RW_FUNCS(clusterpmcr, CLUSTERPMCR)
+DEFINE_COPROCR_RW_FUNCS(clusterpmcntenset, CLUSTERPMCNTENSET)
+DEFINE_COPROCR_RW_FUNCS(clusterpmccntr, CLUSTERPMCCNTR)
+DEFINE_COPROCR_RW_FUNCS(clusterpmovsset, CLUSTERPMOVSSET)
+DEFINE_COPROCR_RW_FUNCS(clusterpmovsclr, CLUSTERPMOVSCLR)
+DEFINE_COPROCR_RW_FUNCS(clusterpmselr, CLUSTERPMSELR)
+DEFINE_COPROCR_RW_FUNCS(clusterpmxevcntr, CLUSTERPMXEVCNTR)
+DEFINE_COPROCR_RW_FUNCS(clusterpmxevtyper, CLUSTERPMXEVTYPER)
 
 /*
  * RNDR is AArch64 only, so just provide a placeholder here to make the
diff --git a/include/arch/aarch64/arch.h b/include/arch/aarch64/arch.h
index ea9aa51..4eb54ed 100644
--- a/include/arch/aarch64/arch.h
+++ b/include/arch/aarch64/arch.h
@@ -1482,4 +1482,17 @@
 /* alternative system register encoding for the "sb" speculation barrier */
 #define SYSREG_SB			S0_3_C3_C0_7
 
+#define CLUSTERPMCR_EL1			S3_0_C15_C5_0
+#define CLUSTERPMCNTENSET_EL1		S3_0_C15_C5_1
+#define CLUSTERPMCCNTR_EL1		S3_0_C15_C6_0
+#define CLUSTERPMOVSSET_EL1		S3_0_C15_C5_3
+#define CLUSTERPMOVSCLR_EL1		S3_0_C15_C5_4
+#define CLUSTERPMSELR_EL1		S3_0_C15_C5_5
+#define CLUSTERPMXEVTYPER_EL1		S3_0_C15_C6_1
+#define CLUSTERPMXEVCNTR_EL1		S3_0_C15_C6_2
+
+#define CLUSTERPMCR_E_BIT		BIT(0)
+#define CLUSTERPMCR_N_SHIFT		U(11)
+#define CLUSTERPMCR_N_MASK		U(0x1f)
+
 #endif /* ARCH_H */
diff --git a/include/arch/aarch64/arch_helpers.h b/include/arch/aarch64/arch_helpers.h
index 57dbc06..59adc7c 100644
--- a/include/arch/aarch64/arch_helpers.h
+++ b/include/arch/aarch64/arch_helpers.h
@@ -663,8 +663,16 @@
 DEFINE_RENAME_SYSREG_RW_FUNCS(gcspr_el1, GCSPR_EL1)
 DEFINE_RENAME_SYSREG_RW_FUNCS(gcspr_el0, GCSPR_EL0)
 
-/* DynamIQ Shared Unit power management */
+/* DynamIQ Control registers */
 DEFINE_RENAME_SYSREG_RW_FUNCS(clusterpwrdn_el1, CLUSTERPWRDN_EL1)
+DEFINE_RENAME_SYSREG_RW_FUNCS(clusterpmcr_el1, CLUSTERPMCR_EL1)
+DEFINE_RENAME_SYSREG_RW_FUNCS(clusterpmcntenset_el1, CLUSTERPMCNTENSET_EL1)
+DEFINE_RENAME_SYSREG_RW_FUNCS(clusterpmccntr_el1, CLUSTERPMCCNTR_EL1)
+DEFINE_RENAME_SYSREG_RW_FUNCS(clusterpmovsset_el1, CLUSTERPMOVSSET_EL1)
+DEFINE_RENAME_SYSREG_RW_FUNCS(clusterpmovsclr_el1, CLUSTERPMOVSCLR_EL1)
+DEFINE_RENAME_SYSREG_RW_FUNCS(clusterpmselr_el1, CLUSTERPMSELR_EL1)
+DEFINE_RENAME_SYSREG_RW_FUNCS(clusterpmxevcntr_el1, CLUSTERPMXEVCNTR_EL1)
+DEFINE_RENAME_SYSREG_RW_FUNCS(clusterpmxevtyper_el1, CLUSTERPMXEVTYPER_EL1)
 
 /* CPU Power/Performance Management registers */
 DEFINE_RENAME_SYSREG_RW_FUNCS(cpuppmcr_el3, CPUPPMCR_EL3)
@@ -827,8 +835,32 @@
 #define read_cpacr()		read_cpacr_el1()
 #define write_cpacr(_v)		write_cpacr_el1(_v)
 
-#define read_clusterpwrdn()	read_clusterpwrdn_el1()
-#define write_clusterpwrdn(_v)	write_clusterpwrdn_el1(_v)
+#define read_clusterpwrdn()		read_clusterpwrdn_el1()
+#define write_clusterpwrdn(_v)		write_clusterpwrdn_el1(_v)
+
+#define read_clusterpmcr()		read_clusterpmcr_el1()
+#define write_clusterpmcr(_v)		write_clusterpmcr_el1(_v)
+
+#define read_clusterpmcntenset()	read_clusterpmcntenset_el1()
+#define write_clusterpmcntenset(_v)	write_clusterpmcntenset_el1(_v)
+
+#define read_clusterpmccntr()		read_clusterpmccntr_el1()
+#define write_clusterpmccntr(_v)	write_clusterpmccntr_el1(_v)
+
+#define read_clusterpmovsset()		read_clusterpmovsset_el1()
+#define write_clusterpmovsset(_v)	write_clusterpmovsset_el1(_v)
+
+#define read_clusterpmovsclr()		read_clusterpmovsclr_el1()
+#define write_clusterpmovsclr(_v)	write_clusterpmovsclr_el1(_v)
+
+#define read_clusterpmselr()		read_clusterpmselr_el1()
+#define write_clusterpmselr(_v)		write_clusterpmselr_el1(_v)
+
+#define read_clusterpmxevcntr()		read_clusterpmxevcntr_el1()
+#define write_clusterpmxevcntr(_v)	write_clusterpmxevcntr_el1(_v)
+
+#define read_clusterpmxevtyper()	read_clusterpmxevtyper_el1()
+#define write_clusterpmxevtyper(_v)	write_clusterpmxevtyper_el1(_v)
 
 #if ERRATA_SPECULATIVE_AT
 /*
diff --git a/include/drivers/arm/css/dsu.h b/include/drivers/arm/css/dsu.h
new file mode 100644
index 0000000..4d7822b
--- /dev/null
+++ b/include/drivers/arm/css/dsu.h
@@ -0,0 +1,42 @@
+/*
+ * Copyright (c) 2024, Arm Limited and Contributors. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#ifndef DSU_H
+#define DSU_H
+
+#define PMCR_N_MAX			0x1f
+
+#define save_pmu_reg(state, reg) state->reg = read_##reg()
+
+#define restore_pmu_reg(context, reg) write_##reg(context->reg)
+
+typedef struct cluster_pmu_state{
+	uint64_t clusterpmcr;
+	uint64_t clusterpmcntenset;
+	uint64_t clusterpmccntr;
+	uint64_t clusterpmovsset;
+	uint64_t clusterpmselr;
+	uint64_t clusterpmsevtyper;
+	uint64_t counter_val[PMCR_N_MAX];
+	uint64_t counter_type[PMCR_N_MAX];
+} cluster_pmu_state_t;
+
+static inline unsigned int read_cluster_eventctr_num(void)
+{
+	return ((read_clusterpmcr() >> CLUSTERPMCR_N_SHIFT) &
+			CLUSTERPMCR_N_MASK);
+}
+
+
+void save_dsu_pmu_state(cluster_pmu_state_t *cluster_pmu_context);
+
+void restore_dsu_pmu_state(cluster_pmu_state_t *cluster_pmu_context);
+
+void cluster_on_dsu_pmu_context_restore(void);
+
+void cluster_off_dsu_pmu_context_save(void);
+
+#endif /* DSU_H */
diff --git a/make_helpers/defaults.mk b/make_helpers/defaults.mk
index b9ea810..2685195 100644
--- a/make_helpers/defaults.mk
+++ b/make_helpers/defaults.mk
@@ -395,3 +395,7 @@
 
 # Enable early console
 EARLY_CONSOLE			:= 0
+
+# Allow platforms to save/restore DSU PMU registers over a power cycle.
+# Disabled by default and must be enabled by individual platforms.
+PRESERVE_DSU_PMU_REGS		:= 0