summaryrefslogtreecommitdiff
path: root/sys/dev/pci/ksmn.c
diff options
context:
space:
mode:
Diffstat (limited to 'sys/dev/pci/ksmn.c')
-rw-r--r--sys/dev/pci/ksmn.c166
1 files changed, 166 insertions, 0 deletions
diff --git a/sys/dev/pci/ksmn.c b/sys/dev/pci/ksmn.c
new file mode 100644
index 00000000000..cd43125fb31
--- /dev/null
+++ b/sys/dev/pci/ksmn.c
@@ -0,0 +1,166 @@
+/* $OpenBSD: ksmn.c,v 1.1 2019/09/26 12:59:01 brynet Exp $ */
+
+/*
+ * Copyright (c) 2019 Bryan Steele <brynet@openbsd.org>
+ *
+ * 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 <sys/param.h>
+#include <sys/systm.h>
+#include <sys/device.h>
+#include <sys/sensors.h>
+
+#include <machine/bus.h>
+
+#include <dev/pci/pcivar.h>
+#include <dev/pci/pcidevs.h>
+
+/*
+ * 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 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
+
+/*
+ * 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;
+
+ struct ksensor sc_sensor;
+ struct ksensordev sc_sensordev;
+};
+
+int ksmn_match(struct device *, void *, void *);
+void ksmn_attach(struct device *, struct device *, void *);
+void ksmn_refresh(void *);
+
+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_AMD64_17_RC },
+ { PCI_VENDOR_AMD, PCI_PRODUCT_AMD_AMD64_17_1X_RC },
+ { PCI_VENDOR_AMD, PCI_PRODUCT_AMD_AMD64_17_3X_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;
+ 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;
+ 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;
+ }
+
+ if (sensor_task_register(sc, ksmn_refresh, 5) == NULL) {
+ printf(": unable to register update task\n");
+ return;
+ }
+
+ sensordev_install(&sc->sc_sensordev);
+
+ printf("\n");
+}
+
+void
+ksmn_refresh(void *arg)
+{
+ struct ksmn_softc *sc = arg;
+ struct ksensor *s = &sc->sc_sensor;
+ pcireg_t reg;
+ int c;
+
+ pci_conf_write(sc->sc_pc, sc->sc_pcitag, SMN_17H_ADDR_R,
+ SMU_17H_THM);
+ reg = pci_conf_read(sc->sc_pc, sc->sc_pcitag, SMN_17H_DATA_R);
+
+ c = GET_CURTMP(reg) * 5 / 4; /* 1.25 */
+ if ((reg & CURTMP_17H_RANGE_SEL) != 0)
+ c -= CURTMP_17H_RANGE_ADJUST;
+ if (c > sc->sc_tctl_offset)
+ c -= sc->sc_tctl_offset;
+ s->value = c * 125000 + 273150000;
+}