aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/thermal
diff options
context:
space:
mode:
authorSrinivas Pandruvada <srinivas.pandruvada@linux.intel.com>2014-04-07 16:57:15 -0400
committerZhang Rui <rui.zhang@intel.com>2014-05-15 04:37:24 -0400
commitbc40b5e320dfe4a691a6cf09ac5c8005d561eebd (patch)
treea7ae86e4860d22f1dc08c44ec5253817e792a294 /drivers/thermal
parentd6d211db37e75de2ddc3a4f979038c40df7cc79c (diff)
thermal: Intel SoC DTS thermal
In the Intel SoCs like Bay Trail, there are 2 additional digital temperature sensors(DTS), in addition to the standard DTSs in the core. Also they support 4 programmable thresholds, out of which two can be used by OSPM. These thresholds can be used by OSPM thermal control. Out of these two thresholds, one is used by driver and one user mode can change via thermal sysfs to get notifications on threshold violations. The driver defines one critical trip points, which is set to TJ MAX - offset. The offset can be changed via module parameter (default 5C). Also it uses one of the thresholds to get notification for this temperature violation. This is very important for orderly shutdown as the many of these devices don't have ACPI thermal zone, and expects that there is some other thermal control mechanism present in OSPM. When a Linux distro is used without additional specialized thermal control program, BIOS can do force shutdown when thermals are not under control. When temperature reaches critical, the Linux thermal core will initiate an orderly shutdown. 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/Kconfig12
-rw-r--r--drivers/thermal/Makefile1
-rw-r--r--drivers/thermal/intel_soc_dts_thermal.c479
3 files changed, 492 insertions, 0 deletions
diff --git a/drivers/thermal/Kconfig b/drivers/thermal/Kconfig
index 2d51912a6e40..74323d5e3425 100644
--- a/drivers/thermal/Kconfig
+++ b/drivers/thermal/Kconfig
@@ -222,6 +222,18 @@ config ACPI_INT3403_THERMAL
222 the Intel Thermal Daemon can use this information to allow the user 222 the Intel Thermal Daemon can use this information to allow the user
223 to select his laptop to run without turning on the fans. 223 to select his laptop to run without turning on the fans.
224 224
225config INTEL_SOC_DTS_THERMAL
226 tristate "Intel SoCs DTS thermal driver"
227 depends on X86 && IOSF_MBI
228 help
229 Enable this to register Intel SoCs (e.g. Bay Trail) platform digital
230 temperature sensor (DTS). These SoCs have two additional DTSs in
231 addition to DTSs on CPU cores. Each DTS will be registered as a
232 thermal zone. There are two trip points. One of the trip point can
233 be set by user mode programs to get notifications via Linux thermal
234 notification methods.The other trip is a critical trip point, which
235 was set by the driver based on the TJ MAX temperature.
236
225menu "Texas Instruments thermal drivers" 237menu "Texas Instruments thermal drivers"
226source "drivers/thermal/ti-soc-thermal/Kconfig" 238source "drivers/thermal/ti-soc-thermal/Kconfig"
227endmenu 239endmenu
diff --git a/drivers/thermal/Makefile b/drivers/thermal/Makefile
index 54e4ec9eb5df..de0636a57a64 100644
--- a/drivers/thermal/Makefile
+++ b/drivers/thermal/Makefile
@@ -29,5 +29,6 @@ obj-$(CONFIG_IMX_THERMAL) += imx_thermal.o
29obj-$(CONFIG_DB8500_CPUFREQ_COOLING) += db8500_cpufreq_cooling.o 29obj-$(CONFIG_DB8500_CPUFREQ_COOLING) += db8500_cpufreq_cooling.o
30obj-$(CONFIG_INTEL_POWERCLAMP) += intel_powerclamp.o 30obj-$(CONFIG_INTEL_POWERCLAMP) += intel_powerclamp.o
31obj-$(CONFIG_X86_PKG_TEMP_THERMAL) += x86_pkg_temp_thermal.o 31obj-$(CONFIG_X86_PKG_TEMP_THERMAL) += x86_pkg_temp_thermal.o
32obj-$(CONFIG_INTEL_SOC_DTS_THERMAL) += intel_soc_dts_thermal.o
32obj-$(CONFIG_TI_SOC_THERMAL) += ti-soc-thermal/ 33obj-$(CONFIG_TI_SOC_THERMAL) += ti-soc-thermal/
33obj-$(CONFIG_ACPI_INT3403_THERMAL) += int3403_thermal.o 34obj-$(CONFIG_ACPI_INT3403_THERMAL) += int3403_thermal.o
diff --git a/drivers/thermal/intel_soc_dts_thermal.c b/drivers/thermal/intel_soc_dts_thermal.c
new file mode 100644
index 000000000000..a6a0a18ec0aa
--- /dev/null
+++ b/drivers/thermal/intel_soc_dts_thermal.c
@@ -0,0 +1,479 @@
1/*
2 * intel_soc_dts_thermal.c
3 * Copyright (c) 2014, Intel Corporation.
4 *
5 * This program is free software; you can redistribute it and/or modify it
6 * under the terms and conditions of the GNU General Public License,
7 * version 2, as published by the Free Software Foundation.
8 *
9 * This program is distributed in the hope it will be useful, but WITHOUT
10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
12 * more details.
13 *
14 */
15
16#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
17
18#include <linux/module.h>
19#include <linux/slab.h>
20#include <linux/interrupt.h>
21#include <linux/thermal.h>
22#include <asm/cpu_device_id.h>
23#include <asm/iosf_mbi.h>
24
25#define SOC_DTS_OFFSET_ENABLE 0xB0
26#define SOC_DTS_OFFSET_TEMP 0xB1
27
28#define SOC_DTS_OFFSET_PTPS 0xB2
29#define SOC_DTS_OFFSET_PTTS 0xB3
30#define SOC_DTS_OFFSET_PTTSS 0xB4
31#define SOC_DTS_OFFSET_PTMC 0x80
32#define SOC_DTS_TE_AUX0 0xB5
33#define SOC_DTS_TE_AUX1 0xB6
34
35#define SOC_DTS_AUX0_ENABLE_BIT BIT(0)
36#define SOC_DTS_AUX1_ENABLE_BIT BIT(1)
37#define SOC_DTS_CPU_MODULE0_ENABLE_BIT BIT(16)
38#define SOC_DTS_CPU_MODULE1_ENABLE_BIT BIT(17)
39#define SOC_DTS_TE_SCI_ENABLE BIT(9)
40#define SOC_DTS_TE_SMI_ENABLE BIT(10)
41#define SOC_DTS_TE_MSI_ENABLE BIT(11)
42#define SOC_DTS_TE_APICA_ENABLE BIT(14)
43#define SOC_DTS_PTMC_APIC_DEASSERT_BIT BIT(4)
44
45/* DTS encoding for TJ MAX temperature */
46#define SOC_DTS_TJMAX_ENCODING 0x7F
47
48/* IRQ 86 is a fixed APIC interrupt for BYT DTS Aux threshold notifications */
49#define BYT_SOC_DTS_APIC_IRQ 86
50
51/* Only 2 out of 4 is allowed for OSPM */
52#define SOC_MAX_DTS_TRIPS 2
53
54/* Mask for two trips in status bits */
55#define SOC_DTS_TRIP_MASK 0x03
56
57/* DTS0 and DTS 1 */
58#define SOC_MAX_DTS_SENSORS 2
59
60#define CRITICAL_OFFSET_FROM_TJ_MAX 5000
61
62struct soc_sensor_entry {
63 int id;
64 u32 tj_max;
65 u32 temp_mask;
66 u32 temp_shift;
67 u32 store_status;
68 struct thermal_zone_device *tzone;
69};
70
71static struct soc_sensor_entry *soc_dts[SOC_MAX_DTS_SENSORS];
72
73static int crit_offset = CRITICAL_OFFSET_FROM_TJ_MAX;
74module_param(crit_offset, int, 0644);
75MODULE_PARM_DESC(crit_offset,
76 "Critical Temperature offset from tj max in millidegree Celsius.");
77
78static DEFINE_MUTEX(aux_update_mutex);
79static spinlock_t intr_notify_lock;
80static int soc_dts_thres_irq;
81
82static int get_tj_max(u32 *tj_max)
83{
84 u32 eax, edx;
85 u32 val;
86 int err;
87
88 err = rdmsr_safe(MSR_IA32_TEMPERATURE_TARGET, &eax, &edx);
89 if (err)
90 goto err_ret;
91 else {
92 val = (eax >> 16) & 0xff;
93 if (val)
94 *tj_max = val * 1000;
95 else {
96 err = -EINVAL;
97 goto err_ret;
98 }
99 }
100
101 return 0;
102err_ret:
103 *tj_max = 0;
104
105 return err;
106}
107
108static int sys_get_trip_temp(struct thermal_zone_device *tzd,
109 int trip, unsigned long *temp)
110{
111 int status;
112 u32 out;
113 struct soc_sensor_entry *aux_entry;
114
115 aux_entry = tzd->devdata;
116
117 if (!trip) {
118 /* Just return the critical temp */
119 *temp = aux_entry->tj_max - crit_offset;
120 return 0;
121 }
122
123 mutex_lock(&aux_update_mutex);
124 status = iosf_mbi_read(BT_MBI_UNIT_PMC, BT_MBI_BUNIT_READ,
125 SOC_DTS_OFFSET_PTPS, &out);
126 mutex_unlock(&aux_update_mutex);
127 if (status)
128 return status;
129
130 out = (out >> (trip * 8)) & SOC_DTS_TJMAX_ENCODING;
131
132 if (!out)
133 *temp = 0;
134 else
135 *temp = aux_entry->tj_max - out * 1000;
136
137 return 0;
138}
139
140static int update_trip_temp(struct soc_sensor_entry *aux_entry,
141 int thres_index, unsigned long temp)
142{
143 int status;
144 u32 temp_out;
145 u32 out;
146 u32 store_ptps;
147 u32 store_ptmc;
148 u32 store_te_out;
149 u32 te_out;
150
151 u32 int_enable_bit = SOC_DTS_TE_APICA_ENABLE |
152 SOC_DTS_TE_MSI_ENABLE;
153
154 temp_out = (aux_entry->tj_max - temp) / 1000;
155
156 status = iosf_mbi_read(BT_MBI_UNIT_PMC, BT_MBI_BUNIT_READ,
157 SOC_DTS_OFFSET_PTPS, &store_ptps);
158 if (status)
159 return status;
160
161 out = (store_ptps & ~(0xFF << (thres_index * 8)));
162 out |= (temp_out & 0xFF) << (thres_index * 8);
163 status = iosf_mbi_write(BT_MBI_UNIT_PMC, BT_MBI_BUNIT_WRITE,
164 SOC_DTS_OFFSET_PTPS, out);
165 if (status)
166 return status;
167 pr_debug("update_trip_temp PTPS = %x\n", out);
168 status = iosf_mbi_read(BT_MBI_UNIT_PMC, BT_MBI_BUNIT_READ,
169 SOC_DTS_OFFSET_PTMC, &out);
170 if (status)
171 goto err_restore_ptps;
172
173 store_ptmc = out;
174
175 status = iosf_mbi_read(BT_MBI_UNIT_PMC, BT_MBI_BUNIT_READ,
176 SOC_DTS_TE_AUX0 + thres_index,
177 &te_out);
178 if (status)
179 goto err_restore_ptmc;
180
181 store_te_out = te_out;
182
183 /* Enable for CPU module 0 and module 1 */
184 out |= (SOC_DTS_CPU_MODULE0_ENABLE_BIT |
185 SOC_DTS_CPU_MODULE1_ENABLE_BIT);
186 if (temp) {
187 if (thres_index)
188 out |= SOC_DTS_AUX1_ENABLE_BIT;
189 else
190 out |= SOC_DTS_AUX0_ENABLE_BIT;
191 te_out |= int_enable_bit;
192 } else {
193 if (thres_index)
194 out &= ~SOC_DTS_AUX1_ENABLE_BIT;
195 else
196 out &= ~SOC_DTS_AUX0_ENABLE_BIT;
197 te_out &= ~int_enable_bit;
198 }
199 status = iosf_mbi_write(BT_MBI_UNIT_PMC, BT_MBI_BUNIT_WRITE,
200 SOC_DTS_OFFSET_PTMC, out);
201 if (status)
202 goto err_restore_te_out;
203
204 status = iosf_mbi_write(BT_MBI_UNIT_PMC, BT_MBI_BUNIT_WRITE,
205 SOC_DTS_TE_AUX0 + thres_index,
206 te_out);
207 if (status)
208 goto err_restore_te_out;
209
210 return 0;
211
212err_restore_te_out:
213 iosf_mbi_write(BT_MBI_UNIT_PMC, BT_MBI_BUNIT_WRITE,
214 SOC_DTS_OFFSET_PTMC, store_te_out);
215err_restore_ptmc:
216 iosf_mbi_write(BT_MBI_UNIT_PMC, BT_MBI_BUNIT_WRITE,
217 SOC_DTS_OFFSET_PTMC, store_ptmc);
218err_restore_ptps:
219 iosf_mbi_write(BT_MBI_UNIT_PMC, BT_MBI_BUNIT_WRITE,
220 SOC_DTS_OFFSET_PTPS, store_ptps);
221 /* Nothing we can do if restore fails */
222
223 return status;
224}
225
226static int sys_set_trip_temp(struct thermal_zone_device *tzd, int trip,
227 unsigned long temp)
228{
229 struct soc_sensor_entry *aux_entry = tzd->devdata;
230 int status;
231
232 if (temp > (aux_entry->tj_max - crit_offset))
233 return -EINVAL;
234
235 mutex_lock(&aux_update_mutex);
236 status = update_trip_temp(tzd->devdata, trip, temp);
237 mutex_unlock(&aux_update_mutex);
238
239 return status;
240}
241
242static int sys_get_trip_type(struct thermal_zone_device *thermal,
243 int trip, enum thermal_trip_type *type)
244{
245 if (trip)
246 *type = THERMAL_TRIP_PASSIVE;
247 else
248 *type = THERMAL_TRIP_CRITICAL;
249
250 return 0;
251}
252
253static int sys_get_curr_temp(struct thermal_zone_device *tzd,
254 unsigned long *temp)
255{
256 int status;
257 u32 out;
258 struct soc_sensor_entry *aux_entry;
259
260 aux_entry = tzd->devdata;
261
262 status = iosf_mbi_read(BT_MBI_UNIT_PMC, BT_MBI_BUNIT_READ,
263 SOC_DTS_OFFSET_TEMP, &out);
264 if (status)
265 return status;
266
267 out = (out & aux_entry->temp_mask) >> aux_entry->temp_shift;
268 out -= SOC_DTS_TJMAX_ENCODING;
269 *temp = aux_entry->tj_max - out * 1000;
270
271 return 0;
272}
273
274static struct thermal_zone_device_ops tzone_ops = {
275 .get_temp = sys_get_curr_temp,
276 .get_trip_temp = sys_get_trip_temp,
277 .get_trip_type = sys_get_trip_type,
278 .set_trip_temp = sys_set_trip_temp,
279};
280
281static void free_soc_dts(struct soc_sensor_entry *aux_entry)
282{
283 if (aux_entry) {
284 iosf_mbi_write(BT_MBI_UNIT_PMC, BT_MBI_BUNIT_WRITE,
285 SOC_DTS_OFFSET_ENABLE, aux_entry->store_status);
286 thermal_zone_device_unregister(aux_entry->tzone);
287 kfree(aux_entry);
288 }
289}
290
291static int soc_dts_enable(int id)
292{
293 u32 out;
294 int ret;
295
296 ret = iosf_mbi_read(BT_MBI_UNIT_PMC, BT_MBI_BUNIT_READ,
297 SOC_DTS_OFFSET_ENABLE, &out);
298 if (ret)
299 return ret;
300
301 if (!(out & BIT(id))) {
302 out |= BIT(id);
303 ret = iosf_mbi_write(BT_MBI_UNIT_PMC, BT_MBI_BUNIT_WRITE,
304 SOC_DTS_OFFSET_ENABLE, out);
305 if (ret)
306 return ret;
307 }
308
309 return ret;
310}
311
312static struct soc_sensor_entry *alloc_soc_dts(int id, u32 tj_max)
313{
314 struct soc_sensor_entry *aux_entry;
315 char name[10];
316 int err;
317
318 aux_entry = kzalloc(sizeof(*aux_entry), GFP_KERNEL);
319 if (!aux_entry) {
320 err = -ENOMEM;
321 return ERR_PTR(-ENOMEM);
322 }
323
324 /* Store status to restor on exit */
325 err = iosf_mbi_read(BT_MBI_UNIT_PMC, BT_MBI_BUNIT_READ,
326 SOC_DTS_OFFSET_ENABLE,
327 &aux_entry->store_status);
328 if (err)
329 goto err_ret;
330
331 aux_entry->id = id;
332 aux_entry->tj_max = tj_max;
333 aux_entry->temp_mask = 0x00FF << (id * 8);
334 aux_entry->temp_shift = id * 8;
335 snprintf(name, sizeof(name), "soc_dts%d", id);
336 aux_entry->tzone = thermal_zone_device_register(name,
337 SOC_MAX_DTS_TRIPS,
338 0x02,
339 aux_entry, &tzone_ops, NULL, 0, 0);
340 if (IS_ERR(aux_entry->tzone)) {
341 err = PTR_ERR(aux_entry->tzone);
342 goto err_ret;
343 }
344
345 err = soc_dts_enable(id);
346 if (err)
347 goto err_aux_status;
348
349 return aux_entry;
350
351err_aux_status:
352 thermal_zone_device_unregister(aux_entry->tzone);
353err_ret:
354 kfree(aux_entry);
355 return ERR_PTR(err);
356}
357
358static void proc_thermal_interrupt(void)
359{
360 u32 sticky_out;
361 int status;
362 u32 ptmc_out;
363
364 /* Clear APIC interrupt */
365 status = iosf_mbi_read(BT_MBI_UNIT_PMC, BT_MBI_BUNIT_READ,
366 SOC_DTS_OFFSET_PTMC, &ptmc_out);
367
368 ptmc_out |= SOC_DTS_PTMC_APIC_DEASSERT_BIT;
369 status = iosf_mbi_write(BT_MBI_UNIT_PMC, BT_MBI_BUNIT_WRITE,
370 SOC_DTS_OFFSET_PTMC, ptmc_out);
371
372 /* Read status here */
373 status = iosf_mbi_read(BT_MBI_UNIT_PMC, BT_MBI_BUNIT_READ,
374 SOC_DTS_OFFSET_PTTSS, &sticky_out);
375 pr_debug("status %d PTTSS %x\n", status, sticky_out);
376 if (sticky_out & SOC_DTS_TRIP_MASK) {
377 int i;
378 /* reset sticky bit */
379 status = iosf_mbi_write(BT_MBI_UNIT_PMC, BT_MBI_BUNIT_WRITE,
380 SOC_DTS_OFFSET_PTTSS, sticky_out);
381 for (i = 0; i < SOC_MAX_DTS_SENSORS; ++i) {
382 pr_debug("TZD update for zone %d\n", i);
383 thermal_zone_device_update(soc_dts[i]->tzone);
384 }
385 }
386
387}
388
389static irqreturn_t soc_irq_thread_fn(int irq, void *dev_data)
390{
391 unsigned long flags;
392
393 spin_lock_irqsave(&intr_notify_lock, flags);
394 proc_thermal_interrupt();
395 spin_unlock_irqrestore(&intr_notify_lock, flags);
396 pr_debug("proc_thermal_interrupt\n");
397
398 return IRQ_HANDLED;
399}
400
401static const struct x86_cpu_id soc_thermal_ids[] = {
402 { X86_VENDOR_INTEL, X86_FAMILY_ANY, 0x37, 0, BYT_SOC_DTS_APIC_IRQ},
403 {}
404};
405MODULE_DEVICE_TABLE(x86cpu, soc_thermal_ids);
406
407static int __init intel_soc_thermal_init(void)
408{
409 u32 tj_max;
410 int err = 0;
411 int i;
412 const struct x86_cpu_id *match_cpu;
413
414 match_cpu = x86_match_cpu(soc_thermal_ids);
415 if (!match_cpu)
416 return -ENODEV;
417
418 if (get_tj_max(&tj_max))
419 return -EINVAL;
420
421 for (i = 0; i < SOC_MAX_DTS_SENSORS; ++i) {
422 soc_dts[i] = alloc_soc_dts(i, tj_max);
423 if (IS_ERR(soc_dts[i])) {
424 err = PTR_ERR(soc_dts[i]);
425 goto err_free;
426 }
427 }
428
429 spin_lock_init(&intr_notify_lock);
430
431 soc_dts_thres_irq = (int)match_cpu->driver_data;
432
433 err = request_threaded_irq(soc_dts_thres_irq, NULL,
434 soc_irq_thread_fn,
435 IRQF_TRIGGER_RISING | IRQF_ONESHOT,
436 "soc_dts", soc_dts);
437 if (err) {
438 pr_err("request_threaded_irq ret %d\n", err);
439 goto err_free;
440 }
441
442 for (i = 0; i < SOC_MAX_DTS_SENSORS; ++i) {
443 err = update_trip_temp(soc_dts[i], 0, tj_max - crit_offset);
444 if (err)
445 goto err_trip_temp;
446 }
447
448 return 0;
449
450err_trip_temp:
451 i = SOC_MAX_DTS_SENSORS;
452 free_irq(soc_dts_thres_irq, soc_dts);
453err_free:
454 while (--i >= 0)
455 free_soc_dts(soc_dts[i]);
456
457 return err;
458}
459
460static void __exit intel_soc_thermal_exit(void)
461{
462 int i;
463
464 for (i = 0; i < SOC_MAX_DTS_SENSORS; ++i)
465 update_trip_temp(soc_dts[i], 0, 0);
466
467 free_irq(soc_dts_thres_irq, soc_dts);
468
469 for (i = 0; i < SOC_MAX_DTS_SENSORS; ++i)
470 free_soc_dts(soc_dts[i]);
471
472}
473
474module_init(intel_soc_thermal_init)
475module_exit(intel_soc_thermal_exit)
476
477MODULE_DESCRIPTION("Intel SoC DTS Thermal Driver");
478MODULE_AUTHOR("Srinivas Pandruvada <srinivas.pandruvada@linux.intel.com>");
479MODULE_LICENSE("GPL v2");