#include <linux/cpu.h>
#include <asm/apic.h>
#include <asm/memtype.h>
#include <asm/msr.h>
#include <asm/processor.h>
#include "cpu.h"
static bool parse_8000_0008(struct topo_scan *tscan)
{
struct {
u32 cpu_nthreads : 8,
: 4,
apicid_coreid_len : 4,
perf_tsc_len : 2,
: 14;
} ecx;
unsigned int sft;
if (tscan->c->extended_cpuid_level < 0x80000008)
return false;
cpuid_leaf_reg(0x80000008, CPUID_ECX, &ecx);
sft = ecx.apicid_coreid_len;
if (!sft)
sft = get_count_order(ecx.cpu_nthreads + 1);
topology_update_dom(tscan, TOPO_SMT_DOMAIN, 0, 1);
topology_set_dom(tscan, TOPO_CORE_DOMAIN, sft, ecx.cpu_nthreads + 1);
return true;
}
static void store_node(struct topo_scan *tscan, u16 nr_nodes, u16 node_id)
{
tscan->amd_nodes_per_pkg = nr_nodes;
tscan->amd_node_id = node_id;
}
static bool parse_8000_001e(struct topo_scan *tscan, bool has_topoext)
{
struct {
u32 ext_apic_id : 32;
u32 core_id : 8,
core_nthreads : 8,
: 16;
u32 node_id : 8,
nnodes_per_socket : 3,
: 21;
u32 : 32;
} leaf;
if (!boot_cpu_has(X86_FEATURE_TOPOEXT))
return false;
cpuid_leaf(0x8000001e, &leaf);
if (!has_topoext) {
tscan->c->topo.initial_apicid = leaf.ext_apic_id;
if (tscan->c->x86 >= 0x17) {
unsigned int nthreads = leaf.core_nthreads + 1;
topology_update_dom(tscan, TOPO_SMT_DOMAIN,
get_count_order(nthreads), nthreads);
}
}
store_node(tscan, leaf.nnodes_per_socket + 1, leaf.node_id);
if (tscan->c->x86_vendor == X86_VENDOR_AMD) {
if (tscan->c->x86 == 0x15)
tscan->c->topo.cu_id = leaf.core_id;
cacheinfo_amd_init_llc_id(tscan->c, leaf.node_id);
} else {
if (!boot_cpu_has(X86_FEATURE_HYPERVISOR) && tscan->c->x86_model <= 0x3) {
topology_set_dom(tscan, TOPO_CORE_DOMAIN, 6,
tscan->dom_ncpus[TOPO_CORE_DOMAIN]);
}
cacheinfo_hygon_init_llc_id(tscan->c);
}
return true;
}
static void parse_fam10h_node_id(struct topo_scan *tscan)
{
union {
struct {
u64 node_id : 3,
nodes_per_pkg : 3,
unused : 58;
};
u64 msr;
} nid;
if (!boot_cpu_has(X86_FEATURE_NODEID_MSR))
return;
rdmsrq(MSR_FAM10H_NODE_ID, nid.msr);
store_node(tscan, nid.nodes_per_pkg + 1, nid.node_id);
tscan->c->topo.llc_id = nid.node_id;
}
static void legacy_set_llc(struct topo_scan *tscan)
{
unsigned int apicid = tscan->c->topo.initial_apicid;
if (tscan->c->topo.llc_id == BAD_APICID)
tscan->c->topo.llc_id = apicid >> tscan->dom_shifts[TOPO_CORE_DOMAIN];
}
static void topoext_fixup(struct topo_scan *tscan)
{
struct cpuinfo_x86 *c = tscan->c;
u64 msrval;
if (cpu_has(c, X86_FEATURE_TOPOEXT) || c->x86_vendor != X86_VENDOR_AMD ||
c->x86 != 0x15 || c->x86_model < 0x10 || c->x86_model > 0x6f)
return;
if (msr_set_bit(0xc0011005, 54) <= 0)
return;
rdmsrq(0xc0011005, msrval);
if (msrval & BIT_64(54)) {
set_cpu_cap(c, X86_FEATURE_TOPOEXT);
pr_info_once(FW_INFO "CPU: Re-enabling disabled Topology Extensions Support.\n");
}
}
static void parse_topology_amd(struct topo_scan *tscan)
{
bool has_topoext = false;
if (cpu_feature_enabled(X86_FEATURE_TOPOEXT))
has_topoext = cpu_parse_topology_ext(tscan);
if (cpu_feature_enabled(X86_FEATURE_AMD_HTR_CORES))
tscan->c->topo.cpu_type = cpuid_ebx(0x80000026);
if (!has_topoext && !parse_8000_0008(tscan))
return;
if (parse_8000_001e(tscan, has_topoext))
return;
parse_fam10h_node_id(tscan);
}
void cpu_parse_topology_amd(struct topo_scan *tscan)
{
tscan->amd_nodes_per_pkg = 1;
topoext_fixup(tscan);
parse_topology_amd(tscan);
legacy_set_llc(tscan);
if (tscan->amd_nodes_per_pkg > 1)
set_cpu_cap(tscan->c, X86_FEATURE_AMD_DCM);
}
void cpu_topology_fixup_amd(struct topo_scan *tscan)
{
struct cpuinfo_x86 *c = tscan->c;
if (tscan->c->x86 < 0x17 && tscan->amd_nodes_per_pkg > 1)
c->topo.core_id %= tscan->dom_ncpus[TOPO_CORE_DOMAIN] / tscan->amd_nodes_per_pkg;
}