1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
|
/*
* This file and its contents are supplied under the terms of the
* Common Development and Distribution License ("CDDL"), version 1.0.
* You may only use this file in accordance with the terms of version
* 1.0 of the CDDL.
*
* A full copy of the text of the CDDL should have accompanied this
* source. A copy of the CDDL is also available via the Internet at
* http://www.illumos.org/license/CDDL.
*/
/*
* Copyright 2024 Oxide Computer Company
*/
#ifndef _SYS_AMDZEN_CCD_H
#define _SYS_AMDZEN_CCD_H
/*
* SMN registers that are CCD-specific (core complex die) but are spread across
* multiple functional units. This could be combined with <sys/amdzen/ccx.h>
* once the duplication between that and <sys/controlregs.h> is dealt with.
*
* Currently this covers two different groups:
*
* SMU::PWR This group describes information about the CCD and, unlike the
* DF CCM entries, this is only present if an actual die is
* present in the package. These registers are always present
* starting in Zen 2.
*
* L3::SCFCTP The Scalable Control Fabric, Clocks, Test, and Power Gating
* registers exist on a per-core basis within each CCD. The first
* point that we can find that this exists started in Zen 3.
*
* L3::SOC This was added starting in Zen 5 and contains several of the
* registers that used to exist in SMU::PWR.
*
* The register naming and fields generally follows the conventions that the DF
* and UMC have laid out. The one divergence right now is that the functional
* blocks only exist starting in a given Zen uarch (e.g. Zen 2). Once we have
* divergences from that introduction point then like the MSRs and others we
* will introduce the generation-specific part of the name.
*/
#include <sys/bitext.h>
#include <sys/debug.h>
#include <sys/types.h>
#include <sys/amdzen/smn.h>
#ifdef __cplusplus
extern "C" {
#endif
/*
* SMU::PWR registers, per-CCD. This functional unit is present starting in Zen
* based platforms. Note that there is another aperture at 0x4008_1000 that is
* documented to alias CCD 0. It's not really clear what if any utility that's
* supposed to have, except that the name given to these aliases contains
* "LOCAL" which implies that perhaps rather than aliasing CCD 0 it instead is
* decoded by the unit on the originating CCD. We don't use that in any case.
*
* Once SoCs started supporting more than 8 CCDs with Zen 4, they added a second
* aperture that starts at 4a08_1000h and uses the same shifts. This leads to
* some awkwardness below. This does make it harder to get at this. We should
* investigate to include the uarch to determine limits at some point in the
* future like we have done with some of our DF registers.
*
* Starting in Zen 5, a chunk of the registers described here are all now in
* the L3::SOC block.
*/
static inline smn_reg_t
amdzen_smupwr_smn_reg(const uint8_t ccdno, const smn_reg_def_t def,
const uint16_t reginst)
{
const uint32_t APERTURE_BASE = 0x30081000;
const uint32_t APERTURE_HI_BASE = 0x4a081000;
const uint32_t APERTURE_MASK = 0xfffff000;
CTASSERT((APERTURE_BASE & ~APERTURE_MASK) == 0);
CTASSERT((APERTURE_HI_BASE & ~APERTURE_MASK) == 0);
const uint32_t ccdno32 = (const uint32_t)ccdno;
const uint32_t reginst32 = (const uint32_t)reginst;
const uint32_t size32 = (def.srd_size == 0) ? 4 :
(const uint32_t)def.srd_size;
const uint32_t stride = (def.srd_stride == 0) ? size32 : def.srd_stride;
const uint32_t nents = (def.srd_nents == 0) ? 1 :
(const uint32_t)def.srd_nents;
ASSERT(size32 == 1 || size32 == 2 || size32 == 4);
ASSERT3S(def.srd_unit, ==, SMN_UNIT_SMUPWR);
ASSERT3U(ccdno32, <, 16);
ASSERT3U(nents, >, reginst32);
uint32_t aperture_base, aperture_off;
if (ccdno >= 8) {
aperture_base = APERTURE_HI_BASE;
aperture_off = (ccdno32 - 8) << 25;
} else {
aperture_base = APERTURE_BASE;
aperture_off = ccdno32 << 25;
}
ASSERT3U(aperture_off, <=, UINT32_MAX - aperture_base);
const uint32_t aperture = aperture_base + aperture_off;
ASSERT0(aperture & ~APERTURE_MASK);
const uint32_t reg = def.srd_reg + reginst32 * stride;
ASSERT0(reg & APERTURE_MASK);
return (SMN_MAKE_REG_SIZED(aperture + reg, size32));
}
/*
* SMU::PWR::CCD_DIE_ID - does what it says.
*/
/*CSTYLED*/
#define D_SMUPWR_CCD_DIE_ID (const smn_reg_def_t){ \
.srd_unit = SMN_UNIT_SMUPWR, \
.srd_reg = 0x00 \
}
#define SMUPWR_CCD_DIE_ID(c) \
amdzen_smupwr_smn_reg(c, D_SMUPWR_CCD_DIE_ID, 0)
#define SMUPWR_CCD_DIE_ID_GET(_r) bitx32(_r, 3, 0)
/*
* SMU::PWR::THREAD_ENABLE - also does what it says; this is a bitmap of each of
* the possible threads. If the bit is set, the thread runs. Clearing bits
* is not allowed. A bit set in here corresponds to a logical thread, though
* the exact layout is a bit tricky in the multi-CCX case. When there are two
* core complexes on the die, all of CCX0's possible threads will come first,
* followed by all of CCX1's. However, while this always describes _logical_
* threads, the spacing is based upon the width of the total possible physical
* cores in the CCX.
*
* For example, consider a Zen 2 system. It has 2 core complexes with 4 cores
* each. Regardless of how many logical cores and threads are enabled in each
* complex, CCX0 logical thread 0 always starts at bit 0 and CCX1 logical thread
* 0 always starts at bit 8. In a system that only has 3/4 cores enabled then
* we'd see this register set to 0x3f3f. In Zen 3 and non-Bergamo Zen 4, this
* is the same width, but there is only one core complex. In Bergamo, this is
* instead 32-bits wide with CCX1 thread 0 starting at bit 16. All of this is
* to say that even though these bits correspond to logical threads, the CCX
* resets the bit position.
*
* However, if we move to a case where SMT is disabled then the CCX starting
* point is still the same, but the there will not be a gap for threads within
* the CCX. So bit 0 will be logical CPU 0 thread 0, bit 1 logical CPU 1 thread
* 0, etc.
*/
/*CSTYLED*/
#define D_SMUPWR_THREAD_EN (const smn_reg_def_t){ \
.srd_unit = SMN_UNIT_SMUPWR, \
.srd_reg = 0x18 \
}
#define SMUPWR_THREAD_EN(c) \
amdzen_smupwr_smn_reg(c, D_SMUPWR_THREAD_EN, 0)
#define SMUPWR_THREAD_EN_GET_T(_r, _t) bitx32(_r, _t, _t)
#define SMUPWR_THREAD_EN_SET_T(_r, _t) bitset32(_r, _t, _t, 1)
/*
* SMU::PWR::THREAD_CONFIGURATION - provides core and CCX counts for the die as
* well as whether SMT is enabled, and a bit to enable or disable SMT *after the
* next warm reset* (which we don't use).
*/
/*CSTYLED*/
#define D_SMUPWR_THREAD_CFG (const smn_reg_def_t){ \
.srd_unit = SMN_UNIT_SMUPWR, \
.srd_reg = 0x1c \
}
#define SMUPWR_THREAD_CFG(c) \
amdzen_smupwr_smn_reg(c, D_SMUPWR_THREAD_CFG, 0)
#define SMUPWR_THREAD_CFG_GET_SMT_MODE(_r) bitx32(_r, 8, 8)
#define SMUPWR_THREAD_CFG_SMT_MODE_1T 1
#define SMUPWR_THREAD_CFG_SMT_MODE_SMT 0
#define SMUPWR_THREAD_CFG_GET_COMPLEX_COUNT(_r) bitx32(_r, 7, 4)
#define SMUPWR_THREAD_CFG_GET_CORE_COUNT(_r) bitx32(_r, 3, 0)
/*
* SMU::PWR::SOFT_DOWNCORE - provides a bitmap of cores that may exist; setting
* each bit disables the corresponding core. Presumably after a warm reset.
*/
/*CSTYLED*/
#define D_SMUPWR_SOFT_DOWNCORE (const smn_reg_def_t){ \
.srd_unit = SMN_UNIT_SMUPWR, \
.srd_reg = 0x20 \
}
#define SMUPWR_SOFT_DOWNCORE(c) \
amdzen_smupwr_smn_reg(c, D_SMUPWR_SOFT_DOWNCORE, 0)
#define SMUPWR_SOFT_DOWNCORE_GET_DISCORE(_r) bitx32(_r, 7, 0)
#define SMUPWR_SOFT_DOWNCORE_GET_DISCORE_C(_r, _c) bitx32(_r, _c, _c)
#define SMUPWR_SOFT_DOWNCORE_SET_DISCORE(_r, _v) bitset32(_r, 7, 0, _v)
#define SMUPWR_SOFT_DOWNCORE_SET_DISCORE_C(_r, _c) bitset32(_r, _c, _c, 1)
/*
* SMU::PWR::CORE_ENABLE - nominally writable, this register contains a bitmap
* of cores; a bit that is set means the core whose physical ID is that bit
* position is enabled. The effect of modifying this register, if any, is
* undocumented and unknown.
*/
/*CSTYLED*/
#define D_SMUPWR_CORE_EN (const smn_reg_def_t){ \
.srd_unit = SMN_UNIT_SMUPWR, \
.srd_reg = 0x24 \
}
#define SMUPWR_CORE_EN(c) \
amdzen_smupwr_smn_reg(c, D_SMUPWR_CORE_EN, 0)
#define SMUPWR_CORE_EN_GET(_r) bitx32(_r, 7, 0)
#define SMUPWR_CORE_EN_GET_C(_r, _c) bitx32(_r, _c, _c)
#define SMUPWR_CORE_EN_SET(_r, _v) bitset32(_r, 7, 0, _v)
#define SMUPWR_CORE_EN_SET_C(_r, _c) bitset32(_r, _c, _c, 1)
/*
* L3::SOC registers, per-CCD. This functional unit is present starting in Zen
* 5 based platforms. This covers a majority of the things that are described
* above in the SMU::PWR section, except for SMU::PWR::CCD_DIE_ID. CCDs are at a
* 23-bit stride.
*/
static inline smn_reg_t
amdzen_l3soc_smn_reg(const uint8_t ccdno, const smn_reg_def_t def,
const uint16_t reginst)
{
const uint32_t APERTURE_BASE = 0x203c0000;
const uint32_t APERTURE_MASK = 0xfffc0000;
CTASSERT((APERTURE_BASE & ~APERTURE_MASK) == 0);
const uint32_t ccdno32 = (const uint32_t)ccdno;
const uint32_t reginst32 = (const uint32_t)reginst;
const uint32_t size32 = (def.srd_size == 0) ? 4 :
(const uint32_t)def.srd_size;
const uint32_t stride = (def.srd_stride == 0) ? size32 : def.srd_stride;
const uint32_t nents = (def.srd_nents == 0) ? 1 :
(const uint32_t)def.srd_nents;
ASSERT(size32 == 1 || size32 == 2 || size32 == 4);
ASSERT3S(def.srd_unit, ==, SMN_UNIT_L3SOC);
ASSERT3U(ccdno32, <, 16);
ASSERT3U(nents, >, reginst32);
uint32_t aperture_base, aperture_off;
aperture_base = APERTURE_BASE;
aperture_off = ccdno32 << 23;
ASSERT3U(aperture_off, <=, UINT32_MAX - aperture_base);
const uint32_t aperture = aperture_base + aperture_off;
ASSERT0(aperture & ~APERTURE_MASK);
const uint32_t reg = def.srd_reg + reginst32 * stride;
ASSERT0(reg & APERTURE_MASK);
return (SMN_MAKE_REG_SIZED(aperture + reg, size32));
}
/*
* L3::L3SOC::CcxThreadEnable0 - the Zen 5+ variant of SMU::PWR::THREAD_ENABLE.
* See the description there.
*/
/*CSTYLED*/
#define D_L3SOC_THREAD_EN (const smn_reg_def_t){ \
.srd_unit = SMN_UNIT_L3SOC, \
.srd_reg = 0x20 \
}
#define L3SOC_THREAD_EN(c) \
amdzen_l3soc_smn_reg(c, D_L3SOC_THREAD_EN, 0)
#define L3SOC_THREAD_EN_GET_T(_r, _t) bitx32(_r, _t, _t)
#define L3SOC_THREAD_EN_SET_T(_r, _t) bitset32(_r, _t, _t, 1)
/*
* L3::L3SOC::CcxThreadConfiguration - the Zen 5+ variant of
* SMU::PWR::THREAD_CONFIGURATION. Indicates information about enabled cores,
* complexes, and SMT. The fields have similar semantics but are at different
* locations.
*/
/*CSTYLED*/
#define D_L3SOC_THREAD_CFG (const smn_reg_def_t){ \
.srd_unit = SMN_UNIT_L3SOC, \
.srd_reg = 0x30 \
}
#define L3SOC_THREAD_CFG(c) \
amdzen_l3soc_smn_reg(c, D_L3SOC_THREAD_CFG, 0)
#define L3SOC_THREAD_CFG_GET_SMT_MODE(_r) bitx32(_r, 10, 10)
#define L3SOC_THREAD_CFG_SMT_MODE_1T 1
#define L3SOC_THREAD_CFG_SMT_MODE_SMT 0
#define L3SOC_THREAD_CFG_GET_COMPLEX_COUNT(_r) bitx32(_r, 9, 6)
#define L3SOC_THREAD_CFG_GET_CORE_COUNT(_r) bitx32(_r, 3, 0)
/*
* L3::L3SOC::CcxSoftDownCore0 - see SMU::PWR::SOFT_DOWNCORE.
*/
/*CSTYLED*/
#define D_L3SOC_SOFT_DOWNCORE (const smn_reg_def_t){ \
.srd_unit = SMN_UNIT_L3SOC, \
.srd_reg = 0x34 \
}
#define L3SOC_SOFT_DOWNCORE(c) \
amdzen_l3soc_smn_reg(c, D_L3SOC_SOFT_DOWNCORE, 0)
#define L3SOC_SOFT_DOWNCORE_GET_DISCORE(_r) bitx32(_r, 15, 0)
#define L3SOC_SOFT_DOWNCORE_GET_DISCORE_C(_r, _c) bitx32(_r, _c, _c)
#define L3SOC_SOFT_DOWNCORE_SET_DISCORE(_r, _v) bitset32(_r, 15, 0, _v)
#define L3SOC_SOFT_DOWNCORE_SET_DISCORE_C(_r, _c) bitset32(_r, _c, _c, 1)
/*
* L3::L3SOC::CcxCoreEnable0 -- see SMU::PWR::CORE_ENABLE.
*/
/*CSTYLED*/
#define D_L3SOC_CORE_EN (const smn_reg_def_t){ \
.srd_unit = SMN_UNIT_L3SOC, \
.srd_reg = 0x3c \
}
#define L3SOC_CORE_EN(c) \
amdzen_l3soc_smn_reg(c, D_L3SOC_CORE_EN, 0)
#define L3SOC_CORE_EN_GET(_r) bitx32(_r, 15, 0)
#define L3SOC_CORE_EN_GET_C(_r, _c) bitx32(_r, _c, _c)
#define L3SOC_CORE_EN_SET(_r, _v) bitset32(_r, 15, 0, _v)
#define L3SOC_CORE_EN_SET_C(_r, _c) bitset32(_r, _c, _c, 1)
/*
* SCFCTP registers. A copy of these exists for each core. One thing to be aware
* of is that not all cores are enabled and this requires looking at the
* SMU::PWR/L3::SOC registers above or the DF::CoreEnable. The aperture for
* these starts at 2000_0000h. Each core is then spaced 2_0000h apart while each
* CCD has a 23-bit stride and each CCX has a 22 bit stride. The number of cores
* and CCXes varies based upon the generation. We size this based on what we
* anticipate the maximums to be.
*
* In the future, it'd be good to have a way to constrain the values we accept
* to something less than the maximum across all products, but this is often
* used before we have fully flushed out the uarchrev part of CPUID making it
* challenging at the moment.
*/
#define SCFCTP_CORE_STRIDE 0x20000
#define SCFCTP_MAX_ENTS 16
static inline smn_reg_t
amdzen_scfctp_smn_reg(const uint8_t ccdno, const uint8_t ccxno,
const smn_reg_def_t def, const uint16_t reginst)
{
const uint32_t APERTURE_BASE = 0x20000000;
const uint32_t APERTURE_MASK = 0xffc00000;
CTASSERT((APERTURE_BASE & ~APERTURE_MASK) == 0);
const uint32_t ccdno32 = (const uint32_t)ccdno;
const uint32_t ccxno32 = (const uint32_t)ccxno;
const uint32_t reginst32 = (const uint32_t)reginst;
const uint32_t size32 = (def.srd_size == 0) ? 4 :
(const uint32_t)def.srd_size;
const uint32_t stride = (def.srd_stride == 0) ? 4 : def.srd_stride;
const uint32_t nents = (def.srd_nents == 0) ? 1 :
(const uint32_t)def.srd_nents;
ASSERT(size32 == 1 || size32 == 2 || size32 == 4);
ASSERT3S(def.srd_unit, ==, SMN_UNIT_SCFCTP);
ASSERT3U(stride, ==, SCFCTP_CORE_STRIDE);
ASSERT3U(nents, ==, SCFCTP_MAX_ENTS);
ASSERT3U(ccdno32, <, 16);
ASSERT3U(ccxno32, <, 2);
ASSERT3U(nents, >, reginst32);
const uint32_t aperture_off = (ccdno32 << 23) + (ccxno << 22);
ASSERT3U(aperture_off, <=, UINT32_MAX - APERTURE_BASE);
const uint32_t aperture = APERTURE_BASE + aperture_off;
ASSERT0(aperture & ~APERTURE_MASK);
const uint32_t reg = def.srd_reg + reginst32 * stride;
ASSERT0(reg & APERTURE_MASK);
return (SMN_MAKE_REG_SIZED(aperture + reg, size32));
}
/*
* L3::SCFCTP::PMREG_INITPKG0 - Nominally writable, this register contains
* information allowing us to discover where this core fits into the logical and
* physical topology of the processor.
*/
/*CSTYLED*/
#define D_SCFCTP_PMREG_INITPKG0 (const smn_reg_def_t){ \
.srd_unit = SMN_UNIT_SCFCTP, \
.srd_reg = 0x2fd0, \
.srd_nents = SCFCTP_MAX_ENTS, \
.srd_stride = SCFCTP_CORE_STRIDE \
}
#define SCFCTP_PMREG_INITPKG0(ccd, ccx, core) \
amdzen_scfctp_smn_reg(ccd, ccx, D_SCFCTP_PMREG_INITPKG0, core)
#define SCFCTP_PMREG_INITPKG0_GET_LOG_DIE(_r) bitx32(_r, 22, 19)
#define SCFCTP_PMREG_INITPKG0_GET_LOG_CCX(_r) bitx32(_r, 18, 18)
#define SCFCTP_PMREG_INITPKG0_GET_LOG_CORE(_r) bitx32(_r, 17, 14)
#define SCFCTP_PMREG_INITPKG0_GET_SOCKET(_r) bitx32(_r, 13, 12)
#define SCFCTP_PMREG_INITPKG0_GET_PHYS_DIE(_r) bitx32(_r, 11, 8)
#define SCFCTP_PMREG_INITPKG0_GET_PHYS_CCX(_r) bitx32(_r, 7, 7)
#define SCFCTP_PMREG_INITPKG0_GET_PHYS_CORE(_r) bitx32(_r, 6, 3)
#define SCFCTP_PMREG_INITPKG0_GET_SMTEN(_r) bitx32(_r, 2, 0)
/*
* L3::SCFCTP::PMREG_INITPKG7 - Similarly, this register describes this
* processor's overall internal core topology. The most notable addition to this
* register has been the addition of a bit which causes the APIC ID for the CCX
* to be shifted and covered by at least 4 bits. That is, if the number of bits
* required to cover SCFCTP_PMREG_INITPKG7_GET_N_CCXS is less than 4, it should
* be assumed to require 4 bits.
*/
/*CSTYLED*/
#define D_SCFCTP_PMREG_INITPKG7 (const smn_reg_def_t){ \
.srd_unit = SMN_UNIT_SCFCTP, \
.srd_reg = 0x2fec, \
.srd_nents = SCFCTP_MAX_ENTS, \
.srd_stride = SCFCTP_CORE_STRIDE \
}
#define SCFCTP_PMREG_INITPKG7(ccd, ccx, core) \
amdzen_scfctp_smn_reg(ccd, ccx, D_SCFCTP_PMREG_INITPKG7, core)
#define SCFCTP_PMREG_INITPKG7_GET_N_SOCKETS(_r) bitx32(_r, 26, 25)
#define SCFCTP_PMREG_INITPKG7_GET_N_DIES(_r) bitx32(_r, 24, 21)
#define SCFCTP_PMREG_INITPKG7_GET_N_CCXS(_r) bitx32(_r, 20, 20)
#define SCFCTP_PMREG_INITPKG7_GET_N_CORES(_r) bitx32(_r, 19, 16)
#define SCFCTP_PMREG_INITPKG7_ZEN4_GET_16TAPIC(_r) bitx32(_r, 11, 11)
#define SCFCTP_PMREG_INITPKG7_GET_CHIDXHASHEN(_r) bitx32(_r, 10, 10)
#define SCFCTP_PMREG_INITPKG7_GET_S3(_r) bitx32(_r, 9, 9)
#define SCFCTP_PMREG_INITPKG7_ZEN3_GET_S0I3(_r) bitx32(_r, 8, 8)
#define SCFCTP_PMREG_INITPKG7_GET_CORETYPEISARM(_r) bitx32(_r, 7, 7)
#define SCFCTP_PMREG_INITPKG7_GET_SOCID(_r) bitx32(_r, 6, 3)
#ifdef __cplusplus
}
#endif
#endif /* _SYS_AMDZEN_CCD_H */
|