summaryrefslogtreecommitdiffstats
path: root/sys/dev/amdtemp
diff options
context:
space:
mode:
authorjkim <jkim@FreeBSD.org>2009-09-11 21:47:44 +0000
committerjkim <jkim@FreeBSD.org>2009-09-11 21:47:44 +0000
commit46839b8eeb4c03f6da5ea0dd9f8e62273ea9bfbe (patch)
tree4317211423954909781b4d5add14e21716a1bfc5 /sys/dev/amdtemp
parent75d000f4cedbe5d5e381c135f97120e3ecad9be1 (diff)
downloadFreeBSD-src-46839b8eeb4c03f6da5ea0dd9f8e62273ea9bfbe.zip
FreeBSD-src-46839b8eeb4c03f6da5ea0dd9f8e62273ea9bfbe.tar.gz
Improve amdtemp(4) significantly:
- Improve newer AMD processor support (Family 0Fh Revision F and later). - Adjust offset if DiodeOffet is set and valid. Note it is experimental but it seems to give us more realistic temperatures. Newer Linux driver blindly adds 21C for Family 0Fh desktop processors, however. - Always populate dev.cpu and dev.amdtemp sysctl trees regardless of probe order for consistency. Previously, dev.cpu.N.temperature was not populated if amdtemp was loaded later than ACPI CPU driver and temperatures were not accessible from dev.amdtemp.N.sensor0 tree for Family 10h/11h processors. - Read the CPUID from PCI register instead of CPUID instruction to prevent possible revision mismatches on multi-socket system. - Change macros and variables to make them closer to AMD documents. - Fix style(9) nits and improve comments.
Diffstat (limited to 'sys/dev/amdtemp')
-rw-r--r--sys/dev/amdtemp/amdtemp.c351
1 files changed, 226 insertions, 125 deletions
diff --git a/sys/dev/amdtemp/amdtemp.c b/sys/dev/amdtemp/amdtemp.c
index fdf0875..ababa89 100644
--- a/sys/dev/amdtemp/amdtemp.c
+++ b/sys/dev/amdtemp/amdtemp.c
@@ -1,6 +1,7 @@
/*-
* Copyright (c) 2008, 2009 Rui Paulo <rpaulo@FreeBSD.org>
* Copyright (c) 2009 Norikatsu Shigemura <nork@FreeBSD.org>
+ * Copyright (c) 2009 Jung-uk Kim <jkim@FreeBSD.org>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -26,8 +27,8 @@
*/
/*
- * Driver for the AMD K8/K10/K11 thermal sensors. Initially based on the
- * k8temp Linux driver.
+ * Driver for the AMD CPU on-die thermal sensors for Family 0Fh/10h/11h procs.
+ * Initially based on the k8temp Linux driver.
*/
#include <sys/cdefs.h>
@@ -35,18 +36,15 @@ __FBSDID("$FreeBSD$");
#include <sys/param.h>
#include <sys/bus.h>
-#include <sys/systm.h>
-#include <sys/types.h>
-#include <sys/module.h>
#include <sys/conf.h>
#include <sys/kernel.h>
+#include <sys/module.h>
#include <sys/sysctl.h>
+#include <sys/systm.h>
-#include <machine/specialreg.h>
-#include <machine/cpufunc.h>
#include <machine/md_var.h>
+#include <machine/specialreg.h>
-#include <dev/pci/pcireg.h>
#include <dev/pci/pcivar.h>
typedef enum {
@@ -60,18 +58,19 @@ typedef enum {
struct amdtemp_softc {
device_t sc_dev;
- int sc_temps[4];
+ uint32_t sc_mask;
+ int sc_ncores;
int sc_ntemps;
- struct sysctl_oid *sc_oid;
- struct sysctl_oid *sc_sysctl_cpu[2];
+ int sc_swap;
+ int32_t (*sc_gettemp)(device_t, amdsensor_t);
+ struct sysctl_oid *sc_sysctl_cpu[MAXCPU];
struct intr_config_hook sc_ich;
- int32_t (*sc_gettemp)(device_t, amdsensor_t);
};
-#define VENDORID_AMD 0x1022
-#define DEVICEID_AMD_MISC0F 0x1103
-#define DEVICEID_AMD_MISC10 0x1203
-#define DEVICEID_AMD_MISC11 0x1303
+#define VENDORID_AMD 0x1022
+#define DEVICEID_AMD_MISC0F 0x1103
+#define DEVICEID_AMD_MISC10 0x1203
+#define DEVICEID_AMD_MISC11 0x1303
static struct amdtemp_product {
uint16_t amdtemp_vendorid;
@@ -84,22 +83,21 @@ static struct amdtemp_product {
};
/*
- * Register control (K8 family)
+ * Reported Temperature Control Register (Family 10h/11h only)
*/
-#define AMDTEMP_REG0F 0xe4
-#define AMDTEMP_REG_SELSENSOR 0x40
-#define AMDTEMP_REG_SELCORE 0x04
+#define AMDTEMP_REPTMP_CTRL 0xa4
/*
- * Register control (K10 & K11) family
+ * Thermaltrip Status Register
*/
-#define AMDTEMP_REG 0xa4
-
-#define TZ_ZEROC 2732
+#define AMDTEMP_THERMTP_STAT 0xe4
+#define AMDTEMP_TTSR_SELCORE 0x04 /* Family 0Fh only */
+#define AMDTEMP_TTSR_SELSENSOR 0x40 /* Family 0Fh only */
- /* -49 C is the mininum temperature */
-#define AMDTEMP_OFFSET0F (TZ_ZEROC-490)
-#define AMDTEMP_OFFSET (TZ_ZEROC)
+/*
+ * CPU Family/Model Register
+ */
+#define AMDTEMP_CPUID 0xfc
/*
* Device methods.
@@ -138,8 +136,8 @@ amdtemp_match(device_t dev)
{
int i;
uint16_t vendor, devid;
-
- vendor = pci_get_vendor(dev);
+
+ vendor = pci_get_vendor(dev);
devid = pci_get_device(dev);
for (i = 0; amdtemp_products[i].amdtemp_vendorid != 0; i++) {
@@ -159,32 +157,47 @@ amdtemp_identify(driver_t *driver, device_t parent)
/* Make sure we're not being doubly invoked. */
if (device_find_child(parent, "amdtemp", -1) != NULL)
return;
-
+
if (amdtemp_match(parent)) {
child = device_add_child(parent, "amdtemp", -1);
if (child == NULL)
device_printf(parent, "add amdtemp child failed\n");
}
-
}
static int
amdtemp_probe(device_t dev)
{
- uint32_t regs[4];
-
+ uint32_t cpuid, family, model, temp;
+
if (resource_disabled("amdtemp", 0))
return (ENXIO);
- do_cpuid(1, regs);
- switch (regs[0]) {
- case 0xf40:
- case 0xf50:
- case 0xf51:
+ cpuid = pci_read_config(dev, AMDTEMP_CPUID, 4);
+ family = CPUID_TO_FAMILY(cpuid);
+ model = CPUID_TO_MODEL(cpuid);
+
+ switch (family) {
+ case 0x0f:
+ if ((model == 0x04 && (cpuid & CPUID_STEPPING) == 0) ||
+ (model == 0x05 && (cpuid & CPUID_STEPPING) <= 1))
+ return (ENXIO);
+ break;
+ case 0x10:
+ case 0x11:
+ /*
+ * DiodeOffset must be non-zero if thermal diode is supported.
+ */
+ temp = pci_read_config(dev, AMDTEMP_THERMTP_STAT, 4);
+ temp = (temp >> 8) & 0x7f;
+ if (temp == 0)
+ return (ENXIO);
+ break;
+ default:
return (ENXIO);
}
- device_set_desc(dev, "AMD K8 Thermal Sensors");
-
+ device_set_desc(dev, "AMD CPU On-Die Thermal Sensors");
+
return (BUS_PROBE_GENERIC);
}
@@ -194,63 +207,119 @@ amdtemp_attach(device_t dev)
struct amdtemp_softc *sc = device_get_softc(dev);
struct sysctl_ctx_list *sysctlctx;
struct sysctl_oid *sysctlnode;
+ uint32_t cpuid, family, model;
+
+ cpuid = pci_read_config(dev, AMDTEMP_CPUID, 4);
+ family = CPUID_TO_FAMILY(cpuid);
+ model = CPUID_TO_MODEL(cpuid);
+
+ switch (family) {
+ case 0x0f:
+ /*
+ * Thermaltrip Status Register - CurTmp
+ *
+ * Revision G: bits 23-14
+ * Earlier: bits 23-16
+ */
+ if (model >= 0x60 && model != 0xc1)
+ sc->sc_mask = 0x3ff << 14;
+ else
+ sc->sc_mask = 0xff << 16;
+
+ /*
+ * Thermaltrip Status Register - ThermSenseCoreSel
+ *
+ * Revision F: 0 - Core1, 1 - Core0
+ * Earlier: 0 - Core0, 1 - Core1
+ */
+ sc->sc_swap = (model >= 0x40);
+
+ /*
+ * There are two sensors per core.
+ */
+ sc->sc_ntemps = 2;
-
- /*
- * Setup intrhook function to create dev.cpu sysctl entries. This is
- * needed because the cpu driver may be loaded late on boot, after
- * us.
- */
- sc->sc_ich.ich_func = amdtemp_intrhook;
- sc->sc_ich.ich_arg = dev;
- if (config_intrhook_establish(&sc->sc_ich) != 0) {
- device_printf(dev, "config_intrhook_establish "
- "failed!\n");
- return (ENXIO);
- }
-
- if (pci_get_device(dev) == DEVICEID_AMD_MISC0F)
sc->sc_gettemp = amdtemp_gettemp0f;
- else {
+ break;
+ case 0x10:
+ case 0x11:
+ /*
+ * Reported Temperature Control Register - Curtmp
+ */
+ sc->sc_mask = 0x3ff << 21;
+
+ /*
+ * There is only one sensor per package.
+ */
+ sc->sc_ntemps = 1;
+
sc->sc_gettemp = amdtemp_gettemp;
- return (0);
+ break;
}
+ /* Find number of cores per package. */
+ sc->sc_ncores = (amd_feature2 & AMDID2_CMP) != 0 ?
+ (cpu_procinfo2 & AMDID_CMP_CORES) + 1 : 1;
+ if (sc->sc_ncores > MAXCPU)
+ return (ENXIO);
+
+ if (bootverbose)
+ device_printf(dev, "Found %d cores and %d sensors.\n",
+ sc->sc_ncores,
+ sc->sc_ntemps > 1 ? sc->sc_ntemps * sc->sc_ncores : 1);
+
/*
* dev.amdtemp.N tree.
*/
sysctlctx = device_get_sysctl_ctx(dev);
sysctlnode = SYSCTL_ADD_NODE(sysctlctx,
- SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO, "sensor0",
- CTLFLAG_RD, 0, "Sensor 0");
-
+ SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO,
+ "sensor0", CTLFLAG_RD, 0, "Sensor 0");
+
SYSCTL_ADD_PROC(sysctlctx,
SYSCTL_CHILDREN(sysctlnode),
OID_AUTO, "core0", CTLTYPE_INT | CTLFLAG_RD,
dev, SENSOR0_CORE0, amdtemp_sysctl, "IK",
"Sensor 0 / Core 0 temperature");
-
- SYSCTL_ADD_PROC(sysctlctx,
- SYSCTL_CHILDREN(sysctlnode),
- OID_AUTO, "core1", CTLTYPE_INT | CTLFLAG_RD,
- dev, SENSOR0_CORE1, amdtemp_sysctl, "IK",
- "Sensor 0 / Core 1 temperature");
-
- sysctlnode = SYSCTL_ADD_NODE(sysctlctx,
- SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO, "sensor1",
- CTLFLAG_RD, 0, "Sensor 1");
-
- SYSCTL_ADD_PROC(sysctlctx,
- SYSCTL_CHILDREN(sysctlnode),
- OID_AUTO, "core0", CTLTYPE_INT | CTLFLAG_RD,
- dev, SENSOR1_CORE0, amdtemp_sysctl, "IK",
- "Sensor 1 / Core 0 temperature");
-
- SYSCTL_ADD_PROC(sysctlctx,
- SYSCTL_CHILDREN(sysctlnode),
- OID_AUTO, "core1", CTLTYPE_INT | CTLFLAG_RD,
- dev, SENSOR1_CORE1, amdtemp_sysctl, "IK",
- "Sensor 1 / Core 1 temperature");
+
+ if (sc->sc_ntemps > 1) {
+ if (sc->sc_ncores > 1)
+ SYSCTL_ADD_PROC(sysctlctx,
+ SYSCTL_CHILDREN(sysctlnode),
+ OID_AUTO, "core1", CTLTYPE_INT | CTLFLAG_RD,
+ dev, SENSOR0_CORE1, amdtemp_sysctl, "IK",
+ "Sensor 0 / Core 1 temperature");
+
+ sysctlnode = SYSCTL_ADD_NODE(sysctlctx,
+ SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO,
+ "sensor1", CTLFLAG_RD, 0, "Sensor 1");
+
+ SYSCTL_ADD_PROC(sysctlctx,
+ SYSCTL_CHILDREN(sysctlnode),
+ OID_AUTO, "core0", CTLTYPE_INT | CTLFLAG_RD,
+ dev, SENSOR1_CORE0, amdtemp_sysctl, "IK",
+ "Sensor 1 / Core 0 temperature");
+
+ if (sc->sc_ncores > 1)
+ SYSCTL_ADD_PROC(sysctlctx,
+ SYSCTL_CHILDREN(sysctlnode),
+ OID_AUTO, "core1", CTLTYPE_INT | CTLFLAG_RD,
+ dev, SENSOR1_CORE1, amdtemp_sysctl, "IK",
+ "Sensor 1 / Core 1 temperature");
+ }
+
+ /*
+ * Try to create dev.cpu sysctl entries and setup intrhook function.
+ * This is needed because the cpu driver may be loaded late on boot,
+ * after us.
+ */
+ amdtemp_intrhook(dev);
+ sc->sc_ich.ich_func = amdtemp_intrhook;
+ sc->sc_ich.ich_arg = dev;
+ if (config_intrhook_establish(&sc->sc_ich) != 0) {
+ device_printf(dev, "config_intrhook_establish failed!\n");
+ return (ENXIO);
+ }
return (0);
}
@@ -258,61 +327,67 @@ amdtemp_attach(device_t dev)
void
amdtemp_intrhook(void *arg)
{
- int i;
- device_t nexus, acpi, cpu;
- device_t dev = (device_t) arg;
struct amdtemp_softc *sc;
struct sysctl_ctx_list *sysctlctx;
+ device_t dev = (device_t)arg;
+ device_t acpi, cpu, nexus;
+ amdsensor_t sensor;
+ int i;
sc = device_get_softc(dev);
-
+
/*
* dev.cpu.N.temperature.
*/
nexus = device_find_child(root_bus, "nexus", 0);
acpi = device_find_child(nexus, "acpi", 0);
- for (i = 0; i < 2; i++) {
+ for (i = 0; i < sc->sc_ncores; i++) {
+ if (sc->sc_sysctl_cpu[i] != NULL)
+ continue;
cpu = device_find_child(acpi, "cpu",
- device_get_unit(dev) * 2 + i);
- if (cpu) {
+ device_get_unit(dev) * sc->sc_ncores + i);
+ if (cpu != NULL) {
sysctlctx = device_get_sysctl_ctx(cpu);
+ sensor = sc->sc_ntemps > 1 ?
+ (i == 0 ? CORE0 : CORE1) : SENSOR0_CORE0;
sc->sc_sysctl_cpu[i] = SYSCTL_ADD_PROC(sysctlctx,
SYSCTL_CHILDREN(device_get_sysctl_tree(cpu)),
OID_AUTO, "temperature", CTLTYPE_INT | CTLFLAG_RD,
- dev, CORE0, amdtemp_sysctl, "IK",
- "Max of sensor 0 / 1");
+ dev, sensor, amdtemp_sysctl, "IK",
+ "Current temparature");
}
}
- config_intrhook_disestablish(&sc->sc_ich);
+ if (sc->sc_ich.ich_arg != NULL)
+ config_intrhook_disestablish(&sc->sc_ich);
}
int
amdtemp_detach(device_t dev)
{
- int i;
struct amdtemp_softc *sc = device_get_softc(dev);
-
- for (i = 0; i < 2; i++) {
- if (sc->sc_sysctl_cpu[i])
+ int i;
+
+ for (i = 0; i < sc->sc_ncores; i++)
+ if (sc->sc_sysctl_cpu[i] != NULL)
sysctl_remove_oid(sc->sc_sysctl_cpu[i], 1, 0);
- }
/* NewBus removes the dev.amdtemp.N tree by itself. */
-
+
return (0);
}
static int
amdtemp_sysctl(SYSCTL_HANDLER_ARGS)
{
- device_t dev = (device_t) arg1;
+ device_t dev = (device_t)arg1;
struct amdtemp_softc *sc = device_get_softc(dev);
+ amdsensor_t sensor = (amdsensor_t)arg2;
+ int32_t auxtemp[2], temp;
int error;
- int32_t temp, auxtemp[2];
- switch (arg2) {
+ switch (sensor) {
case CORE0:
auxtemp[0] = sc->sc_gettemp(dev, SENSOR0_CORE0);
auxtemp[1] = sc->sc_gettemp(dev, SENSOR1_CORE0);
@@ -324,54 +399,80 @@ amdtemp_sysctl(SYSCTL_HANDLER_ARGS)
temp = imax(auxtemp[0], auxtemp[1]);
break;
default:
- temp = sc->sc_gettemp(dev, arg2);
+ temp = sc->sc_gettemp(dev, sensor);
break;
}
error = sysctl_handle_int(oidp, &temp, 0, req);
-
+
return (error);
}
+#define AMDTEMP_ZERO_C_TO_K 2732
+
static int32_t
amdtemp_gettemp0f(device_t dev, amdsensor_t sensor)
{
- uint8_t cfg;
+ struct amdtemp_softc *sc = device_get_softc(dev);
uint32_t temp;
-
- cfg = pci_read_config(dev, AMDTEMP_REG0F, 1);
+ int32_t diode_offset, offset;
+ uint8_t cfg, sel;
+
+ /* Set Sensor/Core selector. */
+ sel = 0;
switch (sensor) {
- case SENSOR0_CORE0:
- cfg &= ~(AMDTEMP_REG_SELSENSOR | AMDTEMP_REG_SELCORE);
- break;
- case SENSOR0_CORE1:
- cfg &= ~AMDTEMP_REG_SELSENSOR;
- cfg |= AMDTEMP_REG_SELCORE;
- break;
case SENSOR1_CORE0:
- cfg &= ~AMDTEMP_REG_SELCORE;
- cfg |= AMDTEMP_REG_SELSENSOR;
+ sel |= AMDTEMP_TTSR_SELSENSOR;
+ /* FALLTROUGH */
+ case SENSOR0_CORE0:
+ case CORE0:
+ if (sc->sc_swap)
+ sel |= AMDTEMP_TTSR_SELCORE;
break;
case SENSOR1_CORE1:
- cfg |= (AMDTEMP_REG_SELSENSOR | AMDTEMP_REG_SELCORE);
- break;
- default:
- cfg = 0;
+ sel |= AMDTEMP_TTSR_SELSENSOR;
+ /* FALLTROUGH */
+ case SENSOR0_CORE1:
+ case CORE1:
+ if (!sc->sc_swap)
+ sel |= AMDTEMP_TTSR_SELCORE;
break;
}
- pci_write_config(dev, AMDTEMP_REG0F, cfg, 1);
- temp = pci_read_config(dev, AMDTEMP_REG0F, 4);
- temp = ((temp >> 16) & 0xff) * 10 + AMDTEMP_OFFSET0F;
-
+ cfg = pci_read_config(dev, AMDTEMP_THERMTP_STAT, 1);
+ cfg &= ~(AMDTEMP_TTSR_SELSENSOR | AMDTEMP_TTSR_SELCORE);
+ pci_write_config(dev, AMDTEMP_THERMTP_STAT, cfg | sel, 1);
+
+ /* CurTmp starts from -49C. */
+ offset = AMDTEMP_ZERO_C_TO_K - 490;
+
+ /* Adjust offset if DiodeOffset is set and valid. */
+ temp = pci_read_config(dev, AMDTEMP_THERMTP_STAT, 4);
+ diode_offset = (temp >> 8) & 0x3f;
+ if (diode_offset != 0)
+ offset += (diode_offset - 11) * 10;
+
+ temp = ((temp & sc->sc_mask) >> 14) * 5 / 2 + offset;
+
return (temp);
}
static int32_t
amdtemp_gettemp(device_t dev, amdsensor_t sensor)
{
+ struct amdtemp_softc *sc = device_get_softc(dev);
uint32_t temp;
+ int32_t diode_offset, offset;
+
+ /* CurTmp starts from 0C. */
+ offset = AMDTEMP_ZERO_C_TO_K;
+
+ /* Adjust offset if DiodeOffset is set and valid. */
+ temp = pci_read_config(dev, AMDTEMP_THERMTP_STAT, 4);
+ diode_offset = (temp >> 8) & 0x7f;
+ if (diode_offset > 0 && diode_offset < 0x40)
+ offset += (diode_offset - 11) * 10;
- temp = pci_read_config(dev, AMDTEMP_REG, 4);
- temp = ((temp >> 21) & 0x3ff) * 10 / 8 + AMDTEMP_OFFSET;
+ temp = pci_read_config(dev, AMDTEMP_REPTMP_CTRL, 4);
+ temp = ((temp & sc->sc_mask) >> 21) * 5 / 4 + offset;
return (temp);
}
OpenPOWER on IntegriCloud