xref: /openbsd/sys/dev/pci/ksmn.c (revision 4bdff4be)
1 /*	$OpenBSD: ksmn.c,v 1.9 2023/09/05 13:06:01 stsp 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 };
111 
112 int
113 ksmn_match(struct device *parent, void *match, void *aux)
114 {
115 	/* successful match supersedes pchb(4) */
116 	return pci_matchbyid((struct pci_attach_args *)aux, ksmn_devices,
117 	    sizeof(ksmn_devices) / sizeof(ksmn_devices[0])) * 2;
118 }
119 
120 void
121 ksmn_attach(struct device *parent, struct device *self, void *aux)
122 {
123 	struct ksmn_softc	*sc = (struct ksmn_softc *)self;
124 	struct pci_attach_args	*pa = aux;
125 	struct curtmp_offset	*p;
126 	struct cpu_info		*ci = curcpu();
127 	extern char		 cpu_model[];
128 
129 
130 	sc->sc_pc = pa->pa_pc;
131 	sc->sc_pcitag = pa->pa_tag;
132 
133 	strlcpy(sc->sc_sensordev.xname, sc->sc_dev.dv_xname,
134 	    sizeof(sc->sc_sensordev.xname));
135 
136 	sc->sc_sensor.type = SENSOR_TEMP;
137 	snprintf(sc->sc_sensor.desc, sizeof(sc->sc_sensor.desc), "Tctl");
138 	sensor_attach(&sc->sc_sensordev, &sc->sc_sensor);
139 
140 	/*
141 	 * Zen/Zen+ CPUs are offset if TDP > 65, otherwise 0.
142 	 * Zen 2 models appear to have no tCTL offset, so always 0.
143 	 *
144 	 * XXX: Does any public documentation exist for this?
145 	 */
146 	for (p = cpu_model_offsets; p->cpu_model != NULL; p++) {
147 		/* match partial string */
148 		if (!strncmp(cpu_model, p->cpu_model, strlen(p->cpu_model)))
149 			sc->sc_tctl_offset = p->tctl_offset;
150 	}
151 
152 	sc->sc_ccd_offset = 0x154;
153 
154 	if (ci->ci_family == 0x17 || ci->ci_family == 0x18) {
155 		switch (ci->ci_model) {
156 		case 0x1:	/* Zen */
157 		case 0x8:	/* Zen+ */
158 		case 0x11:	/* Zen APU */
159 		case 0x18:	/* Zen+ APU */
160 			ksmn_ccd_attach(sc, 4);
161 			break;
162 		case 0x31:	/* Zen2 Threadripper */
163 		case 0x60:	/* Renoir */
164 		case 0x68:	/* Lucienne */
165 		case 0x71:	/* Zen2 */
166 			ksmn_ccd_attach(sc, 8);
167 			break;
168 		}
169 	} else if (ci->ci_family == 0x19) {
170 		uint32_t m = ci->ci_model;
171 
172 		if ((m >= 0x40 && m <= 0x4f) ||
173 		    (m >= 0x10 && m <= 0x1f) ||
174 		    (m >= 0xa0 && m <= 0xaf))
175 			sc->sc_ccd_offset = 0x300;
176 
177 		if (m >= 0x60 && m <= 0x6f)
178 			sc->sc_ccd_offset = 0x308;
179 
180 		if ((m >= 0x10 && m <= 0x1f) ||
181 		    (m >= 0xa0 && m <= 0xaf))
182 			ksmn_ccd_attach(sc, 12);
183 		else
184 			ksmn_ccd_attach(sc, 8);
185 	}
186 
187 	if (sensor_task_register(sc, ksmn_refresh, 5) == NULL) {
188 		printf(": unable to register update task\n");
189 		return;
190 	}
191 
192 	sensordev_install(&sc->sc_sensordev);
193 
194 	printf("\n");
195 }
196 
197 uint32_t
198 ksmn_read_reg(struct ksmn_softc *sc, uint32_t addr)
199 {
200 	uint32_t reg;
201 	int s;
202 
203 	s = splhigh();
204 	pci_conf_write(sc->sc_pc, sc->sc_pcitag, SMN_17H_ADDR_R, addr);
205 	reg = pci_conf_read(sc->sc_pc, sc->sc_pcitag, SMN_17H_DATA_R);
206 	splx(s);
207 	return reg;
208 }
209 
210 void
211 ksmn_ccd_attach(struct ksmn_softc *sc, int nccd)
212 {
213 	struct ksensor *s;
214 	uint32_t reg;
215 	int i;
216 
217 	KASSERT(nccd > 0 && nccd < nitems(sc->sc_ccd_sensor));
218 
219 	for (i = 0; i < nccd; i++) {
220 		reg = ksmn_read_reg(sc, SMU_17H_CCD_THM(sc->sc_ccd_offset, i));
221 		if (reg & CURTMP_CCD_VALID) {
222 			sc->sc_ccd_valid |= (1 << i);
223 			s = &sc->sc_ccd_sensor[i];
224 			s->type = SENSOR_TEMP;
225 			snprintf(s->desc, sizeof(s->desc), "Tccd%d", i);
226 			sensor_attach(&sc->sc_sensordev, s);
227 		}
228 	}
229 }
230 
231 void
232 ksmn_refresh(void *arg)
233 {
234 	struct ksmn_softc	*sc = arg;
235 	struct ksensor		*s = &sc->sc_sensor;
236 	pcireg_t		reg;
237 	int 			i, raw, offset = 0;
238 
239 	reg = ksmn_read_reg(sc, SMU_17H_THM);
240 	raw = GET_CURTMP(reg);
241 	if ((reg & CURTMP_17H_RANGE_SEL) != 0)
242 		offset -= CURTMP_17H_RANGE_ADJUST;
243 	offset -= sc->sc_tctl_offset;
244 	/* convert to uC */
245 	offset *= 100000;
246 
247 	/* convert raw (in steps of 0.125C) to uC, add offset, uC to uK. */
248 	s->value = raw * 125000 + offset + 273150000;
249 
250 	offset = CURTMP_17H_RANGE_ADJUST * 100000;
251 	for (i = 0; i < nitems(sc->sc_ccd_sensor); i++) {
252 		if (sc->sc_ccd_valid & (1 << i)) {
253 			s = &sc->sc_ccd_sensor[i];
254 			reg = ksmn_read_reg(sc,
255 			    SMU_17H_CCD_THM(sc->sc_ccd_offset, i));
256 			s->value = (reg & CURTMP_CCD_MASK) * 125000 - offset +
257 			    273150000;
258 		}
259 	}
260 }
261