feat(qemu-sbsa): handle the information of CPU topology

We add the support for adding cpus/topology to device tree in sbsaQemu
platform, and we can get this information via SMC calls:

- counting the number of sockets
- counting the number of clusters in one socket
- counting the number of cores in one cluster
- counting the number of threads in one core

Signed-off-by: Xiong Yining <xiongyining1480@phytium.com.cn>
Change-Id: I0059a5c7bb7055aba1aa5ec5bfd0ec78801874f8
diff --git a/plat/qemu/qemu_sbsa/sbsa_sip_svc.c b/plat/qemu/qemu_sbsa/sbsa_sip_svc.c
index 535f0eb..4085d47 100644
--- a/plat/qemu/qemu_sbsa/sbsa_sip_svc.c
+++ b/plat/qemu/qemu_sbsa/sbsa_sip_svc.c
@@ -30,6 +30,7 @@
 #define SIP_SVC_GET_GIC_ITS SIP_FUNCTION_ID(101)
 #define SIP_SVC_GET_CPU_COUNT SIP_FUNCTION_ID(200)
 #define SIP_SVC_GET_CPU_NODE SIP_FUNCTION_ID(201)
+#define SIP_SVC_GET_CPU_TOPOLOGY SIP_FUNCTION_ID(202)
 #define SIP_SVC_GET_MEMORY_NODE_COUNT SIP_FUNCTION_ID(300)
 #define SIP_SVC_GET_MEMORY_NODE SIP_FUNCTION_ID(301)
 
@@ -46,10 +47,24 @@
 	uint64_t addr_size;
 } memory_data;
 
+/*
+ * sockets: the number of sockets on sbsa-ref platform.
+ * clusters: the number of clusters in one socket.
+ * cores: the number of cores in one cluster.
+ * threads: the number of threads in one core.
+ */
+typedef struct {
+	uint32_t sockets;
+	uint32_t clusters;
+	uint32_t cores;
+	uint32_t threads;
+} cpu_topology;
+
 static struct {
 	uint32_t num_cpus;
 	uint32_t num_memnodes;
 	cpu_data cpu[PLATFORM_CORE_COUNT];
+	cpu_topology cpu_topo;
 	memory_data memory[PLAT_MAX_MEM_NODES];
 } dynamic_platform_info;
 
@@ -71,6 +86,57 @@
  * cope.
  */
 
+static void read_cpu_topology_from_dt(void *dtb)
+{
+	int node;
+	uint32_t sockets = 0;
+	uint32_t clusters = 0;
+	uint32_t cores = 0;
+	uint32_t threads = 0;
+
+	/*
+	 * QEMU gives us this DeviceTree node when we config:
+	 * -smp 16,sockets=2,clusters=2,cores=2,threads=2
+	 *
+	 * topology {
+	 *	threads = <0x02>;
+	 *	cores = <0x02>;
+	 *	clusters = <0x02>;
+	 *	sockets = <0x02>;
+	 * };
+	 */
+
+	node = fdt_path_offset(dtb, "/cpus/topology");
+	if (node > 0) {
+		if (fdt_getprop(dtb, node, "sockets", NULL))  {
+			fdt_read_uint32(dtb, node, "sockets", &sockets);
+		}
+
+		if (fdt_getprop(dtb, node, "clusters", NULL))  {
+			fdt_read_uint32(dtb, node, "clusters", &clusters);
+		}
+
+		if (fdt_getprop(dtb, node, "cores", NULL))  {
+			fdt_read_uint32(dtb, node, "cores", &cores);
+		}
+
+		if (fdt_getprop(dtb, node, "threads", NULL))  {
+			fdt_read_uint32(dtb, node, "threads", &threads);
+		}
+	}
+
+	dynamic_platform_info.cpu_topo.sockets = sockets;
+	dynamic_platform_info.cpu_topo.clusters = clusters;
+	dynamic_platform_info.cpu_topo.cores = cores;
+	dynamic_platform_info.cpu_topo.threads = threads;
+
+	INFO("Cpu topology: sockets: %d, clusters: %d, cores: %d, threads: %d\n",
+		dynamic_platform_info.cpu_topo.sockets,
+		dynamic_platform_info.cpu_topo.clusters,
+		dynamic_platform_info.cpu_topo.cores,
+		dynamic_platform_info.cpu_topo.threads);
+}
+
 void read_cpuinfo_from_dt(void *dtb)
 {
 	int node;
@@ -135,6 +201,8 @@
 
 	dynamic_platform_info.num_cpus = cpu;
 	INFO("Found %d cpus\n", dynamic_platform_info.num_cpus);
+
+	read_cpu_topology_from_dt(dtb);
 }
 
 void read_meminfo_from_dt(void *dtb)
@@ -354,6 +422,18 @@
 			SMC_RET1(handle, SMC_ARCH_CALL_INVAL_PARAM);
 		}
 
+	case SIP_SVC_GET_CPU_TOPOLOGY:
+		if (dynamic_platform_info.cpu_topo.cores > 0) {
+			SMC_RET5(handle, NULL,
+			dynamic_platform_info.cpu_topo.sockets,
+			dynamic_platform_info.cpu_topo.clusters,
+			dynamic_platform_info.cpu_topo.cores,
+			dynamic_platform_info.cpu_topo.threads);
+		} else {
+			/* we do not know topology so we report SMC as unknown */
+			SMC_RET1(handle, SMC_UNK);
+		}
+
 	case SIP_SVC_GET_MEMORY_NODE_COUNT:
 		SMC_RET2(handle, NULL, dynamic_platform_info.num_memnodes);