From 462fe87b491b5277a4e5425e6e04f161f0ebb18a Mon Sep 17 00:00:00 2001
From: Jan Engelhardt <jengelh@inai.de>
Date: Sat, 24 Oct 2026 00:22:24 +0200
Subject: [PATCH] sparc: populate sysfs cache topology information on sun4u
 TRYOUT

Reads out all the qemu OpenBIOS properties, in particular
the properties: ecache-nlines, ecache-associativity that were
ignored previously.
---
 arch/sparc/kernel/prom_common.c | 149 +++++++++++++++++++++++++++++++-
 1 file changed, 148 insertions(+), 1 deletion(-)

diff --git a/arch/sparc/kernel/prom_common.c b/arch/sparc/kernel/prom_common.c
index 34f3b2650547..0a5a49fdfc98 100644
--- a/arch/sparc/kernel/prom_common.c
+++ b/arch/sparc/kernel/prom_common.c
@@ -114,13 +114,159 @@ int of_find_in_proplist(const char *list, const char *match, int len)
 }
 EXPORT_SYMBOL(of_find_in_proplist);
 
+static int of_init_cache_level(unsigned int cpuid)
+{
+	static const char *const l1_obp_names[] = {
+		"dcache-size", "icache-size", "l1-dcache-size", "l1-icache-size"
+	};
+	static const char *const l2_obp_names[] = {
+		"ecache-size", "l2-cache-size",
+	};
+	struct device_node *devn = of_find_node_by_cpuid(cpuid);
+	struct cpu_cacheinfo *ci;
+
+	if (devn == NULL)
+		return 0;
+
+	ci = get_cpu_cacheinfo(cpuid);
+	for (unsigned int i = 0; i < ARRAY_SIZE(l1_obp_names); ++i) {
+		if (!of_property_present(devn, l1_obp_names[i]))
+			continue;
+		ci->num_levels = 1;
+		++ci->num_leaves;
+	}
+
+	/*
+	 * devn might be a core. Try going upwards up to two levels to find
+	 * a potential node for the socket, identified by "portid" /
+	 * "upa-portid" presence, to find the properties for the L2.
+	 */
+	for (unsigned int j = 0; j < 2; ++j) {
+		for (unsigned int i = 0; i < ARRAY_SIZE(l2_obp_names); ++i) {
+			if (of_property_present(devn, l2_obp_names[i])) {
+				ci->num_levels = 2;
+				++ci->num_leaves;
+				return 0;
+			}
+		}
+
+		if (!of_property_present(devn, "cpuid"))
+			break;
+		devn = devn->parent;
+		if (devn == NULL || of_property_present(devn, "upa-portid") ||
+		    of_property_present(devn, "portid"))
+			break;
+	}
+	return 0;
+}
+
+static void of_set_leaf(struct cacheinfo *leaf, struct device_node *devn,
+    unsigned int level, unsigned int type, unsigned int size,
+    const char *ls, const char *ns, const char *as)
+{
+	leaf->fw_token = (void *)(uintptr_t)devn->phandle;
+	leaf->level = level;
+	leaf->type = type;
+	leaf->size = size;
+	if (ls != NULL)
+		leaf->coherency_line_size = of_getintprop_default(devn, ls, 0);
+	if (ns != NULL)
+		leaf->number_of_sets = of_getintprop_default(devn, ns, 0);
+	if (as != NULL)
+		leaf->ways_of_associativity = of_getintprop_default(devn, as, 0);
+}
+
+static int of_populate_cache_leaves(unsigned int cpuid)
+{
+	struct device_node *devn = of_find_node_by_cpuid(cpuid);
+	struct cpu_cacheinfo *ci;
+	unsigned int npop = 0;
+	int size;
+
+	if (devn == NULL)
+		return 0;
+
+	ci = get_cpu_cacheinfo(cpuid);
+	size = of_getintprop_default(devn, "l1-icache-size", -1);
+	if (size != -1) {
+		/* Property typical for Spitfire systems */
+		if (npop >= ci->num_leaves)
+			return -ELOOP;
+		of_set_leaf(&ci->info_list[npop++], devn, 1, CACHE_TYPE_INST,
+			    size, "l1-icache-line-size", NULL, NULL);
+	}
+
+	size = of_getintprop_default(devn, "icache-size", -1);
+	if (size != -1) {
+		/* Property used in e.g. OpenBIOS */
+		if (npop >= ci->num_leaves)
+			return -ELOOP;
+		of_set_leaf(&ci->info_list[npop++], devn, 1, CACHE_TYPE_INST,
+			    size, "icache-line-size", "icache-nlines",
+			    "icache-associativity");
+	}
+
+	size = of_getintprop_default(devn, "l1-dcache-size", -1);
+	if (size != -1) {
+		if (npop >= ci->num_leaves)
+			return -ELOOP;
+		of_set_leaf(&ci->info_list[npop++], devn, 1, CACHE_TYPE_DATA,
+			    size, "l1-dcache-line-size", NULL, NULL);
+	}
+
+	size = of_getintprop_default(devn, "dcache-size", -1);
+	if (size != -1) {
+		if (npop >= ci->num_leaves)
+			return -ELOOP;
+		of_set_leaf(&ci->info_list[npop++], devn, 1, CACHE_TYPE_DATA,
+			    size, "dcache-line-size", "dcache-nlines",
+			    "dcache-associativity");
+	}
+
+	for (unsigned int i = 0; i < 2; ++i) {
+		size = of_getintprop_default(devn, "l2-cache-size", -1);
+		if (size != -1) {
+			if (npop >= ci->num_leaves)
+				return -ELOOP;
+			of_set_leaf(&ci->info_list[npop++], devn, 2,
+				    CACHE_TYPE_UNIFIED, size,
+				    "l2-cache-line-size", NULL, NULL);
+			break;
+		}
+
+		size = of_getintprop_default(devn, "ecache-size", -1);
+		if (size != -1) {
+			if (npop >= ci->num_leaves)
+				return -ELOOP;
+			of_set_leaf(&ci->info_list[npop++], devn, 2,
+				    CACHE_TYPE_UNIFIED, size,
+				    "ecache-line-size", "ecache-nlines",
+				    "ecache-associativity");
+			break;
+		}
+
+		/* Upwards-walking logic follows */
+		if (!of_property_present(devn, "cpuid"))
+			break;
+		devn = devn->parent;
+		if (devn == NULL || of_property_present(devn, "upa-portid") ||
+		    of_property_present(devn, "portid"))
+			break;
+	}
+	return 0;
+}
+
 int init_cache_level(unsigned int cpuid)
 {
 #ifdef CONFIG_SPARC64
 	if (tlb_type == hypervisor)
 		return mdesc_cache_walk(cpuid, false);
+		/*
+		 * MDESC systems do not have the OF properties for caches,
+		 * so no point looking for them there.
+		 */
 #endif
-	return -ENOENT;
+	return of_init_cache_level(cpuid);
 }
 
 int populate_cache_leaves(unsigned int cpuid)
@@ -138,6 +284,7 @@ int populate_cache_leaves(unsigned int cpuid)
 		break;
 #endif
 	default:
+		ret = of_populate_cache_leaves(cpuid);
 		break;
 	}
 	if (ret == -ELOOP)
-- 
2.55.0

