1 /* $OpenBSD: ksmn.c,v 1.10 2024/08/07 17:39:00 brynet Exp $ */
2
3 /*
4 * Copyright (c) 2019 Bryan Steele <brynet@openbsd.org>
5 *
6 * Permission to use, copy, modify, and distribute this software for any
7 * purpose with or without fee is hereby granted, provided that the above
8 * copyright notice and this permission notice appear in all copies.
9 *
10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17 */
18
19 #include <sys/param.h>
20 #include <sys/systm.h>
21 #include <sys/device.h>
22 #include <sys/sensors.h>
23
24 #include <machine/bus.h>
25
26 #include <dev/pci/pcivar.h>
27 #include <dev/pci/pcidevs.h>
28
29 /*
30 * AMD temperature sensors on Family 17h (and some 15h) must be
31 * read from the System Management Unit (SMU) co-processor over
32 * the System Management Network (SMN).
33 */
34
35 #define SMN_17H_ADDR_R 0x60
36 #define SMN_17H_DATA_R 0x64
37
38 /*
39 * AMD Family 17h SMU Thermal Registers (THM)
40 *
41 * 4.2.1, OSRR (Open-Source Register Reference) Guide for Family 17h
42 * [31:21] Current reported temperature.
43 */
44 #define SMU_17H_THM 0x59800
45 #define SMU_17H_CCD_THM(o, x) (SMU_17H_THM + (o) + ((x) * 4))
46 #define GET_CURTMP(r) (((r) >> 21) & 0x7ff)
47
48 /*
49 * Bit 19 set: "Report on -49C to 206C scale range."
50 * clear: "Report on 0C to 225C (255C?) scale range."
51 */
52 #define CURTMP_17H_RANGE_SEL (1 << 19)
53 #define CURTMP_17H_RANGE_ADJUST 490
54 #define CURTMP_CCD_VALID (1 << 11)
55 #define CURTMP_CCD_MASK 0x7ff
56
57 /*
58 * Undocumented tCTL offsets gleamed from Linux k10temp driver.
59 */
60 struct curtmp_offset {
61 const char *const cpu_model;
62 int tctl_offset;
63 } cpu_model_offsets[] = {
64 { "AMD Ryzen 5 1600X", 200 },
65 { "AMD Ryzen 7 1700X", 200 },
66 { "AMD Ryzen 7 1800X", 200 },
67 { "AMD Ryzen 7 2700X", 100 },
68 { "AMD Ryzen Threadripper 19", 270 }, /* many models */
69 { "AMD Ryzen Threadripper 29", 270 }, /* many models */
70 /* ... */
71 { NULL, 0 },
72 };
73
74 struct ksmn_softc {
75 struct device sc_dev;
76
77 pci_chipset_tag_t sc_pc;
78 pcitag_t sc_pcitag;
79
80 int sc_tctl_offset;
81 unsigned int sc_ccd_valid; /* available Tccds */
82 unsigned int sc_ccd_offset;
83
84 struct ksensordev sc_sensordev;
85 struct ksensor sc_sensor; /* Tctl */
86 struct ksensor sc_ccd_sensor[12]; /* Tccd */
87 };
88
89 int ksmn_match(struct device *, void *, void *);
90 void ksmn_attach(struct device *, struct device *, void *);
91 uint32_t ksmn_read_reg(struct ksmn_softc *, uint32_t);
92 void ksmn_ccd_attach(struct ksmn_softc *, int);
93 void ksmn_refresh(void *);
94
95 const struct cfattach ksmn_ca = {
96 sizeof(struct ksmn_softc), ksmn_match, ksmn_attach
97 };
98
99 struct cfdriver ksmn_cd = {
100 NULL, "ksmn", DV_DULL
101 };
102
103 static const struct pci_matchid ksmn_devices[] = {
104 { PCI_VENDOR_AMD, PCI_PRODUCT_AMD_17_RC },
105 { PCI_VENDOR_AMD, PCI_PRODUCT_AMD_17_1X_RC },
106 { PCI_VENDOR_AMD, PCI_PRODUCT_AMD_17_3X_RC },
107 { PCI_VENDOR_AMD, PCI_PRODUCT_AMD_17_6X_RC },
108 { PCI_VENDOR_AMD, PCI_PRODUCT_AMD_19_4X_RC },
109 { PCI_VENDOR_AMD, PCI_PRODUCT_AMD_19_6X_RC },
110 { PCI_VENDOR_AMD, PCI_PRODUCT_AMD_19_7X_RC },
111 };
112
113 int
ksmn_match(struct device * parent,void * match,void * aux)114 ksmn_match(struct device *parent, void *match, void *aux)
115 {
116 /* successful match supersedes pchb(4) */
117 return pci_matchbyid((struct pci_attach_args *)aux, ksmn_devices,
118 sizeof(ksmn_devices) / sizeof(ksmn_devices[0])) * 2;
119 }
120
121 void
ksmn_attach(struct device * parent,struct device * self,void * aux)122 ksmn_attach(struct device *parent, struct device *self, void *aux)
123 {
124 struct ksmn_softc *sc = (struct ksmn_softc *)self;
125 struct pci_attach_args *pa = aux;
126 struct curtmp_offset *p;
127 struct cpu_info *ci = curcpu();
128 extern char cpu_model[];
129
130
131 sc->sc_pc = pa->pa_pc;
132 sc->sc_pcitag = pa->pa_tag;
133
134 strlcpy(sc->sc_sensordev.xname, sc->sc_dev.dv_xname,
135 sizeof(sc->sc_sensordev.xname));
136
137 sc->sc_sensor.type = SENSOR_TEMP;
138 snprintf(sc->sc_sensor.desc, sizeof(sc->sc_sensor.desc), "Tctl");
139 sensor_attach(&sc->sc_sensordev, &sc->sc_sensor);
140
141 /*
142 * Zen/Zen+ CPUs are offset if TDP > 65, otherwise 0.
143 * Zen 2 models appear to have no tCTL offset, so always 0.
144 *
145 * XXX: Does any public documentation exist for this?
146 */
147 for (p = cpu_model_offsets; p->cpu_model != NULL; p++) {
148 /* match partial string */
149 if (!strncmp(cpu_model, p->cpu_model, strlen(p->cpu_model)))
150 sc->sc_tctl_offset = p->tctl_offset;
151 }
152
153 sc->sc_ccd_offset = 0x154;
154
155 if (ci->ci_family == 0x17 || ci->ci_family == 0x18) {
156 switch (ci->ci_model) {
157 case 0x1: /* Zen */
158 case 0x8: /* Zen+ */
159 case 0x11: /* Zen APU */
160 case 0x18: /* Zen+ APU */
161 ksmn_ccd_attach(sc, 4);
162 break;
163 case 0x31: /* Zen2 Threadripper */
164 case 0x60: /* Renoir */
165 case 0x68: /* Lucienne */
166 case 0x71: /* Zen2 */
167 ksmn_ccd_attach(sc, 8);
168 break;
169 }
170 } else if (ci->ci_family == 0x19) {
171 uint32_t m = ci->ci_model;
172
173 if ((m >= 0x40 && m <= 0x4f) ||
174 (m >= 0x10 && m <= 0x1f) ||
175 (m >= 0xa0 && m <= 0xaf))
176 sc->sc_ccd_offset = 0x300;
177
178 if (m >= 0x60 && m <= 0x6f)
179 sc->sc_ccd_offset = 0x308;
180
181 if ((m >= 0x10 && m <= 0x1f) ||
182 (m >= 0xa0 && m <= 0xaf))
183 ksmn_ccd_attach(sc, 12);
184 else
185 ksmn_ccd_attach(sc, 8);
186 }
187
188 if (sensor_task_register(sc, ksmn_refresh, 5) == NULL) {
189 printf(": unable to register update task\n");
190 return;
191 }
192
193 sensordev_install(&sc->sc_sensordev);
194
195 printf("\n");
196 }
197
198 uint32_t
ksmn_read_reg(struct ksmn_softc * sc,uint32_t addr)199 ksmn_read_reg(struct ksmn_softc *sc, uint32_t addr)
200 {
201 uint32_t reg;
202 int s;
203
204 s = splhigh();
205 pci_conf_write(sc->sc_pc, sc->sc_pcitag, SMN_17H_ADDR_R, addr);
206 reg = pci_conf_read(sc->sc_pc, sc->sc_pcitag, SMN_17H_DATA_R);
207 splx(s);
208 return reg;
209 }
210
211 void
ksmn_ccd_attach(struct ksmn_softc * sc,int nccd)212 ksmn_ccd_attach(struct ksmn_softc *sc, int nccd)
213 {
214 struct ksensor *s;
215 uint32_t reg;
216 int i;
217
218 KASSERT(nccd > 0 && nccd < nitems(sc->sc_ccd_sensor));
219
220 for (i = 0; i < nccd; i++) {
221 reg = ksmn_read_reg(sc, SMU_17H_CCD_THM(sc->sc_ccd_offset, i));
222 if (reg & CURTMP_CCD_VALID) {
223 sc->sc_ccd_valid |= (1 << i);
224 s = &sc->sc_ccd_sensor[i];
225 s->type = SENSOR_TEMP;
226 snprintf(s->desc, sizeof(s->desc), "Tccd%d", i);
227 sensor_attach(&sc->sc_sensordev, s);
228 }
229 }
230 }
231
232 void
ksmn_refresh(void * arg)233 ksmn_refresh(void *arg)
234 {
235 struct ksmn_softc *sc = arg;
236 struct ksensor *s = &sc->sc_sensor;
237 pcireg_t reg;
238 int i, raw, offset = 0;
239
240 reg = ksmn_read_reg(sc, SMU_17H_THM);
241 raw = GET_CURTMP(reg);
242 if ((reg & CURTMP_17H_RANGE_SEL) != 0)
243 offset -= CURTMP_17H_RANGE_ADJUST;
244 offset -= sc->sc_tctl_offset;
245 /* convert to uC */
246 offset *= 100000;
247
248 /* convert raw (in steps of 0.125C) to uC, add offset, uC to uK. */
249 s->value = raw * 125000 + offset + 273150000;
250
251 offset = CURTMP_17H_RANGE_ADJUST * 100000;
252 for (i = 0; i < nitems(sc->sc_ccd_sensor); i++) {
253 if (sc->sc_ccd_valid & (1 << i)) {
254 s = &sc->sc_ccd_sensor[i];
255 reg = ksmn_read_reg(sc,
256 SMU_17H_CCD_THM(sc->sc_ccd_offset, i));
257 s->value = (reg & CURTMP_CCD_MASK) * 125000 - offset +
258 273150000;
259 }
260 }
261 }
262