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/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);
+	}
+}