xref: /openbsd/sys/dev/pci/ksmn.c (revision 73471bf0)
1 /*	$OpenBSD: ksmn.c,v 1.5 2021/07/20 18:33:59 jcs 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 GET_CURTMP(r)	(((r) >> 21) & 0x7ff)
46 
47 /*
48  * Bit 19 set: "Report on -49C to 206C scale range."
49  *      clear: "Report on 0C to 225C (255C?) scale range."
50  */
51 #define CURTMP_17H_RANGE_SEL	(1 << 19)
52 #define CURTMP_17H_RANGE_ADJUST	490
53 
54 /*
55  * Undocumented tCTL offsets gleamed from Linux k10temp driver.
56  */
57 struct curtmp_offset {
58 	const char *const cpu_model;
59 	int tctl_offset;
60 } cpu_model_offsets[] = {
61 	{ "AMD Ryzen 5 1600X", 200 },
62 	{ "AMD Ryzen 7 1700X", 200 },
63 	{ "AMD Ryzen 7 1800X", 200 },
64 	{ "AMD Ryzen 7 2700X", 100 },
65 	{ "AMD Ryzen Threadripper 19", 270 }, /* many models */
66 	{ "AMD Ryzen Threadripper 29", 270 }, /* many models */
67 	/* ... */
68 	{ NULL, 0 },
69 };
70 
71 struct ksmn_softc {
72 	struct device		sc_dev;
73 
74 	pci_chipset_tag_t	sc_pc;
75 	pcitag_t		sc_pcitag;
76 
77 	int			sc_tctl_offset;
78 
79 	struct ksensor		sc_sensor;
80 	struct ksensordev	sc_sensordev;
81 };
82 
83 int	ksmn_match(struct device *, void *, void *);
84 void	ksmn_attach(struct device *, struct device *, void *);
85 void	ksmn_refresh(void *);
86 
87 struct cfattach ksmn_ca = {
88 	sizeof(struct ksmn_softc), ksmn_match, ksmn_attach
89 };
90 
91 struct cfdriver ksmn_cd = {
92 	NULL, "ksmn", DV_DULL
93 };
94 
95 static const struct pci_matchid ksmn_devices[] = {
96 	{ PCI_VENDOR_AMD, PCI_PRODUCT_AMD_17_RC },
97 	{ PCI_VENDOR_AMD, PCI_PRODUCT_AMD_17_1X_RC },
98 	{ PCI_VENDOR_AMD, PCI_PRODUCT_AMD_17_3X_RC },
99 	{ PCI_VENDOR_AMD, PCI_PRODUCT_AMD_17_6X_RC },
100 };
101 
102 int
103 ksmn_match(struct device *parent, void *match, void *aux)
104 {
105 	/* successful match supersedes pchb(4) */
106 	return pci_matchbyid((struct pci_attach_args *)aux, ksmn_devices,
107 	    sizeof(ksmn_devices) / sizeof(ksmn_devices[0])) * 2;
108 }
109 
110 void
111 ksmn_attach(struct device *parent, struct device *self, void *aux)
112 {
113 	struct ksmn_softc	*sc = (struct ksmn_softc *)self;
114 	struct pci_attach_args	*pa = aux;
115 	struct curtmp_offset	*p;
116 	extern char		cpu_model[];
117 
118 	sc->sc_pc = pa->pa_pc;
119 	sc->sc_pcitag = pa->pa_tag;
120 
121 	strlcpy(sc->sc_sensordev.xname, sc->sc_dev.dv_xname,
122 	    sizeof(sc->sc_sensordev.xname));
123 
124 	sc->sc_sensor.type = SENSOR_TEMP;
125 	sensor_attach(&sc->sc_sensordev, &sc->sc_sensor);
126 
127 	/*
128 	 * Zen/Zen+ CPUs are offset if TDP > 65, otherwise 0.
129 	 * Zen 2 models appear to have no tCTL offset, so always 0.
130 	 *
131 	 * XXX: Does any public documentation exist for this?
132 	 */
133 	for (p = cpu_model_offsets; p->cpu_model != NULL; p++) {
134 		/* match partial string */
135 		if (!strncmp(cpu_model, p->cpu_model, strlen(p->cpu_model)))
136 			sc->sc_tctl_offset = p->tctl_offset;
137 	}
138 
139 	if (sensor_task_register(sc, ksmn_refresh, 5) == NULL) {
140 		printf(": unable to register update task\n");
141 		return;
142 	}
143 
144 	sensordev_install(&sc->sc_sensordev);
145 
146 	printf("\n");
147 }
148 
149 void
150 ksmn_refresh(void *arg)
151 {
152 	struct ksmn_softc	*sc = arg;
153 	struct ksensor		*s = &sc->sc_sensor;
154 	pcireg_t		reg;
155 	int 			raw, offset = 0;
156 
157 	pci_conf_write(sc->sc_pc, sc->sc_pcitag, SMN_17H_ADDR_R,
158 	    SMU_17H_THM);
159 	reg = pci_conf_read(sc->sc_pc, sc->sc_pcitag, SMN_17H_DATA_R);
160 
161 	raw = GET_CURTMP(reg);
162 	if ((reg & CURTMP_17H_RANGE_SEL) != 0)
163 		offset -= CURTMP_17H_RANGE_ADJUST;
164 	offset -= sc->sc_tctl_offset;
165 	/* convert to uC */
166 	offset *= 100000;
167 
168 	/* convert raw (in steps of 0.125C) to uC, add offset, uC to uK. */
169 	s->value = ((raw * 125000) + offset) + 273150000;
170 }
171