summaryrefslogtreecommitdiff
path: root/sys
diff options
context:
space:
mode:
authorMike Larkin <mlarkin@cvs.openbsd.org>2010-07-03 01:02:26 +0000
committerMike Larkin <mlarkin@cvs.openbsd.org>2010-07-03 01:02:26 +0000
commit068b9a96a35ba933a788d371e9a3c8e282c10f5a (patch)
treebe5733d15eec9444d218e5a8e65a91f3ad946497 /sys
parentc7fb6fb68e43859610679081e1851b4cb1a25e2a (diff)
Intel 3400 Thermal Sensor driver, found on some new Intel machines.
This is also the same as the Intel Series 5 Thermal Sensor. ok deraadt@, kettenis@
Diffstat (limited to 'sys')
-rw-r--r--sys/arch/amd64/conf/GENERIC3
-rw-r--r--sys/arch/i386/conf/GENERIC3
-rw-r--r--sys/dev/pci/files.pci7
-rw-r--r--sys/dev/pci/itherm.c387
4 files changed, 397 insertions, 3 deletions
diff --git a/sys/arch/amd64/conf/GENERIC b/sys/arch/amd64/conf/GENERIC
index f9853c899f8..c6b26ccd4bc 100644
--- a/sys/arch/amd64/conf/GENERIC
+++ b/sys/arch/amd64/conf/GENERIC
@@ -1,4 +1,4 @@
-# $OpenBSD: GENERIC,v 1.290 2010/06/27 21:00:22 kettenis Exp $
+# $OpenBSD: GENERIC,v 1.291 2010/07/03 01:02:25 mlarkin Exp $
#
# For further information on compiling OpenBSD kernels, see the config(8)
# man page.
@@ -113,6 +113,7 @@ iic* at nviic?
amdpm* at pci? # AMD-7xx/8111 and NForce SMBus controller
iic* at amdpm?
+itherm* at pci? # Intel 3400 Thermal Sensor
adc* at iic? # Analog Devices AD7416/AD7417/7418
adl* at iic? # Andigilog aSC7621
admtemp* at iic? # Analog Devices ADM1021
diff --git a/sys/arch/i386/conf/GENERIC b/sys/arch/i386/conf/GENERIC
index 7babe24783f..a280478ecfb 100644
--- a/sys/arch/i386/conf/GENERIC
+++ b/sys/arch/i386/conf/GENERIC
@@ -1,4 +1,4 @@
-# $OpenBSD: GENERIC,v 1.685 2010/07/01 17:30:25 tedu Exp $
+# $OpenBSD: GENERIC,v 1.686 2010/07/03 01:02:25 mlarkin Exp $
#
# For further information on compiling OpenBSD kernels, see the config(8)
# man page.
@@ -152,6 +152,7 @@ fins0 at isa? port 0x4e # Fintek F71805 Super I/O
aps0 at isa? port 0x1600 # ThinkPad Active Protection System
+itherm* at pci? # Intel 3400 Thermal Sensor
adc* at iic? # Analog Devices AD7416/AD7417/7418
andl* at iic? # Andigilog aSC7611
adl* at iic? # Andigilog aSC7621
diff --git a/sys/dev/pci/files.pci b/sys/dev/pci/files.pci
index 2d7b76f24e2..a713c49394d 100644
--- a/sys/dev/pci/files.pci
+++ b/sys/dev/pci/files.pci
@@ -1,4 +1,4 @@
-# $OpenBSD: files.pci,v 1.272 2010/06/27 01:47:36 dlg Exp $
+# $OpenBSD: files.pci,v 1.273 2010/07/03 01:02:25 mlarkin Exp $
# $NetBSD: files.pci,v 1.20 1996/09/24 17:47:15 christos Exp $
#
# Config file and device description for machine-independent PCI code.
@@ -806,3 +806,8 @@ device auglx: audio, ac97
attach auglx at pci
file dev/pci/auglx.c auglx
+# Intel 3400 Thermal
+device itherm
+attach itherm at pci
+file dev/pci/itherm.c itherm
+
diff --git a/sys/dev/pci/itherm.c b/sys/dev/pci/itherm.c
new file mode 100644
index 00000000000..839668e3431
--- /dev/null
+++ b/sys/dev/pci/itherm.c
@@ -0,0 +1,387 @@
+/*
+ * Copyright (c) 2010 Mike Larkin <mlarkin@openbsd.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+/*
+ * Intel 3400 thermal sensor controller driver
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/device.h>
+#include <sys/sensors.h>
+
+#include <dev/pci/pcireg.h>
+#include <dev/pci/pcivar.h>
+#include <dev/pci/pcidevs.h>
+
+#define ITHERM_NUM_SENSORS 12
+#define ITHERM_SENSOR_THERMOMETER 0
+#define ITHERM_SENSOR_CORETEMP1 1
+#define ITHERM_SENSOR_CORETEMP2 2
+#define ITHERM_SENSOR_COREENERGY 3
+#define ITHERM_SENSOR_GPUTEMP 4
+#define ITHERM_SENSOR_MAXPROCTEMP 5
+#define ITHERM_SENSOR_DIMMTEMP1 6
+#define ITHERM_SENSOR_DIMMTEMP2 7
+#define ITHERM_SENSOR_DIMMTEMP3 8
+#define ITHERM_SENSOR_DIMMTEMP4 9
+#define ITHERM_SENSOR_GPUTEMP_ABSOLUTE 10
+#define ITHERM_SENSOR_PCHTEMP_ABSOLUTE 11
+
+/* Intel 3400 Thermal Sensor Data */
+#define ITHERM_TSE 0x1
+#define ITHERM_TSTR 0x3
+#define ITHERM_TRC 0x1A
+#define ITHERM_CTV1 0x30
+#define ITHERM_CTV2 0x32
+#define ITHERM_CEV1 0x34
+#define ITHERM_MGTV 0x58
+#define ITHERM_PTV 0x60
+#define ITHERM_DTV 0xAC
+#define ITHERM_ITV 0xD8
+
+#define ITHERM_TEMP_READ_ENABLE 0xFF
+#define ITHERM_TDR_ENABLE 0x1000
+#define ITHERM_SECOND_CORE_ENABLE 0x8000
+
+#define ITHERM_TSE_ENABLE 0xB8
+
+#define ITHERM_CTV_INVALID 0x8000
+#define ITHERM_CTV_INT_MASK 0x1FE0
+#define ITHERM_CTV_FRAC_MASK 0x001F
+
+#define ITHERM_REFRESH_INTERVAL 5
+
+struct itherm_softc {
+ struct device sc_dev;
+
+ bus_addr_t sc_addr;
+ bus_space_tag_t iot;
+ bus_space_handle_t ioh;
+ bus_size_t size;
+
+ int64_t energy_prev;
+
+ struct ksensor sensors[ITHERM_NUM_SENSORS];
+ struct ksensordev sensordev;
+ void (*refresh_sensor_data)(struct itherm_softc *);
+};
+
+#define IREAD1(sc, a) bus_space_read_1((sc)->iot, (sc)->ioh, (a))
+#define IREAD2(sc, a) bus_space_read_2((sc)->iot, (sc)->ioh, (a))
+#define IREAD4(sc, a) bus_space_read_4((sc)->iot, (sc)->ioh, (a))
+#define IWRITE1(sc, a, x) bus_space_write_1((sc)->iot, (sc)->ioh, (a), (x))
+#define IWRITE2(sc, a, x) bus_space_write_2((sc)->iot, (sc)->ioh, (a), (x))
+
+int itherm_probe(struct device *, void *, void *);
+void itherm_attach(struct device *, struct device *, void *);
+void itherm_refresh(void *);
+void itherm_enable(struct itherm_softc *);
+void itherm_refresh_sensor_data(struct itherm_softc *);
+int itherm_activate(struct device *, int);
+void itherm_bias_temperature_sensor(struct ksensor *);
+
+const struct pci_matchid itherm_devices[] = {
+ { PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_3400_THERMAL }
+};
+
+struct cfdriver itherm_cd = {
+ NULL, "itherm", DV_DULL
+};
+
+struct cfattach itherm_ca = {
+ sizeof(struct itherm_softc), itherm_probe, itherm_attach, NULL,
+ itherm_activate
+};
+
+int
+itherm_probe(struct device *parent, void *match, void *aux)
+{
+ return (pci_matchbyid((struct pci_attach_args *)aux, itherm_devices,
+ sizeof(itherm_devices)/sizeof(itherm_devices[0])));
+}
+
+void
+itherm_attach(struct device *parent, struct device *self, void *aux)
+{
+ struct itherm_softc *sc = (struct itherm_softc *)self;
+ struct pci_attach_args *pa = aux;
+ int i;
+ pcireg_t v;
+
+ v = pci_conf_read(pa->pa_pc, pa->pa_tag, PCI_MAPREG_START);
+ v &= PCI_MAPREG_TYPE_MASK | PCI_MAPREG_MEM_TYPE_MASK;
+ if (pci_mapreg_map(pa, PCI_MAPREG_START,
+ v, 0, &sc->iot, &sc->ioh, NULL, &sc->size, 0)) {
+ printf(": can't map mem space\n");
+ return;
+ }
+
+ sc->sensors[ITHERM_SENSOR_THERMOMETER].type = SENSOR_TEMP;
+ sc->sensors[ITHERM_SENSOR_CORETEMP1].type = SENSOR_TEMP;
+ sc->sensors[ITHERM_SENSOR_CORETEMP2].type = SENSOR_TEMP;
+ sc->sensors[ITHERM_SENSOR_COREENERGY].type = SENSOR_WATTS;
+ sc->sensors[ITHERM_SENSOR_GPUTEMP].type = SENSOR_TEMP;
+ sc->sensors[ITHERM_SENSOR_MAXPROCTEMP].type = SENSOR_TEMP;
+ sc->sensors[ITHERM_SENSOR_DIMMTEMP1].type = SENSOR_TEMP;
+ sc->sensors[ITHERM_SENSOR_DIMMTEMP2].type = SENSOR_TEMP;
+ sc->sensors[ITHERM_SENSOR_DIMMTEMP3].type = SENSOR_TEMP;
+ sc->sensors[ITHERM_SENSOR_DIMMTEMP4].type = SENSOR_TEMP;
+ sc->sensors[ITHERM_SENSOR_GPUTEMP_ABSOLUTE].type = SENSOR_TEMP;
+ sc->sensors[ITHERM_SENSOR_PCHTEMP_ABSOLUTE].type = SENSOR_TEMP;
+
+ strlcpy(sc->sensors[ITHERM_SENSOR_THERMOMETER].desc,
+ "Thermometer",
+ sizeof(sc->sensors[ITHERM_SENSOR_THERMOMETER].desc));
+
+ strlcpy(sc->sensors[ITHERM_SENSOR_CORETEMP1].desc,
+ "Core 1",
+ sizeof(sc->sensors[ITHERM_SENSOR_CORETEMP1].desc));
+
+ strlcpy(sc->sensors[ITHERM_SENSOR_CORETEMP2].desc,
+ "Core 2",
+ sizeof(sc->sensors[ITHERM_SENSOR_CORETEMP2].desc));
+
+ strlcpy(sc->sensors[ITHERM_SENSOR_COREENERGY].desc,
+ "CPU power consumption",
+ sizeof(sc->sensors[ITHERM_SENSOR_COREENERGY].desc));
+
+ strlcpy(sc->sensors[ITHERM_SENSOR_GPUTEMP].desc,
+ "GPU/Memory Controller Temp",
+ sizeof(sc->sensors[ITHERM_SENSOR_GPUTEMP].desc));
+
+ strlcpy(sc->sensors[ITHERM_SENSOR_MAXPROCTEMP].desc,
+ "CPU/GPU Max temp",
+ sizeof(sc->sensors[ITHERM_SENSOR_MAXPROCTEMP].desc));
+
+ strlcpy(sc->sensors[ITHERM_SENSOR_DIMMTEMP1].desc,
+ "DIMM 1",
+ sizeof(sc->sensors[ITHERM_SENSOR_DIMMTEMP1].desc));
+
+ strlcpy(sc->sensors[ITHERM_SENSOR_DIMMTEMP2].desc,
+ "DIMM 2",
+ sizeof(sc->sensors[ITHERM_SENSOR_DIMMTEMP2].desc));
+
+ strlcpy(sc->sensors[ITHERM_SENSOR_DIMMTEMP3].desc,
+ "DIMM 3",
+ sizeof(sc->sensors[ITHERM_SENSOR_DIMMTEMP3].desc));
+
+ strlcpy(sc->sensors[ITHERM_SENSOR_DIMMTEMP4].desc,
+ "DIMM 4",
+ sizeof(sc->sensors[ITHERM_SENSOR_DIMMTEMP4].desc));
+
+ strlcpy(sc->sensors[ITHERM_SENSOR_GPUTEMP_ABSOLUTE].desc,
+ "GPU/Memory controller abs.",
+ sizeof(sc->sensors[ITHERM_SENSOR_GPUTEMP_ABSOLUTE].desc));
+
+ strlcpy(sc->sensors[ITHERM_SENSOR_PCHTEMP_ABSOLUTE].desc,
+ "PCH abs.",
+ sizeof(sc->sensors[ITHERM_SENSOR_PCHTEMP_ABSOLUTE].desc));
+
+ strlcpy(sc->sensordev.xname, sc->sc_dev.dv_xname,
+ sizeof(sc->sensordev.xname));
+
+ itherm_enable(sc);
+
+ for (i=0; i < ITHERM_NUM_SENSORS; i++)
+ sensor_attach(&sc->sensordev, &sc->sensors[i]);
+
+ sensordev_install(&sc->sensordev);
+ sensor_task_register(sc, itherm_refresh, ITHERM_REFRESH_INTERVAL);
+
+ printf("\n");
+
+ return;
+}
+
+void
+itherm_enable(struct itherm_softc *sc)
+{
+ sc->energy_prev = 0;
+
+ /* Enable thermal sensor */
+ IWRITE1(sc, ITHERM_TSE, ITHERM_TSE_ENABLE);
+
+ /* Enable thermal reporting */
+ IWRITE2(sc, ITHERM_TRC, (ITHERM_TEMP_READ_ENABLE |
+ ITHERM_TDR_ENABLE | ITHERM_SECOND_CORE_ENABLE));
+}
+
+int
+itherm_activate(struct device *self, int act)
+{
+ struct itherm_softc *sc = (struct itherm_softc *)self;
+
+ switch(act) {
+ case DVACT_RESUME:
+ itherm_enable(sc);
+ }
+
+ return (0);
+}
+
+void
+itherm_refresh_sensor_data(struct itherm_softc *sc)
+{
+ u_int16_t data;
+ int64_t energy;
+ u_int32_t i;
+
+ /* Thermometer sensor */
+ sc->sensors[ITHERM_SENSOR_THERMOMETER].value =
+ IREAD1(sc, ITHERM_TSTR);
+
+ itherm_bias_temperature_sensor(
+ &sc->sensors[ITHERM_SENSOR_THERMOMETER]);
+
+ /*
+ * The Intel 3400 Thermal Sensor has separate sensors for each
+ * core, reported as a 16 bit value. Bits 13:6 are the integer
+ * part of the temperature in C and bits 5:0 are the fractional
+ * part of the temperature, in 1/64 degree C intervals.
+ * Bit 15 is used to indicate an invalid temperature
+ */
+
+ /* Core 1 temperature */
+ data = IREAD2(sc, ITHERM_CTV1);
+ if (data & ITHERM_CTV_INVALID)
+ sc->sensors[ITHERM_SENSOR_CORETEMP1].flags |=
+ SENSOR_FINVALID;
+ else {
+ sc->sensors[ITHERM_SENSOR_CORETEMP1].flags &=
+ ~SENSOR_FINVALID;
+ sc->sensors[ITHERM_SENSOR_CORETEMP1].value =
+ (data & ITHERM_CTV_INT_MASK) >> 6;
+ sc->sensors[ITHERM_SENSOR_CORETEMP1].value *=
+ 1000000;
+ data &= ITHERM_CTV_FRAC_MASK;
+ data *= 1000000 / 64;
+ sc->sensors[ITHERM_SENSOR_CORETEMP1].value +=
+ data;
+ }
+
+ /* Core 2 temperature */
+ data = IREAD2(sc, ITHERM_CTV2);
+ if (data & ITHERM_CTV_INVALID)
+ sc->sensors[ITHERM_SENSOR_CORETEMP2].flags |=
+ SENSOR_FINVALID;
+ else {
+ sc->sensors[ITHERM_SENSOR_CORETEMP2].flags &=
+ ~SENSOR_FINVALID;
+ sc->sensors[ITHERM_SENSOR_CORETEMP2].value =
+ (data & ITHERM_CTV_INT_MASK) >> 6;
+ sc->sensors[ITHERM_SENSOR_CORETEMP2].value *=
+ 1000000;
+ data &= ITHERM_CTV_FRAC_MASK;
+ data *= 1000000 / 64;
+ sc->sensors[ITHERM_SENSOR_CORETEMP2].value +=
+ data;
+ }
+
+ /*
+ * The core energy sensor reports the number of Joules
+ * of energy consumed by the processor since powerup.
+ * This number is scaled by 65535 and is continually
+ * increasing, so we save the old value and compute
+ * the difference for the Watt sensor value.
+ */
+
+ i = IREAD4(sc, ITHERM_CEV1);
+ /* Convert to Joules per interval */
+ energy = (i / 65535);
+ energy = energy - sc->energy_prev;
+ sc->energy_prev = (i / 65535);
+ /* Convert to Joules per second */
+ energy = energy / ITHERM_REFRESH_INTERVAL;
+ /* Convert to micro Joules per second (micro Watts) */
+ energy = energy * 1000 * 1000;
+
+ sc->sensors[ITHERM_SENSOR_COREENERGY].value = energy;
+
+ /*
+ * XXX - the GPU temp is reported as a 64 bit value with no
+ * documented structure. Disabled for now
+ */
+ sc->sensors[ITHERM_SENSOR_GPUTEMP].flags |= SENSOR_FINVALID;
+#if 0
+ bus_space_read_multi_4(sc->iot, sc->ioh, ITHERM_MGTV,
+ (u_int32_t *)&sc->sensors[ITHERM_SENSOR_GPUTEMP].value, 2);
+ sc->sensors[ITHERM_SENSOR_GPUTEMP].value *= 1000000;
+ sc->sensors[ITHERM_SENSOR_GPUTEMP].value += 273150000;
+#endif
+
+ /* Max processor temperature */
+ sc->sensors[ITHERM_SENSOR_MAXPROCTEMP].value =
+ IREAD1(sc, ITHERM_PTV) * 1000000;
+ itherm_bias_temperature_sensor(
+ &sc->sensors[ITHERM_SENSOR_MAXPROCTEMP]);
+
+ /* DIMM 1 */
+ sc->sensors[ITHERM_SENSOR_DIMMTEMP1].value =
+ IREAD1(sc, ITHERM_DTV) * 1000000;
+ itherm_bias_temperature_sensor(
+ &sc->sensors[ITHERM_SENSOR_DIMMTEMP1]);
+
+ /* DIMM 2 */
+ sc->sensors[ITHERM_SENSOR_DIMMTEMP2].value =
+ IREAD1(sc, ITHERM_DTV+1) * 1000000;
+ itherm_bias_temperature_sensor(
+ &sc->sensors[ITHERM_SENSOR_DIMMTEMP2]);
+
+ /* DIMM 3 */
+ sc->sensors[ITHERM_SENSOR_DIMMTEMP3].value =
+ IREAD1(sc, ITHERM_DTV+2) * 1000000;
+ itherm_bias_temperature_sensor(
+ &sc->sensors[ITHERM_SENSOR_DIMMTEMP3]);
+
+ /* DIMM 4 */
+ sc->sensors[ITHERM_SENSOR_DIMMTEMP4].value =
+ IREAD1(sc, ITHERM_DTV+3) * 1000000;
+ itherm_bias_temperature_sensor(
+ &sc->sensors[ITHERM_SENSOR_DIMMTEMP4]);
+
+ /* GPU Temperature */
+ sc->sensors[ITHERM_SENSOR_GPUTEMP_ABSOLUTE].value =
+ IREAD1(sc, ITHERM_ITV+1) * 1000000;
+ itherm_bias_temperature_sensor(
+ &sc->sensors[ITHERM_SENSOR_GPUTEMP_ABSOLUTE]);
+
+ /* PCH Temperature */
+ sc->sensors[ITHERM_SENSOR_PCHTEMP_ABSOLUTE].value =
+ IREAD1(sc, ITHERM_ITV) * 1000000;
+ itherm_bias_temperature_sensor(
+ &sc->sensors[ITHERM_SENSOR_PCHTEMP_ABSOLUTE]);
+}
+
+void
+itherm_bias_temperature_sensor(struct ksensor *sensor)
+{
+ if (sensor->value == 0 || sensor->value == 0xff)
+ sensor->flags |= SENSOR_FINVALID;
+ else
+ sensor->flags &= ~SENSOR_FINVALID;
+
+ /* Bias anyway from degC to degK, even if invalid */
+ sensor->value += 273150000;
+}
+
+void
+itherm_refresh(void *arg)
+{
+ struct itherm_softc *sc = (struct itherm_softc *)arg;
+
+ itherm_refresh_sensor_data(sc);
+}