Coretemp(4) driver for Intel Core on-die digital thermal sensor with patch
authorHasso Tepper <hasso@dragonflybsd.org>
Tue, 2 Oct 2007 13:16:42 +0000 (13:16 +0000)
committerHasso Tepper <hasso@dragonflybsd.org>
Tue, 2 Oct 2007 13:16:42 +0000 (13:16 +0000)
from Constantine A. Murenin <mureninc at gmail.com> to make it use
hw.sensors framework.

Obtained-from: FreeBSD with modifications from Constantine A. Murenin

share/man/man4/Makefile
share/man/man4/coretemp.4 [new file with mode: 0644]
sys/conf/files
sys/config/LINT
sys/cpu/i386/include/specialreg.h
sys/dev/powermng/Makefile
sys/dev/powermng/coretemp/Makefile [new file with mode: 0644]
sys/dev/powermng/coretemp/coretemp.c [new file with mode: 0644]

index b0427ac..33cd77f 100644 (file)
@@ -1,6 +1,6 @@
 #      @(#)Makefile    8.1 (Berkeley) 6/18/93
 # $FreeBSD: src/share/man/man4/Makefile,v 1.83.2.66 2003/06/04 17:10:30 sam Exp $
-# $DragonFly: src/share/man/man4/Makefile,v 1.67 2007/09/16 18:26:18 swildner Exp $
+# $DragonFly: src/share/man/man4/Makefile,v 1.68 2007/10/02 13:16:42 hasso Exp $
 
 MAN=   aac.4 \
        acpi.4 \
@@ -41,6 +41,7 @@ MAN=  aac.4 \
        cd.4 \
        ch.4 \
        ciss.4 \
+       coretemp.4 \
        crypto.4 \
        csa.4 \
        cue.4 \
diff --git a/share/man/man4/coretemp.4 b/share/man/man4/coretemp.4
new file mode 100644 (file)
index 0000000..be9c8f4
--- /dev/null
@@ -0,0 +1,82 @@
+.\"-
+.\" Copyright (c) 2007 Dag-Erling Coïdan Smørgrav
+.\" All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\"    notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\"    notice, this list of conditions and the following disclaimer in the
+.\"    documentation and/or other materials provided with the distribution.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" $FreeBSD: src/share/man/man4/coretemp.4,v 1.1 2007/08/23 20:05:09 des Exp $
+.\" $DragonFly: src/share/man/man4/coretemp.4,v 1.1 2007/10/02 13:16:42 hasso Exp $
+.\"
+.Dd September 13, 2007
+.Dt CORETEMP 4
+.Os
+.Sh NAME
+.Nm coretemp
+.Nd device driver for Intel Core on-die digital thermal sensor
+.Sh SYNOPSIS
+To compile this driver into the kernel,
+place the following line in your
+kernel configuration file:
+.Bd -ragged -offset indent
+.Cd "device coretemp"
+.Ed
+.Pp
+Alternatively, to load the driver as a
+module at boot time, place the following line in
+.Xr loader.conf 5 :
+.Bd -literal -offset indent
+coretemp_load="YES"
+.Ed
+.Sh DESCRIPTION
+The
+.Nm
+driver provides support for the on-die digital thermal sensor present
+in Intel Core and newer CPUs.
+The values are exposed through the
+.Va HW_SENSORS
+.Xr sysctl 3
+tree.
+For example:
+.Bd -literal -offset indent
+%sysctl hw.sensors
+hw.sensors.cpu0.temp0: 28.00 degC
+hw.sensors.cpu1.temp0: 29.00 degC
+.Ed
+.Sh SEE ALSO
+.Xr systat 1 ,
+.Xr sysctl 3 ,
+.Xr sensorsd 8 ,
+.Xr sysctl 8
+.Sh HISTORY
+The
+.Nm
+driver first appeared in
+.Fx 7.0 .
+.Sh AUTHORS
+.An -nosplit
+The
+.Nm
+driver was written by
+.An Rui Paulo Aq rpaulo@FreeBSD.org
+as part of a Google Summer of Code project.
+This manual page was written by
+.An Dag-Erling Sm\(/orgrav Aq des@FreeBSD.org .
index 9054252..01c7d16 100644 (file)
@@ -1,5 +1,5 @@
 # $FreeBSD: src/sys/conf/files,v 1.340.2.137 2003/06/04 17:10:30 sam Exp $
-# $DragonFly: src/sys/conf/files,v 1.182 2007/10/02 12:57:00 hasso Exp $
+# $DragonFly: src/sys/conf/files,v 1.183 2007/10/02 13:16:42 hasso Exp $
 #
 # The long compile-with and dependency lines are required because of
 # limitations in config: backslash-newline doesn't work in strings, and
@@ -251,6 +251,7 @@ dev/atm/hfa/fore_vcm.c              optional hfa
 dev/netif/ie/if_ie.c           optional ie isa
 dev/powermng/ichsmb/ichsmb.c   optional ichsmb
 dev/powermng/ichsmb/ichsmb_pci.c       optional ichsmb pci
+dev/powermng/coretemp/coretemp.c       optional coretemp
 dev/raid/ida/ida.c             optional ida
 dev/raid/ida/ida_disk.c        optional ida
 dev/raid/ida/ida_eisa.c        optional ida eisa
index a839da1..d167921 100644 (file)
@@ -3,7 +3,7 @@
 #      as much of the source tree as it can.
 #
 # $FreeBSD: src/sys/i386/conf/LINT,v 1.749.2.144 2003/06/04 17:56:59 sam Exp $
-# $DragonFly: src/sys/config/LINT,v 1.131 2007/09/02 13:27:23 sephe Exp $
+# $DragonFly: src/sys/config/LINT,v 1.132 2007/10/02 13:16:42 hasso Exp $
 #
 # NB: You probably don't want to try running a kernel built from this
 # file.  Instead, you should start from GENERIC, and add options from
@@ -2068,6 +2068,9 @@ device            iicsmb          # smb over i2c bridge
 
 device         pcf0    at isa? port 0x320 irq 5
 
+# Intel Core and newer CPUs on-die digital thermal sensor support
+device         coretemp
+
 #---------------------------------------------------------------------------
 # ISDN4BSD
 #
index 059058a..e0207ec 100644 (file)
@@ -32,7 +32,7 @@
  *
  *     from: @(#)specialreg.h  7.1 (Berkeley) 5/9/91
  * $FreeBSD: src/sys/i386/include/specialreg.h,v 1.19.2.3 2003/01/22 17:24:28 jhb Exp $
- * $DragonFly: src/sys/cpu/i386/include/specialreg.h,v 1.8 2007/01/12 23:12:59 tgen Exp $
+ * $DragonFly: src/sys/cpu/i386/include/specialreg.h,v 1.9 2007/10/02 13:16:42 hasso Exp $
  */
 
 #ifndef _CPU_SPECIALREG_H_
 #define MSR_BIOS_SIGN          0x08b
 #define MSR_PERFCTR0           0x0c1
 #define MSR_PERFCTR1           0x0c2
+#define MSR_IA32_EXT_CONFIG    0x0ee   /* Undocumented. Core Solo/Duo only */
 #define MSR_MTRRcap            0x0fe
 #define MSR_MCG_CAP            0x179
 #define MSR_MCG_STATUS         0x17a
index a6f73b4..05746ce 100644 (file)
@@ -1,6 +1,6 @@
-# $DragonFly: src/sys/dev/powermng/Makefile,v 1.1 2003/08/15 08:32:31 dillon Exp $
+# $DragonFly: src/sys/dev/powermng/Makefile,v 1.2 2007/10/02 13:16:42 hasso Exp $
 #
 
-SUBDIR=
+SUBDIR=        coretemp
 
 .include <bsd.subdir.mk>
diff --git a/sys/dev/powermng/coretemp/Makefile b/sys/dev/powermng/coretemp/Makefile
new file mode 100644 (file)
index 0000000..a72c216
--- /dev/null
@@ -0,0 +1,6 @@
+# $DragonFly: src/sys/dev/powermng/coretemp/Makefile,v 1.1 2007/10/02 13:16:42 hasso Exp $
+
+KMOD=  coretemp
+SRCS=  coretemp.c bus_if.h device_if.h
+
+.include <bsd.kmod.mk>
diff --git a/sys/dev/powermng/coretemp/coretemp.c b/sys/dev/powermng/coretemp/coretemp.c
new file mode 100644 (file)
index 0000000..c9de4ce
--- /dev/null
@@ -0,0 +1,291 @@
+/*
+ * Copyright (c) 2007 Rui Paulo <rpaulo@FreeBSD.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+ * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ *
+ * $FreeBSD: src/sys/dev/coretemp/coretemp.c,v 1.2 2007/08/23 10:53:03 des Exp $
+ * $DragonFly: src/sys/dev/powermng/coretemp/coretemp.c,v 1.1 2007/10/02 13:16:42 hasso Exp $
+ */
+
+/*
+ * Device driver for Intel's On Die thermal sensor via MSR.
+ * First introduced in Intel's Core line of processors.
+ */
+
+#include <sys/cdefs.h>
+#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/sensors.h>
+#include <sys/spinlock.h>
+#include <sys/spinlock2.h>
+#include <sys/proc.h>  /* for curthread */
+#include <sys/sched.h>
+
+#include <machine/specialreg.h>
+#include <machine/cpufunc.h>
+#include <machine/md_var.h>
+
+static struct spinlock         coretemp_lock;
+
+struct coretemp_softc {
+       struct ksensordev       sc_sensordev;
+       struct ksensor          sc_sensor;
+       device_t                sc_dev;
+       int                     sc_tjmax;
+};
+
+/*
+ * Device methods.
+ */
+static void    coretemp_identify(driver_t *driver, device_t parent);
+static int     coretemp_probe(device_t dev);
+static int     coretemp_attach(device_t dev);
+static int     coretemp_detach(device_t dev);
+
+static int     coretemp_get_temp(device_t dev);
+static void    coretemp_refresh(void *arg);
+
+static device_method_t coretemp_methods[] = {
+       /* Device interface */
+       DEVMETHOD(device_identify,      coretemp_identify),
+       DEVMETHOD(device_probe,         coretemp_probe),
+       DEVMETHOD(device_attach,        coretemp_attach),
+       DEVMETHOD(device_detach,        coretemp_detach),
+
+       {0, 0}
+};
+
+static driver_t coretemp_driver = {
+       "coretemp",
+       coretemp_methods,
+       sizeof(struct coretemp_softc),
+};
+
+static devclass_t coretemp_devclass;
+DRIVER_MODULE(coretemp, cpu, coretemp_driver, coretemp_devclass, NULL, NULL);
+
+static void
+coretemp_identify(driver_t *driver, device_t parent)
+{
+       device_t child;
+       u_int regs[4];
+
+       /* Make sure we're not being doubly invoked. */
+       if (device_find_child(parent, "coretemp", -1) != NULL)
+               return;
+
+       /* Check that CPUID is supported and the vendor is Intel.*/
+       if (cpu_high == 0 || strcmp(cpu_vendor, "GenuineIntel"))
+               return;
+       /*
+        * CPUID 0x06 returns 1 if the processor has on-die thermal
+        * sensors. EBX[0:3] contains the number of sensors.
+        */
+       do_cpuid(0x06, regs);
+       if ((regs[0] & 0x1) != 1)
+               return;
+
+       /*
+        * We add a child for each CPU since settings must be performed
+        * on each CPU in the SMP case.
+        */
+       child = device_add_child(parent, "coretemp", -1);
+       if (child == NULL)
+               device_printf(parent, "add coretemp child failed\n");
+}
+
+static int
+coretemp_probe(device_t dev)
+{
+       if (resource_disabled("coretemp", 0))
+               return (ENXIO);
+
+       device_set_desc(dev, "CPU On-Die Thermal Sensors");
+
+       return (BUS_PROBE_GENERIC);
+}
+
+static int
+coretemp_attach(device_t dev)
+{
+       struct coretemp_softc *sc = device_get_softc(dev);
+       device_t pdev;
+       uint64_t msr;
+       int cpu_model;
+       int cpu_mask;
+
+       sc->sc_dev = dev;
+       pdev = device_get_parent(dev);
+       cpu_model = (cpu_id >> 4) & 15;
+       /* extended model */
+       cpu_model += ((cpu_id >> 16) & 0xf) << 4;
+       cpu_mask = cpu_id & 15;
+
+       /*
+        * Check for errata AE18.
+        * "Processor Digital Thermal Sensor (DTS) Readout stops
+        *  updating upon returning from C3/C4 state."
+        *
+        * Adapted from the Linux coretemp driver.
+        */
+       if (cpu_model == 0xe && cpu_mask < 0xc) {
+               msr = rdmsr(MSR_BIOS_SIGN);
+               msr = msr >> 32;
+               if (msr < 0x39) {
+                       device_printf(dev, "not supported (Intel errata "
+                           "AE18), try updating your BIOS\n");
+                       return (ENXIO);
+               }
+       }
+       /*
+        * On some Core 2 CPUs, there's an undocumented MSR that
+        * can tell us if Tj(max) is 100 or 85.
+        *
+        * The if-clause for CPUs having the MSR_IA32_EXT_CONFIG was adapted
+        * from the Linux coretemp driver.
+        */
+       sc->sc_tjmax = 100;
+       if ((cpu_model == 0xf && cpu_mask >= 2) || cpu_model == 0xe) {
+               msr = rdmsr(MSR_IA32_EXT_CONFIG);
+               if (msr & (1 << 30))
+                       sc->sc_tjmax = 85;
+       }
+
+       /*
+        * Add hw.sensors.cpuN.temp0 MIB.
+        */
+       strlcpy(sc->sc_sensordev.xname, device_get_nameunit(pdev),
+           sizeof(sc->sc_sensordev.xname));
+       sc->sc_sensor.type = SENSOR_TEMP;
+       sensor_attach(&sc->sc_sensordev, &sc->sc_sensor);
+       if (sensor_task_register(sc, coretemp_refresh, 2)) {
+               device_printf(dev, "unable to register update task\n");
+               return (ENXIO);
+       }
+       sensordev_install(&sc->sc_sensordev);
+       spin_init(&coretemp_lock);
+
+       return (0);
+}
+
+static int
+coretemp_detach(device_t dev)
+{
+       struct coretemp_softc *sc = device_get_softc(dev);
+
+       spin_lock_wr(&coretemp_lock);
+       sensordev_deinstall(&sc->sc_sensordev);
+       sensor_task_unregister(sc);
+       spin_unlock_wr(&coretemp_lock);
+       spin_uninit(&coretemp_lock);
+
+       return (0);
+}
+
+
+static int
+coretemp_get_temp(device_t dev)
+{
+       uint64_t msr;
+       int temp;
+       int cpu = device_get_unit(dev);
+       struct coretemp_softc *sc = device_get_softc(dev);
+       char stemp[16];
+
+       /*
+        * Bind to specific CPU to read the correct temperature.
+        * If not all CPUs are initialised, then only read from
+        * cpu0, returning -1 on all other CPUs.
+        */
+       if (ncpus > 1) {
+               spin_lock_rd(&coretemp_lock);
+               msr = rdmsr(MSR_THERM_STATUS);
+               spin_unlock_rd(&coretemp_lock);
+       } else if (cpu != 0)
+               return (-1);
+       else
+               msr = rdmsr(MSR_THERM_STATUS);
+
+       /*
+        * Check for Thermal Status and Thermal Status Log.
+        */
+       if ((msr & 0x3) == 0x3)
+               device_printf(dev, "PROCHOT asserted\n");
+
+       /*
+        * Bit 31 contains "Reading valid"
+        */
+       if (((msr >> 31) & 0x1) == 1) {
+               /*
+                * Starting on bit 16 and ending on bit 22.
+                */
+               temp = sc->sc_tjmax - ((msr >> 16) & 0x7f);
+       } else
+               temp = -1;
+
+       /*
+        * Check for Critical Temperature Status and Critical
+        * Temperature Log.
+        * It doesn't really matter if the current temperature is
+        * invalid because the "Critical Temperature Log" bit will
+        * tell us if the Critical Temperature has been reached in
+        * past. It's not directly related to the current temperature.
+        *
+        * If we reach a critical level, allow devctl(4) to catch this
+        * and shutdown the system.
+        */
+       if (((msr >> 4) & 0x3) == 0x3) {
+               device_printf(dev, "critical temperature detected, "
+                   "suggest system shutdown\n");
+               ksnprintf(stemp, sizeof(stemp), "%d", temp);
+#if 0
+               devctl_notify("coretemp", "Thermal", stemp, "notify=0xcc");
+#endif
+       }
+
+       return (temp);
+}
+
+static void
+coretemp_refresh(void *arg)
+{
+       struct coretemp_softc *sc = arg;
+       device_t dev = sc->sc_dev;
+       struct ksensor *s = &sc->sc_sensor;
+       int temp;
+
+       temp = coretemp_get_temp(dev);
+
+       if (temp == -1) {
+               s->flags |= SENSOR_FINVALID;
+               s->value = 0;
+       } else {
+               s->flags &= ~SENSOR_FINVALID;
+               s->value = temp * 1000000 + 273150000;
+       }
+}