Merge tag 'intel-pinctrl-v6.9-1' of git://git.kernel.org/pub/scm/linux/kernel/git...
[linux-block.git] / arch / x86 / kernel / cpu / topology_amd.c
1 // SPDX-License-Identifier: GPL-2.0
2 #include <linux/cpu.h>
3
4 #include <asm/apic.h>
5 #include <asm/memtype.h>
6 #include <asm/processor.h>
7
8 #include "cpu.h"
9
10 static bool parse_8000_0008(struct topo_scan *tscan)
11 {
12         struct {
13                 // ecx
14                 u32     cpu_nthreads            :  8, // Number of physical threads - 1
15                                                 :  4, // Reserved
16                         apicid_coreid_len       :  4, // Number of thread core ID bits (shift) in APIC ID
17                         perf_tsc_len            :  2, // Performance time-stamp counter size
18                                                 : 14; // Reserved
19         } ecx;
20         unsigned int sft;
21
22         if (tscan->c->extended_cpuid_level < 0x80000008)
23                 return false;
24
25         cpuid_leaf_reg(0x80000008, CPUID_ECX, &ecx);
26
27         /* If the thread bits are 0, then get the shift value from ecx.cpu_nthreads */
28         sft = ecx.apicid_coreid_len;
29         if (!sft)
30                 sft = get_count_order(ecx.cpu_nthreads + 1);
31
32         topology_set_dom(tscan, TOPO_SMT_DOMAIN, sft, ecx.cpu_nthreads + 1);
33         return true;
34 }
35
36 static void store_node(struct topo_scan *tscan, unsigned int nr_nodes, u16 node_id)
37 {
38         /*
39          * Starting with Fam 17h the DIE domain could probably be used to
40          * retrieve the node info on AMD/HYGON. Analysis of CPUID dumps
41          * suggests it's the topmost bit(s) of the CPU cores area, but
42          * that's guess work and neither enumerated nor documented.
43          *
44          * Up to Fam 16h this does not work at all and the legacy node ID
45          * has to be used.
46          */
47         tscan->amd_nodes_per_pkg = nr_nodes;
48         tscan->amd_node_id = node_id;
49 }
50
51 static bool parse_8000_001e(struct topo_scan *tscan, bool has_0xb)
52 {
53         struct {
54                 // eax
55                 u32     ext_apic_id             : 32; // Extended APIC ID
56                 // ebx
57                 u32     core_id                 :  8, // Unique per-socket logical core unit ID
58                         core_nthreads           :  8, // #Threads per core (zero-based)
59                                                 : 16; // Reserved
60                 // ecx
61                 u32     node_id                 :  8, // Node (die) ID of invoking logical CPU
62                         nnodes_per_socket       :  3, // #nodes in invoking logical CPU's package/socket
63                                                 : 21; // Reserved
64                 // edx
65                 u32                             : 32; // Reserved
66         } leaf;
67
68         if (!boot_cpu_has(X86_FEATURE_TOPOEXT))
69                 return false;
70
71         cpuid_leaf(0x8000001e, &leaf);
72
73         tscan->c->topo.initial_apicid = leaf.ext_apic_id;
74
75         /*
76          * If leaf 0xb is available, then SMT shift is set already. If not
77          * take it from ecx.threads_per_core and use topo_update_dom() -
78          * topology_set_dom() would propagate and overwrite the already
79          * propagated CORE level.
80          */
81         if (!has_0xb) {
82                 unsigned int nthreads = leaf.core_nthreads + 1;
83
84                 topology_update_dom(tscan, TOPO_SMT_DOMAIN, get_count_order(nthreads), nthreads);
85         }
86
87         store_node(tscan, leaf.nnodes_per_socket + 1, leaf.node_id);
88
89         if (tscan->c->x86_vendor == X86_VENDOR_AMD) {
90                 if (tscan->c->x86 == 0x15)
91                         tscan->c->topo.cu_id = leaf.core_id;
92
93                 cacheinfo_amd_init_llc_id(tscan->c, leaf.node_id);
94         } else {
95                 /*
96                  * Package ID is ApicId[6..] on certain Hygon CPUs. See
97                  * commit e0ceeae708ce for explanation. The topology info
98                  * is screwed up: The package shift is always 6 and the
99                  * node ID is bit [4:5].
100                  */
101                 if (!boot_cpu_has(X86_FEATURE_HYPERVISOR) && tscan->c->x86_model <= 0x3) {
102                         topology_set_dom(tscan, TOPO_CORE_DOMAIN, 6,
103                                          tscan->dom_ncpus[TOPO_CORE_DOMAIN]);
104                 }
105                 cacheinfo_hygon_init_llc_id(tscan->c);
106         }
107         return true;
108 }
109
110 static bool parse_fam10h_node_id(struct topo_scan *tscan)
111 {
112         struct {
113                 union {
114                         u64     node_id         :  3,
115                                 nodes_per_pkg   :  3,
116                                 unused          : 58;
117                         u64     msr;
118                 };
119         } nid;
120
121         if (!boot_cpu_has(X86_FEATURE_NODEID_MSR))
122                 return false;
123
124         rdmsrl(MSR_FAM10H_NODE_ID, nid.msr);
125         store_node(tscan, nid.nodes_per_pkg + 1, nid.node_id);
126         tscan->c->topo.llc_id = nid.node_id;
127         return true;
128 }
129
130 static void legacy_set_llc(struct topo_scan *tscan)
131 {
132         unsigned int apicid = tscan->c->topo.initial_apicid;
133
134         /* parse_8000_0008() set everything up except llc_id */
135         tscan->c->topo.llc_id = apicid >> tscan->dom_shifts[TOPO_CORE_DOMAIN];
136 }
137
138 static void parse_topology_amd(struct topo_scan *tscan)
139 {
140         bool has_0xb = false;
141
142         /*
143          * If the extended topology leaf 0x8000_001e is available
144          * try to get SMT and CORE shift from leaf 0xb first, then
145          * try to get the CORE shift from leaf 0x8000_0008.
146          */
147         if (cpu_feature_enabled(X86_FEATURE_TOPOEXT))
148                 has_0xb = cpu_parse_topology_ext(tscan);
149
150         if (!has_0xb && !parse_8000_0008(tscan))
151                 return;
152
153         /* Prefer leaf 0x8000001e if available */
154         if (parse_8000_001e(tscan, has_0xb))
155                 return;
156
157         /* Try the NODEID MSR */
158         if (parse_fam10h_node_id(tscan))
159                 return;
160
161         legacy_set_llc(tscan);
162 }
163
164 void cpu_parse_topology_amd(struct topo_scan *tscan)
165 {
166         tscan->amd_nodes_per_pkg = 1;
167         parse_topology_amd(tscan);
168
169         if (tscan->amd_nodes_per_pkg > 1)
170                 set_cpu_cap(tscan->c, X86_FEATURE_AMD_DCM);
171 }
172
173 void cpu_topology_fixup_amd(struct topo_scan *tscan)
174 {
175         struct cpuinfo_x86 *c = tscan->c;
176
177         /*
178          * Adjust the core_id relative to the node when there is more than
179          * one node.
180          */
181         if (tscan->c->x86 < 0x17 && tscan->amd_nodes_per_pkg > 1)
182                 c->topo.core_id %= tscan->dom_ncpus[TOPO_CORE_DOMAIN] / tscan->amd_nodes_per_pkg;
183 }