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>
This commit is contained in:
parent
8c1d04192e
commit
c814a4c7c4
@ -35,6 +35,13 @@ 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').
|
All Sysfs entries are named with their core_id (represented here by 'X').
|
||||||
tempX_input - Core temperature (in millidegrees Celsius).
|
tempX_input - Core temperature (in millidegrees Celsius).
|
||||||
tempX_max - All cooling devices should be turned on (on Core2).
|
tempX_max - All cooling devices should be turned on (on Core2).
|
||||||
|
Initialized with IA32_THERM_INTERRUPT. When the CPU
|
||||||
|
temperature reaches this temperature, an interrupt is
|
||||||
|
generated and tempX_max_alarm is set.
|
||||||
|
tempX_max_hyst - If the CPU temperature falls below than temperature,
|
||||||
|
an interrupt is generated and tempX_max_alarm is reset.
|
||||||
|
tempX_max_alarm - Set if the temperature reaches or exceeds tempX_max.
|
||||||
|
Reset if the temperature drops to or below tempX_max_hyst.
|
||||||
tempX_crit - Maximum junction temperature (in millidegrees Celsius).
|
tempX_crit - Maximum junction temperature (in millidegrees Celsius).
|
||||||
tempX_crit_alarm - Set when Out-of-spec bit is set, never clears.
|
tempX_crit_alarm - Set when Out-of-spec bit is set, never clears.
|
||||||
Correct CPU operation is no longer guaranteed.
|
Correct CPU operation is no longer guaranteed.
|
||||||
|
@ -44,7 +44,9 @@
|
|||||||
#define BASE_SYSFS_ATTR_NO 2 /* Sysfs Base attr no for coretemp */
|
#define BASE_SYSFS_ATTR_NO 2 /* Sysfs Base attr no for coretemp */
|
||||||
#define NUM_REAL_CORES 16 /* Number of Real cores per cpu */
|
#define NUM_REAL_CORES 16 /* Number of Real cores per cpu */
|
||||||
#define CORETEMP_NAME_LENGTH 17 /* String Length of attrs */
|
#define CORETEMP_NAME_LENGTH 17 /* String Length of attrs */
|
||||||
#define MAX_ATTRS 5 /* Maximum no of per-core 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 MAX_CORE_DATA (NUM_REAL_CORES + BASE_SYSFS_ATTR_NO)
|
#define MAX_CORE_DATA (NUM_REAL_CORES + BASE_SYSFS_ATTR_NO)
|
||||||
|
|
||||||
#ifdef CONFIG_SMP
|
#ifdef CONFIG_SMP
|
||||||
@ -67,6 +69,9 @@
|
|||||||
* This value is passed as "id" field to rdmsr/wrmsr functions.
|
* This value is passed as "id" field to rdmsr/wrmsr functions.
|
||||||
* @status_reg: One of IA32_THERM_STATUS or IA32_PACKAGE_THERM_STATUS,
|
* @status_reg: One of IA32_THERM_STATUS or IA32_PACKAGE_THERM_STATUS,
|
||||||
* from where the temperature values should be read.
|
* 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.
|
* @is_pkg_data: If this is 1, the temp_data holds pkgtemp data.
|
||||||
* Otherwise, temp_data holds coretemp data.
|
* Otherwise, temp_data holds coretemp data.
|
||||||
* @valid: If this is 1, the current temperature is valid.
|
* @valid: If this is 1, the current temperature is valid.
|
||||||
@ -74,15 +79,18 @@
|
|||||||
struct temp_data {
|
struct temp_data {
|
||||||
int temp;
|
int temp;
|
||||||
int ttarget;
|
int ttarget;
|
||||||
|
int tmin;
|
||||||
int tjmax;
|
int tjmax;
|
||||||
unsigned long last_updated;
|
unsigned long last_updated;
|
||||||
unsigned int cpu;
|
unsigned int cpu;
|
||||||
u32 cpu_core_id;
|
u32 cpu_core_id;
|
||||||
u32 status_reg;
|
u32 status_reg;
|
||||||
|
u32 intrpt_reg;
|
||||||
|
int attr_size;
|
||||||
bool is_pkg_data;
|
bool is_pkg_data;
|
||||||
bool valid;
|
bool valid;
|
||||||
struct sensor_device_attribute sd_attrs[MAX_ATTRS];
|
struct sensor_device_attribute sd_attrs[TOTAL_ATTRS];
|
||||||
char attr_name[MAX_ATTRS][CORETEMP_NAME_LENGTH];
|
char attr_name[TOTAL_ATTRS][CORETEMP_NAME_LENGTH];
|
||||||
struct mutex update_lock;
|
struct mutex update_lock;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -135,6 +143,19 @@ static ssize_t show_crit_alarm(struct device *dev,
|
|||||||
return sprintf(buf, "%d\n", (eax >> 5) & 1);
|
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,
|
static ssize_t show_tjmax(struct device *dev,
|
||||||
struct device_attribute *devattr, char *buf)
|
struct device_attribute *devattr, char *buf)
|
||||||
{
|
{
|
||||||
@ -153,6 +174,83 @@ static ssize_t show_ttarget(struct device *dev,
|
|||||||
return sprintf(buf, "%d\n", pdata->core_data[attr->index]->ttarget);
|
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,
|
static ssize_t show_temp(struct device *dev,
|
||||||
struct device_attribute *devattr, char *buf)
|
struct device_attribute *devattr, char *buf)
|
||||||
{
|
{
|
||||||
@ -344,23 +442,31 @@ static int create_core_attrs(struct temp_data *tdata, struct device *dev,
|
|||||||
int attr_no)
|
int attr_no)
|
||||||
{
|
{
|
||||||
int err, i;
|
int err, i;
|
||||||
static ssize_t (*rd_ptr[MAX_ATTRS]) (struct device *dev,
|
static ssize_t (*rd_ptr[TOTAL_ATTRS]) (struct device *dev,
|
||||||
struct device_attribute *devattr, char *buf) = {
|
struct device_attribute *devattr, char *buf) = {
|
||||||
show_label, show_crit_alarm, show_ttarget,
|
show_label, show_crit_alarm, show_temp, show_tjmax,
|
||||||
show_temp, show_tjmax };
|
show_max_alarm, show_ttarget, show_tmin };
|
||||||
static const char *names[MAX_ATTRS] = {
|
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] = {
|
||||||
"temp%d_label", "temp%d_crit_alarm",
|
"temp%d_label", "temp%d_crit_alarm",
|
||||||
"temp%d_max", "temp%d_input",
|
"temp%d_input", "temp%d_crit",
|
||||||
"temp%d_crit" };
|
"temp%d_max_alarm", "temp%d_max",
|
||||||
|
"temp%d_max_hyst" };
|
||||||
|
|
||||||
for (i = 0; i < MAX_ATTRS; i++) {
|
for (i = 0; i < tdata->attr_size; i++) {
|
||||||
snprintf(tdata->attr_name[i], CORETEMP_NAME_LENGTH, names[i],
|
snprintf(tdata->attr_name[i], CORETEMP_NAME_LENGTH, names[i],
|
||||||
attr_no);
|
attr_no);
|
||||||
sysfs_attr_init(&tdata->sd_attrs[i].dev_attr.attr);
|
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.name = tdata->attr_name[i];
|
||||||
tdata->sd_attrs[i].dev_attr.attr.mode = S_IRUGO;
|
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].dev_attr.show = rd_ptr[i];
|
||||||
tdata->sd_attrs[i].dev_attr.store = NULL;
|
|
||||||
tdata->sd_attrs[i].index = attr_no;
|
tdata->sd_attrs[i].index = attr_no;
|
||||||
err = device_create_file(dev, &tdata->sd_attrs[i].dev_attr);
|
err = device_create_file(dev, &tdata->sd_attrs[i].dev_attr);
|
||||||
if (err)
|
if (err)
|
||||||
@ -374,38 +480,6 @@ exit_free:
|
|||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void update_ttarget(__u8 cpu_model, struct temp_data *tdata,
|
|
||||||
struct device *dev)
|
|
||||||
{
|
|
||||||
int err;
|
|
||||||
u32 eax, edx;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Initialize ttarget value. Eventually this will be
|
|
||||||
* initialized with the value from MSR_IA32_THERM_INTERRUPT
|
|
||||||
* register. If IA32_TEMPERATURE_TARGET is supported, this
|
|
||||||
* value will be over written below.
|
|
||||||
* To Do: Patch to initialize ttarget from MSR_IA32_THERM_INTERRUPT
|
|
||||||
*/
|
|
||||||
tdata->ttarget = tdata->tjmax - 20000;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Read the still undocumented IA32_TEMPERATURE_TARGET. It exists
|
|
||||||
* on older CPUs but not in this register,
|
|
||||||
* Atoms don't have it either.
|
|
||||||
*/
|
|
||||||
if (cpu_model > 0xe && cpu_model != 0x1c) {
|
|
||||||
err = rdmsr_safe_on_cpu(tdata->cpu,
|
|
||||||
MSR_IA32_TEMPERATURE_TARGET, &eax, &edx);
|
|
||||||
if (err) {
|
|
||||||
dev_warn(dev,
|
|
||||||
"Unable to read IA32_TEMPERATURE_TARGET MSR\n");
|
|
||||||
} else {
|
|
||||||
tdata->ttarget = tdata->tjmax -
|
|
||||||
((eax >> 8) & 0xff) * 1000;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static int __devinit chk_ucode_version(struct platform_device *pdev)
|
static int __devinit chk_ucode_version(struct platform_device *pdev)
|
||||||
{
|
{
|
||||||
@ -464,9 +538,12 @@ static struct temp_data *init_temp_data(unsigned int cpu, int pkg_flag)
|
|||||||
|
|
||||||
tdata->status_reg = pkg_flag ? MSR_IA32_PACKAGE_THERM_STATUS :
|
tdata->status_reg = pkg_flag ? MSR_IA32_PACKAGE_THERM_STATUS :
|
||||||
MSR_IA32_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->is_pkg_data = pkg_flag;
|
||||||
tdata->cpu = cpu;
|
tdata->cpu = cpu;
|
||||||
tdata->cpu_core_id = TO_CORE_ID(cpu);
|
tdata->cpu_core_id = TO_CORE_ID(cpu);
|
||||||
|
tdata->attr_size = MAX_CORE_ATTRS;
|
||||||
mutex_init(&tdata->update_lock);
|
mutex_init(&tdata->update_lock);
|
||||||
return tdata;
|
return tdata;
|
||||||
}
|
}
|
||||||
@ -516,7 +593,17 @@ static int create_core_data(struct platform_data *pdata,
|
|||||||
else
|
else
|
||||||
tdata->tjmax = get_tjmax(c, cpu, &pdev->dev);
|
tdata->tjmax = get_tjmax(c, cpu, &pdev->dev);
|
||||||
|
|
||||||
update_ttarget(c->x86_model, tdata, &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
|
||||||
|
*/
|
||||||
|
err = rdmsr_safe_on_cpu(cpu, tdata->intrpt_reg, &eax, &edx);
|
||||||
|
if (!err) {
|
||||||
|
tdata->attr_size += MAX_THRESH_ATTRS;
|
||||||
|
tdata->ttarget = tdata->tjmax - ((eax >> 16) & 0x7f) * 1000;
|
||||||
|
}
|
||||||
|
|
||||||
pdata->core_data[attr_no] = tdata;
|
pdata->core_data[attr_no] = tdata;
|
||||||
|
|
||||||
/* Create sysfs interfaces */
|
/* Create sysfs interfaces */
|
||||||
@ -553,7 +640,7 @@ static void coretemp_remove_core(struct platform_data *pdata,
|
|||||||
struct temp_data *tdata = pdata->core_data[indx];
|
struct temp_data *tdata = pdata->core_data[indx];
|
||||||
|
|
||||||
/* Remove the sysfs attributes */
|
/* Remove the sysfs attributes */
|
||||||
for (i = 0; i < MAX_ATTRS; i++)
|
for (i = 0; i < tdata->attr_size; i++)
|
||||||
device_remove_file(dev, &tdata->sd_attrs[i].dev_attr);
|
device_remove_file(dev, &tdata->sd_attrs[i].dev_attr);
|
||||||
|
|
||||||
kfree(pdata->core_data[indx]);
|
kfree(pdata->core_data[indx]);
|
||||||
|
Loading…
Reference in New Issue
Block a user