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.
This commit is contained in:
Jung-uk Kim 2009-09-11 21:47:44 +00:00
parent d16d35fd54
commit a4165bba6f
Notes: svn2git 2020-12-20 02:59:44 +00:00
svn path=/head/; revision=197102

View File

@ -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 AMDTEMP_THERMTP_STAT 0xe4
#define AMDTEMP_TTSR_SELCORE 0x04 /* Family 0Fh only */
#define AMDTEMP_TTSR_SELSENSOR 0x40 /* Family 0Fh only */
#define TZ_ZEROC 2732
/* -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;
temp = pci_read_config(dev, AMDTEMP_REG, 4);
temp = ((temp >> 21) & 0x3ff) * 10 / 8 + AMDTEMP_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_REPTMP_CTRL, 4);
temp = ((temp & sc->sc_mask) >> 21) * 5 / 4 + offset;
return (temp);
}