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