From patchwork Tue Dec 14 11:55:35 2010 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: durgadoss.r@intel.com X-Patchwork-Id: 409621 Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by demeter1.kernel.org (8.14.4/8.14.3) with ESMTP id oBEBx3Dk012779 for ; Tue, 14 Dec 2010 11:59:04 GMT Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1757819Ab0LNL7D (ORCPT ); Tue, 14 Dec 2010 06:59:03 -0500 Received: from mga03.intel.com ([143.182.124.21]:46254 "EHLO mga03.intel.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1754922Ab0LNL7B (ORCPT ); Tue, 14 Dec 2010 06:59:01 -0500 Received: from azsmga001.ch.intel.com ([10.2.17.19]) by azsmga101.ch.intel.com with ESMTP; 14 Dec 2010 03:59:00 -0800 X-ExtLoop1: 1 X-IronPort-AV: E=Sophos;i="4.59,341,1288594800"; d="scan'208";a="361421268" Received: from pgsmsx601.gar.corp.intel.com ([10.221.43.69]) by azsmga001.ch.intel.com with ESMTP; 14 Dec 2010 03:58:58 -0800 Received: from bgsmsx502.gar.corp.intel.com (10.223.4.248) by pgsmsx601.gar.corp.intel.com (10.221.43.69) with Microsoft SMTP Server (TLS) id 8.2.254.0; Tue, 14 Dec 2010 19:55:37 +0800 Received: from bgsmsx502.gar.corp.intel.com ([10.223.4.248]) by bgsmsx502.gar.corp.intel.com ([10.223.4.248]) with mapi; Tue, 14 Dec 2010 17:25:36 +0530 From: "R, Durgadoss" To: "Yu, Fenghua" , Chen Gong , "lenb@kernel.org" , "khali@linux-fr.org" CC: "linux-acpi@vger.kernel.org" , "lm-sensors@lm-sensors.org" Date: Tue, 14 Dec 2010 17:25:35 +0530 Subject: [Patch] Adding threshold support to coretemp Thread-Topic: [Patch] Adding threshold support to coretemp Thread-Index: AcubhdEmbyPz5bs2TqKW5NKfGwB6rg== Message-ID: Accept-Language: en-US Content-Language: en-US X-MS-Has-Attach: yes X-MS-TNEF-Correlator: acceptlanguage: en-US MIME-Version: 1.0 Sender: linux-acpi-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-acpi@vger.kernel.org X-Greylist: IP, sender and recipient auto-whitelisted, not delayed by milter-greylist-4.2.3 (demeter1.kernel.org [140.211.167.41]); Tue, 14 Dec 2010 11:59:04 +0000 (UTC) diff --git a/arch/x86/include/asm/msr-index.h b/arch/x86/include/asm/msr-index.h index 3ea3dc4..31cefad 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) +/* Thermal Thresholds 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..4c221b8 100644 --- a/drivers/hwmon/coretemp.c +++ b/drivers/hwmon/coretemp.c @@ -42,6 +42,9 @@ typedef enum { SHOW_TEMP, SHOW_TJMAX, SHOW_TTARGET, SHOW_LABEL, SHOW_NAME } SHOW; +/* C indicates core thermal thresholds */ +enum thresholds { C_TTHRESH0, C_TTHRESH1} THRESH; + /* * Functions declaration */ @@ -59,9 +62,13 @@ struct coretemp_data { int temp; int tjmax; int ttarget; + int c_tthresh0; + int c_tthresh1; u8 alarm; }; +static int set_core_threshold(struct coretemp_data *data, int val, + enum thresholds thresh); /* * Sysfs stuff */ @@ -104,6 +111,41 @@ 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); + 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; + + err = set_core_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 +156,19 @@ 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 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 +347,77 @@ static void __devinit get_ucode_rev_on_cpu(void *edx) rdmsr(MSR_IA32_UCODE_REV, eax, *(u32 *)edx); } +static void configure_apic(void *info) +{ + u32 l; + int *flag = (int *)info; + + l = apic_read(APIC_LVTTHMR); + + if (*flag) /* Non-Zero flag Masks the APIC */ + apic_write(APIC_LVTTHMR, l | APIC_LVT_MASKED); + else /* Zero flag UnMasks the APIC */ + apic_write(APIC_LVTTHMR, l & ~APIC_LVT_MASKED); +} + +static int set_core_threshold(struct coretemp_data *data, int temp, + enum thresholds thresh) +{ + u32 eax, edx; + int diff; + int flag = 1; + + if (temp > data->tjmax) + return -EINVAL; + + mutex_lock(&data->update_lock); + + diff = (data->tjmax - temp)/1000; + + /* Mask the APIC */ + smp_call_function_single(data->id, &configure_apic, &flag, 1); + + 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 APIC */ + flag = 0; + smp_call_function_single(data->id, &configure_apic, &flag, 1); + + mutex_unlock(&data->update_lock); + return 0; +} + +static int __devinit enable_thresh_support(struct coretemp_data *data) +{ + u32 eax, edx; + int flag = 1; /* Non-Zero Flag masks the apic */ + + smp_call_function_single(data->id, &configure_apic, &flag, 1); + + 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); + + flag = 0; /*Flag should be zero to unmask the apic */ + smp_call_function_single(data->id, &configure_apic, &flag, 1); + + return 0; +} + static int __devinit coretemp_probe(struct platform_device *pdev) { struct coretemp_data *data; @@ -353,6 +473,15 @@ 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. + * The lower and upper threshold values here are assumed + */ + set_core_threshold(data, 0, C_TTHRESH0); + set_core_threshold(data, 90000, C_TTHRESH1); + /* * read the still undocumented IA32_TEMPERATURE_TARGET. It exists * on older CPUs but not in this register, @@ -405,6 +534,10 @@ 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); platform_set_drvdata(pdev, NULL); kfree(data); return 0;