1 /* $OpenBSD: ksmn.c,v 1.4 2020/01/04 01:34:24 jsg 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 }; 100 101 int 102 ksmn_match(struct device *parent, void *match, void *aux) 103 { 104 /* successful match supersedes pchb(4) */ 105 return pci_matchbyid((struct pci_attach_args *)aux, ksmn_devices, 106 sizeof(ksmn_devices) / sizeof(ksmn_devices[0])) * 2; 107 } 108 109 void 110 ksmn_attach(struct device *parent, struct device *self, void *aux) 111 { 112 struct ksmn_softc *sc = (struct ksmn_softc *)self; 113 struct pci_attach_args *pa = aux; 114 struct curtmp_offset *p; 115 extern char cpu_model[]; 116 117 sc->sc_pc = pa->pa_pc; 118 sc->sc_pcitag = pa->pa_tag; 119 120 strlcpy(sc->sc_sensordev.xname, sc->sc_dev.dv_xname, 121 sizeof(sc->sc_sensordev.xname)); 122 123 sc->sc_sensor.type = SENSOR_TEMP; 124 sensor_attach(&sc->sc_sensordev, &sc->sc_sensor); 125 126 /* 127 * Zen/Zen+ CPUs are offset if TDP > 65, otherwise 0. 128 * Zen 2 models appear to have no tCTL offset, so always 0. 129 * 130 * XXX: Does any public documentation exist for this? 131 */ 132 for (p = cpu_model_offsets; p->cpu_model != NULL; p++) { 133 /* match partial string */ 134 if (!strncmp(cpu_model, p->cpu_model, strlen(p->cpu_model))) 135 sc->sc_tctl_offset = p->tctl_offset; 136 } 137 138 if (sensor_task_register(sc, ksmn_refresh, 5) == NULL) { 139 printf(": unable to register update task\n"); 140 return; 141 } 142 143 sensordev_install(&sc->sc_sensordev); 144 145 printf("\n"); 146 } 147 148 void 149 ksmn_refresh(void *arg) 150 { 151 struct ksmn_softc *sc = arg; 152 struct ksensor *s = &sc->sc_sensor; 153 pcireg_t reg; 154 int raw, offset = 0; 155 156 pci_conf_write(sc->sc_pc, sc->sc_pcitag, SMN_17H_ADDR_R, 157 SMU_17H_THM); 158 reg = pci_conf_read(sc->sc_pc, sc->sc_pcitag, SMN_17H_DATA_R); 159 160 raw = GET_CURTMP(reg); 161 if ((reg & CURTMP_17H_RANGE_SEL) != 0) 162 offset -= CURTMP_17H_RANGE_ADJUST; 163 offset -= sc->sc_tctl_offset; 164 /* convert to uC */ 165 offset *= 100000; 166 167 /* convert raw (in steps of 0.125C) to uC, add offset, uC to uK. */ 168 s->value = ((raw * 125000) + offset) + 273150000; 169 } 170