Merge "feat(dsu): support power control and autonomous powerdown config" into integration
diff --git a/bl31/bl31.mk b/bl31/bl31.mk
index 8abafcd..d267b11 100644
--- a/bl31/bl31.mk
+++ b/bl31/bl31.mk
@@ -164,6 +164,10 @@
 				${RMMD_SOURCES}
 endif
 
+ifeq (${USE_DSU_DRIVER},1)
+BL31_SOURCES		+=	drivers/arm/dsu/dsu.c
+endif
+
 ifeq ($(FEATURE_DETECTION),1)
 BL31_SOURCES		+=	common/feat_detect.c
 endif
@@ -205,11 +209,13 @@
 	CRASH_REPORTING \
 	EL3_EXCEPTION_HANDLING \
 	SDEI_SUPPORT \
+	USE_DSU_DRIVER \
 )))
 
 $(eval $(call add_defines,\
     $(sort \
-        CRASH_REPORTING \
-        EL3_EXCEPTION_HANDLING \
-        SDEI_SUPPORT \
+	CRASH_REPORTING \
+	EL3_EXCEPTION_HANDLING \
+	SDEI_SUPPORT \
+	USE_DSU_DRIVER \
 )))
diff --git a/bl31/bl31_main.c b/bl31/bl31_main.c
index 47b3ac8..ba26366 100644
--- a/bl31/bl31_main.c
+++ b/bl31/bl31_main.c
@@ -17,6 +17,7 @@
 #include <common/debug.h>
 #include <common/feat_detect.h>
 #include <common/runtime_svc.h>
+#include <drivers/arm/dsu.h>
 #include <drivers/arm/gic.h>
 #include <drivers/console.h>
 #include <lib/bootmarker_capture.h>
@@ -146,6 +147,10 @@
 	/* Perform platform setup in BL31 */
 	bl31_platform_setup();
 
+#if USE_DSU_DRIVER
+	dsu_driver_init(&plat_dsu_data);
+#endif
+
 #if USE_GIC_DRIVER
 	/*
 	 * Initialize the GIC driver as well as per-cpu and global interfaces.
diff --git a/docs/design/firmware-design.rst b/docs/design/firmware-design.rst
index 1306ecb..669bd96 100644
--- a/docs/design/firmware-design.rst
+++ b/docs/design/firmware-design.rst
@@ -1056,6 +1056,18 @@
 integrating PSCI library with AArch32 EL3 Runtime Software can be found
 at :ref:`PSCI Library Integration guide for Armv8-A AArch32 systems`.
 
+DSU driver
+----------
+
+Platforms that include a DSU (DynamIQ Shared Unit) can define
+the ``USE_DSU_DRIVER`` build flag to enable the DSU driver.
+This driver is responsible for configuring DSU-related powerdown
+and power feature settings using ``dsu_driver_init()`` and for
+preserving the context of DSU PMU system registers.
+
+To support the DSU driver, platforms must define the ``plat_dsu_data``
+structure.
+
 .. _firmware_design_sel1_spd:
 
 Secure-EL1 Payloads and Dispatchers
diff --git a/docs/getting_started/build-options.rst b/docs/getting_started/build-options.rst
index 9d2df33..f99840b 100644
--- a/docs/getting_started/build-options.rst
+++ b/docs/getting_started/build-options.rst
@@ -1119,6 +1119,11 @@
    (Coherent memory region is included) or 0 (Coherent memory region is
    excluded). Default is 1.
 
+-  ``USE_DSU_DRIVER``: This flag enables DSU (DynamIQ Shared Unit) driver.
+   The DSU driver allows save/restore of DSU PMU registers through
+   ``PRESERVE_DSU_PMU_REGS`` build option and allows platforms to
+   configure powerdown and power settings of DSU.
+
 -  ``ARM_IO_IN_DTB``: This flag determines whether to use IO based on the
    firmware configuration framework. This will move the io_policies into a
    configuration device tree, instead of static structure in the code base.
diff --git a/drivers/arm/css/dsu/dsu.c b/drivers/arm/dsu/dsu.c
similarity index 83%
rename from drivers/arm/css/dsu/dsu.c
rename to drivers/arm/dsu/dsu.c
index f0e8df1..dea89c5 100644
--- a/drivers/arm/css/dsu/dsu.c
+++ b/drivers/arm/dsu/dsu.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2024, Arm Limited. All rights reserved.
+ * Copyright (c) 2025, Arm Limited. All rights reserved.
  *
  * SPDX-License-Identifier: BSD-3-Clause
  */
@@ -10,7 +10,9 @@
 #include <arch_helpers.h>
 #include <common/bl_common.h>
 #include <common/debug.h>
-#include <drivers/arm/css/dsu.h>
+#include <drivers/arm/dsu.h>
+#include <dsu_def.h>
+#include <lib/utils_def.h>
 
 #include <plat/arm/common/plat_arm.h>
 #include <plat/common/platform.h>
@@ -133,3 +135,30 @@
 	restore_dsu_pmu_state(&cluster_pmu_context[cluster_pos]);
 }
 
+void dsu_driver_init(const dsu_driver_data_t *plat_driver_data)
+{
+	uint64_t actlr_el3 = read_actlr_el3();
+	uint64_t pwrctlr = read_clusterpwrctlr_el1();
+	uint64_t pwrdn = read_clusterpwrdn_el1();
+
+	/* enable access to power control registers. */
+	actlr_el3 |= ACTLR_EL3_PWREN_BIT;
+	write_actlr_el3(actlr_el3);
+
+	UPDATE_REG_FIELD(CLUSTERPWRCTLR_FUNCRET, pwrctlr,
+			plat_driver_data->clusterpwrctlr_funcret);
+
+	UPDATE_REG_FIELD(CLUSTERPWRCTLR_CACHEPWR, pwrctlr,
+			plat_driver_data->clusterpwrctlr_cachepwr);
+
+	write_clusterpwrctlr_el1(pwrctlr);
+
+	UPDATE_REG_FIELD(CLUSTERPWRDN_PWRDN, pwrdn,
+			plat_driver_data->clusterpwrdwn_pwrdn);
+
+	UPDATE_REG_FIELD(CLUSTERPWRDN_MEMRET, pwrdn,
+			plat_driver_data->clusterpwrdwn_memret);
+
+	write_clusterpwrdn_el1(pwrdn);
+}
+
diff --git a/include/arch/aarch64/arch.h b/include/arch/aarch64/arch.h
index b607945..5506cb1 100644
--- a/include/arch/aarch64/arch.h
+++ b/include/arch/aarch64/arch.h
@@ -1576,7 +1576,7 @@
 /*******************************************************************************
  * Definitions for DynamicIQ Shared Unit registers
  ******************************************************************************/
-#define CLUSTERPWRDN_EL1	S3_0_c15_c3_6
+#define CLUSTERPWRDN_EL1	S3_0_C15_C3_6
 
 /*******************************************************************************
  * FEAT_FPMR - Floating point Mode Register
diff --git a/include/drivers/arm/css/dsu.h b/include/drivers/arm/css/dsu.h
deleted file mode 100644
index 4d7822b..0000000
--- a/include/drivers/arm/css/dsu.h
+++ /dev/null
@@ -1,42 +0,0 @@
-/*
- * 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/include/drivers/arm/dsu.h b/include/drivers/arm/dsu.h
new file mode 100644
index 0000000..492babd
--- /dev/null
+++ b/include/drivers/arm/dsu.h
@@ -0,0 +1,63 @@
+/*
+ * Copyright (c) 2025, Arm Limited and Contributors. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#ifndef DSU_H
+#define DSU_H
+
+#if defined(__aarch64__)
+#include <dsu_def.h>
+
+/*
+ * Power Control Registers enable bit of Auxilary Control register.
+ * ACTLR_EL3_PWREN_BIT definition is same among cores like Cortex-X925,
+ * Cortex-X4, Cortex-A520, Cortex-A725 that are used in a cluster
+ * with DSU.
+ */
+#define ACTLR_EL3_PWREN_BIT		BIT(7)
+
+#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;
+
+typedef struct dsu_driver_data {
+	uint8_t clusterpwrdwn_pwrdn;
+	uint8_t clusterpwrdwn_memret;
+	uint8_t clusterpwrctlr_cachepwr;
+	uint8_t clusterpwrctlr_funcret;
+} dsu_driver_data_t;
+
+extern const dsu_driver_data_t plat_dsu_data;
+
+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);
+
+void dsu_driver_init(const dsu_driver_data_t *data);
+#endif
+#endif /* DSU_H */
diff --git a/include/lib/cpus/aarch64/dsu_def.h b/include/lib/cpus/aarch64/dsu_def.h
index 3f6dbfe..089ea52 100644
--- a/include/lib/cpus/aarch64/dsu_def.h
+++ b/include/lib/cpus/aarch64/dsu_def.h
@@ -32,13 +32,27 @@
  * DSU Cluster Auxiliary Control registers definitions
  ********************************************************************/
 #define CLUSTERACTLR_EL1	S3_0_C15_C3_3
-#define CLUSTERPWRCTLR_EL1	S3_0_C15_C3_5
+
+/* CLUSTERPWRCTLR_EL1 register definitions */
+#define CLUSTERPWRCTLR_EL1		S3_0_C15_C3_5
+#define CLUSTERPWRCTLR_FUNCRET_WIDTH	U(3)
+#define CLUSTERPWRCTLR_FUNCRET_SHIFT	U(0)
+#define CLUSTERPWRCTLR_FUNCRET_RESET	U(0)
+#define CLUSTERPWRCTLR_CACHEPWR_WIDTH	U(4)
+#define CLUSTERPWRCTLR_CACHEPWR_SHIFT	U(4)
+#define CLUSTERPWRCTLR_CACHEPWR_RESET	U(7)
 
 #define CLUSTERACTLR_EL1_ASSERT_CBUSY			(ULL(1) << 8)
 #define CLUSTERACTLR_EL1_DISABLE_CLOCK_GATING		(ULL(1) << 15)
 #define CLUSTERACTLR_EL1_DISABLE_SCLK_GATING		(ULL(3) << 15)
 #define CLUSTERACTLR_EL1_IGNORE_INTERCONNECT_CBUSY	(ULL(3) << 20)
 
+/* CLUSTERPWRDN_EL1 register definitions */
+#define CLUSTERPWRDN_PWRDN_WIDTH	U(1)
+#define CLUSTERPWRDN_PWRDN_SHIFT	U(0)
+#define CLUSTERPWRDN_MEMRET_WIDTH	U(1)
+#define CLUSTERPWRDN_MEMRET_SHIFT	U(1)
+
 /********************************************************************
  * Masks applied for DSU errata workarounds
  ********************************************************************/
diff --git a/include/lib/utils_def.h b/include/lib/utils_def.h
index 68e464a..7dcc5ce 100644
--- a/include/lib/utils_def.h
+++ b/include/lib/utils_def.h
@@ -86,6 +86,12 @@
 #define EXTRACT(regfield, reg) \
 	(((reg) & MASK(regfield)) >> (regfield##_SHIFT))
 
+#define UPDATE_REG_FIELD(regfield, reg, val) \
+	do { \
+		(reg) &= ~(MASK(regfield)); \
+		(reg) |= ((uint64_t)(val) << (regfield##_SHIFT)); \
+	} while (0)
+
 /*
  * This variant of div_round_up can be used in macro definition but should not
  * be used in C code as the `div` parameter is evaluated twice.
diff --git a/make_helpers/defaults.mk b/make_helpers/defaults.mk
index 2657387..b9df27e 100644
--- a/make_helpers/defaults.mk
+++ b/make_helpers/defaults.mk
@@ -438,3 +438,6 @@
 
 # Live firmware activation support
 LFA_SUPPORT			:= 0
+
+# Enable support for arm DSU driver.
+USE_DSU_DRIVER			:= 0
diff --git a/plat/arm/board/tc/platform.mk b/plat/arm/board/tc/platform.mk
index b29f0d6..9dd867d 100644
--- a/plat/arm/board/tc/platform.mk
+++ b/plat/arm/board/tc/platform.mk
@@ -162,7 +162,7 @@
 				${TC_BASE}/tc_topology.c	\
 				lib/fconf/fconf.c			\
 				lib/fconf/fconf_dyn_cfg_getter.c	\
-				drivers/arm/css/dsu/dsu.c			\
+				drivers/arm/dsu/dsu.c			\
 				drivers/cfi/v2m/v2m_flash.c		\
 				lib/utils/mem_region.c			\
 				plat/arm/common/arm_nor_psci_mem_protect.c	\
diff --git a/plat/arm/css/common/css_pm.c b/plat/arm/css/common/css_pm.c
index 18882d3..80da3d9 100644
--- a/plat/arm/css/common/css_pm.c
+++ b/plat/arm/css/common/css_pm.c
@@ -12,7 +12,7 @@
 #include <bl31/interrupt_mgmt.h>
 #include <common/debug.h>
 #include <drivers/arm/css/css_scp.h>
-#include <drivers/arm/css/dsu.h>
+#include <drivers/arm/dsu.h>
 #include <lib/cassert.h>
 #include <plat/arm/common/plat_arm.h>