|
@@ -36,17 +36,25 @@
|
|
|
#include <linux/cpu.h>
|
|
|
#include <linux/pci.h>
|
|
|
#include <linux/smp.h>
|
|
|
+#include <linux/moduleparam.h>
|
|
|
#include <asm/msr.h>
|
|
|
#include <asm/processor.h>
|
|
|
|
|
|
#define DRVNAME "coretemp"
|
|
|
|
|
|
+/*
|
|
|
+ * force_tjmax only matters when TjMax can't be read from the CPU itself.
|
|
|
+ * When set, it replaces the driver's suboptimal heuristic.
|
|
|
+ */
|
|
|
+static int force_tjmax;
|
|
|
+module_param_named(tjmax, force_tjmax, int, 0444);
|
|
|
+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 MAX_THRESH_ATTRS 3 /* Maximum no of Threshold attrs */
|
|
|
-#define TOTAL_ATTRS (MAX_CORE_ATTRS + MAX_THRESH_ATTRS)
|
|
|
+#define TOTAL_ATTRS (MAX_CORE_ATTRS + 1)
|
|
|
#define MAX_CORE_DATA (NUM_REAL_CORES + BASE_SYSFS_ATTR_NO)
|
|
|
|
|
|
#ifdef CONFIG_SMP
|
|
@@ -69,8 +77,6 @@
|
|
|
* 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.
|
|
@@ -79,13 +85,11 @@
|
|
|
struct temp_data {
|
|
|
int temp;
|
|
|
int ttarget;
|
|
|
- int tmin;
|
|
|
int tjmax;
|
|
|
unsigned long last_updated;
|
|
|
unsigned int cpu;
|
|
|
u32 cpu_core_id;
|
|
|
u32 status_reg;
|
|
|
- u32 intrpt_reg;
|
|
|
int attr_size;
|
|
|
bool is_pkg_data;
|
|
|
bool valid;
|
|
@@ -143,19 +147,6 @@ static ssize_t show_crit_alarm(struct device *dev,
|
|
|
return sprintf(buf, "%d\n", (eax >> 5) & 1);
|
|
|
}
|
|
|
|
|
|
-static ssize_t show_max_alarm(struct device *dev,
|
|
|
- struct device_attribute *devattr, char *buf)
|
|
|
-{
|
|
|
- u32 eax, edx;
|
|
|
- struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
|
|
|
- struct platform_data *pdata = dev_get_drvdata(dev);
|
|
|
- struct temp_data *tdata = pdata->core_data[attr->index];
|
|
|
-
|
|
|
- rdmsr_on_cpu(tdata->cpu, tdata->status_reg, &eax, &edx);
|
|
|
-
|
|
|
- return sprintf(buf, "%d\n", !!(eax & THERM_STATUS_THRESHOLD1));
|
|
|
-}
|
|
|
-
|
|
|
static ssize_t show_tjmax(struct device *dev,
|
|
|
struct device_attribute *devattr, char *buf)
|
|
|
{
|
|
@@ -174,83 +165,6 @@ static ssize_t show_ttarget(struct device *dev,
|
|
|
return sprintf(buf, "%d\n", pdata->core_data[attr->index]->ttarget);
|
|
|
}
|
|
|
|
|
|
-static ssize_t store_ttarget(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 *attr = to_sensor_dev_attr(devattr);
|
|
|
- struct temp_data *tdata = pdata->core_data[attr->index];
|
|
|
- u32 eax, edx;
|
|
|
- unsigned long val;
|
|
|
- int diff;
|
|
|
-
|
|
|
- if (strict_strtoul(buf, 10, &val))
|
|
|
- return -EINVAL;
|
|
|
-
|
|
|
- /*
|
|
|
- * THERM_MASK_THRESHOLD1 is 7 bits wide. Values are entered in terms
|
|
|
- * of milli degree celsius. Hence don't accept val > (127 * 1000)
|
|
|
- */
|
|
|
- if (val > tdata->tjmax || val > 127000)
|
|
|
- return -EINVAL;
|
|
|
-
|
|
|
- diff = (tdata->tjmax - val) / 1000;
|
|
|
-
|
|
|
- mutex_lock(&tdata->update_lock);
|
|
|
- rdmsr_on_cpu(tdata->cpu, tdata->intrpt_reg, &eax, &edx);
|
|
|
- eax = (eax & ~THERM_MASK_THRESHOLD1) |
|
|
|
- (diff << THERM_SHIFT_THRESHOLD1);
|
|
|
- wrmsr_on_cpu(tdata->cpu, tdata->intrpt_reg, eax, edx);
|
|
|
- tdata->ttarget = val;
|
|
|
- mutex_unlock(&tdata->update_lock);
|
|
|
-
|
|
|
- return count;
|
|
|
-}
|
|
|
-
|
|
|
-static ssize_t show_tmin(struct device *dev,
|
|
|
- struct device_attribute *devattr, char *buf)
|
|
|
-{
|
|
|
- struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
|
|
|
- struct platform_data *pdata = dev_get_drvdata(dev);
|
|
|
-
|
|
|
- return sprintf(buf, "%d\n", pdata->core_data[attr->index]->tmin);
|
|
|
-}
|
|
|
-
|
|
|
-static ssize_t store_tmin(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 *attr = to_sensor_dev_attr(devattr);
|
|
|
- struct temp_data *tdata = pdata->core_data[attr->index];
|
|
|
- u32 eax, edx;
|
|
|
- unsigned long val;
|
|
|
- int diff;
|
|
|
-
|
|
|
- if (strict_strtoul(buf, 10, &val))
|
|
|
- return -EINVAL;
|
|
|
-
|
|
|
- /*
|
|
|
- * THERM_MASK_THRESHOLD0 is 7 bits wide. Values are entered in terms
|
|
|
- * of milli degree celsius. Hence don't accept val > (127 * 1000)
|
|
|
- */
|
|
|
- if (val > tdata->tjmax || val > 127000)
|
|
|
- return -EINVAL;
|
|
|
-
|
|
|
- diff = (tdata->tjmax - val) / 1000;
|
|
|
-
|
|
|
- mutex_lock(&tdata->update_lock);
|
|
|
- rdmsr_on_cpu(tdata->cpu, tdata->intrpt_reg, &eax, &edx);
|
|
|
- eax = (eax & ~THERM_MASK_THRESHOLD0) |
|
|
|
- (diff << THERM_SHIFT_THRESHOLD0);
|
|
|
- wrmsr_on_cpu(tdata->cpu, tdata->intrpt_reg, eax, edx);
|
|
|
- tdata->tmin = val;
|
|
|
- mutex_unlock(&tdata->update_lock);
|
|
|
-
|
|
|
- return count;
|
|
|
-}
|
|
|
-
|
|
|
static ssize_t show_temp(struct device *dev,
|
|
|
struct device_attribute *devattr, char *buf)
|
|
|
{
|
|
@@ -374,7 +288,6 @@ static int adjust_tjmax(struct cpuinfo_x86 *c, u32 id, struct device *dev)
|
|
|
|
|
|
static int get_tjmax(struct cpuinfo_x86 *c, u32 id, struct device *dev)
|
|
|
{
|
|
|
- /* The 100C is default for both mobile and non mobile CPUs */
|
|
|
int err;
|
|
|
u32 eax, edx;
|
|
|
u32 val;
|
|
@@ -385,7 +298,8 @@ static int get_tjmax(struct cpuinfo_x86 *c, u32 id, struct device *dev)
|
|
|
*/
|
|
|
err = rdmsr_safe_on_cpu(id, MSR_IA32_TEMPERATURE_TARGET, &eax, &edx);
|
|
|
if (err) {
|
|
|
- dev_warn(dev, "Unable to read TjMax from CPU.\n");
|
|
|
+ if (c->x86_model > 0xe && c->x86_model != 0x1c)
|
|
|
+ dev_warn(dev, "Unable to read TjMax from CPU %u\n", id);
|
|
|
} else {
|
|
|
val = (eax >> 16) & 0xff;
|
|
|
/*
|
|
@@ -393,11 +307,17 @@ static int get_tjmax(struct cpuinfo_x86 *c, u32 id, struct device *dev)
|
|
|
* will be used
|
|
|
*/
|
|
|
if (val) {
|
|
|
- dev_info(dev, "TjMax is %d C.\n", val);
|
|
|
+ dev_dbg(dev, "TjMax is %d degrees C\n", val);
|
|
|
return val * 1000;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+ if (force_tjmax) {
|
|
|
+ dev_notice(dev, "TjMax forced to %d degrees C by user\n",
|
|
|
+ force_tjmax);
|
|
|
+ return force_tjmax * 1000;
|
|
|
+ }
|
|
|
+
|
|
|
/*
|
|
|
* An assumption is made for early CPUs and unreadable MSR.
|
|
|
* NOTE: the calculated value may not be correct.
|
|
@@ -414,21 +334,6 @@ static void __devinit get_ucode_rev_on_cpu(void *edx)
|
|
|
rdmsr(MSR_IA32_UCODE_REV, eax, *(u32 *)edx);
|
|
|
}
|
|
|
|
|
|
-static int get_pkg_tjmax(unsigned int cpu, struct device *dev)
|
|
|
-{
|
|
|
- int err;
|
|
|
- u32 eax, edx, val;
|
|
|
-
|
|
|
- err = rdmsr_safe_on_cpu(cpu, MSR_IA32_TEMPERATURE_TARGET, &eax, &edx);
|
|
|
- if (!err) {
|
|
|
- val = (eax >> 16) & 0xff;
|
|
|
- if (val)
|
|
|
- return val * 1000;
|
|
|
- }
|
|
|
- dev_warn(dev, "Unable to read Pkg-TjMax from CPU:%u\n", cpu);
|
|
|
- return 100000; /* Default TjMax: 100 degree celsius */
|
|
|
-}
|
|
|
-
|
|
|
static int create_name_attr(struct platform_data *pdata, struct device *dev)
|
|
|
{
|
|
|
sysfs_attr_init(&pdata->name_attr.attr);
|
|
@@ -442,19 +347,14 @@ static int create_core_attrs(struct temp_data *tdata, struct device *dev,
|
|
|
int attr_no)
|
|
|
{
|
|
|
int err, i;
|
|
|
- static ssize_t (*rd_ptr[TOTAL_ATTRS]) (struct device *dev,
|
|
|
+ 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_max_alarm, show_ttarget, show_tmin };
|
|
|
- static ssize_t (*rw_ptr[TOTAL_ATTRS]) (struct device *dev,
|
|
|
- struct device_attribute *devattr, const char *buf,
|
|
|
- size_t count) = { NULL, NULL, NULL, NULL, NULL,
|
|
|
- store_ttarget, store_tmin };
|
|
|
- static const char *names[TOTAL_ATTRS] = {
|
|
|
+ show_ttarget };
|
|
|
+ static const char *const names[TOTAL_ATTRS] = {
|
|
|
"temp%d_label", "temp%d_crit_alarm",
|
|
|
"temp%d_input", "temp%d_crit",
|
|
|
- "temp%d_max_alarm", "temp%d_max",
|
|
|
- "temp%d_max_hyst" };
|
|
|
+ "temp%d_max" };
|
|
|
|
|
|
for (i = 0; i < tdata->attr_size; i++) {
|
|
|
snprintf(tdata->attr_name[i], CORETEMP_NAME_LENGTH, names[i],
|
|
@@ -462,10 +362,6 @@ static int create_core_attrs(struct temp_data *tdata, struct device *dev,
|
|
|
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;
|
|
|
err = device_create_file(dev, &tdata->sd_attrs[i].dev_attr);
|
|
@@ -481,9 +377,9 @@ exit_free:
|
|
|
}
|
|
|
|
|
|
|
|
|
-static int __devinit chk_ucode_version(struct platform_device *pdev)
|
|
|
+static int __cpuinit chk_ucode_version(unsigned int cpu)
|
|
|
{
|
|
|
- struct cpuinfo_x86 *c = &cpu_data(pdev->id);
|
|
|
+ struct cpuinfo_x86 *c = &cpu_data(cpu);
|
|
|
int err;
|
|
|
u32 edx;
|
|
|
|
|
@@ -494,17 +390,15 @@ static int __devinit chk_ucode_version(struct platform_device *pdev)
|
|
|
*/
|
|
|
if (c->x86_model == 0xe && c->x86_mask < 0xc) {
|
|
|
/* check for microcode update */
|
|
|
- err = smp_call_function_single(pdev->id, get_ucode_rev_on_cpu,
|
|
|
+ err = smp_call_function_single(cpu, get_ucode_rev_on_cpu,
|
|
|
&edx, 1);
|
|
|
if (err) {
|
|
|
- dev_err(&pdev->dev,
|
|
|
- "Cannot determine microcode revision of "
|
|
|
- "CPU#%u (%d)!\n", pdev->id, err);
|
|
|
+ pr_err("Cannot determine microcode revision of "
|
|
|
+ "CPU#%u (%d)!\n", cpu, err);
|
|
|
return -ENODEV;
|
|
|
} else if (edx < 0x39) {
|
|
|
- dev_err(&pdev->dev,
|
|
|
- "Errata AE18 not fixed, update BIOS or "
|
|
|
- "microcode of the CPU!\n");
|
|
|
+ pr_err("Errata AE18 not fixed, update BIOS or "
|
|
|
+ "microcode of the CPU!\n");
|
|
|
return -ENODEV;
|
|
|
}
|
|
|
}
|
|
@@ -538,8 +432,6 @@ 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);
|
|
@@ -548,11 +440,11 @@ static struct temp_data *init_temp_data(unsigned int cpu, int pkg_flag)
|
|
|
return tdata;
|
|
|
}
|
|
|
|
|
|
-static int create_core_data(struct platform_data *pdata,
|
|
|
- struct platform_device *pdev,
|
|
|
+static int create_core_data(struct platform_device *pdev,
|
|
|
unsigned int cpu, int pkg_flag)
|
|
|
{
|
|
|
struct temp_data *tdata;
|
|
|
+ struct platform_data *pdata = platform_get_drvdata(pdev);
|
|
|
struct cpuinfo_x86 *c = &cpu_data(cpu);
|
|
|
u32 eax, edx;
|
|
|
int err, attr_no;
|
|
@@ -588,25 +480,21 @@ static int create_core_data(struct platform_data *pdata,
|
|
|
goto exit_free;
|
|
|
|
|
|
/* We can access status register. Get Critical Temperature */
|
|
|
- if (pkg_flag)
|
|
|
- tdata->tjmax = get_pkg_tjmax(pdev->id, &pdev->dev);
|
|
|
- else
|
|
|
- tdata->tjmax = get_tjmax(c, cpu, &pdev->dev);
|
|
|
+ tdata->tjmax = get_tjmax(c, cpu, &pdev->dev);
|
|
|
|
|
|
/*
|
|
|
- * Test if we can access the intrpt register. If so, increase the
|
|
|
- * 'size' enough to have ttarget/tmin/max_alarm interfaces.
|
|
|
- * Initialize ttarget with bits 16:22 of MSR_IA32_THERM_INTERRUPT
|
|
|
+ * Read the still undocumented bits 8:15 of IA32_TEMPERATURE_TARGET.
|
|
|
+ * The target temperature is available on older CPUs but not in this
|
|
|
+ * register. Atoms don't have the register at all.
|
|
|
*/
|
|
|
- err = rdmsr_safe_on_cpu(cpu, tdata->intrpt_reg, &eax, &edx);
|
|
|
- if (!err) {
|
|
|
- tdata->attr_size += MAX_THRESH_ATTRS;
|
|
|
- tdata->tmin = tdata->tjmax -
|
|
|
- ((eax & THERM_MASK_THRESHOLD0) >>
|
|
|
- THERM_SHIFT_THRESHOLD0) * 1000;
|
|
|
- tdata->ttarget = tdata->tjmax -
|
|
|
- ((eax & THERM_MASK_THRESHOLD1) >>
|
|
|
- THERM_SHIFT_THRESHOLD1) * 1000;
|
|
|
+ if (c->x86_model > 0xe && c->x86_model != 0x1c) {
|
|
|
+ err = rdmsr_safe_on_cpu(cpu, MSR_IA32_TEMPERATURE_TARGET,
|
|
|
+ &eax, &edx);
|
|
|
+ if (!err) {
|
|
|
+ tdata->ttarget
|
|
|
+ = tdata->tjmax - ((eax >> 8) & 0xff) * 1000;
|
|
|
+ tdata->attr_size++;
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
pdata->core_data[attr_no] = tdata;
|
|
@@ -618,22 +506,20 @@ static int create_core_data(struct platform_data *pdata,
|
|
|
|
|
|
return 0;
|
|
|
exit_free:
|
|
|
+ pdata->core_data[attr_no] = NULL;
|
|
|
kfree(tdata);
|
|
|
return err;
|
|
|
}
|
|
|
|
|
|
static void coretemp_add_core(unsigned int cpu, int pkg_flag)
|
|
|
{
|
|
|
- struct platform_data *pdata;
|
|
|
struct platform_device *pdev = coretemp_get_pdev(cpu);
|
|
|
int err;
|
|
|
|
|
|
if (!pdev)
|
|
|
return;
|
|
|
|
|
|
- pdata = platform_get_drvdata(pdev);
|
|
|
-
|
|
|
- err = create_core_data(pdata, pdev, cpu, pkg_flag);
|
|
|
+ err = create_core_data(pdev, cpu, pkg_flag);
|
|
|
if (err)
|
|
|
dev_err(&pdev->dev, "Adding Core %u failed\n", cpu);
|
|
|
}
|
|
@@ -657,11 +543,6 @@ static int __devinit coretemp_probe(struct platform_device *pdev)
|
|
|
struct platform_data *pdata;
|
|
|
int err;
|
|
|
|
|
|
- /* Check the microcode version of the CPU */
|
|
|
- err = chk_ucode_version(pdev);
|
|
|
- if (err)
|
|
|
- return err;
|
|
|
-
|
|
|
/* Initialize the per-package data structures */
|
|
|
pdata = kzalloc(sizeof(struct platform_data), GFP_KERNEL);
|
|
|
if (!pdata)
|
|
@@ -671,7 +552,7 @@ static int __devinit coretemp_probe(struct platform_device *pdev)
|
|
|
if (err)
|
|
|
goto exit_free;
|
|
|
|
|
|
- pdata->phys_proc_id = TO_PHYS_ID(pdev->id);
|
|
|
+ pdata->phys_proc_id = pdev->id;
|
|
|
platform_set_drvdata(pdev, pdata);
|
|
|
|
|
|
pdata->hwmon_dev = hwmon_device_register(&pdev->dev);
|
|
@@ -723,7 +604,7 @@ static int __cpuinit coretemp_device_add(unsigned int cpu)
|
|
|
|
|
|
mutex_lock(&pdev_list_mutex);
|
|
|
|
|
|
- pdev = platform_device_alloc(DRVNAME, cpu);
|
|
|
+ pdev = platform_device_alloc(DRVNAME, TO_PHYS_ID(cpu));
|
|
|
if (!pdev) {
|
|
|
err = -ENOMEM;
|
|
|
pr_err("Device allocation failed\n");
|
|
@@ -743,7 +624,7 @@ static int __cpuinit coretemp_device_add(unsigned int cpu)
|
|
|
}
|
|
|
|
|
|
pdev_entry->pdev = pdev;
|
|
|
- pdev_entry->phys_proc_id = TO_PHYS_ID(cpu);
|
|
|
+ pdev_entry->phys_proc_id = pdev->id;
|
|
|
|
|
|
list_add_tail(&pdev_entry->list, &pdev_list);
|
|
|
mutex_unlock(&pdev_list_mutex);
|
|
@@ -804,6 +685,10 @@ static void __cpuinit get_core_online(unsigned int cpu)
|
|
|
return;
|
|
|
|
|
|
if (!pdev) {
|
|
|
+ /* Check the microcode version of the CPU */
|
|
|
+ if (chk_ucode_version(cpu))
|
|
|
+ return;
|
|
|
+
|
|
|
/*
|
|
|
* Alright, we have DTS support.
|
|
|
* We are bringing the _first_ core in this pkg
|