feat(mpmm): add support for MPMM
MPMM - the Maximum Power Mitigation Mechanism - is an optional
microarchitectural feature present on some Armv9-A cores, introduced
with the Cortex-X2, Cortex-A710 and Cortex-A510 cores.
MPMM allows the SoC firmware to detect and limit high activity events
to assist in SoC processor power domain dynamic power budgeting and
limit the triggering of whole-rail (i.e. clock chopping) responses to
overcurrent conditions.
This feature is enabled via the `ENABLE_MPMM` build option.
Configuration can be done via FCONF by enabling `ENABLE_MPMM_FCONF`, or
by via the plaform-implemented `plat_mpmm_topology` function.
Change-Id: I77da82808ad4744ece8263f0bf215c5a091c3167
Signed-off-by: Chris Kay <chris.kay@arm.com>
diff --git a/lib/extensions/amu/aarch64/amu.c b/lib/extensions/amu/aarch64/amu.c
index f712520..35efd21 100644
--- a/lib/extensions/amu/aarch64/amu.c
+++ b/lib/extensions/amu/aarch64/amu.c
@@ -23,6 +23,10 @@
# include <lib/fconf/fconf_amu_getter.h>
#endif
+#if ENABLE_MPMM
+# include <lib/mpmm/mpmm.h>
+#endif
+
struct amu_ctx {
uint64_t group0_cnts[AMU_GROUP0_MAX_COUNTERS];
#if ENABLE_AMU_AUXILIARY_COUNTERS
@@ -273,26 +277,31 @@
/* Initialize FEAT_AMUv1p1 features if present. */
if (id_aa64pfr0_el1_amu >= ID_AA64PFR0_AMU_V1P1) {
- return;
- }
-
- if (el2_unused) {
- /* Make sure virtual offsets are disabled if EL2 not used. */
- write_hcr_el2_amvoffen(0U);
- }
+ if (el2_unused) {
+ /*
+ * Make sure virtual offsets are disabled if EL2 not
+ * used.
+ */
+ write_hcr_el2_amvoffen(0U);
+ }
#if AMU_RESTRICT_COUNTERS
- /*
- * FEAT_AMUv1p1 adds a register field to restrict access to group 1
- * counters at all but the highest implemented EL. This is controlled
- * with the AMU_RESTRICT_COUNTERS compile time flag, when set, system
- * register reads at lower ELs return zero. Reads from the memory
- * mapped view are unaffected.
- */
- VERBOSE("AMU group 1 counter access restricted.\n");
- write_amcr_el0_cg1rz(1U);
+ /*
+ * FEAT_AMUv1p1 adds a register field to restrict access to
+ * group 1 counters at all but the highest implemented EL. This
+ * is controlled with the `AMU_RESTRICT_COUNTERS` compile time
+ * flag, when set, system register reads at lower ELs return
+ * zero. Reads from the memory mapped view are unaffected.
+ */
+ VERBOSE("AMU group 1 counter access restricted.\n");
+ write_amcr_el0_cg1rz(1U);
#else
- write_amcr_el0_cg1rz(0U);
+ write_amcr_el0_cg1rz(0U);
+#endif
+ }
+
+#if ENABLE_MPMM
+ mpmm_enable();
#endif
}
@@ -616,6 +625,10 @@
}
#endif
+#if ENABLE_MPMM
+ mpmm_enable();
+#endif
+
return (void *)0;
}
diff --git a/lib/fconf/fconf.mk b/lib/fconf/fconf.mk
index 18bcb35..fb88910 100644
--- a/lib/fconf/fconf.mk
+++ b/lib/fconf/fconf.mk
@@ -14,3 +14,6 @@
FCONF_AMU_SOURCES := lib/fconf/fconf_amu_getter.c
FCONF_AMU_SOURCES += ${FDT_WRAPPERS_SOURCES}
+
+FCONF_MPMM_SOURCES := lib/fconf/fconf_mpmm_getter.c
+FCONF_MPMM_SOURCES += ${FDT_WRAPPERS_SOURCES}
diff --git a/lib/fconf/fconf_mpmm_getter.c b/lib/fconf/fconf_mpmm_getter.c
new file mode 100644
index 0000000..02a566d
--- /dev/null
+++ b/lib/fconf/fconf_mpmm_getter.c
@@ -0,0 +1,80 @@
+/*
+ * Copyright (c) 2021, Arm Limited. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#include <stddef.h>
+#include <stdint.h>
+
+#include <common/debug.h>
+#include <common/fdt_wrappers.h>
+#include <lib/fconf/fconf.h>
+#include <lib/fconf/fconf_mpmm_getter.h>
+#include <libfdt.h>
+
+#include <plat/common/platform.h>
+
+struct fconf_mpmm_config fconf_mpmm_config;
+static struct mpmm_topology fconf_mpmm_topology;
+
+/*
+ * Within a `cpu` node, determine support for MPMM via the `supports-mpmm`
+ * property.
+ *
+ * Returns `0` on success, or a negative integer representing an error code.
+ */
+static int fconf_populate_mpmm_cpu(const void *fdt, int off, uintptr_t mpidr)
+{
+ int ret, len;
+
+ int core_pos;
+ struct mpmm_core *core;
+
+ core_pos = plat_core_pos_by_mpidr(mpidr);
+ if (core_pos < 0) {
+ return -FDT_ERR_BADVALUE;
+ }
+
+ core = &fconf_mpmm_topology.cores[core_pos];
+
+ fdt_getprop(fdt, off, "supports-mpmm", &len);
+ if (len >= 0) {
+ core->supported = true;
+ ret = 0;
+ } else {
+ core->supported = false;
+ ret = len;
+ }
+
+ return ret;
+}
+
+/*
+ * Populates the global `fconf_mpmm_config` structure based on what's described
+ * by the hardware configuration device tree blob.
+ *
+ * The device tree is expected to provide a `supports-mpmm` property for each
+ * `cpu` node, like so:
+ *
+ * cpu@0 {
+ * supports-mpmm;
+ * };
+ *
+ * This property indicates whether the core implements MPMM, as we cannot detect
+ * support for it dynamically.
+ */
+static int fconf_populate_mpmm(uintptr_t config)
+{
+ int ret = fdtw_for_each_cpu(
+ (const void *)config, fconf_populate_mpmm_cpu);
+ if (ret == 0) {
+ fconf_mpmm_config.topology = &fconf_mpmm_topology;
+ } else {
+ ERROR("FCONF: failed to configure MPMM: %d\n", ret);
+ }
+
+ return ret;
+}
+
+FCONF_REGISTER_POPULATOR(HW_CONFIG, mpmm, fconf_populate_mpmm);
diff --git a/lib/mpmm/mpmm.c b/lib/mpmm/mpmm.c
new file mode 100644
index 0000000..a66f2aa
--- /dev/null
+++ b/lib/mpmm/mpmm.c
@@ -0,0 +1,72 @@
+/*
+ * Copyright (c) 2021, Arm Limited. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#include <stdbool.h>
+
+#include <common/debug.h>
+#include <lib/mpmm/mpmm.h>
+
+#include <plat/common/platform.h>
+
+#if ENABLE_MPMM_FCONF
+# include <lib/fconf/fconf.h>
+# include <lib/fconf/fconf_mpmm_getter.h>
+#endif
+
+static uint64_t read_cpuppmcr_el3_mpmmpinctl(void)
+{
+ return (read_cpuppmcr_el3() >> CPUPPMCR_EL3_MPMMPINCTL_SHIFT) &
+ CPUPPMCR_EL3_MPMMPINCTL_MASK;
+}
+
+static void write_cpumpmmcr_el3_mpmm_en(uint64_t mpmm_en)
+{
+ uint64_t value = read_cpumpmmcr_el3();
+
+ value &= ~(CPUMPMMCR_EL3_MPMM_EN_MASK << CPUMPMMCR_EL3_MPMM_EN_SHIFT);
+ value |= (mpmm_en & CPUMPMMCR_EL3_MPMM_EN_MASK) <<
+ CPUMPMMCR_EL3_MPMM_EN_SHIFT;
+
+ write_cpumpmmcr_el3(value);
+}
+
+static bool mpmm_supported(void)
+{
+ bool supported = false;
+ const struct mpmm_topology *topology;
+
+#if ENABLE_MPMM_FCONF
+ topology = FCONF_GET_PROPERTY(mpmm, config, topology);
+#else
+ topology = plat_mpmm_topology();
+#endif /* ENABLE_MPMM_FCONF */
+
+ /*
+ * For the current core firstly try to find out if the platform
+ * configuration has claimed support for MPMM, then make sure that MPMM
+ * is controllable through the system registers.
+ */
+
+ if (topology != NULL) {
+ unsigned int core_pos = plat_my_core_pos();
+
+ supported = topology->cores[core_pos].supported &&
+ (read_cpuppmcr_el3_mpmmpinctl() == 0U);
+ } else {
+ ERROR("MPMM: failed to generate MPMM topology\n");
+ }
+
+ return supported;
+}
+
+void mpmm_enable(void)
+{
+ bool supported = mpmm_supported();
+
+ if (supported) {
+ write_cpumpmmcr_el3_mpmm_en(1U);
+ }
+}
diff --git a/lib/mpmm/mpmm.mk b/lib/mpmm/mpmm.mk
new file mode 100644
index 0000000..826f925
--- /dev/null
+++ b/lib/mpmm/mpmm.mk
@@ -0,0 +1,29 @@
+#
+# Copyright (c) 2021, Arm Limited. All rights reserved.
+#
+# SPDX-License-Identifier: BSD-3-Clause
+#
+
+include lib/extensions/amu/amu.mk
+include lib/fconf/fconf.mk
+
+ifneq (${ENABLE_MPMM},0)
+ ifneq ($(ARCH),aarch64)
+ $(error MPMM support (`ENABLE_MPMM`) can only be enabled in AArch64 images (`ARCH`))
+ endif
+
+ ifeq (${ENABLE_AMU_AUXILIARY_COUNTERS},0) # For MPMM gear AMU counters
+ $(error MPMM support (`ENABLE_MPM`) requires auxiliary AMU counter support (`ENABLE_AMU_AUXILIARY_COUNTERS`))
+ endif
+endif
+
+MPMM_SOURCES := lib/mpmm/mpmm.c
+MPMM_SOURCES += ${AMU_SOURCES}
+
+ifneq (${ENABLE_MPMM_FCONF},0)
+ ifeq (${ENABLE_MPMM},0)
+ $(error MPMM FCONF support (`ENABLE_MPMM_FCONF`) requires MPMM support (`ENABLE_MPMM`))
+ endif
+
+ MPMM_SOURCES += ${FCONF_MPMM_SOURCES}
+endif