intel/common/block/cpu: Add APIs to get CPU info from lapic ID

Add support to get core, package and thread ID from lapic ID.
Implementation uses CPUID extended topology instruction to derive
bit offsets for core, package and thread info in apic ID.

BUG=b:179113790

Change-Id: If26d34d4250f5a88bdafacdd5d56b8882b69409e
Signed-off-by: Aamir Bohra <aamir.bohra@intel.com>
Reviewed-on: https://review.coreboot.org/c/coreboot/+/50913
Tested-by: build bot (Jenkins) <no-reply@coreboot.org>
Reviewed-by: Angel Pons <th3fanbus@gmail.com>
Reviewed-by: EricR Lai <ericr_lai@compal.corp-partner.google.com>
diff --git a/src/soc/intel/common/block/cpu/cpulib.c b/src/soc/intel/common/block/cpu/cpulib.c
index 854da2e..34ec233 100644
--- a/src/soc/intel/common/block/cpu/cpulib.c
+++ b/src/soc/intel/common/block/cpu/cpulib.c
@@ -1,5 +1,6 @@
 /* SPDX-License-Identifier: GPL-2.0-only */
 
+#include <assert.h>
 #include <acpi/acpigen.h>
 #include <console/console.h>
 #include <cpu/intel/turbo.h>
@@ -12,6 +13,24 @@
 #include <soc/soc_chip.h>
 #include <types.h>
 
+#define CPUID_EXTENDED_CPU_TOPOLOGY 0x0b
+#define CPUID_EXTENDED_CPU_TOPOLOGY_V2 0x1f
+#define LEVEL_TYPE_CORE 2
+#define LEVEL_TYPE_SMT 1
+
+#define CPUID_CPU_TOPOLOGY(x, val) \
+	(((val) >> CPUID_CPU_TOPOLOGY_##x##_SHIFT) & CPUID_CPU_TOPOLOGY_##x##_MASK)
+
+#define CPUID_CPU_TOPOLOGY_LEVEL_TYPE_SHIFT 0x8
+#define CPUID_CPU_TOPOLOGY_LEVEL_TYPE_MASK 0xff
+#define CPUID_CPU_TOPOLOGY_LEVEL(res) CPUID_CPU_TOPOLOGY(LEVEL_TYPE, (res).ecx)
+
+#define CPUID_CPU_TOPOLOGY_LEVEL_BITS_SHIFT 0x0
+#define CPUID_CPU_TOPOLOGY_LEVEL_BITS_MASK 0x1f
+#define CPUID_CPU_TOPOLOGY_THREAD_BITS(res) CPUID_CPU_TOPOLOGY(LEVEL_BITS, (res).eax)
+#define CPUID_CPU_TOPOLOGY_CORE_BITS(res, threadbits) \
+	((CPUID_CPU_TOPOLOGY(LEVEL_BITS, (res).eax)) - threadbits)
+
 /*
  * Set PERF_CTL MSR (0x199) P_Req with
  * Turbo Ratio which is the Maximum Ratio.
@@ -380,3 +399,53 @@
 
 	return valid_size;
 }
+
+/* Get number of bits for core ID and SMT ID */
+static void get_cpu_core_thread_bits(uint32_t *core_bits, uint32_t *thread_bits)
+{
+	struct cpuid_result cpuid_regs;
+	int level_num, cpu_id_op = 0;
+	const uint32_t cpuid_max_func = cpuid_get_max_func();
+
+	/* Assert if extended CPU topology not supported */
+	assert(cpuid_max_func >= CPUID_EXTENDED_CPU_TOPOLOGY);
+
+	/* Check for extended CPU topology CPUID support */
+	if (cpuid_max_func >= CPUID_EXTENDED_CPU_TOPOLOGY_V2)
+		cpu_id_op = CPUID_EXTENDED_CPU_TOPOLOGY_V2;
+	else if (cpuid_max_func >= CPUID_EXTENDED_CPU_TOPOLOGY)
+		cpu_id_op = CPUID_EXTENDED_CPU_TOPOLOGY;
+
+	*core_bits = level_num = 0;
+	cpuid_regs = cpuid_ext(cpu_id_op, level_num);
+
+	/* Sub-leaf index 0 enumerates SMT level, if not assert */
+	assert(CPUID_CPU_TOPOLOGY_LEVEL(cpuid_regs) == LEVEL_TYPE_SMT);
+
+	*thread_bits = CPUID_CPU_TOPOLOGY_THREAD_BITS(cpuid_regs);
+	do {
+		level_num++;
+		cpuid_regs = cpuid_ext(cpu_id_op, level_num);
+		if (CPUID_CPU_TOPOLOGY_LEVEL(cpuid_regs) == LEVEL_TYPE_CORE) {
+			*core_bits = CPUID_CPU_TOPOLOGY_CORE_BITS(cpuid_regs, *thread_bits);
+			break;
+		}
+	/* Stop when level type is invalid i.e 0 */
+	} while (CPUID_CPU_TOPOLOGY_LEVEL(cpuid_regs));
+}
+
+void get_cpu_topology_from_apicid(uint32_t apicid, uint8_t *package,
+		uint8_t *core, uint8_t *thread)
+{
+
+	uint32_t core_bits, thread_bits;
+
+	get_cpu_core_thread_bits(&core_bits, &thread_bits);
+
+	if (package)
+		*package = apicid >> (thread_bits + core_bits);
+	if (core)
+		*core = (apicid  >> thread_bits) & ((1 << core_bits) - 1);
+	if (thread)
+		*thread = apicid  & ((1 << thread_bits) - 1);
+}
diff --git a/src/soc/intel/common/block/include/intelblocks/cpulib.h b/src/soc/intel/common/block/include/intelblocks/cpulib.h
index 4dfbef4..6fc31b3 100644
--- a/src/soc/intel/common/block/include/intelblocks/cpulib.h
+++ b/src/soc/intel/common/block/include/intelblocks/cpulib.h
@@ -162,4 +162,8 @@
  */
 void enable_pm_timer_emulation(void);
 
+/* Derive core, package and thread information from lapic ID */
+void get_cpu_topology_from_apicid(uint32_t apicid, uint8_t *package,
+		uint8_t *core, uint8_t *thread);
+
 #endif	/* SOC_INTEL_COMMON_BLOCK_CPULIB_H */