Patch[1/2] Adding Core and Package Thermal Threshold Support to Coretemp

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Hi Fenghua,

This patch adds the core and package thermal threshold support to coretemp.c.
These thresholds can be read/written using the hwmon sysfs interfaces
temp1_core_thresh[0/1] and temp1_pkg_thresh[0/1].

These thresholds are managed dynamically to generate interrupts,
to do power management.

This patch is generated against the stable Linux-2.6 tree.

Kindly review and merge.
-------------------------------------------------------------------------

From: Durgadoss R <durgadoss.r@xxxxxxxxx>

Date: Tue, 7 Dec 2010 03:27:00 +0530
Subject: [PATCH 1/2] Adding_Threshold_support_to_coretemp

This patch adds the core and package threshold support to coretemp.c.
These thresholds can be read/written using the sysfs interfaces
temp1_core_thresh[0/1] and temp1_pkg_thresh[0/1].
These can be used to generate interrupts, to do dynamic power management.

Signed-off-by: Durgadoss R <durgadoss.r@xxxxxxxxx>

---
 arch/x86/include/asm/msr-index.h |   12 ++
 drivers/hwmon/coretemp.c         |  225 +++++++++++++++++++++++++++++++++++++-
 2 files changed, 233 insertions(+), 4 deletions(-)

diff --git a/arch/x86/include/asm/msr-index.h b/arch/x86/include/asm/msr-index.h
index 3ea3dc4..f0fea26 100644
--- a/arch/x86/include/asm/msr-index.h
+++ b/arch/x86/include/asm/msr-index.h
@@ -253,6 +253,18 @@
 #define PACKAGE_THERM_INT_LOW_ENABLE		(1 << 1)
 #define PACKAGE_THERM_INT_PLN_ENABLE		(1 << 24)
 
+/* Threshold Support */
+#define THERM_INT_THRESHOLD0_ENABLE    (1 << 15)
+#define THERM_OFFSET_THRESHOLD0        8
+#define THERM_MASK_THRESHOLD0          (0x7f << THERM_OFFSET_THRESHOLD0)
+#define THERM_INT_THRESHOLD1_ENABLE    (1 << 23)
+#define THERM_OFFSET_THRESHOLD1        16
+#define THERM_MASK_THRESHOLD1          (0x7f << THERM_OFFSET_THRESHOLD1)
+#define THERM_STATUS_THRESHOLD0        (1 << 6)
+#define THERM_LOG_THRESHOLD0           (1 << 7)
+#define THERM_STATUS_THRESHOLD1        (1 << 8)
+#define THERM_LOG_THRESHOLD1           (1 << 9)
+
 /* MISC_ENABLE bits: architectural */
 #define MSR_IA32_MISC_ENABLE_FAST_STRING	(1ULL << 0)
 #define MSR_IA32_MISC_ENABLE_TCC		(1ULL << 1)
diff --git a/drivers/hwmon/coretemp.c b/drivers/hwmon/coretemp.c
index 42de98d..7aea83e 100644
--- a/drivers/hwmon/coretemp.c
+++ b/drivers/hwmon/coretemp.c
@@ -41,6 +41,10 @@
 
 typedef enum { SHOW_TEMP, SHOW_TJMAX, SHOW_TTARGET, SHOW_LABEL,
 		SHOW_NAME } SHOW;
+/* C indicates core thermal thresholds
+ * P indicates Package thermal thresholds
+ */
+enum thresholds { C_TTHRESH0, C_TTHRESH1, P_TTHRESH0, P_TTHRESH1 } THRESH;
 
 /*
  * Functions declaration
@@ -59,9 +63,18 @@ struct coretemp_data {
 	int temp;
 	int tjmax;
 	int ttarget;
+	int c_tthresh0;
+	int c_tthresh1;
+	int p_tthresh0;
+	int p_tthresh1;
+	int pkg_support;	/* zero if pkg thresh support is not there */
 	u8 alarm;
 };
 
+static int set_core_threshold(struct coretemp_data *data, int val,
+						enum thresholds thresh);
+static int set_pkg_threshold(struct coretemp_data *data, int val,
+						enum thresholds thresh);
 /*
  * Sysfs stuff
  */
@@ -104,6 +117,48 @@ static ssize_t show_temp(struct device *dev,
 	return err;
 }
 
+static ssize_t show_threshold(struct device *dev,
+				struct device_attribute *devattr, char *buf)
+{
+	struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
+	struct coretemp_data *data = coretemp_update_device(dev);
+
+	if (!data->valid)
+		return -EINVAL;
+
+	switch (attr->index) {
+	case C_TTHRESH0:
+		return sprintf(buf, "%d\n", data->c_tthresh0);
+	case C_TTHRESH1:
+		return sprintf(buf, "%d\n", data->c_tthresh1);
+	case P_TTHRESH0:
+		return sprintf(buf, "%d\n", data->p_tthresh0);
+	case P_TTHRESH1:
+		return sprintf(buf, "%d\n", data->p_tthresh1);
+	default:
+		return -EINVAL;
+	}
+}
+
+static ssize_t set_threshold(struct device *dev,
+		struct device_attribute *devattr, const char *buf, size_t count)
+{
+	struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
+	struct coretemp_data *data = coretemp_update_device(dev);
+	unsigned long val;
+	int err;
+
+	if (strict_strtoul(buf, 10, &val))
+		return -EINVAL;
+
+	if (attr->index == C_TTHRESH0 || attr->index == C_TTHRESH1)
+		err = set_core_threshold(data, val, attr->index);
+	else
+		err = set_pkg_threshold(data, val, attr->index);
+
+	return (err) ? -EINVAL : count;
+}
+
 static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO, show_temp, NULL,
 			  SHOW_TEMP);
 static SENSOR_DEVICE_ATTR(temp1_crit, S_IRUGO, show_temp, NULL,
@@ -114,12 +169,23 @@ static DEVICE_ATTR(temp1_crit_alarm, S_IRUGO, show_alarm, NULL);
 static SENSOR_DEVICE_ATTR(temp1_label, S_IRUGO, show_name, NULL, SHOW_LABEL);
 static SENSOR_DEVICE_ATTR(name, S_IRUGO, show_name, NULL, SHOW_NAME);
 
+static SENSOR_DEVICE_ATTR(temp1_core_thresh0, S_IWUSR | S_IRUGO,
+				show_threshold,	set_threshold, C_TTHRESH0);
+static SENSOR_DEVICE_ATTR(temp1_core_thresh1, S_IWUSR | S_IRUGO,
+				show_threshold, set_threshold, C_TTHRESH1);
+static SENSOR_DEVICE_ATTR(temp1_pkg_thresh0, S_IWUSR | S_IRUGO,
+				show_threshold,	set_threshold, P_TTHRESH0);
+static SENSOR_DEVICE_ATTR(temp1_pkg_thresh1, S_IWUSR | S_IRUGO,
+				show_threshold, set_threshold, P_TTHRESH1);
+
 static struct attribute *coretemp_attributes[] = {
 	&sensor_dev_attr_name.dev_attr.attr,
 	&sensor_dev_attr_temp1_label.dev_attr.attr,
 	&dev_attr_temp1_crit_alarm.attr,
 	&sensor_dev_attr_temp1_input.dev_attr.attr,
 	&sensor_dev_attr_temp1_crit.dev_attr.attr,
+	&sensor_dev_attr_temp1_core_thresh0.dev_attr.attr,
+	&sensor_dev_attr_temp1_core_thresh1.dev_attr.attr,
 	NULL
 };
 
@@ -298,6 +364,114 @@ static void __devinit get_ucode_rev_on_cpu(void *edx)
 	rdmsr(MSR_IA32_UCODE_REV, eax, *(u32 *)edx);
 }
 
+static int set_core_threshold(struct coretemp_data *data, int temp,
+						enum thresholds thresh)
+{
+	u32 eax, edx, l;
+	int diff;
+
+	if (temp > data->tjmax)
+		return -EINVAL;
+
+	mutex_lock(&data->update_lock);
+
+	diff = (data->tjmax - temp)/1000;
+
+	/* Mask the thermal vector in the lapic */
+	l = apic_read(APIC_LVTTHMR);
+	apic_write(APIC_LVTTHMR, l | APIC_LVT_MASKED);
+
+	rdmsr_on_cpu(data->id, MSR_IA32_THERM_INTERRUPT, &eax, &edx);
+
+	if (thresh == C_TTHRESH0) {
+		eax = (eax & ~THERM_MASK_THRESHOLD0) |
+					(diff << THERM_OFFSET_THRESHOLD0);
+		data->c_tthresh0 = temp;
+	} else {
+		eax = (eax & ~THERM_MASK_THRESHOLD1) |
+					(diff << THERM_OFFSET_THRESHOLD1);
+		data->c_tthresh1 = temp;
+	}
+
+	wrmsr_on_cpu(data->id, MSR_IA32_THERM_INTERRUPT, eax, edx);
+
+	/* Unmask the thermal vector */
+	l = apic_read(APIC_LVTTHMR);
+	apic_write(APIC_LVTTHMR, l & ~APIC_LVT_MASKED);
+
+	mutex_unlock(&data->update_lock);
+	return 0;
+}
+
+static int set_pkg_threshold(struct coretemp_data *data, int temp,
+						enum thresholds thresh)
+{
+	u32 eax, edx, l;
+	int diff;
+
+	if (temp > data->tjmax)
+		return -EINVAL;
+
+	mutex_lock(&data->update_lock);
+
+	diff = (data->tjmax - temp)/1000;
+
+	/* Mask the thermal vector in the lapic */
+	l = apic_read(APIC_LVTTHMR);
+	apic_write(APIC_LVTTHMR, l | APIC_LVT_MASKED);
+
+	rdmsr_on_cpu(data->id, MSR_IA32_PACKAGE_THERM_INTERRUPT, &eax, &edx);
+
+	if (thresh == P_TTHRESH0) {
+		eax = (eax & ~THERM_MASK_THRESHOLD0) |
+					(diff << THERM_OFFSET_THRESHOLD0);
+		data->p_tthresh0 = temp;
+	} else {
+		eax = (eax & ~THERM_MASK_THRESHOLD1) |
+					(diff << THERM_OFFSET_THRESHOLD1);
+		data->p_tthresh1 = temp;
+	}
+
+	wrmsr_on_cpu(data->id, MSR_IA32_PACKAGE_THERM_INTERRUPT, eax, edx);
+
+	/* Unmask the thermal vector */
+	l = apic_read(APIC_LVTTHMR);
+	apic_write(APIC_LVTTHMR, l & ~APIC_LVT_MASKED);
+
+	mutex_unlock(&data->update_lock);
+	return 0;
+}
+
+static int __devinit enable_thresh_support(struct coretemp_data *data)
+{
+	u32 eax, edx, l;
+
+	/* Mask the thermal vector in the lapic */
+	l = apic_read(APIC_LVTTHMR);
+	apic_write(APIC_LVTTHMR, l | APIC_LVT_MASKED);
+
+	rdmsr_on_cpu(data->id, MSR_IA32_THERM_INTERRUPT, &eax, &edx);
+
+	eax |= (THERM_INT_THRESHOLD0_ENABLE | THERM_INT_THRESHOLD1_ENABLE);
+
+	wrmsr_on_cpu(data->id, MSR_IA32_THERM_INTERRUPT, eax, edx);
+
+	if (!data->pkg_support)
+		goto exit;
+
+	rdmsr_on_cpu(data->id, MSR_IA32_PACKAGE_THERM_INTERRUPT, &eax, &edx);
+
+	eax |= (THERM_INT_THRESHOLD0_ENABLE | THERM_INT_THRESHOLD1_ENABLE);
+
+	wrmsr_on_cpu(data->id, MSR_IA32_PACKAGE_THERM_INTERRUPT, eax, edx);
+
+exit:
+	/* Unmask the thermal vector */
+	l = apic_read(APIC_LVTTHMR);
+	apic_write(APIC_LVTTHMR, l & ~APIC_LVT_MASKED);
+	return 0;
+}
+
 static int __devinit coretemp_probe(struct platform_device *pdev)
 {
 	struct coretemp_data *data;
@@ -316,8 +490,11 @@ static int __devinit coretemp_probe(struct platform_device *pdev)
 	data->core_id = c->cpu_core_id;
 #endif
 	data->name = "coretemp";
-	mutex_init(&data->update_lock);
 
+	if (cpu_has(c, X86_FEATURE_PTS))
+		data->pkg_support = 1;
+
+	mutex_init(&data->update_lock);
 	/* test if we can access the THERM_STATUS MSR */
 	err = rdmsr_safe_on_cpu(data->id, MSR_IA32_THERM_STATUS, &eax, &edx);
 	if (err) {
@@ -353,6 +530,30 @@ static int __devinit coretemp_probe(struct platform_device *pdev)
 	data->tjmax = get_tjmax(c, data->id, &pdev->dev);
 	platform_set_drvdata(pdev, data);
 
+	/* Enable threshold support */
+	enable_thresh_support(data);
+
+	/* Set Initial Core thresholds */
+	set_core_threshold(data, 0, C_TTHRESH0);
+	set_core_threshold(data, 90000, C_TTHRESH1);
+
+	/* Set Initial Package thresholds, if HW support is available */
+	if (data->pkg_support) {
+		set_pkg_threshold(data, 0, P_TTHRESH0);
+		set_pkg_threshold(data, 80000, P_TTHRESH1);
+	}
+
+	if (data->pkg_support) {
+		err = device_create_file(&pdev->dev,
+				&sensor_dev_attr_temp1_pkg_thresh0.dev_attr);
+		if (err)
+			goto exit_free;
+		err = device_create_file(&pdev->dev,
+				&sensor_dev_attr_temp1_pkg_thresh1.dev_attr);
+		if (err)
+			goto exit_pkg_thresh0;
+	}
+
 	/*
 	 * read the still undocumented IA32_TEMPERATURE_TARGET. It exists
 	 * on older CPUs but not in this register,
@@ -371,12 +572,12 @@ static int __devinit coretemp_probe(struct platform_device *pdev)
 			err = device_create_file(&pdev->dev,
 					&sensor_dev_attr_temp1_max.dev_attr);
 			if (err)
-				goto exit_free;
+				goto exit_pkg_thresh1;
 		}
 	}
 
 	if ((err = sysfs_create_group(&pdev->dev.kobj, &coretemp_group)))
-		goto exit_dev;
+		goto exit_dev_max;
 
 	data->hwmon_dev = hwmon_device_register(&pdev->dev);
 	if (IS_ERR(data->hwmon_dev)) {
@@ -390,8 +591,14 @@ static int __devinit coretemp_probe(struct platform_device *pdev)
 
 exit_class:
 	sysfs_remove_group(&pdev->dev.kobj, &coretemp_group);
-exit_dev:
+exit_dev_max:
 	device_remove_file(&pdev->dev, &sensor_dev_attr_temp1_max.dev_attr);
+exit_pkg_thresh1:
+	device_remove_file(&pdev->dev,
+				&sensor_dev_attr_temp1_pkg_thresh1.dev_attr);
+exit_pkg_thresh0:
+	device_remove_file(&pdev->dev,
+				&sensor_dev_attr_temp1_pkg_thresh0.dev_attr);
 exit_free:
 	kfree(data);
 exit:
@@ -405,6 +612,16 @@ static int __devexit coretemp_remove(struct platform_device *pdev)
 	hwmon_device_unregister(data->hwmon_dev);
 	sysfs_remove_group(&pdev->dev.kobj, &coretemp_group);
 	device_remove_file(&pdev->dev, &sensor_dev_attr_temp1_max.dev_attr);
+	device_remove_file(&pdev->dev,
+				&sensor_dev_attr_temp1_core_thresh0.dev_attr);
+	device_remove_file(&pdev->dev,
+				&sensor_dev_attr_temp1_core_thresh1.dev_attr);
+	if (data->pkg_support) {
+		device_remove_file(&pdev->dev,
+				&sensor_dev_attr_temp1_pkg_thresh0.dev_attr);
+		device_remove_file(&pdev->dev,
+				&sensor_dev_attr_temp1_pkg_thresh1.dev_attr);
+	}
 	platform_set_drvdata(pdev, NULL);
 	kfree(data);
 	return 0;
-- 
1.6.5.2

Attachment: 0001-Adding_Threshold_support_to_coretemp.patch
Description: 0001-Adding_Threshold_support_to_coretemp.patch


[Index of Archives]     [Linux IBM ACPI]     [Linux Power Management]     [Linux Kernel]     [Linux Laptop]     [Kernel Newbies]     [Share Photos]     [Security]     [Netfilter]     [Bugtraq]     [Yosemite News]     [MIPS Linux]     [ARM Linux]     [Linux Security]     [Linux RAID]     [Samba]     [Video 4 Linux]     [Device Mapper]     [Linux Resources]

  Powered by Linux