diff --git a/tools/perf/arch/arm64/util/header.c b/tools/perf/arch/arm64/util/header.c
index 80b9f6287fe2..637ad21721c2 100644
--- a/tools/perf/arch/arm64/util/header.c
+++ b/tools/perf/arch/arm64/util/header.c
@@ -1,3 +1,6 @@
+#include <linux/kernel.h>
+#include <linux/bits.h>
+#include <linux/bitfield.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <perf/cpumap.h>
@@ -10,14 +13,12 @@
 
 #define MIDR "/regs/identification/midr_el1"
 #define MIDR_SIZE 19
-#define MIDR_REVISION_MASK      0xf
-#define MIDR_VARIANT_SHIFT      20
-#define MIDR_VARIANT_MASK       (0xf << MIDR_VARIANT_SHIFT)
+#define MIDR_REVISION_MASK      GENMASK(3, 0)
+#define MIDR_VARIANT_MASK	GENMASK(23, 20)
 
 static int _get_cpuid(char *buf, size_t sz, struct perf_cpu_map *cpus)
 {
 	const char *sysfs = sysfs__mountpoint();
-	u64 midr = 0;
 	int cpu;
 
 	if (!sysfs || sz < MIDR_SIZE)
@@ -44,21 +45,11 @@ static int _get_cpuid(char *buf, size_t sz, struct perf_cpu_map *cpus)
 		}
 		fclose(file);
 
-		/* Ignore/clear Variant[23:20] and
-		 * Revision[3:0] of MIDR
-		 */
-		midr = strtoul(buf, NULL, 16);
-		midr &= (~(MIDR_VARIANT_MASK | MIDR_REVISION_MASK));
-		scnprintf(buf, MIDR_SIZE, "0x%016lx", midr);
 		/* got midr break loop */
 		break;
 	}
 
 	perf_cpu_map__put(cpus);
-
-	if (!midr)
-		return EINVAL;
-
 	return 0;
 }
 
@@ -99,3 +90,45 @@ char *get_cpuid_str(struct perf_pmu *pmu)
 
 	return buf;
 }
+
+
+int strcmp_cpuid_str(const char *mapcpuid, const char *idstr)
+{
+	u64 map_id = strtoull(mapcpuid, NULL, 16);
+	char map_id_variant = FIELD_GET(MIDR_VARIANT_MASK, map_id);
+	char map_id_revision = FIELD_GET(MIDR_REVISION_MASK, map_id);
+	u64 id = strtoull(idstr, NULL, 16);
+	char id_variant = FIELD_GET(MIDR_VARIANT_MASK, id);
+	char id_revision = FIELD_GET(MIDR_REVISION_MASK, id);
+	u64 id_fields = ~(MIDR_VARIANT_MASK | MIDR_REVISION_MASK);
+
+	/* Compare without version first */
+	if ((map_id & id_fields) != (id & id_fields))
+		return 1;
+
+	/*
+	 * ID matches, now compare version.
+	 *
+	 * Arm revisions (like r0p0) are compared here like two digit semver
+	 * values eg. 1.3 < 2.0 < 2.1 < 2.2. The events json file with the
+	 * highest matching version is used.
+	 *
+	 *  r = high value = 'Variant' field in MIDR
+	 *  p = low value  = 'Revision' field in MIDR
+	 *
+	 * Because the Variant field is further to the left, iterating through a
+	 * reverse sorted mapfile.csv gives the correct comparison behavior.
+	 * This relies on jevents.py sorting the list in print_mapping_table().
+	 */
+	if (id_variant > map_id_variant)
+		return 0;
+
+	if (id_variant == map_id_variant && id_revision >= map_id_revision)
+		return 0;
+
+	/*
+	 * variant is less than mapfile variant or variants are the same but
+	 * the revision doesn't match. Return no match.
+	 */
+	return 1;
+}
diff --git a/tools/perf/pmu-events/jevents.py b/tools/perf/pmu-events/jevents.py
index 12e80bb7939b..c6a848f8d93a 100755
--- a/tools/perf/pmu-events/jevents.py
+++ b/tools/perf/pmu-events/jevents.py
@@ -620,28 +620,34 @@ const struct pmu_events_map pmu_events_map[] = {
 },
 """)
     else:
+      def int_or_string_key(row):
+        try:
+          return int(row[0], 0)
+        except:
+          return row[0]
       with open(f'{_args.starting_dir}/{arch}/mapfile.csv') as csvfile:
-        table = csv.reader(csvfile)
-        first = True
-        for row in table:
-          # Skip the first row or any row beginning with #.
-          if not first and len(row) > 0 and not row[0].startswith('#'):
-            event_tblname = file_name_to_table_name('pmu_events_', [], row[2].replace('/', '_'))
-            if event_tblname in _event_tables:
-              event_size = f'ARRAY_SIZE({event_tblname})'
-            else:
-              event_tblname = 'NULL'
-              event_size = '0'
-            metric_tblname = file_name_to_table_name('pmu_metrics_', [], row[2].replace('/', '_'))
-            if metric_tblname in _metric_tables:
-              metric_size = f'ARRAY_SIZE({metric_tblname})'
-            else:
-              metric_tblname = 'NULL'
-              metric_size = '0'
-            if event_size == '0' and metric_size == '0':
-              continue
-            cpuid = row[0].replace('\\', '\\\\')
-            _args.output_file.write(f"""{{
+        table = [row for row in csv.reader(csvfile)]
+      # Strip the first row or any row beginning with #.
+      table = [row for row in table[1:] if len(row) > 0 and not row[0].startswith('#')]
+      # Sort on CPUID field for predictable >= version comparisons later on
+      table = sorted(table, key=int_or_string_key, reverse=True)
+      for row in table:
+        event_tblname = file_name_to_table_name('pmu_events_', [], row[2].replace('/', '_'))
+        if event_tblname in _event_tables:
+          event_size = f'ARRAY_SIZE({event_tblname})'
+        else:
+          event_tblname = 'NULL'
+          event_size = '0'
+        metric_tblname = file_name_to_table_name('pmu_metrics_', [], row[2].replace('/', '_'))
+        if metric_tblname in _metric_tables:
+          metric_size = f'ARRAY_SIZE({metric_tblname})'
+        else:
+          metric_tblname = 'NULL'
+          metric_size = '0'
+        if event_size == '0' and metric_size == '0':
+          continue
+        cpuid = row[0].replace('\\', '\\\\')
+        _args.output_file.write(f"""{{
 \t.arch = "{arch}",
 \t.cpuid = "{cpuid}",
 \t.event_table = {{
@@ -654,7 +660,6 @@ const struct pmu_events_map pmu_events_map[] = {
 \t}}
 }},
 """)
-          first = False
 
   _args.output_file.write("""{
 \t.arch = 0,
diff --git a/tools/perf/tests/pmu-events.c b/tools/perf/tests/pmu-events.c
index 64383fc34ef1..e730d4792bbe 100644
--- a/tools/perf/tests/pmu-events.c
+++ b/tools/perf/tests/pmu-events.c
@@ -14,6 +14,7 @@
 #include "util/evlist.h"
 #include "util/expr.h"
 #include "util/hashmap.h"
+#include "util/header.h"
 #include "util/parse-events.h"
 #include "metricgroup.h"
 #include "stat.h"
@@ -1027,6 +1028,38 @@ static int test__parsing_threshold(struct test_suite *test __maybe_unused,
 	return pmu_for_each_sys_metric(test__parsing_threshold_callback, NULL);
 }
 
+static int test__cpuid_match(struct test_suite *test __maybe_unused,
+				       int subtest __maybe_unused)
+{
+#ifdef __aarch64__
+	/* midr with no leading zeros matches */
+	if (strcmp_cpuid_str("0x410fd0c0", "0x00000000410fd0c0"))
+		return -1;
+	/* Upper case matches */
+	if (strcmp_cpuid_str("0x410fd0c0", "0x00000000410FD0C0"))
+		return -1;
+	/* r0p0 = r0p0 matches */
+	if (strcmp_cpuid_str("0x00000000410fd480", "0x00000000410fd480"))
+		return -1;
+	/* r0p1 > r0p0 matches */
+	if (strcmp_cpuid_str("0x00000000410fd480", "0x00000000410fd481"))
+		return -1;
+	/* r1p0 > r0p0 matches*/
+	if (strcmp_cpuid_str("0x00000000410fd480", "0x00000000411fd480"))
+		return -1;
+	/* r0p0 < r0p1 doesn't match */
+	if (!strcmp_cpuid_str("0x00000000410fd481", "0x00000000410fd480"))
+		return -1;
+	/* r0p0 < r1p0 doesn't match */
+	if (!strcmp_cpuid_str("0x00000000411fd480", "0x00000000410fd480"))
+		return -1;
+	/* Different CPU doesn't match */
+	if (!strcmp_cpuid_str("0x00000000410fd4c0", "0x00000000430f0af0"))
+		return -1;
+#endif
+	return 0;
+}
+
 static struct test_case pmu_events_tests[] = {
 	TEST_CASE("PMU event table sanity", pmu_event_table),
 	TEST_CASE("PMU event map aliases", aliases),
@@ -1034,6 +1067,7 @@ static struct test_case pmu_events_tests[] = {
 			 "some metrics failed"),
 	TEST_CASE("Parsing of PMU event table metrics with fake PMUs", parsing_fake),
 	TEST_CASE("Parsing of metric thresholds with fake PMUs", parsing_threshold),
+	TEST_CASE("CPUID matching", cpuid_match),
 	{ .name = NULL, }
 };
 
