diff --git a/Makefile b/Makefile index 57e27ca2c..0ecd79409 100644 --- a/Makefile +++ b/Makefile @@ -1207,6 +1207,7 @@ $(eval $(call assert_booleans,\ INIT_UNUSED_NS_EL2 \ PLATFORM_REPORT_CTX_MEM_USE \ EARLY_CONSOLE \ + PRESERVE_DSU_PMU_REGS \ ))) # Numeric_Flags @@ -1405,6 +1406,7 @@ $(eval $(call add_defines,\ 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 2892dc6e6..6ab95f9b6 100644 --- a/docs/getting_started/build-options.rst +++ b/docs/getting_started/build-options.rst @@ -768,6 +768,11 @@ Common build options ``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 000000000..f0e8df126 --- /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 + +#include +#include +#include +#include +#include + +#include +#include + +/* + * 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 73b2d7679..d32ead4a2 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 3244d3b2a..adc96ae0f 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 @@ DEFINE_DCOP_PARAM_FUNC(cvac, DCCMVAC) * 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 ea9aa5105..4eb54ed84 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 57dbc0672..59adc7c90 100644 --- a/include/arch/aarch64/arch_helpers.h +++ b/include/arch/aarch64/arch_helpers.h @@ -663,8 +663,16 @@ DEFINE_RENAME_SYSREG_RW_FUNCS(gcscre0_el1, GCSCRE0_EL1) 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 @@ void gpt_tlbi_by_pa_ll(uint64_t pa, size_t size); #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 000000000..4d7822b5b --- /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 b9ea81081..2685195ff 100644 --- a/make_helpers/defaults.mk +++ b/make_helpers/defaults.mk @@ -395,3 +395,7 @@ PLATFORM_REPORT_CTX_MEM_USE := 0 # 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