feat(dsu): support power control and autonomous powerdown config
This patch allows platforms to enable certain DSU settings
to ensure memory retention and control over
cache power requests. We also move the driver out of css
into drivers/arm. Platforms can configure the
CLUSTERPWRCTLR and CLUSTERPWRDN registers [1] to improve
power efficiency.
These registers enable finer-grained control of
DSU power state transitions, including
powerdown and retention.
IMP_CLUSTERPWRCTLR_EL1 provides:
- Functional retention: Allows configuration of the
duration of inactivity before the DSU uses
CLUSTERPACTIVE to request functional retention.
- Cache power request: These bits are output on
CLUSTERPACTIVE[19:16] to indicate to the power controller
which cache portions must remain powered.
IMP_CLUSTERPWRDN_EL1 includes:
- Powerdown: Triggers full cluster powerdown, including
control logic.
- Memory retention: Requests memory retention mode,
keeping L3 RAM contents while powering off
the rest of the DSU.
The DSU-120 TRM [2] provides the full field definitions,
which are used as references in the `dsu_driver_data` structure.
References:
[1]: https://developer.arm.com/documentation/100453/latest/
[2]: https://developer.arm.com/documentation/102547/0201/?lang=en
Signed-off-by: Arvind Ram Prakash <arvind.ramprakash@arm.com>
Change-Id: I2eba808b8f2a27797782a333c65dd092b03208fe
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 a9f89fc..d8336b7 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>