/* $OpenBSD: ksmn.c,v 1.9 2023/09/05 13:06:01 stsp Exp $ */ /* * Copyright (c) 2019 Bryan Steele * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include #include #include #include #include /* * AMD temperature sensors on Family 17h (and some 15h) must be * read from the System Management Unit (SMU) co-processor over * the System Management Network (SMN). */ #define SMN_17H_ADDR_R 0x60 #define SMN_17H_DATA_R 0x64 /* * AMD Family 17h SMU Thermal Registers (THM) * * 4.2.1, OSRR (Open-Source Register Reference) Guide for Family 17h * [31:21] Current reported temperature. */ #define SMU_17H_THM 0x59800 #define SMU_17H_CCD_THM(o, x) (SMU_17H_THM + (o) + ((x) * 4)) #define GET_CURTMP(r) (((r) >> 21) & 0x7ff) /* * Bit 19 set: "Report on -49C to 206C scale range." * clear: "Report on 0C to 225C (255C?) scale range." */ #define CURTMP_17H_RANGE_SEL (1 << 19) #define CURTMP_17H_RANGE_ADJUST 490 #define CURTMP_CCD_VALID (1 << 11) #define CURTMP_CCD_MASK 0x7ff /* * Undocumented tCTL offsets gleamed from Linux k10temp driver. */ struct curtmp_offset { const char *const cpu_model; int tctl_offset; } cpu_model_offsets[] = { { "AMD Ryzen 5 1600X", 200 }, { "AMD Ryzen 7 1700X", 200 }, { "AMD Ryzen 7 1800X", 200 }, { "AMD Ryzen 7 2700X", 100 }, { "AMD Ryzen Threadripper 19", 270 }, /* many models */ { "AMD Ryzen Threadripper 29", 270 }, /* many models */ /* ... */ { NULL, 0 }, }; struct ksmn_softc { struct device sc_dev; pci_chipset_tag_t sc_pc; pcitag_t sc_pcitag; int sc_tctl_offset; unsigned int sc_ccd_valid; /* available Tccds */ unsigned int sc_ccd_offset; struct ksensordev sc_sensordev; struct ksensor sc_sensor; /* Tctl */ struct ksensor sc_ccd_sensor[12]; /* Tccd */ }; int ksmn_match(struct device *, void *, void *); void ksmn_attach(struct device *, struct device *, void *); uint32_t ksmn_read_reg(struct ksmn_softc *, uint32_t); void ksmn_ccd_attach(struct ksmn_softc *, int); void ksmn_refresh(void *); const struct cfattach ksmn_ca = { sizeof(struct ksmn_softc), ksmn_match, ksmn_attach }; struct cfdriver ksmn_cd = { NULL, "ksmn", DV_DULL }; static const struct pci_matchid ksmn_devices[] = { { PCI_VENDOR_AMD, PCI_PRODUCT_AMD_17_RC }, { PCI_VENDOR_AMD, PCI_PRODUCT_AMD_17_1X_RC }, { PCI_VENDOR_AMD, PCI_PRODUCT_AMD_17_3X_RC }, { PCI_VENDOR_AMD, PCI_PRODUCT_AMD_17_6X_RC }, { PCI_VENDOR_AMD, PCI_PRODUCT_AMD_19_4X_RC }, { PCI_VENDOR_AMD, PCI_PRODUCT_AMD_19_6X_RC }, }; int ksmn_match(struct device *parent, void *match, void *aux) { /* successful match supersedes pchb(4) */ return pci_matchbyid((struct pci_attach_args *)aux, ksmn_devices, sizeof(ksmn_devices) / sizeof(ksmn_devices[0])) * 2; } void ksmn_attach(struct device *parent, struct device *self, void *aux) { struct ksmn_softc *sc = (struct ksmn_softc *)self; struct pci_attach_args *pa = aux; struct curtmp_offset *p; struct cpu_info *ci = curcpu(); extern char cpu_model[]; sc->sc_pc = pa->pa_pc; sc->sc_pcitag = pa->pa_tag; strlcpy(sc->sc_sensordev.xname, sc->sc_dev.dv_xname, sizeof(sc->sc_sensordev.xname)); sc->sc_sensor.type = SENSOR_TEMP; snprintf(sc->sc_sensor.desc, sizeof(sc->sc_sensor.desc), "Tctl"); sensor_attach(&sc->sc_sensordev, &sc->sc_sensor); /* * Zen/Zen+ CPUs are offset if TDP > 65, otherwise 0. * Zen 2 models appear to have no tCTL offset, so always 0. * * XXX: Does any public documentation exist for this? */ for (p = cpu_model_offsets; p->cpu_model != NULL; p++) { /* match partial string */ if (!strncmp(cpu_model, p->cpu_model, strlen(p->cpu_model))) sc->sc_tctl_offset = p->tctl_offset; } sc->sc_ccd_offset = 0x154; if (ci->ci_family == 0x17 || ci->ci_family == 0x18) { switch (ci->ci_model) { case 0x1: /* Zen */ case 0x8: /* Zen+ */ case 0x11: /* Zen APU */ case 0x18: /* Zen+ APU */ ksmn_ccd_attach(sc, 4); break; case 0x31: /* Zen2 Threadripper */ case 0x60: /* Renoir */ case 0x68: /* Lucienne */ case 0x71: /* Zen2 */ ksmn_ccd_attach(sc, 8); break; } } else if (ci->ci_family == 0x19) { uint32_t m = ci->ci_model; if ((m >= 0x40 && m <= 0x4f) || (m >= 0x10 && m <= 0x1f) || (m >= 0xa0 && m <= 0xaf)) sc->sc_ccd_offset = 0x300; if (m >= 0x60 && m <= 0x6f) sc->sc_ccd_offset = 0x308; if ((m >= 0x10 && m <= 0x1f) || (m >= 0xa0 && m <= 0xaf)) ksmn_ccd_attach(sc, 12); else ksmn_ccd_attach(sc, 8); } if (sensor_task_register(sc, ksmn_refresh, 5) == NULL) { printf(": unable to register update task\n"); return; } sensordev_install(&sc->sc_sensordev); printf("\n"); } uint32_t ksmn_read_reg(struct ksmn_softc *sc, uint32_t addr) { uint32_t reg; int s; s = splhigh(); pci_conf_write(sc->sc_pc, sc->sc_pcitag, SMN_17H_ADDR_R, addr); reg = pci_conf_read(sc->sc_pc, sc->sc_pcitag, SMN_17H_DATA_R); splx(s); return reg; } void ksmn_ccd_attach(struct ksmn_softc *sc, int nccd) { struct ksensor *s; uint32_t reg; int i; KASSERT(nccd > 0 && nccd < nitems(sc->sc_ccd_sensor)); for (i = 0; i < nccd; i++) { reg = ksmn_read_reg(sc, SMU_17H_CCD_THM(sc->sc_ccd_offset, i)); if (reg & CURTMP_CCD_VALID) { sc->sc_ccd_valid |= (1 << i); s = &sc->sc_ccd_sensor[i]; s->type = SENSOR_TEMP; snprintf(s->desc, sizeof(s->desc), "Tccd%d", i); sensor_attach(&sc->sc_sensordev, s); } } } void ksmn_refresh(void *arg) { struct ksmn_softc *sc = arg; struct ksensor *s = &sc->sc_sensor; pcireg_t reg; int i, raw, offset = 0; reg = ksmn_read_reg(sc, SMU_17H_THM); raw = GET_CURTMP(reg); if ((reg & CURTMP_17H_RANGE_SEL) != 0) offset -= CURTMP_17H_RANGE_ADJUST; offset -= sc->sc_tctl_offset; /* convert to uC */ offset *= 100000; /* convert raw (in steps of 0.125C) to uC, add offset, uC to uK. */ s->value = raw * 125000 + offset + 273150000; offset = CURTMP_17H_RANGE_ADJUST * 100000; for (i = 0; i < nitems(sc->sc_ccd_sensor); i++) { if (sc->sc_ccd_valid & (1 << i)) { s = &sc->sc_ccd_sensor[i]; reg = ksmn_read_reg(sc, SMU_17H_CCD_THM(sc->sc_ccd_offset, i)); s->value = (reg & CURTMP_CCD_MASK) * 125000 - offset + 273150000; } } }