aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/thermal/int340x_thermal/processor_thermal_device.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/thermal/int340x_thermal/processor_thermal_device.c')
-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}