diff --git a/manual/tunables.texi b/manual/tunables.texi
index e1d9fbae9cebe961fe45203251a761c3323a9917..17ff92501d0e0fcbb47512589289f933f1e82119 100644
--- a/manual/tunables.texi
+++ b/manual/tunables.texi
@@ -469,16 +469,6 @@ indicates that the process may use device memory.
 This tunable is specific to powerpc, powerpc64 and powerpc64le.
 @end deftp
 
-@deftp Tunable glibc.cpu.name
-The @code{glibc.cpu.name=xxx} tunable allows the user to tell @theglibc{} to
-assume that the CPU is @code{xxx} where xxx may have one of these values:
-@code{generic}, @code{thunderxt88}, @code{thunderx2t99},
-@code{thunderx2t99p1}, @code{ares}, @code{emag}, @code{kunpeng},
-@code{a64fx}.
-
-This tunable is specific to aarch64.
-@end deftp
-
 @deftp Tunable glibc.cpu.x86_data_cache_size
 The @code{glibc.cpu.x86_data_cache_size} tunable allows the user to set
 data cache size in bytes for use in memory and string routines.
diff --git a/sysdeps/aarch64/dl-tunables.list b/sysdeps/aarch64/dl-tunables.list
index a2ccba0b293a2d728ec6986f7ddd5b9793c1de17..c876f3fe0d4891be425f0c022e4adcaa11f86af4 100644
--- a/sysdeps/aarch64/dl-tunables.list
+++ b/sysdeps/aarch64/dl-tunables.list
@@ -18,7 +18,7 @@
 
 glibc {
   cpu {
-    name {
+    hwcaps {
       type: STRING
     }
     aarch64_bti {
diff --git a/sysdeps/unix/sysv/linux/aarch64/Makefile b/sysdeps/unix/sysv/linux/aarch64/Makefile
index d1fcb48aa250dffd8b7e5b41f5b290512dc797e2..baefac15b5985414a5044766048cab5e84e4636f 100644
--- a/sysdeps/unix/sysv/linux/aarch64/Makefile
+++ b/sysdeps/unix/sysv/linux/aarch64/Makefile
@@ -17,7 +17,7 @@ tests += \
 tests-static += \
   tst-cpu-tunable-static-pie \
   # tests-static
-tst-cpu-tunable-static-pie-TUNABLES = glibc.cpu.name=generic
+tst-cpu-tunable-static-pie-TUNABLES = glibc.cpu.hwcaps=-midr,-sve,-mops
 endif
 endif
 
diff --git a/sysdeps/unix/sysv/linux/aarch64/cpu-features.c b/sysdeps/unix/sysv/linux/aarch64/cpu-features.c
index 9a87332ac49c0272930200ece5276eb5a4a2fae4..6a13638368673870b9a9e35bbb43108612561ae9 100644
--- a/sysdeps/unix/sysv/linux/aarch64/cpu-features.c
+++ b/sysdeps/unix/sysv/linux/aarch64/cpu-features.c
@@ -33,56 +33,39 @@
    to see when pointer have been correctly tagged.  */
 #define MTE_ALLOWED_TAGS (0xfffe << PR_MTE_TAG_SHIFT)
 
-static const char cpu_list_name[] = {
-  "kunpeng920\0"
-  "kunpeng950\0"
-  "a64fx\0"
-  "generic\0",
-};
-
-static const uint64_t cpu_list_midr[] = {
-  0x481FD010,
-  0x480FD060,
-  0x460F0010,
-  0x0,
-};
-
-static uint64_t
-get_midr_from_mcpu (const struct tunable_str_t *mcpu)
+static void
+TUNABLE_CALLBACK (set_hwcaps) (tunable_val_t *val)
 {
-  const char *name = cpu_list_name;
-  size_t offset = 0;
-  for (int i = 0; i < array_length (cpu_list_midr); i++)
+  struct cpu_features *cpu_features = &GLRO(dl_aarch64_cpu_features);
+  struct tunable_str_comma_state_t cs;
+  tunable_str_comma_init (&cs, val);
+
+  struct tunable_str_comma_t n;
+  while (tunable_str_comma_next (&cs, &n))
     {
-      size_t len = strlen (name);
-      if (tunable_strcmp (mcpu, cpu_list_name + offset, len))
-	return cpu_list_midr[i];
-      offset += len;
+      /* Support disabling of features to select more generic ifuncs.  */
+      if (!n.disable)
+	continue;
+      if (tunable_str_comma_strcmp_cte (&n, "midr"))
+	cpu_features->midr_el1 = 0;
+      else if (tunable_str_comma_strcmp_cte (&n, "zva"))
+	cpu_features->zva_size = 0;
+      else if (tunable_str_comma_strcmp_cte (&n, "sve"))
+	cpu_features->sve = false;
+      else if (tunable_str_comma_strcmp_cte (&n, "sve2"))
+	cpu_features->sve2 = false;
+      else if (tunable_str_comma_strcmp_cte (&n, "mops"))
+	cpu_features->mops = false;
     }
-  return UINT64_MAX;
 }
 
 static inline void
 init_cpu_features (struct cpu_features *cpu_features)
 {
-  register uint64_t midr = UINT64_MAX;
-
-  /* Get the tunable override.  */
-  const struct tunable_str_t *mcpu = TUNABLE_GET (glibc, cpu, name,
-						  struct tunable_str_t *,
-						  NULL);
-  if (mcpu != NULL)
-    midr = get_midr_from_mcpu (mcpu);
-
-  /* If there was no useful tunable override, query the MIDR if the kernel
-     allows it.  */
-  if (midr == UINT64_MAX)
-    {
-      if (GLRO (dl_hwcap) & HWCAP_CPUID)
-	asm volatile ("mrs %0, midr_el1" : "=r"(midr));
-      else
-	midr = 0;
-    }
+  uint64_t midr = 0;
+
+  if (GLRO (dl_hwcap) & HWCAP_CPUID)
+    asm volatile ("mrs %0, midr_el1" : "=r"(midr));
 
   cpu_features->midr_el1 = midr;
 
@@ -108,4 +91,8 @@ init_cpu_features (struct cpu_features *cpu_features)
   if (GLRO (dl_hwcap) & HWCAP_GCS)
     /* GCS status may be updated later by binary compatibility checks.  */
     GL (dl_aarch64_gcs) = TUNABLE_GET (glibc, cpu, aarch64_gcs, uint64_t, 0);
+
+  /* Allow override by glibc.cpu.hwcaps tunable after setting features.  */
+  TUNABLE_GET (glibc, cpu, hwcaps, tunable_val_t *,
+	       TUNABLE_CALLBACK (set_hwcaps));
 }
