diff options
Diffstat (limited to 'drivers/thermal/int340x_thermal/processor_thermal_device.c')
-rw-r--r-- | drivers/thermal/int340x_thermal/processor_thermal_device.c | 90 |
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 | ||
44 | enum proc_thermal_emum_mode_type { | 47 | enum 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 | ||
123 | static int stored_tjmax; /* since it is fixed, we can have local storage */ | ||
124 | |||
125 | static 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 | |||
142 | static 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; | ||
169 | err_ret: | ||
170 | return err; | ||
171 | } | ||
172 | |||
173 | static 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 | |||
185 | static struct thermal_zone_device_ops proc_thermal_local_ops = { | ||
186 | .get_temp = proc_thermal_get_zone_temp, | ||
187 | }; | ||
188 | |||
120 | static int proc_thermal_add(struct device *dev, | 189 | static 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 | ||
182 | free_buffer: | 271 | free_buffer: |
183 | kfree(buf.pointer); | 272 | kfree(buf.pointer); |
@@ -187,6 +276,7 @@ free_buffer: | |||
187 | 276 | ||
188 | void proc_thermal_remove(struct proc_thermal_device *proc_priv) | 277 | void 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 | } |