aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/thermal
diff options
context:
space:
mode:
authorSrinivas Pandruvada <srinivas.pandruvada@linux.intel.com>2015-01-16 18:59:06 -0500
committerZhang Rui <rui.zhang@intel.com>2015-01-19 20:30:28 -0500
commit1c55be020806eeffbda6f1a981511e46427eb4ec (patch)
tree4801e9e53a17114ee8e39ce324bb07e548a31c3c /drivers/thermal
parent593df40475b719103e095aa1116653636662077e (diff)
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 @@
18#include <linux/pci.h> 18#include <linux/pci.h>
19#include <linux/platform_device.h> 19#include <linux/platform_device.h>
20#include <linux/acpi.h> 20#include <linux/acpi.h>
21#include <linux/thermal.h>
22#include "int340x_thermal_zone.h"
21 23
22/* Broadwell-U/HSB thermal reporting device */ 24/* Broadwell-U/HSB thermal reporting device */
23#define PCI_DEVICE_ID_PROC_BDW_THERMAL 0x1603 25#define PCI_DEVICE_ID_PROC_BDW_THERMAL 0x1603
@@ -39,6 +41,7 @@ struct proc_thermal_device {
39 struct device *dev; 41 struct device *dev;
40 struct acpi_device *adev; 42 struct acpi_device *adev;
41 struct power_config power_limits[2]; 43 struct power_config power_limits[2];
44 struct int34x_thermal_zone *int340x_zone;
42}; 45};
43 46
44enum proc_thermal_emum_mode_type { 47enum proc_thermal_emum_mode_type {
@@ -117,6 +120,72 @@ static struct attribute_group power_limit_attribute_group = {
117 .name = "power_limits" 120 .name = "power_limits"
118}; 121};
119 122
123static int stored_tjmax; /* since it is fixed, we can have local storage */
124
125static int get_tjmax(void)
126{
127 u32 eax, edx;
128 u32 val;
129 int err;
130
131 err = rdmsr_safe(MSR_IA32_TEMPERATURE_TARGET, &eax, &edx);
132 if (err)
133 return err;
134
135 val = (eax >> 16) & 0xff;
136 if (val)
137 return val;
138
139 return -EINVAL;
140}
141
142static int read_temp_msr(unsigned long *temp)
143{
144 int cpu;
145 u32 eax, edx;
146 int err;
147 unsigned long curr_temp_off = 0;
148
149 *temp = 0;
150
151 for_each_online_cpu(cpu) {
152 err = rdmsr_safe_on_cpu(cpu, MSR_IA32_THERM_STATUS, &eax,
153 &edx);
154 if (err)
155 goto err_ret;
156 else {
157 if (eax & 0x80000000) {
158 curr_temp_off = (eax >> 16) & 0x7f;
159 if (!*temp || curr_temp_off < *temp)
160 *temp = curr_temp_off;
161 } else {
162 err = -EINVAL;
163 goto err_ret;
164 }
165 }
166 }
167
168 return 0;
169err_ret:
170 return err;
171}
172
173static int proc_thermal_get_zone_temp(struct thermal_zone_device *zone,
174 unsigned long *temp)
175{
176 int ret;
177
178 ret = read_temp_msr(temp);
179 if (!ret)
180 *temp = (stored_tjmax - *temp) * 1000;
181
182 return ret;
183}
184
185static struct thermal_zone_device_ops proc_thermal_local_ops = {
186 .get_temp = proc_thermal_get_zone_temp,
187};
188
120static int proc_thermal_add(struct device *dev, 189static int proc_thermal_add(struct device *dev,
121 struct proc_thermal_device **priv) 190 struct proc_thermal_device **priv)
122{ 191{
@@ -126,6 +195,8 @@ static int proc_thermal_add(struct device *dev,
126 struct acpi_buffer buf = { ACPI_ALLOCATE_BUFFER, NULL }; 195 struct acpi_buffer buf = { ACPI_ALLOCATE_BUFFER, NULL };
127 union acpi_object *elements, *ppcc; 196 union acpi_object *elements, *ppcc;
128 union acpi_object *p; 197 union acpi_object *p;
198 unsigned long long tmp;
199 struct thermal_zone_device_ops *ops = NULL;
129 int i; 200 int i;
130 int ret; 201 int ret;
131 202
@@ -178,6 +249,24 @@ static int proc_thermal_add(struct device *dev,
178 249
179 ret = sysfs_create_group(&dev->kobj, 250 ret = sysfs_create_group(&dev->kobj,
180 &power_limit_attribute_group); 251 &power_limit_attribute_group);
252 if (ret)
253 goto free_buffer;
254
255 status = acpi_evaluate_integer(adev->handle, "_TMP", NULL, &tmp);
256 if (ACPI_FAILURE(status)) {
257 /* there is no _TMP method, add local method */
258 stored_tjmax = get_tjmax();
259 if (stored_tjmax > 0)
260 ops = &proc_thermal_local_ops;
261 }
262
263 proc_priv->int340x_zone = int340x_thermal_zone_add(adev, ops);
264 if (IS_ERR(proc_priv->int340x_zone)) {
265 sysfs_remove_group(&proc_priv->dev->kobj,
266 &power_limit_attribute_group);
267 ret = PTR_ERR(proc_priv->int340x_zone);
268 } else
269 ret = 0;
181 270
182free_buffer: 271free_buffer:
183 kfree(buf.pointer); 272 kfree(buf.pointer);
@@ -187,6 +276,7 @@ free_buffer:
187 276
188void proc_thermal_remove(struct proc_thermal_device *proc_priv) 277void proc_thermal_remove(struct proc_thermal_device *proc_priv)
189{ 278{
279 int340x_thermal_zone_remove(proc_priv->int340x_zone);
190 sysfs_remove_group(&proc_priv->dev->kobj, 280 sysfs_remove_group(&proc_priv->dev->kobj,
191 &power_limit_attribute_group); 281 &power_limit_attribute_group);
192} 282}