xref: /openbsd/sys/dev/pci/ksmn.c (revision 3072e24c)
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