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