Add support for T0 and T1 temperature thresholds using the new sysfs ABI attributes tempX_thresholdY and tempX_thresholdY_triggered. This patch is based on commit c814a4c7c4aad795835583344353963a0a673eb0, which was reverted. For details on the threshold registers, see IA Manual vol 3A, which can be downloaded from here: http://download.intel.com/design/processor/manuals/253668.pdf Signed-off-by: Guenter Roeck <guenter.roeck@xxxxxxxxxxxx> --- Documentation/hwmon/coretemp | 12 +++ drivers/hwmon/coretemp.c | 153 ++++++++++++++++++++++++++++++++++++++---- 2 files changed, 151 insertions(+), 14 deletions(-) diff --git a/Documentation/hwmon/coretemp b/Documentation/hwmon/coretemp index 84d46c0..d9f09fc 100644 --- a/Documentation/hwmon/coretemp +++ b/Documentation/hwmon/coretemp @@ -35,6 +35,18 @@ the Out-Of-Spec bit. Following table summarizes the exported sysfs files: All Sysfs entries are named with their core_id (represented here by 'X'). tempX_input - Core temperature (in millidegrees Celsius). tempX_max - All cooling devices should be turned on (on Core2). +tempX_threshold1 - Reflects value of CPU thermal threshold T0. +tempX_threshold1_triggered + - A value of 1 indicates that T0 was crossed from either + direction. Reflects value of CPU thermal status register + bit 7 (THERM_LOG_THRESHOLD0). + Value is reset by writing 0 into the file. +tempX_threshold2 - Reflects value of CPU thermal threshold T1. +tempX_threshold2_triggered + - A value of 1 indicates that T1 was crossed from either + direction. Reflects value of CPU thermal status register + bit 9 (THERM_LOG_THRESHOLD1). + Value is reset by writing 0 into the file. tempX_crit - Maximum junction temperature (in millidegrees Celsius). tempX_crit_alarm - Set when Out-of-spec bit is set, never clears. Correct CPU operation is no longer guaranteed. diff --git a/drivers/hwmon/coretemp.c b/drivers/hwmon/coretemp.c index 44b2391..b0cb454 100644 --- a/drivers/hwmon/coretemp.c +++ b/drivers/hwmon/coretemp.c @@ -52,9 +52,10 @@ MODULE_PARM_DESC(tjmax, "TjMax value in degrees Celsius"); #define BASE_SYSFS_ATTR_NO 2 /* Sysfs Base attr no for coretemp */ #define NUM_REAL_CORES 16 /* Number of Real cores per cpu */ -#define CORETEMP_NAME_LENGTH 17 /* String Length of attrs */ -#define MAX_CORE_ATTRS 4 /* Maximum no of basic attrs */ -#define TOTAL_ATTRS (MAX_CORE_ATTRS + 1) +#define CORETEMP_NAME_LENGTH 33 /* String Length of attrs */ +#define MAX_CORE_ATTRS 5 /* Maximum no of basic attrs */ +#define MAX_THRESH_ATTRS 4 /* Maximum no of threshold attrs */ +#define TOTAL_ATTRS (MAX_CORE_ATTRS + MAX_THRESH_ATTRS) #define MAX_CORE_DATA (NUM_REAL_CORES + BASE_SYSFS_ATTR_NO) #ifdef CONFIG_SMP @@ -77,6 +78,8 @@ MODULE_PARM_DESC(tjmax, "TjMax value in degrees Celsius"); * This value is passed as "id" field to rdmsr/wrmsr functions. * @status_reg: One of IA32_THERM_STATUS or IA32_PACKAGE_THERM_STATUS, * from where the temperature values should be read. + * @intrpt_reg: One of IA32_THERM_INTERRUPT or IA32_PACKAGE_THERM_INTERRUPT, + * from where the thresholds are read. * @attr_size: Total number of pre-core attrs displayed in the sysfs. * @is_pkg_data: If this is 1, the temp_data holds pkgtemp data. * Otherwise, temp_data holds coretemp data. @@ -90,10 +93,11 @@ struct temp_data { unsigned int cpu; u32 cpu_core_id; u32 status_reg; + u32 intrpt_reg; int attr_size; bool is_pkg_data; bool valid; - struct sensor_device_attribute sd_attrs[TOTAL_ATTRS]; + struct sensor_device_attribute_2 sd_attrs[TOTAL_ATTRS]; char attr_name[TOTAL_ATTRS][CORETEMP_NAME_LENGTH]; struct mutex update_lock; }; @@ -138,7 +142,7 @@ static ssize_t show_crit_alarm(struct device *dev, struct device_attribute *devattr, char *buf) { u32 eax, edx; - struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); + struct sensor_device_attribute_2 *attr = to_sensor_dev_attr_2(devattr); struct platform_data *pdata = dev_get_drvdata(dev); struct temp_data *tdata = pdata->core_data[attr->index]; @@ -150,7 +154,7 @@ static ssize_t show_crit_alarm(struct device *dev, static ssize_t show_tjmax(struct device *dev, struct device_attribute *devattr, char *buf) { - struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); + struct sensor_device_attribute_2 *attr = to_sensor_dev_attr_2(devattr); struct platform_data *pdata = dev_get_drvdata(dev); return sprintf(buf, "%d\n", pdata->core_data[attr->index]->tjmax); @@ -159,12 +163,98 @@ static ssize_t show_tjmax(struct device *dev, static ssize_t show_ttarget(struct device *dev, struct device_attribute *devattr, char *buf) { - struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); + struct sensor_device_attribute_2 *attr = to_sensor_dev_attr_2(devattr); struct platform_data *pdata = dev_get_drvdata(dev); return sprintf(buf, "%d\n", pdata->core_data[attr->index]->ttarget); } +static const u32 thresh_log_mask[2] = { + THERM_LOG_THRESHOLD0, THERM_LOG_THRESHOLD1 }; + +static ssize_t show_thresh_triggered(struct device *dev, + struct device_attribute *devattr, + char *buf) +{ + struct sensor_device_attribute_2 *attr = to_sensor_dev_attr_2(devattr); + struct platform_data *pdata = dev_get_drvdata(dev); + struct temp_data *tdata = pdata->core_data[attr->index]; + u32 eax, edx; + + rdmsr_on_cpu(tdata->cpu, tdata->status_reg, &eax, &edx); + + return sprintf(buf, "%d\n", !!(eax & thresh_log_mask[attr->nr])); +} + +static ssize_t store_thresh_triggered(struct device *dev, + struct device_attribute *devattr, + const char *buf, size_t count) +{ + struct platform_data *pdata = dev_get_drvdata(dev); + struct sensor_device_attribute_2 *attr = to_sensor_dev_attr_2(devattr); + struct temp_data *tdata = pdata->core_data[attr->index]; + u32 mask = thresh_log_mask[attr->nr]; + u32 eax, edx; + unsigned long val; + + if (strict_strtoul(buf, 10, &val) || val != 0) + return -EINVAL; + + rdmsr_on_cpu(tdata->cpu, tdata->status_reg, &eax, &edx); + if (eax & mask) + wrmsr_on_cpu(tdata->cpu, tdata->status_reg, eax & ~mask, edx); + return count; +} + +static const u32 thresh_mask[2] = { + THERM_MASK_THRESHOLD0, THERM_MASK_THRESHOLD1 }; +static const u32 thresh_shift[2] = { + THERM_SHIFT_THRESHOLD0, THERM_SHIFT_THRESHOLD1 }; + +static ssize_t show_thresh(struct device *dev, + struct device_attribute *devattr, char *buf) +{ + struct platform_data *pdata = dev_get_drvdata(dev); + struct sensor_device_attribute_2 *attr = to_sensor_dev_attr_2(devattr); + struct temp_data *tdata = pdata->core_data[attr->index]; + u32 eax, edx; + int thresh; + u32 mask = thresh_mask[attr->nr]; + u32 shift = thresh_shift[attr->nr]; + + rdmsr_on_cpu(tdata->cpu, tdata->intrpt_reg, &eax, &edx); + thresh = tdata->tjmax - ((eax & mask) >> shift) * 1000; + return sprintf(buf, "%d\n", thresh); +} + +static ssize_t store_thresh(struct device *dev, + struct device_attribute *devattr, + const char *buf, size_t count) +{ + struct platform_data *pdata = dev_get_drvdata(dev); + struct sensor_device_attribute_2 *attr = to_sensor_dev_attr_2(devattr); + struct temp_data *tdata = pdata->core_data[attr->index]; + u32 eax, edx; + long val, diff; + u32 mask = thresh_mask[attr->nr]; + u32 shift = thresh_shift[attr->nr]; + + if (strict_strtol(buf, 10, &val)) + return -EINVAL; + + diff = (tdata->tjmax - val) / 1000; + if (diff < 0 || diff > 127) + return -EINVAL; + + mutex_lock(&tdata->update_lock); + rdmsr_on_cpu(tdata->cpu, tdata->intrpt_reg, &eax, &edx); + eax = (eax & ~mask) | (diff << shift); + wrmsr_on_cpu(tdata->cpu, tdata->intrpt_reg, eax, edx); + mutex_unlock(&tdata->update_lock); + + return count; +} + static ssize_t show_temp(struct device *dev, struct device_attribute *devattr, char *buf) { @@ -344,26 +434,44 @@ static int create_name_attr(struct platform_data *pdata, struct device *dev) } static int create_core_attrs(struct temp_data *tdata, struct device *dev, - int attr_no) + int attr_no, bool have_ttarget) { int err, i; static ssize_t (*const rd_ptr[TOTAL_ATTRS]) (struct device *dev, struct device_attribute *devattr, char *buf) = { show_label, show_crit_alarm, show_temp, show_tjmax, - show_ttarget }; + show_ttarget, show_thresh, show_thresh_triggered, + show_thresh, show_thresh_triggered }; + static ssize_t (*const rw_ptr[TOTAL_ATTRS]) (struct device *dev, + struct device_attribute *devattr, const char *buf, + size_t count) = { NULL, NULL, NULL, NULL, NULL, + store_thresh, store_thresh_triggered, + store_thresh, store_thresh_triggered }; + static const int attr_nr[TOTAL_ATTRS] = { 0, 0, 0, 0, 0, 0, 0, 1, 1 }; static const char *const names[TOTAL_ATTRS] = { "temp%d_label", "temp%d_crit_alarm", "temp%d_input", "temp%d_crit", - "temp%d_max" }; + "temp%d_max", + "temp%d_threshold1", + "temp%d_threshold1_triggered", + "temp%d_threshold2", + "temp%d_threshold2_triggered" }; for (i = 0; i < tdata->attr_size; i++) { + if (rd_ptr[i] == show_ttarget && !have_ttarget) + continue; snprintf(tdata->attr_name[i], CORETEMP_NAME_LENGTH, names[i], attr_no); sysfs_attr_init(&tdata->sd_attrs[i].dev_attr.attr); tdata->sd_attrs[i].dev_attr.attr.name = tdata->attr_name[i]; tdata->sd_attrs[i].dev_attr.attr.mode = S_IRUGO; + if (rw_ptr[i]) { + tdata->sd_attrs[i].dev_attr.attr.mode |= S_IWUSR; + tdata->sd_attrs[i].dev_attr.store = rw_ptr[i]; + } tdata->sd_attrs[i].dev_attr.show = rd_ptr[i]; tdata->sd_attrs[i].index = attr_no; + tdata->sd_attrs[i].nr = attr_nr[i]; err = device_create_file(dev, &tdata->sd_attrs[i].dev_attr); if (err) goto exit_free; @@ -371,8 +479,11 @@ static int create_core_attrs(struct temp_data *tdata, struct device *dev, return 0; exit_free: - while (--i >= 0) + while (--i >= 0) { + if (!tdata->sd_attrs[i].dev_attr.attr.name) + continue; device_remove_file(dev, &tdata->sd_attrs[i].dev_attr); + } return err; } @@ -434,6 +545,8 @@ static struct temp_data *init_temp_data(unsigned int cpu, int pkg_flag) tdata->status_reg = pkg_flag ? MSR_IA32_PACKAGE_THERM_STATUS : MSR_IA32_THERM_STATUS; + tdata->intrpt_reg = pkg_flag ? MSR_IA32_PACKAGE_THERM_INTERRUPT : + MSR_IA32_THERM_INTERRUPT; tdata->is_pkg_data = pkg_flag; tdata->cpu = cpu; tdata->cpu_core_id = TO_CORE_ID(cpu); @@ -450,6 +563,7 @@ static int create_core_data(struct platform_device *pdev, struct cpuinfo_x86 *c = &cpu_data(cpu); u32 eax, edx; int err, attr_no; + bool have_ttarget = false; /* * Find attr number for sysfs: @@ -495,14 +609,22 @@ static int create_core_data(struct platform_device *pdev, if (!err) { tdata->ttarget = tdata->tjmax - ((eax >> 8) & 0xff) * 1000; - tdata->attr_size++; + have_ttarget = true; } } + /* + * Test if we can access the intrpt register. If so, increase + * 'size' enough to support t0 and t1 attributes. + */ + err = rdmsr_safe_on_cpu(cpu, tdata->intrpt_reg, &eax, &edx); + if (!err) + tdata->attr_size += MAX_THRESH_ATTRS; + pdata->core_data[attr_no] = tdata; /* Create sysfs interfaces */ - err = create_core_attrs(tdata, &pdev->dev, attr_no); + err = create_core_attrs(tdata, &pdev->dev, attr_no, have_ttarget); if (err) goto exit_free; @@ -532,8 +654,11 @@ static void coretemp_remove_core(struct platform_data *pdata, struct temp_data *tdata = pdata->core_data[indx]; /* Remove the sysfs attributes */ - for (i = 0; i < tdata->attr_size; i++) + for (i = 0; i < tdata->attr_size; i++) { + if (!tdata->sd_attrs[i].dev_attr.attr.name) + continue; device_remove_file(dev, &tdata->sd_attrs[i].dev_attr); + } kfree(pdata->core_data[indx]); pdata->core_data[indx] = NULL; -- 1.7.3.1 _______________________________________________ lm-sensors mailing list lm-sensors@xxxxxxxxxxxxxx http://lists.lm-sensors.org/mailman/listinfo/lm-sensors