src/sys/dev/pci/ksmn.c

261 lines
6.8 KiB
C

/* $OpenBSD: ksmn.c,v 1.9 2023/09/05 13:06:01 stsp 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 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;
}
}
}