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