Re: [PATCH 1/4] hwmon: (coretemp) Add support for thermal threshold attributes

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



> From: Srinivas Pandruvada [mailto:srinivas.pandruvada@xxxxxxxxxxxxxxx]
> From: Guenter Roeck <linux@xxxxxxxxxxxx>
> 
> 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: Srinivas Pandruvada <srinivas.pandruvada@xxxxxxxxxxxxxxx>
> ---
>  Documentation/hwmon/coretemp |   8 +++
>  drivers/hwmon/coretemp.c     | 161
> ++++++++++++++++++++++++++++++++++++++++---
>  2 files changed, 159 insertions(+), 10 deletions(-)
> 
> diff --git a/Documentation/hwmon/coretemp
> b/Documentation/hwmon/coretemp
> index fec5a9b..bb75856 100644
> --- a/Documentation/hwmon/coretemp
> +++ b/Documentation/hwmon/coretemp
> @@ -37,6 +37,14 @@ 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
> +	         - Reflects status of CPU thermal status register bit 6
> +		   (THERM_STATUS_THRESHOLD0).
> +tempX_threshold2 - Reflects value of CPU thermal threshold T1.
> +tempX_threshold2_triggered
> +	         - Reflects status of CPU thermal status register bit 8
> +		   (THERM_STATUS_THRESHOLD1).
>  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 3f1e297..5a8973d 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		32	/* 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)
> 
>  #define TO_PHYS_ID(cpu)		(cpu_data(cpu).phys_proc_id)
> @@ -88,6 +89,7 @@ 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;
> @@ -145,6 +147,110 @@ static ssize_t show_crit_alarm(struct device *dev,
>  	return sprintf(buf, "%d\n", (eax >> 5) & 1);
>  }
> 
> +static ssize_t show_tx_triggered(struct device *dev,
> +				 struct device_attribute *devattr, char *buf,
> +				 u32 mask)
> +{
> +	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 & mask));
> +}
> +
> +static ssize_t show_t0_triggered(struct device *dev,
> +				 struct device_attribute *devattr, char *buf)
> +{
> +	return show_tx_triggered(dev, devattr, buf,
> THERM_STATUS_THRESHOLD0);
> +}
> +
> +static ssize_t show_t1_triggered(struct device *dev,
> +				 struct device_attribute *devattr, char *buf)
> +{
> +	return show_tx_triggered(dev, devattr, buf,
> THERM_STATUS_THRESHOLD1);
> +}
> +
> +static ssize_t show_tx(struct device *dev,
> +		       struct device_attribute *devattr, char *buf,
> +		       u32 mask, int shift)
> +{
> +	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;
> +	int t;
> +
> +	rdmsr_on_cpu(tdata->cpu, tdata->intrpt_reg, &eax, &edx);
> +	t = tdata->tjmax - ((eax & mask) >> shift) * 1000;
> +	return sprintf(buf, "%d\n", t);
> +}
> +
> +static ssize_t store_tx(struct device *dev,
> +			struct device_attribute *devattr,
> +			const char *buf, size_t count,
> +			u32 mask, int shift)
> +{
> +	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 (kstrtoul(buf, 0, &val))
> +		return -EINVAL;
> +
> +	/*
> +	 * Thermal threshold mask 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 & ~mask) | (diff << shift);
> +	wrmsr_on_cpu(tdata->cpu, tdata->intrpt_reg, eax, edx);

Is it safe to set up intrpt_reg while interrupt is still enabled?
Should the interrupt be disabled before the reg is changed?

> +	mutex_unlock(&tdata->update_lock);
> +
> +	return count;
> +}
> +
> +static ssize_t show_t0(struct device *dev,
> +		       struct device_attribute *devattr, char *buf)
> +{
> +	return show_tx(dev, devattr, buf, THERM_MASK_THRESHOLD0,
> +		       THERM_SHIFT_THRESHOLD0);
> +}
> +
> +static ssize_t store_t0(struct device *dev,
> +			struct device_attribute *devattr,
> +			const char *buf, size_t count)
> +{
> +	return store_tx(dev, devattr, buf, count, THERM_MASK_THRESHOLD0,
> +			THERM_SHIFT_THRESHOLD0);
> +}
> +
> +static ssize_t show_t1(struct device *dev,
> +		       struct device_attribute *devattr, char *buf)
> +{
> +	return show_tx(dev, devattr, buf, THERM_MASK_THRESHOLD1,
> +		       THERM_SHIFT_THRESHOLD1);
> +}
> +
> +static ssize_t store_t1(struct device *dev,
> +			struct device_attribute *devattr,
> +			const char *buf, size_t count)
> +{
> +	return store_tx(dev, devattr, buf, count, THERM_MASK_THRESHOLD1,
> +			THERM_SHIFT_THRESHOLD1);
> +}
> +
>  static ssize_t show_tjmax(struct device *dev,
>  			struct device_attribute *devattr, char *buf)
>  {
> @@ -368,24 +474,40 @@ static int create_name_attr(struct platform_data
> *pdata,
>  }
> 
>  static int __cpuinit create_core_attrs(struct temp_data *tdata,
> -				       struct device *dev, int attr_no)
> +				       struct device *dev,
> +					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_t0, show_t0_triggered,
> +			show_t1, show_t1_triggered };
> +	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_t0, NULL, store_t1, NULL };
>  	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;
>  		err = device_create_file(dev, &tdata->sd_attrs[i].dev_attr);
> @@ -395,8 +517,11 @@ static int __cpuinit create_core_attrs(struct
> temp_data *tdata,
>  	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;
>  }
> 
> @@ -446,6 +571,9 @@ static struct temp_data __cpuinit
> *init_temp_data(unsigned int cpu,
> 
>  	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);
> @@ -462,6 +590,7 @@ static int __cpuinit 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:
> @@ -507,14 +636,23 @@ static int __cpuinit 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;
> 
> @@ -545,8 +683,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.11.7


_______________________________________________
lm-sensors mailing list
lm-sensors@xxxxxxxxxxxxxx
http://lists.lm-sensors.org/mailman/listinfo/lm-sensors




[Index of Archives]     [Linux Kernel]     [Linux Hardware Monitoring]     [Linux USB Devel]     [Linux Audio Users]     [Linux Kernel]     [Linux SCSI]     [Yosemite Backpacking]

  Powered by Linux