summaryrefslogtreecommitdiffstats
path: root/drivers/thermal
diff options
context:
space:
mode:
authorSrinivas Pandruvada <srinivas.pandruvada@linux.intel.com>2015-01-17 00:59:06 +0100
committerZhang Rui <rui.zhang@intel.com>2015-01-20 02:30:28 +0100
commit1c55be020806eeffbda6f1a981511e46427eb4ec (patch)
tree4801e9e53a17114ee8e39ce324bb07e548a31c3c /drivers/thermal
parentThermal/int340x/int3403: Use int340x thermal API (diff)
downloadlinux-1c55be020806eeffbda6f1a981511e46427eb4ec.tar.xz
linux-1c55be020806eeffbda6f1a981511e46427eb4ec.zip
Thermal/int340x/processor_thermal: Add thermal zone support
Added thermal zones for processor thermal using APIs provided by int340x thermal zone module. Like other INT340x devices, processor thermal device can also contain trip points and way to get temperature. On some platform there is no ACPI _TMP method, in those platform using IA64 architecture MSRs to get temperature. Signed-off-by: Srinivas Pandruvada <srinivas.pandruvada@linux.intel.com> Signed-off-by: Zhang Rui <rui.zhang@intel.com>
Diffstat (limited to 'drivers/thermal')
-rw-r--r--drivers/thermal/int340x_thermal/processor_thermal_device.c90
1 files changed, 90 insertions, 0 deletions
diff --git a/drivers/thermal/int340x_thermal/processor_thermal_device.c b/drivers/thermal/int340x_thermal/processor_thermal_device.c
index 0fe5dbbea968..7c3848da2df0 100644
--- a/drivers/thermal/int340x_thermal/processor_thermal_device.c
+++ b/drivers/thermal/int340x_thermal/processor_thermal_device.c
@@ -18,6 +18,8 @@
#include <linux/pci.h>
#include <linux/platform_device.h>
#include <linux/acpi.h>
+#include <linux/thermal.h>
+#include "int340x_thermal_zone.h"
/* Broadwell-U/HSB thermal reporting device */
#define PCI_DEVICE_ID_PROC_BDW_THERMAL 0x1603
@@ -39,6 +41,7 @@ struct proc_thermal_device {
struct device *dev;
struct acpi_device *adev;
struct power_config power_limits[2];
+ struct int34x_thermal_zone *int340x_zone;
};
enum proc_thermal_emum_mode_type {
@@ -117,6 +120,72 @@ static struct attribute_group power_limit_attribute_group = {
.name = "power_limits"
};
+static int stored_tjmax; /* since it is fixed, we can have local storage */
+
+static int get_tjmax(void)
+{
+ u32 eax, edx;
+ u32 val;
+ int err;
+
+ err = rdmsr_safe(MSR_IA32_TEMPERATURE_TARGET, &eax, &edx);
+ if (err)
+ return err;
+
+ val = (eax >> 16) & 0xff;
+ if (val)
+ return val;
+
+ return -EINVAL;
+}
+
+static int read_temp_msr(unsigned long *temp)
+{
+ int cpu;
+ u32 eax, edx;
+ int err;
+ unsigned long curr_temp_off = 0;
+
+ *temp = 0;
+
+ for_each_online_cpu(cpu) {
+ err = rdmsr_safe_on_cpu(cpu, MSR_IA32_THERM_STATUS, &eax,
+ &edx);
+ if (err)
+ goto err_ret;
+ else {
+ if (eax & 0x80000000) {
+ curr_temp_off = (eax >> 16) & 0x7f;
+ if (!*temp || curr_temp_off < *temp)
+ *temp = curr_temp_off;
+ } else {
+ err = -EINVAL;
+ goto err_ret;
+ }
+ }
+ }
+
+ return 0;
+err_ret:
+ return err;
+}
+
+static int proc_thermal_get_zone_temp(struct thermal_zone_device *zone,
+ unsigned long *temp)
+{
+ int ret;
+
+ ret = read_temp_msr(temp);
+ if (!ret)
+ *temp = (stored_tjmax - *temp) * 1000;
+
+ return ret;
+}
+
+static struct thermal_zone_device_ops proc_thermal_local_ops = {
+ .get_temp = proc_thermal_get_zone_temp,
+};
+
static int proc_thermal_add(struct device *dev,
struct proc_thermal_device **priv)
{
@@ -126,6 +195,8 @@ static int proc_thermal_add(struct device *dev,
struct acpi_buffer buf = { ACPI_ALLOCATE_BUFFER, NULL };
union acpi_object *elements, *ppcc;
union acpi_object *p;
+ unsigned long long tmp;
+ struct thermal_zone_device_ops *ops = NULL;
int i;
int ret;
@@ -178,6 +249,24 @@ static int proc_thermal_add(struct device *dev,
ret = sysfs_create_group(&dev->kobj,
&power_limit_attribute_group);
+ if (ret)
+ goto free_buffer;
+
+ status = acpi_evaluate_integer(adev->handle, "_TMP", NULL, &tmp);
+ if (ACPI_FAILURE(status)) {
+ /* there is no _TMP method, add local method */
+ stored_tjmax = get_tjmax();
+ if (stored_tjmax > 0)
+ ops = &proc_thermal_local_ops;
+ }
+
+ proc_priv->int340x_zone = int340x_thermal_zone_add(adev, ops);
+ if (IS_ERR(proc_priv->int340x_zone)) {
+ sysfs_remove_group(&proc_priv->dev->kobj,
+ &power_limit_attribute_group);
+ ret = PTR_ERR(proc_priv->int340x_zone);
+ } else
+ ret = 0;
free_buffer:
kfree(buf.pointer);
@@ -187,6 +276,7 @@ free_buffer:
void proc_thermal_remove(struct proc_thermal_device *proc_priv)
{
+ int340x_thermal_zone_remove(proc_priv->int340x_zone);
sysfs_remove_group(&proc_priv->dev->kobj,
&power_limit_attribute_group);
}