|
root / base / usr / src / uts / intel / sys / amdzen / thm.h
thm.h C 186 lines 5.6 KB
  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
/*
 * 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 2025 Oxide Computer Company
 */

#ifndef _SYS_AMDZEN_THM_H
#define	_SYS_AMDZEN_THM_H

#include <sys/bitext.h>
#include <sys/amdzen/smn.h>

/*
 * This header covers the SMU's (system management unit) thermal block. The SMU,
 * often called MP1 in various AMD docs, exists as a single entity in the I/O
 * die (or a Zen 1 Zeppelin die), leaving most registers at a fixed entry point
 * and block.
 *
 * The thermal block SMN registers are generally shadows or calculated
 * information based on a series of internal diodes, slewing, and other related
 * features that exist within the SOC. Only a subset of the overall thermal
 * registers are described here which are used by us to obtain information. The
 * majority of the other registers are only used by the SMU and perhaps the PSP.
 * Note, similar information is provided over the sideband temperature interface
 * (SB-TSI), which is consumed by the service processor on a system board that
 * maintains a thermal loop.
 *
 * Note, CCDs have their own separate thermal block, SMU::THMCCD.
 */

#ifdef __cplusplus
extern "C" {
#endif

/*
 * SMU::THM registers, per-die.  This functional unit is present in all Zen CPUs
 * and is effectively a singleton.
 */
#define	SMU_THM_APERTURE_MASK	0xfffffffffffff800
AMDZEN_MAKE_SMN_REG_FN(amdzen_smuthm_smn_reg, SMU_THM, 0x59800,
    SMU_THM_APERTURE_MASK, 1, 0);

/*
 * SMU::THM::THM_TCON_CUR_TMP -- the primary thermal sensor in a given die. This
 * is where Tctl generally comes from. Regardless of whether it encodes Tctl or
 * Tj, the value is measured in 0.125 steps, hence a granularity of 8. The three
 * lower bits of the temperature are to the right of the decimal.
 */
#define	THM_CURTEMP		SMN_MAKE_REG(0x59800)
#define	THM_CURTEMP_GET_TEMP(r)		bitx32(r, 31, 21)
#define	THM_CURTEMP_TEMP_DEC_BITS	3
#define	THM_CURTEMP_TEMP_DEC_MASK	0x7
#define	THM_CURTEMP_TEMP_DEC_GRAN	8
#define	THM_CURTEMP_GET_MCM(r)		bitx32(r, 20, 20)
#define	THM_CURTEMP_GET_RANGE(r)	bitx32(r, 19, 19)
#define	THM_CURTEMP_RANGE_O_225		0
#define	THM_CURTEMP_RANGE_N49_206	1
#define	THM_CURTEMP_RANGE_ADJ		(-49)
#define	THM_CURTEMP_GET_SLEW_SEL(r)	bitx32(r, 18, 18)
#define	THM_CURTEMP_GET_TJ_SEL(r)	bitx32(r, 17, 16)
#define	THM_CURTEMP_TJ_SEL_TCTL		0
#define	THM_CURTEMP_TJ_SEL_TJ		2
#define	THM_CURTEMP_TJ_SEL_RW		3
#define	THM_CURTEMP_GET_TIME_DOWN(r)	bitx32(r, 12, 8)
#define	THM_CURTEMP_GET_SLEW_DOWN_EN(r)	bitx32(r, 7, 7)
#define	THM_CURTEMP_GET_MAX_DIFF(r)	bitx32(r, 6, 5)
#define	THM_CURTEMP_GET_TIME_UP(r)	bitx32(r, 4, 0)

/*
 * SMU::THM::THM_DIEX_TEMP -- this is a per-die measurement that comes from the
 * thermal monitor. It measures Tdie and is in degrees C. The value is otherwise
 * in the same format as described for SMU::THM::THM_TCON_CUR_TMP. Unlike Tctl
 * above, this has a valid bit that must be consulted. Our understanding is that
 * the valid bit, once set, will generally remain true.
 *
 * This register has a bit of an unfortunate history. The number of these that
 * are valid and their location unfortunately changes on a per-CPU basis. This
 * results in a much more complicated function for getting this with
 * corresponding limits.
 */
static inline uint16_t
THM_DIE_MAX_UNITS(x86_processor_family_t fam)
{
	switch (fam) {
	case X86_PF_AMD_STRIX_HALO:
		return (2);
	case X86_PF_AMD_NAPLES:
	case X86_PF_AMD_PINNACLE_RIDGE:
	case X86_PF_AMD_RAVEN_RIDGE:
	case X86_PF_AMD_PICASSO:
	case X86_PF_AMD_DALI:
	case X86_PF_HYGON_DHYANA:
		return (4);
	case X86_PF_AMD_ROME:
	case X86_PF_AMD_RENOIR:
	case X86_PF_AMD_MATISSE:
	case X86_PF_AMD_VAN_GOGH:
	case X86_PF_AMD_MILAN:
	case X86_PF_AMD_VERMEER:
	case X86_PF_AMD_CEZANNE:
	case X86_PF_AMD_MENDOCINO:
	case X86_PF_AMD_REMBRANDT:
	case X86_PF_AMD_RAPHAEL:
	case X86_PF_AMD_PHOENIX:
	case X86_PF_AMD_GRANITE_RIDGE:
		return (8);
	case X86_PF_AMD_GENOA:
	case X86_PF_AMD_BERGAMO:
		return (12);
	case X86_PF_AMD_TURIN:
	case X86_PF_AMD_DENSE_TURIN:
		return (16);
	case X86_PF_AMD_STRIX:
	case X86_PF_AMD_KRACKAN:
	default:
		return (0);
	}
}

static inline smn_reg_t
THM_DIE(uint8_t dieno, x86_processor_family_t fam)
{
	smn_reg_def_t regdef = { 0 };
	regdef.srd_unit = SMN_UNIT_SMU_THM;
	regdef.srd_nents = THM_DIE_MAX_UNITS(fam);
	ASSERT3U(regdef.srd_nents, !=, 0);

	switch (fam) {
	case X86_PF_AMD_NAPLES:
	case X86_PF_AMD_PINNACLE_RIDGE:
	case X86_PF_AMD_RAVEN_RIDGE:
	case X86_PF_AMD_PICASSO:
	case X86_PF_AMD_DALI:
	case X86_PF_HYGON_DHYANA:
	case X86_PF_AMD_ROME:
	case X86_PF_AMD_RENOIR:
	case X86_PF_AMD_MATISSE:
	case X86_PF_AMD_VAN_GOGH:
	case X86_PF_AMD_MILAN:
	case X86_PF_AMD_VERMEER:
	case X86_PF_AMD_CEZANNE:
		regdef.srd_reg = 0x154;
		break;
	case X86_PF_AMD_MENDOCINO:
	case X86_PF_AMD_REMBRANDT:
	case X86_PF_AMD_GENOA:
	case X86_PF_AMD_BERGAMO:
		regdef.srd_reg = 0x300;
		break;
	case X86_PF_AMD_RAPHAEL:
	case X86_PF_AMD_PHOENIX:
	case X86_PF_AMD_GRANITE_RIDGE:
		regdef.srd_reg = 0x308;
		break;
	case X86_PF_AMD_TURIN:
	case X86_PF_AMD_DENSE_TURIN:
		regdef.srd_reg = 0x1f0;
		break;
	case X86_PF_AMD_STRIX_HALO:
		regdef.srd_reg = 0x78;
		break;
	default:
		panic("encountered unknown family 0x%x while constructing "
		    "SMU::THM::THM_DIEX_TEMP", fam);
	}

	return (amdzen_smuthm_smn_reg(0, regdef, dieno));
}
#define	THM_DIE_GET_VALID(r)	bitx32(r, 11, 11)
#define	THM_DIE_GET_TEMP(r)	bitx32(r, 10, 0)

#ifdef __cplusplus
}
#endif

#endif /* _SYS_AMDZEN_THM_H */