svn commit: r197102 - head/sys/dev/amdtemp

Jung-uk Kim jkim at FreeBSD.org
Fri Sep 11 21:47:45 UTC 2009


Author: jkim
Date: Fri Sep 11 21:47:44 2009
New Revision: 197102
URL: http://svn.freebsd.org/changeset/base/197102

Log:
  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.

Modified:
  head/sys/dev/amdtemp/amdtemp.c

Modified: head/sys/dev/amdtemp/amdtemp.c
==============================================================================
--- head/sys/dev/amdtemp/amdtemp.c	Fri Sep 11 21:09:59 2009	(r197101)
+++ head/sys/dev/amdtemp/amdtemp.c	Fri Sep 11 21:47:44 2009	(r197102)
@@ -1,6 +1,7 @@
 /*-
  * Copyright (c) 2008, 2009 Rui Paulo <rpaulo at FreeBSD.org>
  * Copyright (c) 2009 Norikatsu Shigemura <nork at FreeBSD.org>
+ * Copyright (c) 2009 Jung-uk Kim <jkim at 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, devic
 	/* 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);
 }


More information about the svn-src-head mailing list