diff options
author | Durgadoss R <durgadoss.r@intel.com> | 2011-07-12 07:07:16 -0400 |
---|---|---|
committer | Guenter Roeck <guenter.roeck@ericsson.com> | 2011-07-28 03:17:36 -0400 |
commit | c814a4c7c4aad795835583344353963a0a673eb0 (patch) | |
tree | dbf51076911e529ce082049d26323b560cb7b1f2 | |
parent | 8c1d04192e2ef1e6d38e9d5e9c500e5330d20339 (diff) |
hwmon: (coretemp) Add core/pkg threshold support to Coretemp
This patch adds the core and pkg support to coretemp.
These thresholds can be configured via the sysfs interfaces tempX_max
and tempX_max_hyst. An interrupt is generated when CPU temperature reaches
or crosses above tempX_max OR drops below tempX_max_hyst.
This patch is based on the documentation in IA Manual vol 3A, that can be
downloaded from here:
http://download.intel.com/design/processor/manuals/253668.pdf
Signed-off-by: Durgadoss R <durgadoss.r@intel.com>
Signed-off-by: Guenter Roeck <guenter.roeck@ericsson.com>
-rw-r--r-- | Documentation/hwmon/coretemp | 7 | ||||
-rw-r--r-- | drivers/hwmon/coretemp.c | 177 |
2 files changed, 139 insertions, 45 deletions
diff --git a/Documentation/hwmon/coretemp b/Documentation/hwmon/coretemp index f85e913a3401..fa8776ab9b18 100644 --- a/Documentation/hwmon/coretemp +++ b/Documentation/hwmon/coretemp | |||
@@ -35,6 +35,13 @@ the Out-Of-Spec bit. Following table summarizes the exported sysfs files: | |||
35 | All Sysfs entries are named with their core_id (represented here by 'X'). | 35 | All Sysfs entries are named with their core_id (represented here by 'X'). |
36 | tempX_input - Core temperature (in millidegrees Celsius). | 36 | tempX_input - Core temperature (in millidegrees Celsius). |
37 | tempX_max - All cooling devices should be turned on (on Core2). | 37 | tempX_max - All cooling devices should be turned on (on Core2). |
38 | Initialized with IA32_THERM_INTERRUPT. When the CPU | ||
39 | temperature reaches this temperature, an interrupt is | ||
40 | generated and tempX_max_alarm is set. | ||
41 | tempX_max_hyst - If the CPU temperature falls below than temperature, | ||
42 | an interrupt is generated and tempX_max_alarm is reset. | ||
43 | tempX_max_alarm - Set if the temperature reaches or exceeds tempX_max. | ||
44 | Reset if the temperature drops to or below tempX_max_hyst. | ||
38 | tempX_crit - Maximum junction temperature (in millidegrees Celsius). | 45 | tempX_crit - Maximum junction temperature (in millidegrees Celsius). |
39 | tempX_crit_alarm - Set when Out-of-spec bit is set, never clears. | 46 | tempX_crit_alarm - Set when Out-of-spec bit is set, never clears. |
40 | Correct CPU operation is no longer guaranteed. | 47 | Correct CPU operation is no longer guaranteed. |
diff --git a/drivers/hwmon/coretemp.c b/drivers/hwmon/coretemp.c index 0070d5476dd0..59d83e83da7f 100644 --- a/drivers/hwmon/coretemp.c +++ b/drivers/hwmon/coretemp.c | |||
@@ -44,7 +44,9 @@ | |||
44 | #define BASE_SYSFS_ATTR_NO 2 /* Sysfs Base attr no for coretemp */ | 44 | #define BASE_SYSFS_ATTR_NO 2 /* Sysfs Base attr no for coretemp */ |
45 | #define NUM_REAL_CORES 16 /* Number of Real cores per cpu */ | 45 | #define NUM_REAL_CORES 16 /* Number of Real cores per cpu */ |
46 | #define CORETEMP_NAME_LENGTH 17 /* String Length of attrs */ | 46 | #define CORETEMP_NAME_LENGTH 17 /* String Length of attrs */ |
47 | #define MAX_ATTRS 5 /* Maximum no of per-core attrs */ | 47 | #define MAX_CORE_ATTRS 4 /* Maximum no of basic attrs */ |
48 | #define MAX_THRESH_ATTRS 3 /* Maximum no of Threshold attrs */ | ||
49 | #define TOTAL_ATTRS (MAX_CORE_ATTRS + MAX_THRESH_ATTRS) | ||
48 | #define MAX_CORE_DATA (NUM_REAL_CORES + BASE_SYSFS_ATTR_NO) | 50 | #define MAX_CORE_DATA (NUM_REAL_CORES + BASE_SYSFS_ATTR_NO) |
49 | 51 | ||
50 | #ifdef CONFIG_SMP | 52 | #ifdef CONFIG_SMP |
@@ -67,6 +69,9 @@ | |||
67 | * This value is passed as "id" field to rdmsr/wrmsr functions. | 69 | * This value is passed as "id" field to rdmsr/wrmsr functions. |
68 | * @status_reg: One of IA32_THERM_STATUS or IA32_PACKAGE_THERM_STATUS, | 70 | * @status_reg: One of IA32_THERM_STATUS or IA32_PACKAGE_THERM_STATUS, |
69 | * from where the temperature values should be read. | 71 | * from where the temperature values should be read. |
72 | * @intrpt_reg: One of IA32_THERM_INTERRUPT or IA32_PACKAGE_THERM_INTERRUPT, | ||
73 | * from where the thresholds are read. | ||
74 | * @attr_size: Total number of pre-core attrs displayed in the sysfs. | ||
70 | * @is_pkg_data: If this is 1, the temp_data holds pkgtemp data. | 75 | * @is_pkg_data: If this is 1, the temp_data holds pkgtemp data. |
71 | * Otherwise, temp_data holds coretemp data. | 76 | * Otherwise, temp_data holds coretemp data. |
72 | * @valid: If this is 1, the current temperature is valid. | 77 | * @valid: If this is 1, the current temperature is valid. |
@@ -74,15 +79,18 @@ | |||
74 | struct temp_data { | 79 | struct temp_data { |
75 | int temp; | 80 | int temp; |
76 | int ttarget; | 81 | int ttarget; |
82 | int tmin; | ||
77 | int tjmax; | 83 | int tjmax; |
78 | unsigned long last_updated; | 84 | unsigned long last_updated; |
79 | unsigned int cpu; | 85 | unsigned int cpu; |
80 | u32 cpu_core_id; | 86 | u32 cpu_core_id; |
81 | u32 status_reg; | 87 | u32 status_reg; |
88 | u32 intrpt_reg; | ||
89 | int attr_size; | ||
82 | bool is_pkg_data; | 90 | bool is_pkg_data; |
83 | bool valid; | 91 | bool valid; |
84 | struct sensor_device_attribute sd_attrs[MAX_ATTRS]; | 92 | struct sensor_device_attribute sd_attrs[TOTAL_ATTRS]; |
85 | char attr_name[MAX_ATTRS][CORETEMP_NAME_LENGTH]; | 93 | char attr_name[TOTAL_ATTRS][CORETEMP_NAME_LENGTH]; |
86 | struct mutex update_lock; | 94 | struct mutex update_lock; |
87 | }; | 95 | }; |
88 | 96 | ||
@@ -135,6 +143,19 @@ static ssize_t show_crit_alarm(struct device *dev, | |||
135 | return sprintf(buf, "%d\n", (eax >> 5) & 1); | 143 | return sprintf(buf, "%d\n", (eax >> 5) & 1); |
136 | } | 144 | } |
137 | 145 | ||
146 | static ssize_t show_max_alarm(struct device *dev, | ||
147 | struct device_attribute *devattr, char *buf) | ||
148 | { | ||
149 | u32 eax, edx; | ||
150 | struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); | ||
151 | struct platform_data *pdata = dev_get_drvdata(dev); | ||
152 | struct temp_data *tdata = pdata->core_data[attr->index]; | ||
153 | |||
154 | rdmsr_on_cpu(tdata->cpu, tdata->status_reg, &eax, &edx); | ||
155 | |||
156 | return sprintf(buf, "%d\n", !!(eax & THERM_STATUS_THRESHOLD1)); | ||
157 | } | ||
158 | |||
138 | static ssize_t show_tjmax(struct device *dev, | 159 | static ssize_t show_tjmax(struct device *dev, |
139 | struct device_attribute *devattr, char *buf) | 160 | struct device_attribute *devattr, char *buf) |
140 | { | 161 | { |
@@ -153,6 +174,83 @@ static ssize_t show_ttarget(struct device *dev, | |||
153 | return sprintf(buf, "%d\n", pdata->core_data[attr->index]->ttarget); | 174 | return sprintf(buf, "%d\n", pdata->core_data[attr->index]->ttarget); |
154 | } | 175 | } |
155 | 176 | ||
177 | static ssize_t store_ttarget(struct device *dev, | ||
178 | struct device_attribute *devattr, | ||
179 | const char *buf, size_t count) | ||
180 | { | ||
181 | struct platform_data *pdata = dev_get_drvdata(dev); | ||
182 | struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); | ||
183 | struct temp_data *tdata = pdata->core_data[attr->index]; | ||
184 | u32 eax, edx; | ||
185 | unsigned long val; | ||
186 | int diff; | ||
187 | |||
188 | if (strict_strtoul(buf, 10, &val)) | ||
189 | return -EINVAL; | ||
190 | |||
191 | /* | ||
192 | * THERM_MASK_THRESHOLD1 is 7 bits wide. Values are entered in terms | ||
193 | * of milli degree celsius. Hence don't accept val > (127 * 1000) | ||
194 | */ | ||
195 | if (val > tdata->tjmax || val > 127000) | ||
196 | return -EINVAL; | ||
197 | |||
198 | diff = (tdata->tjmax - val) / 1000; | ||
199 | |||
200 | mutex_lock(&tdata->update_lock); | ||
201 | rdmsr_on_cpu(tdata->cpu, tdata->intrpt_reg, &eax, &edx); | ||
202 | eax = (eax & ~THERM_MASK_THRESHOLD1) | | ||
203 | (diff << THERM_SHIFT_THRESHOLD1); | ||
204 | wrmsr_on_cpu(tdata->cpu, tdata->intrpt_reg, eax, edx); | ||
205 | tdata->ttarget = val; | ||
206 | mutex_unlock(&tdata->update_lock); | ||
207 | |||
208 | return count; | ||
209 | } | ||
210 | |||
211 | static ssize_t show_tmin(struct device *dev, | ||
212 | struct device_attribute *devattr, char *buf) | ||
213 | { | ||
214 | struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); | ||
215 | struct platform_data *pdata = dev_get_drvdata(dev); | ||
216 | |||
217 | return sprintf(buf, "%d\n", pdata->core_data[attr->index]->tmin); | ||
218 | } | ||
219 | |||
220 | static ssize_t store_tmin(struct device *dev, | ||
221 | struct device_attribute *devattr, | ||
222 | const char *buf, size_t count) | ||
223 | { | ||
224 | struct platform_data *pdata = dev_get_drvdata(dev); | ||
225 | struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); | ||
226 | struct temp_data *tdata = pdata->core_data[attr->index]; | ||
227 | u32 eax, edx; | ||
228 | unsigned long val; | ||
229 | int diff; | ||
230 | |||
231 | if (strict_strtoul(buf, 10, &val)) | ||
232 | return -EINVAL; | ||
233 | |||
234 | /* | ||
235 | * THERM_MASK_THRESHOLD0 is 7 bits wide. Values are entered in terms | ||
236 | * of milli degree celsius. Hence don't accept val > (127 * 1000) | ||
237 | */ | ||
238 | if (val > tdata->tjmax || val > 127000) | ||
239 | return -EINVAL; | ||
240 | |||
241 | diff = (tdata->tjmax - val) / 1000; | ||
242 | |||
243 | mutex_lock(&tdata->update_lock); | ||
244 | rdmsr_on_cpu(tdata->cpu, tdata->intrpt_reg, &eax, &edx); | ||
245 | eax = (eax & ~THERM_MASK_THRESHOLD0) | | ||
246 | (diff << THERM_SHIFT_THRESHOLD0); | ||
247 | wrmsr_on_cpu(tdata->cpu, tdata->intrpt_reg, eax, edx); | ||
248 | tdata->tmin = val; | ||
249 | mutex_unlock(&tdata->update_lock); | ||
250 | |||
251 | return count; | ||
252 | } | ||
253 | |||
156 | static ssize_t show_temp(struct device *dev, | 254 | static ssize_t show_temp(struct device *dev, |
157 | struct device_attribute *devattr, char *buf) | 255 | struct device_attribute *devattr, char *buf) |
158 | { | 256 | { |
@@ -344,23 +442,31 @@ static int create_core_attrs(struct temp_data *tdata, struct device *dev, | |||
344 | int attr_no) | 442 | int attr_no) |
345 | { | 443 | { |
346 | int err, i; | 444 | int err, i; |
347 | static ssize_t (*rd_ptr[MAX_ATTRS]) (struct device *dev, | 445 | static ssize_t (*rd_ptr[TOTAL_ATTRS]) (struct device *dev, |
348 | struct device_attribute *devattr, char *buf) = { | 446 | struct device_attribute *devattr, char *buf) = { |
349 | show_label, show_crit_alarm, show_ttarget, | 447 | show_label, show_crit_alarm, show_temp, show_tjmax, |
350 | show_temp, show_tjmax }; | 448 | show_max_alarm, show_ttarget, show_tmin }; |
351 | static const char *names[MAX_ATTRS] = { | 449 | static ssize_t (*rw_ptr[TOTAL_ATTRS]) (struct device *dev, |
450 | struct device_attribute *devattr, const char *buf, | ||
451 | size_t count) = { NULL, NULL, NULL, NULL, NULL, | ||
452 | store_ttarget, store_tmin }; | ||
453 | static const char *names[TOTAL_ATTRS] = { | ||
352 | "temp%d_label", "temp%d_crit_alarm", | 454 | "temp%d_label", "temp%d_crit_alarm", |
353 | "temp%d_max", "temp%d_input", | 455 | "temp%d_input", "temp%d_crit", |
354 | "temp%d_crit" }; | 456 | "temp%d_max_alarm", "temp%d_max", |
457 | "temp%d_max_hyst" }; | ||
355 | 458 | ||
356 | for (i = 0; i < MAX_ATTRS; i++) { | 459 | for (i = 0; i < tdata->attr_size; i++) { |
357 | snprintf(tdata->attr_name[i], CORETEMP_NAME_LENGTH, names[i], | 460 | snprintf(tdata->attr_name[i], CORETEMP_NAME_LENGTH, names[i], |
358 | attr_no); | 461 | attr_no); |
359 | sysfs_attr_init(&tdata->sd_attrs[i].dev_attr.attr); | 462 | sysfs_attr_init(&tdata->sd_attrs[i].dev_attr.attr); |
360 | tdata->sd_attrs[i].dev_attr.attr.name = tdata->attr_name[i]; | 463 | tdata->sd_attrs[i].dev_attr.attr.name = tdata->attr_name[i]; |
361 | tdata->sd_attrs[i].dev_attr.attr.mode = S_IRUGO; | 464 | tdata->sd_attrs[i].dev_attr.attr.mode = S_IRUGO; |
465 | if (rw_ptr[i]) { | ||
466 | tdata->sd_attrs[i].dev_attr.attr.mode |= S_IWUSR; | ||
467 | tdata->sd_attrs[i].dev_attr.store = rw_ptr[i]; | ||
468 | } | ||
362 | tdata->sd_attrs[i].dev_attr.show = rd_ptr[i]; | 469 | tdata->sd_attrs[i].dev_attr.show = rd_ptr[i]; |
363 | tdata->sd_attrs[i].dev_attr.store = NULL; | ||
364 | tdata->sd_attrs[i].index = attr_no; | 470 | tdata->sd_attrs[i].index = attr_no; |
365 | err = device_create_file(dev, &tdata->sd_attrs[i].dev_attr); | 471 | err = device_create_file(dev, &tdata->sd_attrs[i].dev_attr); |
366 | if (err) | 472 | if (err) |
@@ -374,38 +480,6 @@ exit_free: | |||
374 | return err; | 480 | return err; |
375 | } | 481 | } |
376 | 482 | ||
377 | static void update_ttarget(__u8 cpu_model, struct temp_data *tdata, | ||
378 | struct device *dev) | ||
379 | { | ||
380 | int err; | ||
381 | u32 eax, edx; | ||
382 | |||
383 | /* | ||
384 | * Initialize ttarget value. Eventually this will be | ||
385 | * initialized with the value from MSR_IA32_THERM_INTERRUPT | ||
386 | * register. If IA32_TEMPERATURE_TARGET is supported, this | ||
387 | * value will be over written below. | ||
388 | * To Do: Patch to initialize ttarget from MSR_IA32_THERM_INTERRUPT | ||
389 | */ | ||
390 | tdata->ttarget = tdata->tjmax - 20000; | ||
391 | |||
392 | /* | ||
393 | * Read the still undocumented IA32_TEMPERATURE_TARGET. It exists | ||
394 | * on older CPUs but not in this register, | ||
395 | * Atoms don't have it either. | ||
396 | */ | ||
397 | if (cpu_model > 0xe && cpu_model != 0x1c) { | ||
398 | err = rdmsr_safe_on_cpu(tdata->cpu, | ||
399 | MSR_IA32_TEMPERATURE_TARGET, &eax, &edx); | ||
400 | if (err) { | ||
401 | dev_warn(dev, | ||
402 | "Unable to read IA32_TEMPERATURE_TARGET MSR\n"); | ||
403 | } else { | ||
404 | tdata->ttarget = tdata->tjmax - | ||
405 | ((eax >> 8) & 0xff) * 1000; | ||
406 | } | ||
407 | } | ||
408 | } | ||
409 | 483 | ||
410 | static int __devinit chk_ucode_version(struct platform_device *pdev) | 484 | static int __devinit chk_ucode_version(struct platform_device *pdev) |
411 | { | 485 | { |
@@ -464,9 +538,12 @@ static struct temp_data *init_temp_data(unsigned int cpu, int pkg_flag) | |||
464 | 538 | ||
465 | tdata->status_reg = pkg_flag ? MSR_IA32_PACKAGE_THERM_STATUS : | 539 | tdata->status_reg = pkg_flag ? MSR_IA32_PACKAGE_THERM_STATUS : |
466 | MSR_IA32_THERM_STATUS; | 540 | MSR_IA32_THERM_STATUS; |
541 | tdata->intrpt_reg = pkg_flag ? MSR_IA32_PACKAGE_THERM_INTERRUPT : | ||
542 | MSR_IA32_THERM_INTERRUPT; | ||
467 | tdata->is_pkg_data = pkg_flag; | 543 | tdata->is_pkg_data = pkg_flag; |
468 | tdata->cpu = cpu; | 544 | tdata->cpu = cpu; |
469 | tdata->cpu_core_id = TO_CORE_ID(cpu); | 545 | tdata->cpu_core_id = TO_CORE_ID(cpu); |
546 | tdata->attr_size = MAX_CORE_ATTRS; | ||
470 | mutex_init(&tdata->update_lock); | 547 | mutex_init(&tdata->update_lock); |
471 | return tdata; | 548 | return tdata; |
472 | } | 549 | } |
@@ -516,7 +593,17 @@ static int create_core_data(struct platform_data *pdata, | |||
516 | else | 593 | else |
517 | tdata->tjmax = get_tjmax(c, cpu, &pdev->dev); | 594 | tdata->tjmax = get_tjmax(c, cpu, &pdev->dev); |
518 | 595 | ||
519 | update_ttarget(c->x86_model, tdata, &pdev->dev); | 596 | /* |
597 | * Test if we can access the intrpt register. If so, increase the | ||
598 | * 'size' enough to have ttarget/tmin/max_alarm interfaces. | ||
599 | * Initialize ttarget with bits 16:22 of MSR_IA32_THERM_INTERRUPT | ||
600 | */ | ||
601 | err = rdmsr_safe_on_cpu(cpu, tdata->intrpt_reg, &eax, &edx); | ||
602 | if (!err) { | ||
603 | tdata->attr_size += MAX_THRESH_ATTRS; | ||
604 | tdata->ttarget = tdata->tjmax - ((eax >> 16) & 0x7f) * 1000; | ||
605 | } | ||
606 | |||
520 | pdata->core_data[attr_no] = tdata; | 607 | pdata->core_data[attr_no] = tdata; |
521 | 608 | ||
522 | /* Create sysfs interfaces */ | 609 | /* Create sysfs interfaces */ |
@@ -553,7 +640,7 @@ static void coretemp_remove_core(struct platform_data *pdata, | |||
553 | struct temp_data *tdata = pdata->core_data[indx]; | 640 | struct temp_data *tdata = pdata->core_data[indx]; |
554 | 641 | ||
555 | /* Remove the sysfs attributes */ | 642 | /* Remove the sysfs attributes */ |
556 | for (i = 0; i < MAX_ATTRS; i++) | 643 | for (i = 0; i < tdata->attr_size; i++) |
557 | device_remove_file(dev, &tdata->sd_attrs[i].dev_attr); | 644 | device_remove_file(dev, &tdata->sd_attrs[i].dev_attr); |
558 | 645 | ||
559 | kfree(pdata->core_data[indx]); | 646 | kfree(pdata->core_data[indx]); |