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