On Sun, Apr 24, 2022 at 07:06:19PM +0200, Claudio Jeker wrote:
> On Ryzen CPUs each CCD has a temp sensor. If the CPU has CCDs (which
> excludes Zen APU CPUs) this should show additional temp info. This is
> based on info from the Linux k10temp driver.
>
> Additionally use the MSRs defined in "Open-Source Register Reference For
> AMD Family 17h Processors" to measure the CPU core frequency.
> That should be the actuall speed of the CPU core during the measuring
> interval.
>
> On my T14g2 the output is now for example:
> ksmn0.temp0 63.88 degC Tctl
> ksmn0.frequency0 3553141515.00 Hz CPU0
> ksmn0.frequency1 3549080315.00 Hz CPU2
> ksmn0.frequency2 3552369937.00 Hz CPU4
> ksmn0.frequency3 3546055048.00 Hz CPU6
> ksmn0.frequency4 3546854449.00 Hz CPU8
> ksmn0.frequency5 3543869698.00 Hz CPU10
> ksmn0.frequency6 3542551127.00 Hz CPU12
> ksmn0.frequency7 4441623647.00 Hz CPU14
>
> It is intresting to watch turbo kick in and how temp causes the CPU to
> throttle.
>
> I only tested this on systems with APUs so I could not thest the Tccd temp
> reporting.
> --
> :wq Claudio
Awesome! :-)
I can see this adding a bunch of extra frequency sensors on higher-end
Ryzen/Threadripper/EPYC CPUs, considering it's displayed in "Hz" that
might be a bit overwhelming to look at.. but that's just a cosmetic
nit for systat.
I'll test on some of my AMD machines later, if someone doesn't beat me
to it.
Some comments below..
> Index: ksmn.c
> ===================================================================
> RCS file: /cvs/src/sys/dev/pci/ksmn.c,v
> retrieving revision 1.6
> diff -u -p -r1.6 ksmn.c
> --- ksmn.c 11 Mar 2022 18:00:50 -0000 1.6
> +++ ksmn.c 24 Apr 2022 16:47:08 -0000
> @@ -20,6 +20,7 @@
> #include <sys/systm.h>
> #include <sys/device.h>
> #include <sys/sensors.h>
> +#include <sys/timeout.h>
>
> #include <machine/bus.h>
>
> @@ -42,6 +43,7 @@
> * [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)
>
> /*
> @@ -50,6 +52,8 @@
> */
> #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.
> @@ -75,13 +79,24 @@ struct ksmn_softc {
> pcitag_t sc_pcitag;
>
> int sc_tctl_offset;
> + unsigned int sc_ccd_valid; /* available Tccds */
> + unsigned int sc_ccd_offset;
> + int sc_hz_count;
>
> - struct ksensor sc_sensor;
> struct ksensordev sc_sensordev;
> + struct ksensor sc_sensor; /* Tctl */
> + struct ksensor sc_ccd_sensor[12]; /* Tccd */
> +
> + struct timeout sc_hz_timeout;
> + struct ksensor *sc_hz_sensor;
> + struct cpu_info **sc_hz_cpu_info;
> };
>
> 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_hz_task(void *);
> void ksmn_refresh(void *);
>
> const struct cfattach ksmn_ca = {
> @@ -113,7 +128,12 @@ ksmn_attach(struct device *parent, struc
> struct ksmn_softc *sc = (struct ksmn_softc *)self;
> struct pci_attach_args *pa = aux;
> struct curtmp_offset *p;
> - extern char cpu_model[];
> + CPU_INFO_ITERATOR cii;
> + struct cpu_info *ci = curcpu();
> + struct ksensor *s;
> + extern char cpu_model[];
> + int i;
> +
>
> sc->sc_pc = pa->pa_pc;
> sc->sc_pcitag = pa->pa_tag;
> @@ -122,6 +142,7 @@ ksmn_attach(struct device *parent, struc
> 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);
>
> /*
> @@ -136,6 +157,80 @@ ksmn_attach(struct device *parent, struc
> 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 >= 0x10 && m <= 0x1f) ||
> + (m >= 0xa0 && m <= 0xaf))
> + ksmn_ccd_attach(sc, 12);
> + else
> + ksmn_ccd_attach(sc, 8);
> + }
> +
> + CPU_INFO_FOREACH(cii, ci) {
> + if (ci->ci_smt_id != 0)
> + continue;
> + sc->sc_hz_count++;
> + }
> + sc->sc_hz_sensor = mallocarray(sc->sc_hz_count,
> + sizeof(*sc->sc_hz_sensor), M_DEVBUF, M_WAITOK | M_ZERO);
> + sc->sc_hz_cpu_info = mallocarray(sc->sc_hz_count,
> + sizeof(*sc->sc_hz_cpu_info), M_DEVBUF, M_WAITOK | M_ZERO);
> +
> + i = 0;
> + CPU_INFO_FOREACH(cii, ci) {
> + if (ci->ci_smt_id != 0)
> + continue;
> + sc->sc_hz_cpu_info[i] = ci;
> + i++;
> + }
> + /* sort list of CPUs */
> + for (i = 1; i < sc->sc_hz_count; i++) {
> + int j;
> + for (j = 0; j < sc->sc_hz_count - i; j++) {
> + if (CPU_INFO_UNIT(sc->sc_hz_cpu_info[j]) >
> + CPU_INFO_UNIT(sc->sc_hz_cpu_info[j + 1])) {
> + ci = sc->sc_hz_cpu_info[j];
> + sc->sc_hz_cpu_info[j] =
> + sc->sc_hz_cpu_info[j + 1];
> + sc->sc_hz_cpu_info[j + 1] = ci;
> + }
> + }
> + }
> +
> + for (i = 0; i < sc->sc_hz_count; i++) {
> + ci = sc->sc_hz_cpu_info[i];
> + s = &sc->sc_hz_sensor[i];
> + s->type = SENSOR_FREQ;
> + snprintf(s->desc, sizeof(s->desc), "CPU%d", CPU_INFO_UNIT(ci));
> + sensor_attach(&sc->sc_sensordev, &sc->sc_hz_sensor[i]);
> + }
> +
> + timeout_set_proc(&sc->sc_hz_timeout, ksmn_hz_task, sc);
> + timeout_add_sec(&sc->sc_hz_timeout, 1);
> +
> if (sensor_task_register(sc, ksmn_refresh, 5) == NULL) {
> printf(": unable to register update task\n");
> return;
> @@ -146,18 +241,78 @@ ksmn_attach(struct device *parent, struc
> 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_hz_task(void *arg)
> +{
> + extern uint64_t tsc_frequency;
> + struct ksmn_softc *sc = arg;
> + struct cpu_info *ci;
> + struct ksensor *sens;
> + uint64_t mperf, aperf;
> + int i, s;
> +
> + for (i = 0; i < sc->sc_hz_count; i++) {
> + ci = sc->sc_hz_cpu_info[i];
> + sens = &sc->sc_hz_sensor[i];
> + if (!CPU_IS_RUNNING(ci))
> + continue;
> + sched_peg_curproc(ci);
> +
> + s = splhigh();
> + mperf = rdmsr(0xE7);
> + aperf = rdmsr(0xE8);
> + wrmsr(0xE7, 0);
> + wrmsr(0xE8, 0);
Do these MSRs have names?
> + splx(s);
> + sens->value = ((aperf * tsc_frequency) / mperf) * 1000000;
> + }
> +
> + timeout_add_sec(&sc->sc_hz_timeout, 1);
> +}
> +
> void
> ksmn_refresh(void *arg)
> {
> struct ksmn_softc *sc = arg;
> struct ksensor *s = &sc->sc_sensor;
> pcireg_t reg;
> - int raw, offset = 0;
> -
> - 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);
> + 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;
> @@ -166,5 +321,16 @@ ksmn_refresh(void *arg)
> offset *= 100000;
>
> /* convert raw (in steps of 0.125C) to uC, add offset, uC to uK. */
> - s->value = ((raw * 125000) + offset) + 273150000;
> + s->value = raw * 125000 + offset + 273150000;
> +
> + offset = CURTMP_17H_RANGE_ADJUST * 100000;
Do we not need to handle the tCTL offsets for these additional CCD
sensors on older Zen/Zen+ CPUs? We compenstate for that above, which
I think you're losing here.
> + 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;
> + }
> + }
> }
>
>