aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/thermal/intel
diff options
context:
space:
mode:
authorZhang Rui <rui.zhang@intel.com>2018-12-07 03:49:50 -0500
committerZhang Rui <rui.zhang@intel.com>2018-12-07 03:49:50 -0500
commiteaaa598c0a285ef3b0efa54e711a385269fe50ce (patch)
tree6e33bb0f35d84eae9fc0cc8b88dae1570231eafe /drivers/thermal/intel
parent68000a0d983f539c95ebe5dccd4f29535c7ac0af (diff)
parent72e9baf997286610a2a3109e79fdb528590c5523 (diff)
Merge branches 'for-rc' and 'thermal-core' into next
Diffstat (limited to 'drivers/thermal/intel')
-rw-r--r--drivers/thermal/intel/Kconfig77
-rw-r--r--drivers/thermal/intel/Makefile12
-rw-r--r--drivers/thermal/intel/int340x_thermal/Kconfig42
-rw-r--r--drivers/thermal/intel/int340x_thermal/Makefile8
-rw-r--r--drivers/thermal/intel/int340x_thermal/acpi_thermal_rel.c394
-rw-r--r--drivers/thermal/intel/int340x_thermal/acpi_thermal_rel.h85
-rw-r--r--drivers/thermal/intel/int340x_thermal/int3400_thermal.c382
-rw-r--r--drivers/thermal/intel/int340x_thermal/int3402_thermal.c108
-rw-r--r--drivers/thermal/intel/int340x_thermal/int3403_thermal.c311
-rw-r--r--drivers/thermal/intel/int340x_thermal/int3406_thermal.c213
-rw-r--r--drivers/thermal/intel/int340x_thermal/int340x_thermal_zone.c295
-rw-r--r--drivers/thermal/intel/int340x_thermal/int340x_thermal_zone.h70
-rw-r--r--drivers/thermal/intel/int340x_thermal/processor_thermal_device.c525
-rw-r--r--drivers/thermal/intel/intel_bxt_pmic_thermal.c299
-rw-r--r--drivers/thermal/intel/intel_pch_thermal.c432
-rw-r--r--drivers/thermal/intel/intel_powerclamp.c815
-rw-r--r--drivers/thermal/intel/intel_quark_dts_thermal.c471
-rw-r--r--drivers/thermal/intel/intel_soc_dts_iosf.c478
-rw-r--r--drivers/thermal/intel/intel_soc_dts_iosf.h62
-rw-r--r--drivers/thermal/intel/intel_soc_dts_thermal.c132
-rw-r--r--drivers/thermal/intel/x86_pkg_temp_thermal.c558
21 files changed, 5769 insertions, 0 deletions
diff --git a/drivers/thermal/intel/Kconfig b/drivers/thermal/intel/Kconfig
new file mode 100644
index 000000000000..9c06d4ad7c97
--- /dev/null
+++ b/drivers/thermal/intel/Kconfig
@@ -0,0 +1,77 @@
1config INTEL_POWERCLAMP
2 tristate "Intel PowerClamp idle injection driver"
3 depends on THERMAL
4 depends on X86
5 depends on CPU_SUP_INTEL
6 help
7 Enable this to enable Intel PowerClamp idle injection driver. This
8 enforce idle time which results in more package C-state residency. The
9 user interface is exposed via generic thermal framework.
10
11config X86_PKG_TEMP_THERMAL
12 tristate "X86 package temperature thermal driver"
13 depends on X86_THERMAL_VECTOR
14 select THERMAL_GOV_USER_SPACE
15 select THERMAL_WRITABLE_TRIPS
16 default m
17 help
18 Enable this to register CPU digital sensor for package temperature as
19 thermal zone. Each package will have its own thermal zone. There are
20 two trip points which can be set by user to get notifications via thermal
21 notification methods.
22
23config INTEL_SOC_DTS_IOSF_CORE
24 tristate
25 depends on X86 && PCI
26 select IOSF_MBI
27 help
28 This is becoming a common feature for Intel SoCs to expose the additional
29 digital temperature sensors (DTSs) using side band interface (IOSF). This
30 implements the common set of helper functions to register, get temperature
31 and get/set thresholds on DTSs.
32
33config INTEL_SOC_DTS_THERMAL
34 tristate "Intel SoCs DTS thermal driver"
35 depends on X86 && PCI && ACPI
36 select INTEL_SOC_DTS_IOSF_CORE
37 select THERMAL_WRITABLE_TRIPS
38 help
39 Enable this to register Intel SoCs (e.g. Bay Trail) platform digital
40 temperature sensor (DTS). These SoCs have two additional DTSs in
41 addition to DTSs on CPU cores. Each DTS will be registered as a
42 thermal zone. There are two trip points. One of the trip point can
43 be set by user mode programs to get notifications via Linux thermal
44 notification methods.The other trip is a critical trip point, which
45 was set by the driver based on the TJ MAX temperature.
46
47config INTEL_QUARK_DTS_THERMAL
48 tristate "Intel Quark DTS thermal driver"
49 depends on X86_INTEL_QUARK
50 help
51 Enable this to register Intel Quark SoC (e.g. X1000) platform digital
52 temperature sensor (DTS). For X1000 SoC, it has one on-die DTS.
53 The DTS will be registered as a thermal zone. There are two trip points:
54 hot & critical. The critical trip point default value is set by
55 underlying BIOS/Firmware.
56
57menu "ACPI INT340X thermal drivers"
58source drivers/thermal/intel/int340x_thermal/Kconfig
59endmenu
60
61config INTEL_BXT_PMIC_THERMAL
62 tristate "Intel Broxton PMIC thermal driver"
63 depends on X86 && INTEL_SOC_PMIC_BXTWC && REGMAP
64 help
65 Select this driver for Intel Broxton PMIC with ADC channels monitoring
66 system temperature measurements and alerts.
67 This driver is used for monitoring the ADC channels of PMIC and handles
68 the alert trip point interrupts and notifies the thermal framework with
69 the trip point and temperature details of the zone.
70
71config INTEL_PCH_THERMAL
72 tristate "Intel PCH Thermal Reporting Driver"
73 depends on X86 && PCI
74 help
75 Enable this to support thermal reporting on certain intel PCHs.
76 Thermal reporting device will provide temperature reading,
77 programmable trip points and other information.
diff --git a/drivers/thermal/intel/Makefile b/drivers/thermal/intel/Makefile
new file mode 100644
index 000000000000..0d9736ced5d4
--- /dev/null
+++ b/drivers/thermal/intel/Makefile
@@ -0,0 +1,12 @@
1# SPDX-License-Identifier: GPL-2.0
2#
3# Makefile for various Intel thermal drivers.
4
5obj-$(CONFIG_INTEL_POWERCLAMP) += intel_powerclamp.o
6obj-$(CONFIG_X86_PKG_TEMP_THERMAL) += x86_pkg_temp_thermal.o
7obj-$(CONFIG_INTEL_SOC_DTS_IOSF_CORE) += intel_soc_dts_iosf.o
8obj-$(CONFIG_INTEL_SOC_DTS_THERMAL) += intel_soc_dts_thermal.o
9obj-$(CONFIG_INTEL_QUARK_DTS_THERMAL) += intel_quark_dts_thermal.o
10obj-$(CONFIG_INT340X_THERMAL) += int340x_thermal/
11obj-$(CONFIG_INTEL_BXT_PMIC_THERMAL) += intel_bxt_pmic_thermal.o
12obj-$(CONFIG_INTEL_PCH_THERMAL) += intel_pch_thermal.o
diff --git a/drivers/thermal/intel/int340x_thermal/Kconfig b/drivers/thermal/intel/int340x_thermal/Kconfig
new file mode 100644
index 000000000000..0582bd12a239
--- /dev/null
+++ b/drivers/thermal/intel/int340x_thermal/Kconfig
@@ -0,0 +1,42 @@
1#
2# ACPI INT340x thermal drivers configuration
3#
4
5config INT340X_THERMAL
6 tristate "ACPI INT340X thermal drivers"
7 depends on X86 && ACPI
8 select THERMAL_GOV_USER_SPACE
9 select ACPI_THERMAL_REL
10 select ACPI_FAN
11 select INTEL_SOC_DTS_IOSF_CORE
12 help
13 Newer laptops and tablets that use ACPI may have thermal sensors and
14 other devices with thermal control capabilities outside the core
15 CPU/SOC, for thermal safety reasons.
16 They are exposed for the OS to use via the INT3400 ACPI device object
17 as the master, and INT3401~INT340B ACPI device objects as the slaves.
18 Enable this to expose the temperature information and cooling ability
19 from these objects to userspace via the normal thermal framework.
20 This means that a wide range of applications and GUI widgets can show
21 the information to the user or use this information for making
22 decisions. For example, the Intel Thermal Daemon can use this
23 information to allow the user to select his laptop to run without
24 turning on the fans.
25
26config ACPI_THERMAL_REL
27 tristate
28 depends on ACPI
29
30if INT340X_THERMAL
31
32config INT3406_THERMAL
33 tristate "ACPI INT3406 display thermal driver"
34 depends on ACPI_VIDEO
35 help
36 The display thermal device represents the LED/LCD display panel
37 that may or may not include touch support. The main function of
38 the display thermal device is to allow control of the display
39 brightness in order to address a thermal condition or to reduce
40 power consumed by display device.
41
42endif
diff --git a/drivers/thermal/intel/int340x_thermal/Makefile b/drivers/thermal/intel/int340x_thermal/Makefile
new file mode 100644
index 000000000000..287eb0a1476d
--- /dev/null
+++ b/drivers/thermal/intel/int340x_thermal/Makefile
@@ -0,0 +1,8 @@
1# SPDX-License-Identifier: GPL-2.0
2obj-$(CONFIG_INT340X_THERMAL) += int3400_thermal.o
3obj-$(CONFIG_INT340X_THERMAL) += int340x_thermal_zone.o
4obj-$(CONFIG_INT340X_THERMAL) += int3402_thermal.o
5obj-$(CONFIG_INT340X_THERMAL) += int3403_thermal.o
6obj-$(CONFIG_INT340X_THERMAL) += processor_thermal_device.o
7obj-$(CONFIG_INT3406_THERMAL) += int3406_thermal.o
8obj-$(CONFIG_ACPI_THERMAL_REL) += acpi_thermal_rel.o
diff --git a/drivers/thermal/intel/int340x_thermal/acpi_thermal_rel.c b/drivers/thermal/intel/int340x_thermal/acpi_thermal_rel.c
new file mode 100644
index 000000000000..45e7e5cbdffb
--- /dev/null
+++ b/drivers/thermal/intel/int340x_thermal/acpi_thermal_rel.c
@@ -0,0 +1,394 @@
1/* acpi_thermal_rel.c driver for exporting ACPI thermal relationship
2 *
3 * Copyright (c) 2014 Intel Corp
4 *
5 * This program is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 as published by
7 * the Free Software Foundation.
8 *
9 */
10
11/*
12 * Two functionalities included:
13 * 1. Export _TRT, _ART, via misc device interface to the userspace.
14 * 2. Provide parsing result to kernel drivers
15 *
16 */
17#include <linux/init.h>
18#include <linux/export.h>
19#include <linux/module.h>
20#include <linux/device.h>
21#include <linux/platform_device.h>
22#include <linux/io.h>
23#include <linux/acpi.h>
24#include <linux/uaccess.h>
25#include <linux/miscdevice.h>
26#include "acpi_thermal_rel.h"
27
28static acpi_handle acpi_thermal_rel_handle;
29static DEFINE_SPINLOCK(acpi_thermal_rel_chrdev_lock);
30static int acpi_thermal_rel_chrdev_count; /* #times opened */
31static int acpi_thermal_rel_chrdev_exclu; /* already open exclusive? */
32
33static int acpi_thermal_rel_open(struct inode *inode, struct file *file)
34{
35 spin_lock(&acpi_thermal_rel_chrdev_lock);
36 if (acpi_thermal_rel_chrdev_exclu ||
37 (acpi_thermal_rel_chrdev_count && (file->f_flags & O_EXCL))) {
38 spin_unlock(&acpi_thermal_rel_chrdev_lock);
39 return -EBUSY;
40 }
41
42 if (file->f_flags & O_EXCL)
43 acpi_thermal_rel_chrdev_exclu = 1;
44 acpi_thermal_rel_chrdev_count++;
45
46 spin_unlock(&acpi_thermal_rel_chrdev_lock);
47
48 return nonseekable_open(inode, file);
49}
50
51static int acpi_thermal_rel_release(struct inode *inode, struct file *file)
52{
53 spin_lock(&acpi_thermal_rel_chrdev_lock);
54 acpi_thermal_rel_chrdev_count--;
55 acpi_thermal_rel_chrdev_exclu = 0;
56 spin_unlock(&acpi_thermal_rel_chrdev_lock);
57
58 return 0;
59}
60
61/**
62 * acpi_parse_trt - Thermal Relationship Table _TRT for passive cooling
63 *
64 * @handle: ACPI handle of the device contains _TRT
65 * @trt_count: the number of valid entries resulted from parsing _TRT
66 * @trtp: pointer to pointer of array of _TRT entries in parsing result
67 * @create_dev: whether to create platform devices for target and source
68 *
69 */
70int acpi_parse_trt(acpi_handle handle, int *trt_count, struct trt **trtp,
71 bool create_dev)
72{
73 acpi_status status;
74 int result = 0;
75 int i;
76 int nr_bad_entries = 0;
77 struct trt *trts;
78 struct acpi_device *adev;
79 union acpi_object *p;
80 struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL };
81 struct acpi_buffer element = { 0, NULL };
82 struct acpi_buffer trt_format = { sizeof("RRNNNNNN"), "RRNNNNNN" };
83
84 if (!acpi_has_method(handle, "_TRT"))
85 return -ENODEV;
86
87 status = acpi_evaluate_object(handle, "_TRT", NULL, &buffer);
88 if (ACPI_FAILURE(status))
89 return -ENODEV;
90
91 p = buffer.pointer;
92 if (!p || (p->type != ACPI_TYPE_PACKAGE)) {
93 pr_err("Invalid _TRT data\n");
94 result = -EFAULT;
95 goto end;
96 }
97
98 *trt_count = p->package.count;
99 trts = kcalloc(*trt_count, sizeof(struct trt), GFP_KERNEL);
100 if (!trts) {
101 result = -ENOMEM;
102 goto end;
103 }
104
105 for (i = 0; i < *trt_count; i++) {
106 struct trt *trt = &trts[i - nr_bad_entries];
107
108 element.length = sizeof(struct trt);
109 element.pointer = trt;
110
111 status = acpi_extract_package(&(p->package.elements[i]),
112 &trt_format, &element);
113 if (ACPI_FAILURE(status)) {
114 nr_bad_entries++;
115 pr_warn("_TRT package %d is invalid, ignored\n", i);
116 continue;
117 }
118 if (!create_dev)
119 continue;
120
121 result = acpi_bus_get_device(trt->source, &adev);
122 if (result)
123 pr_warn("Failed to get source ACPI device\n");
124
125 result = acpi_bus_get_device(trt->target, &adev);
126 if (result)
127 pr_warn("Failed to get target ACPI device\n");
128 }
129
130 result = 0;
131
132 *trtp = trts;
133 /* don't count bad entries */
134 *trt_count -= nr_bad_entries;
135end:
136 kfree(buffer.pointer);
137 return result;
138}
139EXPORT_SYMBOL(acpi_parse_trt);
140
141/**
142 * acpi_parse_art - Parse Active Relationship Table _ART
143 *
144 * @handle: ACPI handle of the device contains _ART
145 * @art_count: the number of valid entries resulted from parsing _ART
146 * @artp: pointer to pointer of array of art entries in parsing result
147 * @create_dev: whether to create platform devices for target and source
148 *
149 */
150int acpi_parse_art(acpi_handle handle, int *art_count, struct art **artp,
151 bool create_dev)
152{
153 acpi_status status;
154 int result = 0;
155 int i;
156 int nr_bad_entries = 0;
157 struct art *arts;
158 struct acpi_device *adev;
159 union acpi_object *p;
160 struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL };
161 struct acpi_buffer element = { 0, NULL };
162 struct acpi_buffer art_format = {
163 sizeof("RRNNNNNNNNNNN"), "RRNNNNNNNNNNN" };
164
165 if (!acpi_has_method(handle, "_ART"))
166 return -ENODEV;
167
168 status = acpi_evaluate_object(handle, "_ART", NULL, &buffer);
169 if (ACPI_FAILURE(status))
170 return -ENODEV;
171
172 p = buffer.pointer;
173 if (!p || (p->type != ACPI_TYPE_PACKAGE)) {
174 pr_err("Invalid _ART data\n");
175 result = -EFAULT;
176 goto end;
177 }
178
179 /* ignore p->package.elements[0], as this is _ART Revision field */
180 *art_count = p->package.count - 1;
181 arts = kcalloc(*art_count, sizeof(struct art), GFP_KERNEL);
182 if (!arts) {
183 result = -ENOMEM;
184 goto end;
185 }
186
187 for (i = 0; i < *art_count; i++) {
188 struct art *art = &arts[i - nr_bad_entries];
189
190 element.length = sizeof(struct art);
191 element.pointer = art;
192
193 status = acpi_extract_package(&(p->package.elements[i + 1]),
194 &art_format, &element);
195 if (ACPI_FAILURE(status)) {
196 pr_warn("_ART package %d is invalid, ignored", i);
197 nr_bad_entries++;
198 continue;
199 }
200 if (!create_dev)
201 continue;
202
203 if (art->source) {
204 result = acpi_bus_get_device(art->source, &adev);
205 if (result)
206 pr_warn("Failed to get source ACPI device\n");
207 }
208 if (art->target) {
209 result = acpi_bus_get_device(art->target, &adev);
210 if (result)
211 pr_warn("Failed to get target ACPI device\n");
212 }
213 }
214
215 *artp = arts;
216 /* don't count bad entries */
217 *art_count -= nr_bad_entries;
218end:
219 kfree(buffer.pointer);
220 return result;
221}
222EXPORT_SYMBOL(acpi_parse_art);
223
224
225/* get device name from acpi handle */
226static void get_single_name(acpi_handle handle, char *name)
227{
228 struct acpi_buffer buffer = {ACPI_ALLOCATE_BUFFER};
229
230 if (ACPI_FAILURE(acpi_get_name(handle, ACPI_SINGLE_NAME, &buffer)))
231 pr_warn("Failed to get device name from acpi handle\n");
232 else {
233 memcpy(name, buffer.pointer, ACPI_NAME_SIZE);
234 kfree(buffer.pointer);
235 }
236}
237
238static int fill_art(char __user *ubuf)
239{
240 int i;
241 int ret;
242 int count;
243 int art_len;
244 struct art *arts = NULL;
245 union art_object *art_user;
246
247 ret = acpi_parse_art(acpi_thermal_rel_handle, &count, &arts, false);
248 if (ret)
249 goto free_art;
250 art_len = count * sizeof(union art_object);
251 art_user = kzalloc(art_len, GFP_KERNEL);
252 if (!art_user) {
253 ret = -ENOMEM;
254 goto free_art;
255 }
256 /* now fill in user art data */
257 for (i = 0; i < count; i++) {
258 /* userspace art needs device name instead of acpi reference */
259 get_single_name(arts[i].source, art_user[i].source_device);
260 get_single_name(arts[i].target, art_user[i].target_device);
261 /* copy the rest int data in addition to source and target */
262 memcpy(&art_user[i].weight, &arts[i].weight,
263 sizeof(u64) * (ACPI_NR_ART_ELEMENTS - 2));
264 }
265
266 if (copy_to_user(ubuf, art_user, art_len))
267 ret = -EFAULT;
268 kfree(art_user);
269free_art:
270 kfree(arts);
271 return ret;
272}
273
274static int fill_trt(char __user *ubuf)
275{
276 int i;
277 int ret;
278 int count;
279 int trt_len;
280 struct trt *trts = NULL;
281 union trt_object *trt_user;
282
283 ret = acpi_parse_trt(acpi_thermal_rel_handle, &count, &trts, false);
284 if (ret)
285 goto free_trt;
286 trt_len = count * sizeof(union trt_object);
287 trt_user = kzalloc(trt_len, GFP_KERNEL);
288 if (!trt_user) {
289 ret = -ENOMEM;
290 goto free_trt;
291 }
292 /* now fill in user trt data */
293 for (i = 0; i < count; i++) {
294 /* userspace trt needs device name instead of acpi reference */
295 get_single_name(trts[i].source, trt_user[i].source_device);
296 get_single_name(trts[i].target, trt_user[i].target_device);
297 trt_user[i].sample_period = trts[i].sample_period;
298 trt_user[i].influence = trts[i].influence;
299 }
300
301 if (copy_to_user(ubuf, trt_user, trt_len))
302 ret = -EFAULT;
303 kfree(trt_user);
304free_trt:
305 kfree(trts);
306 return ret;
307}
308
309static long acpi_thermal_rel_ioctl(struct file *f, unsigned int cmd,
310 unsigned long __arg)
311{
312 int ret = 0;
313 unsigned long length = 0;
314 int count = 0;
315 char __user *arg = (void __user *)__arg;
316 struct trt *trts = NULL;
317 struct art *arts = NULL;
318
319 switch (cmd) {
320 case ACPI_THERMAL_GET_TRT_COUNT:
321 ret = acpi_parse_trt(acpi_thermal_rel_handle, &count,
322 &trts, false);
323 kfree(trts);
324 if (!ret)
325 return put_user(count, (unsigned long __user *)__arg);
326 return ret;
327 case ACPI_THERMAL_GET_TRT_LEN:
328 ret = acpi_parse_trt(acpi_thermal_rel_handle, &count,
329 &trts, false);
330 kfree(trts);
331 length = count * sizeof(union trt_object);
332 if (!ret)
333 return put_user(length, (unsigned long __user *)__arg);
334 return ret;
335 case ACPI_THERMAL_GET_TRT:
336 return fill_trt(arg);
337 case ACPI_THERMAL_GET_ART_COUNT:
338 ret = acpi_parse_art(acpi_thermal_rel_handle, &count,
339 &arts, false);
340 kfree(arts);
341 if (!ret)
342 return put_user(count, (unsigned long __user *)__arg);
343 return ret;
344 case ACPI_THERMAL_GET_ART_LEN:
345 ret = acpi_parse_art(acpi_thermal_rel_handle, &count,
346 &arts, false);
347 kfree(arts);
348 length = count * sizeof(union art_object);
349 if (!ret)
350 return put_user(length, (unsigned long __user *)__arg);
351 return ret;
352
353 case ACPI_THERMAL_GET_ART:
354 return fill_art(arg);
355
356 default:
357 return -ENOTTY;
358 }
359}
360
361static const struct file_operations acpi_thermal_rel_fops = {
362 .owner = THIS_MODULE,
363 .open = acpi_thermal_rel_open,
364 .release = acpi_thermal_rel_release,
365 .unlocked_ioctl = acpi_thermal_rel_ioctl,
366 .llseek = no_llseek,
367};
368
369static struct miscdevice acpi_thermal_rel_misc_device = {
370 .minor = MISC_DYNAMIC_MINOR,
371 "acpi_thermal_rel",
372 &acpi_thermal_rel_fops
373};
374
375int acpi_thermal_rel_misc_device_add(acpi_handle handle)
376{
377 acpi_thermal_rel_handle = handle;
378
379 return misc_register(&acpi_thermal_rel_misc_device);
380}
381EXPORT_SYMBOL(acpi_thermal_rel_misc_device_add);
382
383int acpi_thermal_rel_misc_device_remove(acpi_handle handle)
384{
385 misc_deregister(&acpi_thermal_rel_misc_device);
386
387 return 0;
388}
389EXPORT_SYMBOL(acpi_thermal_rel_misc_device_remove);
390
391MODULE_AUTHOR("Zhang Rui <rui.zhang@intel.com>");
392MODULE_AUTHOR("Jacob Pan <jacob.jun.pan@intel.com");
393MODULE_DESCRIPTION("Intel acpi thermal rel misc dev driver");
394MODULE_LICENSE("GPL v2");
diff --git a/drivers/thermal/intel/int340x_thermal/acpi_thermal_rel.h b/drivers/thermal/intel/int340x_thermal/acpi_thermal_rel.h
new file mode 100644
index 000000000000..58822575fd54
--- /dev/null
+++ b/drivers/thermal/intel/int340x_thermal/acpi_thermal_rel.h
@@ -0,0 +1,85 @@
1/* SPDX-License-Identifier: GPL-2.0 */
2#ifndef __ACPI_ACPI_THERMAL_H
3#define __ACPI_ACPI_THERMAL_H
4
5#include <asm/ioctl.h>
6
7#define ACPI_THERMAL_MAGIC 's'
8
9#define ACPI_THERMAL_GET_TRT_LEN _IOR(ACPI_THERMAL_MAGIC, 1, unsigned long)
10#define ACPI_THERMAL_GET_ART_LEN _IOR(ACPI_THERMAL_MAGIC, 2, unsigned long)
11#define ACPI_THERMAL_GET_TRT_COUNT _IOR(ACPI_THERMAL_MAGIC, 3, unsigned long)
12#define ACPI_THERMAL_GET_ART_COUNT _IOR(ACPI_THERMAL_MAGIC, 4, unsigned long)
13
14#define ACPI_THERMAL_GET_TRT _IOR(ACPI_THERMAL_MAGIC, 5, unsigned long)
15#define ACPI_THERMAL_GET_ART _IOR(ACPI_THERMAL_MAGIC, 6, unsigned long)
16
17struct art {
18 acpi_handle source;
19 acpi_handle target;
20 u64 weight;
21 u64 ac0_max;
22 u64 ac1_max;
23 u64 ac2_max;
24 u64 ac3_max;
25 u64 ac4_max;
26 u64 ac5_max;
27 u64 ac6_max;
28 u64 ac7_max;
29 u64 ac8_max;
30 u64 ac9_max;
31} __packed;
32
33struct trt {
34 acpi_handle source;
35 acpi_handle target;
36 u64 influence;
37 u64 sample_period;
38 u64 reserved1;
39 u64 reserved2;
40 u64 reserved3;
41 u64 reserved4;
42} __packed;
43
44#define ACPI_NR_ART_ELEMENTS 13
45/* for usrspace */
46union art_object {
47 struct {
48 char source_device[8]; /* ACPI single name */
49 char target_device[8]; /* ACPI single name */
50 u64 weight;
51 u64 ac0_max_level;
52 u64 ac1_max_level;
53 u64 ac2_max_level;
54 u64 ac3_max_level;
55 u64 ac4_max_level;
56 u64 ac5_max_level;
57 u64 ac6_max_level;
58 u64 ac7_max_level;
59 u64 ac8_max_level;
60 u64 ac9_max_level;
61 };
62 u64 __data[ACPI_NR_ART_ELEMENTS];
63};
64
65union trt_object {
66 struct {
67 char source_device[8]; /* ACPI single name */
68 char target_device[8]; /* ACPI single name */
69 u64 influence;
70 u64 sample_period;
71 u64 reserved[4];
72 };
73 u64 __data[8];
74};
75
76#ifdef __KERNEL__
77int acpi_thermal_rel_misc_device_add(acpi_handle handle);
78int acpi_thermal_rel_misc_device_remove(acpi_handle handle);
79int acpi_parse_art(acpi_handle handle, int *art_count, struct art **arts,
80 bool create_dev);
81int acpi_parse_trt(acpi_handle handle, int *trt_count, struct trt **trts,
82 bool create_dev);
83#endif
84
85#endif /* __ACPI_ACPI_THERMAL_H */
diff --git a/drivers/thermal/intel/int340x_thermal/int3400_thermal.c b/drivers/thermal/intel/int340x_thermal/int3400_thermal.c
new file mode 100644
index 000000000000..61ca7ce3624e
--- /dev/null
+++ b/drivers/thermal/intel/int340x_thermal/int3400_thermal.c
@@ -0,0 +1,382 @@
1/*
2 * INT3400 thermal driver
3 *
4 * Copyright (C) 2014, Intel Corporation
5 * Authors: Zhang Rui <rui.zhang@intel.com>
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License version 2 as
9 * published by the Free Software Foundation.
10 *
11 */
12
13#include <linux/module.h>
14#include <linux/platform_device.h>
15#include <linux/acpi.h>
16#include <linux/thermal.h>
17#include "acpi_thermal_rel.h"
18
19#define INT3400_THERMAL_TABLE_CHANGED 0x83
20
21enum int3400_thermal_uuid {
22 INT3400_THERMAL_PASSIVE_1,
23 INT3400_THERMAL_ACTIVE,
24 INT3400_THERMAL_CRITICAL,
25 INT3400_THERMAL_MAXIMUM_UUID,
26};
27
28static char *int3400_thermal_uuids[INT3400_THERMAL_MAXIMUM_UUID] = {
29 "42A441D6-AE6A-462b-A84B-4A8CE79027D3",
30 "3A95C389-E4B8-4629-A526-C52C88626BAE",
31 "97C68AE7-15FA-499c-B8C9-5DA81D606E0A",
32};
33
34struct int3400_thermal_priv {
35 struct acpi_device *adev;
36 struct thermal_zone_device *thermal;
37 int mode;
38 int art_count;
39 struct art *arts;
40 int trt_count;
41 struct trt *trts;
42 u8 uuid_bitmap;
43 int rel_misc_dev_res;
44 int current_uuid_index;
45};
46
47static ssize_t available_uuids_show(struct device *dev,
48 struct device_attribute *attr,
49 char *buf)
50{
51 struct int3400_thermal_priv *priv = dev_get_drvdata(dev);
52 int i;
53 int length = 0;
54
55 for (i = 0; i < INT3400_THERMAL_MAXIMUM_UUID; i++) {
56 if (priv->uuid_bitmap & (1 << i))
57 if (PAGE_SIZE - length > 0)
58 length += snprintf(&buf[length],
59 PAGE_SIZE - length,
60 "%s\n",
61 int3400_thermal_uuids[i]);
62 }
63
64 return length;
65}
66
67static ssize_t current_uuid_show(struct device *dev,
68 struct device_attribute *devattr, char *buf)
69{
70 struct int3400_thermal_priv *priv = dev_get_drvdata(dev);
71
72 if (priv->uuid_bitmap & (1 << priv->current_uuid_index))
73 return sprintf(buf, "%s\n",
74 int3400_thermal_uuids[priv->current_uuid_index]);
75 else
76 return sprintf(buf, "INVALID\n");
77}
78
79static ssize_t current_uuid_store(struct device *dev,
80 struct device_attribute *attr,
81 const char *buf, size_t count)
82{
83 struct int3400_thermal_priv *priv = dev_get_drvdata(dev);
84 int i;
85
86 for (i = 0; i < INT3400_THERMAL_MAXIMUM_UUID; ++i) {
87 if ((priv->uuid_bitmap & (1 << i)) &&
88 !(strncmp(buf, int3400_thermal_uuids[i],
89 sizeof(int3400_thermal_uuids[i]) - 1))) {
90 priv->current_uuid_index = i;
91 return count;
92 }
93 }
94
95 return -EINVAL;
96}
97
98static DEVICE_ATTR_RW(current_uuid);
99static DEVICE_ATTR_RO(available_uuids);
100static struct attribute *uuid_attrs[] = {
101 &dev_attr_available_uuids.attr,
102 &dev_attr_current_uuid.attr,
103 NULL
104};
105
106static const struct attribute_group uuid_attribute_group = {
107 .attrs = uuid_attrs,
108 .name = "uuids"
109};
110
111static int int3400_thermal_get_uuids(struct int3400_thermal_priv *priv)
112{
113 struct acpi_buffer buf = { ACPI_ALLOCATE_BUFFER, NULL};
114 union acpi_object *obja, *objb;
115 int i, j;
116 int result = 0;
117 acpi_status status;
118
119 status = acpi_evaluate_object(priv->adev->handle, "IDSP", NULL, &buf);
120 if (ACPI_FAILURE(status))
121 return -ENODEV;
122
123 obja = (union acpi_object *)buf.pointer;
124 if (obja->type != ACPI_TYPE_PACKAGE) {
125 result = -EINVAL;
126 goto end;
127 }
128
129 for (i = 0; i < obja->package.count; i++) {
130 objb = &obja->package.elements[i];
131 if (objb->type != ACPI_TYPE_BUFFER) {
132 result = -EINVAL;
133 goto end;
134 }
135
136 /* UUID must be 16 bytes */
137 if (objb->buffer.length != 16) {
138 result = -EINVAL;
139 goto end;
140 }
141
142 for (j = 0; j < INT3400_THERMAL_MAXIMUM_UUID; j++) {
143 guid_t guid;
144
145 guid_parse(int3400_thermal_uuids[j], &guid);
146 if (guid_equal((guid_t *)objb->buffer.pointer, &guid)) {
147 priv->uuid_bitmap |= (1 << j);
148 break;
149 }
150 }
151 }
152
153end:
154 kfree(buf.pointer);
155 return result;
156}
157
158static int int3400_thermal_run_osc(acpi_handle handle,
159 enum int3400_thermal_uuid uuid, bool enable)
160{
161 u32 ret, buf[2];
162 acpi_status status;
163 int result = 0;
164 struct acpi_osc_context context = {
165 .uuid_str = int3400_thermal_uuids[uuid],
166 .rev = 1,
167 .cap.length = 8,
168 };
169
170 buf[OSC_QUERY_DWORD] = 0;
171 buf[OSC_SUPPORT_DWORD] = enable;
172
173 context.cap.pointer = buf;
174
175 status = acpi_run_osc(handle, &context);
176 if (ACPI_SUCCESS(status)) {
177 ret = *((u32 *)(context.ret.pointer + 4));
178 if (ret != enable)
179 result = -EPERM;
180 } else
181 result = -EPERM;
182
183 kfree(context.ret.pointer);
184 return result;
185}
186
187static void int3400_notify(acpi_handle handle,
188 u32 event,
189 void *data)
190{
191 struct int3400_thermal_priv *priv = data;
192 char *thermal_prop[5];
193
194 if (!priv)
195 return;
196
197 switch (event) {
198 case INT3400_THERMAL_TABLE_CHANGED:
199 thermal_prop[0] = kasprintf(GFP_KERNEL, "NAME=%s",
200 priv->thermal->type);
201 thermal_prop[1] = kasprintf(GFP_KERNEL, "TEMP=%d",
202 priv->thermal->temperature);
203 thermal_prop[2] = kasprintf(GFP_KERNEL, "TRIP=");
204 thermal_prop[3] = kasprintf(GFP_KERNEL, "EVENT=%d",
205 THERMAL_TABLE_CHANGED);
206 thermal_prop[4] = NULL;
207 kobject_uevent_env(&priv->thermal->device.kobj, KOBJ_CHANGE,
208 thermal_prop);
209 break;
210 default:
211 /* Ignore unknown notification codes sent to INT3400 device */
212 break;
213 }
214}
215
216static int int3400_thermal_get_temp(struct thermal_zone_device *thermal,
217 int *temp)
218{
219 *temp = 20 * 1000; /* faked temp sensor with 20C */
220 return 0;
221}
222
223static int int3400_thermal_get_mode(struct thermal_zone_device *thermal,
224 enum thermal_device_mode *mode)
225{
226 struct int3400_thermal_priv *priv = thermal->devdata;
227
228 if (!priv)
229 return -EINVAL;
230
231 *mode = priv->mode;
232
233 return 0;
234}
235
236static int int3400_thermal_set_mode(struct thermal_zone_device *thermal,
237 enum thermal_device_mode mode)
238{
239 struct int3400_thermal_priv *priv = thermal->devdata;
240 bool enable;
241 int result = 0;
242
243 if (!priv)
244 return -EINVAL;
245
246 if (mode == THERMAL_DEVICE_ENABLED)
247 enable = true;
248 else if (mode == THERMAL_DEVICE_DISABLED)
249 enable = false;
250 else
251 return -EINVAL;
252
253 if (enable != priv->mode) {
254 priv->mode = enable;
255 result = int3400_thermal_run_osc(priv->adev->handle,
256 priv->current_uuid_index,
257 enable);
258 }
259 return result;
260}
261
262static struct thermal_zone_device_ops int3400_thermal_ops = {
263 .get_temp = int3400_thermal_get_temp,
264};
265
266static struct thermal_zone_params int3400_thermal_params = {
267 .governor_name = "user_space",
268 .no_hwmon = true,
269};
270
271static int int3400_thermal_probe(struct platform_device *pdev)
272{
273 struct acpi_device *adev = ACPI_COMPANION(&pdev->dev);
274 struct int3400_thermal_priv *priv;
275 int result;
276
277 if (!adev)
278 return -ENODEV;
279
280 priv = kzalloc(sizeof(struct int3400_thermal_priv), GFP_KERNEL);
281 if (!priv)
282 return -ENOMEM;
283
284 priv->adev = adev;
285
286 result = int3400_thermal_get_uuids(priv);
287 if (result)
288 goto free_priv;
289
290 result = acpi_parse_art(priv->adev->handle, &priv->art_count,
291 &priv->arts, true);
292 if (result)
293 dev_dbg(&pdev->dev, "_ART table parsing error\n");
294
295 result = acpi_parse_trt(priv->adev->handle, &priv->trt_count,
296 &priv->trts, true);
297 if (result)
298 dev_dbg(&pdev->dev, "_TRT table parsing error\n");
299
300 platform_set_drvdata(pdev, priv);
301
302 if (priv->uuid_bitmap & 1 << INT3400_THERMAL_PASSIVE_1) {
303 int3400_thermal_ops.get_mode = int3400_thermal_get_mode;
304 int3400_thermal_ops.set_mode = int3400_thermal_set_mode;
305 }
306 priv->thermal = thermal_zone_device_register("INT3400 Thermal", 0, 0,
307 priv, &int3400_thermal_ops,
308 &int3400_thermal_params, 0, 0);
309 if (IS_ERR(priv->thermal)) {
310 result = PTR_ERR(priv->thermal);
311 goto free_art_trt;
312 }
313
314 priv->rel_misc_dev_res = acpi_thermal_rel_misc_device_add(
315 priv->adev->handle);
316
317 result = sysfs_create_group(&pdev->dev.kobj, &uuid_attribute_group);
318 if (result)
319 goto free_rel_misc;
320
321 result = acpi_install_notify_handler(
322 priv->adev->handle, ACPI_DEVICE_NOTIFY, int3400_notify,
323 (void *)priv);
324 if (result)
325 goto free_sysfs;
326
327 return 0;
328
329free_sysfs:
330 sysfs_remove_group(&pdev->dev.kobj, &uuid_attribute_group);
331free_rel_misc:
332 if (!priv->rel_misc_dev_res)
333 acpi_thermal_rel_misc_device_remove(priv->adev->handle);
334 thermal_zone_device_unregister(priv->thermal);
335free_art_trt:
336 kfree(priv->trts);
337 kfree(priv->arts);
338free_priv:
339 kfree(priv);
340 return result;
341}
342
343static int int3400_thermal_remove(struct platform_device *pdev)
344{
345 struct int3400_thermal_priv *priv = platform_get_drvdata(pdev);
346
347 acpi_remove_notify_handler(
348 priv->adev->handle, ACPI_DEVICE_NOTIFY,
349 int3400_notify);
350
351 if (!priv->rel_misc_dev_res)
352 acpi_thermal_rel_misc_device_remove(priv->adev->handle);
353
354 sysfs_remove_group(&pdev->dev.kobj, &uuid_attribute_group);
355 thermal_zone_device_unregister(priv->thermal);
356 kfree(priv->trts);
357 kfree(priv->arts);
358 kfree(priv);
359 return 0;
360}
361
362static const struct acpi_device_id int3400_thermal_match[] = {
363 {"INT3400", 0},
364 {}
365};
366
367MODULE_DEVICE_TABLE(acpi, int3400_thermal_match);
368
369static struct platform_driver int3400_thermal_driver = {
370 .probe = int3400_thermal_probe,
371 .remove = int3400_thermal_remove,
372 .driver = {
373 .name = "int3400 thermal",
374 .acpi_match_table = ACPI_PTR(int3400_thermal_match),
375 },
376};
377
378module_platform_driver(int3400_thermal_driver);
379
380MODULE_DESCRIPTION("INT3400 Thermal driver");
381MODULE_AUTHOR("Zhang Rui <rui.zhang@intel.com>");
382MODULE_LICENSE("GPL");
diff --git a/drivers/thermal/intel/int340x_thermal/int3402_thermal.c b/drivers/thermal/intel/int340x_thermal/int3402_thermal.c
new file mode 100644
index 000000000000..8e90b3151a42
--- /dev/null
+++ b/drivers/thermal/intel/int340x_thermal/int3402_thermal.c
@@ -0,0 +1,108 @@
1/*
2 * INT3402 thermal driver for memory temperature reporting
3 *
4 * Copyright (C) 2014, Intel Corporation
5 * Authors: Aaron Lu <aaron.lu@intel.com>
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License version 2 as
9 * published by the Free Software Foundation.
10 *
11 */
12
13#include <linux/module.h>
14#include <linux/platform_device.h>
15#include <linux/acpi.h>
16#include <linux/thermal.h>
17#include "int340x_thermal_zone.h"
18
19#define INT3402_PERF_CHANGED_EVENT 0x80
20#define INT3402_THERMAL_EVENT 0x90
21
22struct int3402_thermal_data {
23 acpi_handle *handle;
24 struct int34x_thermal_zone *int340x_zone;
25};
26
27static void int3402_notify(acpi_handle handle, u32 event, void *data)
28{
29 struct int3402_thermal_data *priv = data;
30
31 if (!priv)
32 return;
33
34 switch (event) {
35 case INT3402_PERF_CHANGED_EVENT:
36 break;
37 case INT3402_THERMAL_EVENT:
38 int340x_thermal_zone_device_update(priv->int340x_zone,
39 THERMAL_TRIP_VIOLATED);
40 break;
41 default:
42 break;
43 }
44}
45
46static int int3402_thermal_probe(struct platform_device *pdev)
47{
48 struct acpi_device *adev = ACPI_COMPANION(&pdev->dev);
49 struct int3402_thermal_data *d;
50 int ret;
51
52 if (!acpi_has_method(adev->handle, "_TMP"))
53 return -ENODEV;
54
55 d = devm_kzalloc(&pdev->dev, sizeof(*d), GFP_KERNEL);
56 if (!d)
57 return -ENOMEM;
58
59 d->int340x_zone = int340x_thermal_zone_add(adev, NULL);
60 if (IS_ERR(d->int340x_zone))
61 return PTR_ERR(d->int340x_zone);
62
63 ret = acpi_install_notify_handler(adev->handle,
64 ACPI_DEVICE_NOTIFY,
65 int3402_notify,
66 d);
67 if (ret) {
68 int340x_thermal_zone_remove(d->int340x_zone);
69 return ret;
70 }
71
72 d->handle = adev->handle;
73 platform_set_drvdata(pdev, d);
74
75 return 0;
76}
77
78static int int3402_thermal_remove(struct platform_device *pdev)
79{
80 struct int3402_thermal_data *d = platform_get_drvdata(pdev);
81
82 acpi_remove_notify_handler(d->handle,
83 ACPI_DEVICE_NOTIFY, int3402_notify);
84 int340x_thermal_zone_remove(d->int340x_zone);
85
86 return 0;
87}
88
89static const struct acpi_device_id int3402_thermal_match[] = {
90 {"INT3402", 0},
91 {}
92};
93
94MODULE_DEVICE_TABLE(acpi, int3402_thermal_match);
95
96static struct platform_driver int3402_thermal_driver = {
97 .probe = int3402_thermal_probe,
98 .remove = int3402_thermal_remove,
99 .driver = {
100 .name = "int3402 thermal",
101 .acpi_match_table = int3402_thermal_match,
102 },
103};
104
105module_platform_driver(int3402_thermal_driver);
106
107MODULE_DESCRIPTION("INT3402 Thermal driver");
108MODULE_LICENSE("GPL");
diff --git a/drivers/thermal/intel/int340x_thermal/int3403_thermal.c b/drivers/thermal/intel/int340x_thermal/int3403_thermal.c
new file mode 100644
index 000000000000..0c19fcd56a0d
--- /dev/null
+++ b/drivers/thermal/intel/int340x_thermal/int3403_thermal.c
@@ -0,0 +1,311 @@
1/*
2 * ACPI INT3403 thermal driver
3 * Copyright (c) 2013, 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#include <linux/kernel.h>
16#include <linux/module.h>
17#include <linux/init.h>
18#include <linux/types.h>
19#include <linux/acpi.h>
20#include <linux/thermal.h>
21#include <linux/platform_device.h>
22#include "int340x_thermal_zone.h"
23
24#define INT3403_TYPE_SENSOR 0x03
25#define INT3403_TYPE_CHARGER 0x0B
26#define INT3403_TYPE_BATTERY 0x0C
27#define INT3403_PERF_CHANGED_EVENT 0x80
28#define INT3403_PERF_TRIP_POINT_CHANGED 0x81
29#define INT3403_THERMAL_EVENT 0x90
30
31/* Preserved structure for future expandbility */
32struct int3403_sensor {
33 struct int34x_thermal_zone *int340x_zone;
34};
35
36struct int3403_performance_state {
37 u64 performance;
38 u64 power;
39 u64 latency;
40 u64 linear;
41 u64 control;
42 u64 raw_performace;
43 char *raw_unit;
44 int reserved;
45};
46
47struct int3403_cdev {
48 struct thermal_cooling_device *cdev;
49 unsigned long max_state;
50};
51
52struct int3403_priv {
53 struct platform_device *pdev;
54 struct acpi_device *adev;
55 unsigned long long type;
56 void *priv;
57};
58
59static void int3403_notify(acpi_handle handle,
60 u32 event, void *data)
61{
62 struct int3403_priv *priv = data;
63 struct int3403_sensor *obj;
64
65 if (!priv)
66 return;
67
68 obj = priv->priv;
69 if (priv->type != INT3403_TYPE_SENSOR || !obj)
70 return;
71
72 switch (event) {
73 case INT3403_PERF_CHANGED_EVENT:
74 break;
75 case INT3403_THERMAL_EVENT:
76 int340x_thermal_zone_device_update(obj->int340x_zone,
77 THERMAL_TRIP_VIOLATED);
78 break;
79 case INT3403_PERF_TRIP_POINT_CHANGED:
80 int340x_thermal_read_trips(obj->int340x_zone);
81 int340x_thermal_zone_device_update(obj->int340x_zone,
82 THERMAL_TRIP_CHANGED);
83 break;
84 default:
85 dev_err(&priv->pdev->dev, "Unsupported event [0x%x]\n", event);
86 break;
87 }
88}
89
90static int int3403_sensor_add(struct int3403_priv *priv)
91{
92 int result = 0;
93 struct int3403_sensor *obj;
94
95 obj = devm_kzalloc(&priv->pdev->dev, sizeof(*obj), GFP_KERNEL);
96 if (!obj)
97 return -ENOMEM;
98
99 priv->priv = obj;
100
101 obj->int340x_zone = int340x_thermal_zone_add(priv->adev, NULL);
102 if (IS_ERR(obj->int340x_zone))
103 return PTR_ERR(obj->int340x_zone);
104
105 result = acpi_install_notify_handler(priv->adev->handle,
106 ACPI_DEVICE_NOTIFY, int3403_notify,
107 (void *)priv);
108 if (result)
109 goto err_free_obj;
110
111 return 0;
112
113 err_free_obj:
114 int340x_thermal_zone_remove(obj->int340x_zone);
115 return result;
116}
117
118static int int3403_sensor_remove(struct int3403_priv *priv)
119{
120 struct int3403_sensor *obj = priv->priv;
121
122 acpi_remove_notify_handler(priv->adev->handle,
123 ACPI_DEVICE_NOTIFY, int3403_notify);
124 int340x_thermal_zone_remove(obj->int340x_zone);
125
126 return 0;
127}
128
129/* INT3403 Cooling devices */
130static int int3403_get_max_state(struct thermal_cooling_device *cdev,
131 unsigned long *state)
132{
133 struct int3403_priv *priv = cdev->devdata;
134 struct int3403_cdev *obj = priv->priv;
135
136 *state = obj->max_state;
137 return 0;
138}
139
140static int int3403_get_cur_state(struct thermal_cooling_device *cdev,
141 unsigned long *state)
142{
143 struct int3403_priv *priv = cdev->devdata;
144 unsigned long long level;
145 acpi_status status;
146
147 status = acpi_evaluate_integer(priv->adev->handle, "PPPC", NULL, &level);
148 if (ACPI_SUCCESS(status)) {
149 *state = level;
150 return 0;
151 } else
152 return -EINVAL;
153}
154
155static int
156int3403_set_cur_state(struct thermal_cooling_device *cdev, unsigned long state)
157{
158 struct int3403_priv *priv = cdev->devdata;
159 acpi_status status;
160
161 status = acpi_execute_simple_method(priv->adev->handle, "SPPC", state);
162 if (ACPI_SUCCESS(status))
163 return 0;
164 else
165 return -EINVAL;
166}
167
168static const struct thermal_cooling_device_ops int3403_cooling_ops = {
169 .get_max_state = int3403_get_max_state,
170 .get_cur_state = int3403_get_cur_state,
171 .set_cur_state = int3403_set_cur_state,
172};
173
174static int int3403_cdev_add(struct int3403_priv *priv)
175{
176 int result = 0;
177 acpi_status status;
178 struct int3403_cdev *obj;
179 struct acpi_buffer buf = { ACPI_ALLOCATE_BUFFER, NULL };
180 union acpi_object *p;
181
182 obj = devm_kzalloc(&priv->pdev->dev, sizeof(*obj), GFP_KERNEL);
183 if (!obj)
184 return -ENOMEM;
185
186 status = acpi_evaluate_object(priv->adev->handle, "PPSS", NULL, &buf);
187 if (ACPI_FAILURE(status))
188 return -ENODEV;
189
190 p = buf.pointer;
191 if (!p || (p->type != ACPI_TYPE_PACKAGE)) {
192 printk(KERN_WARNING "Invalid PPSS data\n");
193 kfree(buf.pointer);
194 return -EFAULT;
195 }
196
197 priv->priv = obj;
198 obj->max_state = p->package.count - 1;
199 obj->cdev =
200 thermal_cooling_device_register(acpi_device_bid(priv->adev),
201 priv, &int3403_cooling_ops);
202 if (IS_ERR(obj->cdev))
203 result = PTR_ERR(obj->cdev);
204
205 kfree(buf.pointer);
206 /* TODO: add ACPI notification support */
207
208 return result;
209}
210
211static int int3403_cdev_remove(struct int3403_priv *priv)
212{
213 struct int3403_cdev *obj = priv->priv;
214
215 thermal_cooling_device_unregister(obj->cdev);
216 return 0;
217}
218
219static int int3403_add(struct platform_device *pdev)
220{
221 struct int3403_priv *priv;
222 int result = 0;
223 acpi_status status;
224
225 priv = devm_kzalloc(&pdev->dev, sizeof(struct int3403_priv),
226 GFP_KERNEL);
227 if (!priv)
228 return -ENOMEM;
229
230 priv->pdev = pdev;
231 priv->adev = ACPI_COMPANION(&(pdev->dev));
232 if (!priv->adev) {
233 result = -EINVAL;
234 goto err;
235 }
236
237 status = acpi_evaluate_integer(priv->adev->handle, "PTYP",
238 NULL, &priv->type);
239 if (ACPI_FAILURE(status)) {
240 unsigned long long tmp;
241
242 status = acpi_evaluate_integer(priv->adev->handle, "_TMP",
243 NULL, &tmp);
244 if (ACPI_FAILURE(status)) {
245 result = -EINVAL;
246 goto err;
247 } else {
248 priv->type = INT3403_TYPE_SENSOR;
249 }
250 }
251
252 platform_set_drvdata(pdev, priv);
253 switch (priv->type) {
254 case INT3403_TYPE_SENSOR:
255 result = int3403_sensor_add(priv);
256 break;
257 case INT3403_TYPE_CHARGER:
258 case INT3403_TYPE_BATTERY:
259 result = int3403_cdev_add(priv);
260 break;
261 default:
262 result = -EINVAL;
263 }
264
265 if (result)
266 goto err;
267 return result;
268
269err:
270 return result;
271}
272
273static int int3403_remove(struct platform_device *pdev)
274{
275 struct int3403_priv *priv = platform_get_drvdata(pdev);
276
277 switch (priv->type) {
278 case INT3403_TYPE_SENSOR:
279 int3403_sensor_remove(priv);
280 break;
281 case INT3403_TYPE_CHARGER:
282 case INT3403_TYPE_BATTERY:
283 int3403_cdev_remove(priv);
284 break;
285 default:
286 break;
287 }
288
289 return 0;
290}
291
292static const struct acpi_device_id int3403_device_ids[] = {
293 {"INT3403", 0},
294 {"", 0},
295};
296MODULE_DEVICE_TABLE(acpi, int3403_device_ids);
297
298static struct platform_driver int3403_driver = {
299 .probe = int3403_add,
300 .remove = int3403_remove,
301 .driver = {
302 .name = "int3403 thermal",
303 .acpi_match_table = int3403_device_ids,
304 },
305};
306
307module_platform_driver(int3403_driver);
308
309MODULE_AUTHOR("Srinivas Pandruvada <srinivas.pandruvada@linux.intel.com>");
310MODULE_LICENSE("GPL v2");
311MODULE_DESCRIPTION("ACPI INT3403 thermal driver");
diff --git a/drivers/thermal/intel/int340x_thermal/int3406_thermal.c b/drivers/thermal/intel/int340x_thermal/int3406_thermal.c
new file mode 100644
index 000000000000..f69ab026ba24
--- /dev/null
+++ b/drivers/thermal/intel/int340x_thermal/int3406_thermal.c
@@ -0,0 +1,213 @@
1/*
2 * INT3406 thermal driver for display participant device
3 *
4 * Copyright (C) 2016, Intel Corporation
5 * Authors: Aaron Lu <aaron.lu@intel.com>
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License version 2 as
9 * published by the Free Software Foundation.
10 *
11 */
12
13#include <linux/module.h>
14#include <linux/platform_device.h>
15#include <linux/acpi.h>
16#include <linux/backlight.h>
17#include <linux/thermal.h>
18#include <acpi/video.h>
19
20#define INT3406_BRIGHTNESS_LIMITS_CHANGED 0x80
21
22struct int3406_thermal_data {
23 int upper_limit;
24 int lower_limit;
25 acpi_handle handle;
26 struct acpi_video_device_brightness *br;
27 struct backlight_device *raw_bd;
28 struct thermal_cooling_device *cooling_dev;
29};
30
31/*
32 * According to the ACPI spec,
33 * "Each brightness level is represented by a number between 0 and 100,
34 * and can be thought of as a percentage. For example, 50 can be 50%
35 * power consumption or 50% brightness, as defined by the OEM."
36 *
37 * As int3406 device uses this value to communicate with the native
38 * graphics driver, we make the assumption that it represents
39 * the percentage of brightness only
40 */
41#define ACPI_TO_RAW(v, d) (d->raw_bd->props.max_brightness * v / 100)
42#define RAW_TO_ACPI(v, d) (v * 100 / d->raw_bd->props.max_brightness)
43
44static int
45int3406_thermal_get_max_state(struct thermal_cooling_device *cooling_dev,
46 unsigned long *state)
47{
48 struct int3406_thermal_data *d = cooling_dev->devdata;
49
50 *state = d->upper_limit - d->lower_limit;
51 return 0;
52}
53
54static int
55int3406_thermal_set_cur_state(struct thermal_cooling_device *cooling_dev,
56 unsigned long state)
57{
58 struct int3406_thermal_data *d = cooling_dev->devdata;
59 int acpi_level, raw_level;
60
61 if (state > d->upper_limit - d->lower_limit)
62 return -EINVAL;
63
64 acpi_level = d->br->levels[d->upper_limit - state];
65
66 raw_level = ACPI_TO_RAW(acpi_level, d);
67
68 return backlight_device_set_brightness(d->raw_bd, raw_level);
69}
70
71static int
72int3406_thermal_get_cur_state(struct thermal_cooling_device *cooling_dev,
73 unsigned long *state)
74{
75 struct int3406_thermal_data *d = cooling_dev->devdata;
76 int acpi_level;
77 int index;
78
79 acpi_level = RAW_TO_ACPI(d->raw_bd->props.brightness, d);
80
81 /*
82 * There is no 1:1 mapping between the firmware interface level
83 * with the raw interface level, we will have to find one that is
84 * right above it.
85 */
86 for (index = d->lower_limit; index < d->upper_limit; index++) {
87 if (acpi_level <= d->br->levels[index])
88 break;
89 }
90
91 *state = d->upper_limit - index;
92 return 0;
93}
94
95static const struct thermal_cooling_device_ops video_cooling_ops = {
96 .get_max_state = int3406_thermal_get_max_state,
97 .get_cur_state = int3406_thermal_get_cur_state,
98 .set_cur_state = int3406_thermal_set_cur_state,
99};
100
101static int int3406_thermal_get_index(int *array, int nr, int value)
102{
103 int i;
104
105 for (i = 2; i < nr; i++) {
106 if (array[i] == value)
107 break;
108 }
109 return i == nr ? -ENOENT : i;
110}
111
112static void int3406_thermal_get_limit(struct int3406_thermal_data *d)
113{
114 acpi_status status;
115 unsigned long long lower_limit, upper_limit;
116
117 status = acpi_evaluate_integer(d->handle, "DDDL", NULL, &lower_limit);
118 if (ACPI_SUCCESS(status))
119 d->lower_limit = int3406_thermal_get_index(d->br->levels,
120 d->br->count, lower_limit);
121
122 status = acpi_evaluate_integer(d->handle, "DDPC", NULL, &upper_limit);
123 if (ACPI_SUCCESS(status))
124 d->upper_limit = int3406_thermal_get_index(d->br->levels,
125 d->br->count, upper_limit);
126
127 /* lower_limit and upper_limit should be always set */
128 d->lower_limit = d->lower_limit > 0 ? d->lower_limit : 2;
129 d->upper_limit = d->upper_limit > 0 ? d->upper_limit : d->br->count - 1;
130}
131
132static void int3406_notify(acpi_handle handle, u32 event, void *data)
133{
134 if (event == INT3406_BRIGHTNESS_LIMITS_CHANGED)
135 int3406_thermal_get_limit(data);
136}
137
138static int int3406_thermal_probe(struct platform_device *pdev)
139{
140 struct acpi_device *adev = ACPI_COMPANION(&pdev->dev);
141 struct int3406_thermal_data *d;
142 struct backlight_device *bd;
143 int ret;
144
145 if (!ACPI_HANDLE(&pdev->dev))
146 return -ENODEV;
147
148 d = devm_kzalloc(&pdev->dev, sizeof(*d), GFP_KERNEL);
149 if (!d)
150 return -ENOMEM;
151 d->handle = ACPI_HANDLE(&pdev->dev);
152
153 bd = backlight_device_get_by_type(BACKLIGHT_RAW);
154 if (!bd)
155 return -ENODEV;
156 d->raw_bd = bd;
157
158 ret = acpi_video_get_levels(ACPI_COMPANION(&pdev->dev), &d->br, NULL);
159 if (ret)
160 return ret;
161
162 int3406_thermal_get_limit(d);
163
164 d->cooling_dev = thermal_cooling_device_register(acpi_device_bid(adev),
165 d, &video_cooling_ops);
166 if (IS_ERR(d->cooling_dev))
167 goto err;
168
169 ret = acpi_install_notify_handler(adev->handle, ACPI_DEVICE_NOTIFY,
170 int3406_notify, d);
171 if (ret)
172 goto err_cdev;
173
174 platform_set_drvdata(pdev, d);
175
176 return 0;
177
178err_cdev:
179 thermal_cooling_device_unregister(d->cooling_dev);
180err:
181 kfree(d->br);
182 return -ENODEV;
183}
184
185static int int3406_thermal_remove(struct platform_device *pdev)
186{
187 struct int3406_thermal_data *d = platform_get_drvdata(pdev);
188
189 thermal_cooling_device_unregister(d->cooling_dev);
190 kfree(d->br);
191 return 0;
192}
193
194static const struct acpi_device_id int3406_thermal_match[] = {
195 {"INT3406", 0},
196 {}
197};
198
199MODULE_DEVICE_TABLE(acpi, int3406_thermal_match);
200
201static struct platform_driver int3406_thermal_driver = {
202 .probe = int3406_thermal_probe,
203 .remove = int3406_thermal_remove,
204 .driver = {
205 .name = "int3406 thermal",
206 .acpi_match_table = int3406_thermal_match,
207 },
208};
209
210module_platform_driver(int3406_thermal_driver);
211
212MODULE_DESCRIPTION("INT3406 Thermal driver");
213MODULE_LICENSE("GPL v2");
diff --git a/drivers/thermal/intel/int340x_thermal/int340x_thermal_zone.c b/drivers/thermal/intel/int340x_thermal/int340x_thermal_zone.c
new file mode 100644
index 000000000000..9ec27ac1856b
--- /dev/null
+++ b/drivers/thermal/intel/int340x_thermal/int340x_thermal_zone.c
@@ -0,0 +1,295 @@
1/*
2 * int340x_thermal_zone.c
3 * Copyright (c) 2015, 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#include <linux/kernel.h>
16#include <linux/module.h>
17#include <linux/init.h>
18#include <linux/acpi.h>
19#include <linux/thermal.h>
20#include "int340x_thermal_zone.h"
21
22static int int340x_thermal_get_zone_temp(struct thermal_zone_device *zone,
23 int *temp)
24{
25 struct int34x_thermal_zone *d = zone->devdata;
26 unsigned long long tmp;
27 acpi_status status;
28
29 if (d->override_ops && d->override_ops->get_temp)
30 return d->override_ops->get_temp(zone, temp);
31
32 status = acpi_evaluate_integer(d->adev->handle, "_TMP", NULL, &tmp);
33 if (ACPI_FAILURE(status))
34 return -EIO;
35
36 if (d->lpat_table) {
37 int conv_temp;
38
39 conv_temp = acpi_lpat_raw_to_temp(d->lpat_table, (int)tmp);
40 if (conv_temp < 0)
41 return conv_temp;
42
43 *temp = (unsigned long)conv_temp * 10;
44 } else
45 /* _TMP returns the temperature in tenths of degrees Kelvin */
46 *temp = DECI_KELVIN_TO_MILLICELSIUS(tmp);
47
48 return 0;
49}
50
51static int int340x_thermal_get_trip_temp(struct thermal_zone_device *zone,
52 int trip, int *temp)
53{
54 struct int34x_thermal_zone *d = zone->devdata;
55 int i;
56
57 if (d->override_ops && d->override_ops->get_trip_temp)
58 return d->override_ops->get_trip_temp(zone, trip, temp);
59
60 if (trip < d->aux_trip_nr)
61 *temp = d->aux_trips[trip];
62 else if (trip == d->crt_trip_id)
63 *temp = d->crt_temp;
64 else if (trip == d->psv_trip_id)
65 *temp = d->psv_temp;
66 else if (trip == d->hot_trip_id)
67 *temp = d->hot_temp;
68 else {
69 for (i = 0; i < INT340X_THERMAL_MAX_ACT_TRIP_COUNT; i++) {
70 if (d->act_trips[i].valid &&
71 d->act_trips[i].id == trip) {
72 *temp = d->act_trips[i].temp;
73 break;
74 }
75 }
76 if (i == INT340X_THERMAL_MAX_ACT_TRIP_COUNT)
77 return -EINVAL;
78 }
79
80 return 0;
81}
82
83static int int340x_thermal_get_trip_type(struct thermal_zone_device *zone,
84 int trip,
85 enum thermal_trip_type *type)
86{
87 struct int34x_thermal_zone *d = zone->devdata;
88 int i;
89
90 if (d->override_ops && d->override_ops->get_trip_type)
91 return d->override_ops->get_trip_type(zone, trip, type);
92
93 if (trip < d->aux_trip_nr)
94 *type = THERMAL_TRIP_PASSIVE;
95 else if (trip == d->crt_trip_id)
96 *type = THERMAL_TRIP_CRITICAL;
97 else if (trip == d->hot_trip_id)
98 *type = THERMAL_TRIP_HOT;
99 else if (trip == d->psv_trip_id)
100 *type = THERMAL_TRIP_PASSIVE;
101 else {
102 for (i = 0; i < INT340X_THERMAL_MAX_ACT_TRIP_COUNT; i++) {
103 if (d->act_trips[i].valid &&
104 d->act_trips[i].id == trip) {
105 *type = THERMAL_TRIP_ACTIVE;
106 break;
107 }
108 }
109 if (i == INT340X_THERMAL_MAX_ACT_TRIP_COUNT)
110 return -EINVAL;
111 }
112
113 return 0;
114}
115
116static int int340x_thermal_set_trip_temp(struct thermal_zone_device *zone,
117 int trip, int temp)
118{
119 struct int34x_thermal_zone *d = zone->devdata;
120 acpi_status status;
121 char name[10];
122
123 if (d->override_ops && d->override_ops->set_trip_temp)
124 return d->override_ops->set_trip_temp(zone, trip, temp);
125
126 snprintf(name, sizeof(name), "PAT%d", trip);
127 status = acpi_execute_simple_method(d->adev->handle, name,
128 MILLICELSIUS_TO_DECI_KELVIN(temp));
129 if (ACPI_FAILURE(status))
130 return -EIO;
131
132 d->aux_trips[trip] = temp;
133
134 return 0;
135}
136
137
138static int int340x_thermal_get_trip_hyst(struct thermal_zone_device *zone,
139 int trip, int *temp)
140{
141 struct int34x_thermal_zone *d = zone->devdata;
142 acpi_status status;
143 unsigned long long hyst;
144
145 if (d->override_ops && d->override_ops->get_trip_hyst)
146 return d->override_ops->get_trip_hyst(zone, trip, temp);
147
148 status = acpi_evaluate_integer(d->adev->handle, "GTSH", NULL, &hyst);
149 if (ACPI_FAILURE(status))
150 *temp = 0;
151 else
152 *temp = hyst * 100;
153
154 return 0;
155}
156
157static struct thermal_zone_device_ops int340x_thermal_zone_ops = {
158 .get_temp = int340x_thermal_get_zone_temp,
159 .get_trip_temp = int340x_thermal_get_trip_temp,
160 .get_trip_type = int340x_thermal_get_trip_type,
161 .set_trip_temp = int340x_thermal_set_trip_temp,
162 .get_trip_hyst = int340x_thermal_get_trip_hyst,
163};
164
165static int int340x_thermal_get_trip_config(acpi_handle handle, char *name,
166 int *temp)
167{
168 unsigned long long r;
169 acpi_status status;
170
171 status = acpi_evaluate_integer(handle, name, NULL, &r);
172 if (ACPI_FAILURE(status))
173 return -EIO;
174
175 *temp = DECI_KELVIN_TO_MILLICELSIUS(r);
176
177 return 0;
178}
179
180int int340x_thermal_read_trips(struct int34x_thermal_zone *int34x_zone)
181{
182 int trip_cnt = int34x_zone->aux_trip_nr;
183 int i;
184
185 int34x_zone->crt_trip_id = -1;
186 if (!int340x_thermal_get_trip_config(int34x_zone->adev->handle, "_CRT",
187 &int34x_zone->crt_temp))
188 int34x_zone->crt_trip_id = trip_cnt++;
189
190 int34x_zone->hot_trip_id = -1;
191 if (!int340x_thermal_get_trip_config(int34x_zone->adev->handle, "_HOT",
192 &int34x_zone->hot_temp))
193 int34x_zone->hot_trip_id = trip_cnt++;
194
195 int34x_zone->psv_trip_id = -1;
196 if (!int340x_thermal_get_trip_config(int34x_zone->adev->handle, "_PSV",
197 &int34x_zone->psv_temp))
198 int34x_zone->psv_trip_id = trip_cnt++;
199
200 for (i = 0; i < INT340X_THERMAL_MAX_ACT_TRIP_COUNT; i++) {
201 char name[5] = { '_', 'A', 'C', '0' + i, '\0' };
202
203 if (int340x_thermal_get_trip_config(int34x_zone->adev->handle,
204 name,
205 &int34x_zone->act_trips[i].temp))
206 break;
207
208 int34x_zone->act_trips[i].id = trip_cnt++;
209 int34x_zone->act_trips[i].valid = true;
210 }
211
212 return trip_cnt;
213}
214EXPORT_SYMBOL_GPL(int340x_thermal_read_trips);
215
216static struct thermal_zone_params int340x_thermal_params = {
217 .governor_name = "user_space",
218 .no_hwmon = true,
219};
220
221struct int34x_thermal_zone *int340x_thermal_zone_add(struct acpi_device *adev,
222 struct thermal_zone_device_ops *override_ops)
223{
224 struct int34x_thermal_zone *int34x_thermal_zone;
225 acpi_status status;
226 unsigned long long trip_cnt;
227 int trip_mask = 0;
228 int ret;
229
230 int34x_thermal_zone = kzalloc(sizeof(*int34x_thermal_zone),
231 GFP_KERNEL);
232 if (!int34x_thermal_zone)
233 return ERR_PTR(-ENOMEM);
234
235 int34x_thermal_zone->adev = adev;
236 int34x_thermal_zone->override_ops = override_ops;
237
238 status = acpi_evaluate_integer(adev->handle, "PATC", NULL, &trip_cnt);
239 if (ACPI_FAILURE(status))
240 trip_cnt = 0;
241 else {
242 int34x_thermal_zone->aux_trips =
243 kcalloc(trip_cnt,
244 sizeof(*int34x_thermal_zone->aux_trips),
245 GFP_KERNEL);
246 if (!int34x_thermal_zone->aux_trips) {
247 ret = -ENOMEM;
248 goto err_trip_alloc;
249 }
250 trip_mask = BIT(trip_cnt) - 1;
251 int34x_thermal_zone->aux_trip_nr = trip_cnt;
252 }
253
254 trip_cnt = int340x_thermal_read_trips(int34x_thermal_zone);
255
256 int34x_thermal_zone->lpat_table = acpi_lpat_get_conversion_table(
257 adev->handle);
258
259 int34x_thermal_zone->zone = thermal_zone_device_register(
260 acpi_device_bid(adev),
261 trip_cnt,
262 trip_mask, int34x_thermal_zone,
263 &int340x_thermal_zone_ops,
264 &int340x_thermal_params,
265 0, 0);
266 if (IS_ERR(int34x_thermal_zone->zone)) {
267 ret = PTR_ERR(int34x_thermal_zone->zone);
268 goto err_thermal_zone;
269 }
270
271 return int34x_thermal_zone;
272
273err_thermal_zone:
274 acpi_lpat_free_conversion_table(int34x_thermal_zone->lpat_table);
275 kfree(int34x_thermal_zone->aux_trips);
276err_trip_alloc:
277 kfree(int34x_thermal_zone);
278 return ERR_PTR(ret);
279}
280EXPORT_SYMBOL_GPL(int340x_thermal_zone_add);
281
282void int340x_thermal_zone_remove(struct int34x_thermal_zone
283 *int34x_thermal_zone)
284{
285 thermal_zone_device_unregister(int34x_thermal_zone->zone);
286 acpi_lpat_free_conversion_table(int34x_thermal_zone->lpat_table);
287 kfree(int34x_thermal_zone->aux_trips);
288 kfree(int34x_thermal_zone);
289}
290EXPORT_SYMBOL_GPL(int340x_thermal_zone_remove);
291
292MODULE_AUTHOR("Aaron Lu <aaron.lu@intel.com>");
293MODULE_AUTHOR("Srinivas Pandruvada <srinivas.pandruvada@linux.intel.com>");
294MODULE_DESCRIPTION("Intel INT340x common thermal zone handler");
295MODULE_LICENSE("GPL v2");
diff --git a/drivers/thermal/intel/int340x_thermal/int340x_thermal_zone.h b/drivers/thermal/intel/int340x_thermal/int340x_thermal_zone.h
new file mode 100644
index 000000000000..5f3ba4775c5c
--- /dev/null
+++ b/drivers/thermal/intel/int340x_thermal/int340x_thermal_zone.h
@@ -0,0 +1,70 @@
1/*
2 * int340x_thermal_zone.h
3 * Copyright (c) 2015, 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#ifndef __INT340X_THERMAL_ZONE_H__
17#define __INT340X_THERMAL_ZONE_H__
18
19#include <acpi/acpi_lpat.h>
20
21#define INT340X_THERMAL_MAX_ACT_TRIP_COUNT 10
22
23struct active_trip {
24 int temp;
25 int id;
26 bool valid;
27};
28
29struct int34x_thermal_zone {
30 struct acpi_device *adev;
31 struct active_trip act_trips[INT340X_THERMAL_MAX_ACT_TRIP_COUNT];
32 unsigned long *aux_trips;
33 int aux_trip_nr;
34 int psv_temp;
35 int psv_trip_id;
36 int crt_temp;
37 int crt_trip_id;
38 int hot_temp;
39 int hot_trip_id;
40 struct thermal_zone_device *zone;
41 struct thermal_zone_device_ops *override_ops;
42 void *priv_data;
43 struct acpi_lpat_conversion_table *lpat_table;
44};
45
46struct int34x_thermal_zone *int340x_thermal_zone_add(struct acpi_device *,
47 struct thermal_zone_device_ops *override_ops);
48void int340x_thermal_zone_remove(struct int34x_thermal_zone *);
49int int340x_thermal_read_trips(struct int34x_thermal_zone *int34x_zone);
50
51static inline void int340x_thermal_zone_set_priv_data(
52 struct int34x_thermal_zone *tzone, void *priv_data)
53{
54 tzone->priv_data = priv_data;
55}
56
57static inline void *int340x_thermal_zone_get_priv_data(
58 struct int34x_thermal_zone *tzone)
59{
60 return tzone->priv_data;
61}
62
63static inline void int340x_thermal_zone_device_update(
64 struct int34x_thermal_zone *tzone,
65 enum thermal_notify_event event)
66{
67 thermal_zone_device_update(tzone->zone, event);
68}
69
70#endif
diff --git a/drivers/thermal/intel/int340x_thermal/processor_thermal_device.c b/drivers/thermal/intel/int340x_thermal/processor_thermal_device.c
new file mode 100644
index 000000000000..284cf2c5a8fd
--- /dev/null
+++ b/drivers/thermal/intel/int340x_thermal/processor_thermal_device.c
@@ -0,0 +1,525 @@
1/*
2 * processor_thermal_device.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#include <linux/kernel.h>
16#include <linux/module.h>
17#include <linux/init.h>
18#include <linux/pci.h>
19#include <linux/interrupt.h>
20#include <linux/platform_device.h>
21#include <linux/acpi.h>
22#include <linux/thermal.h>
23#include "int340x_thermal_zone.h"
24#include "../intel_soc_dts_iosf.h"
25
26/* Broadwell-U/HSB thermal reporting device */
27#define PCI_DEVICE_ID_PROC_BDW_THERMAL 0x1603
28#define PCI_DEVICE_ID_PROC_HSB_THERMAL 0x0A03
29
30/* Skylake thermal reporting device */
31#define PCI_DEVICE_ID_PROC_SKL_THERMAL 0x1903
32
33/* CannonLake thermal reporting device */
34#define PCI_DEVICE_ID_PROC_CNL_THERMAL 0x5a03
35#define PCI_DEVICE_ID_PROC_CFL_THERMAL 0x3E83
36
37/* Braswell thermal reporting device */
38#define PCI_DEVICE_ID_PROC_BSW_THERMAL 0x22DC
39
40/* Broxton thermal reporting device */
41#define PCI_DEVICE_ID_PROC_BXT0_THERMAL 0x0A8C
42#define PCI_DEVICE_ID_PROC_BXT1_THERMAL 0x1A8C
43#define PCI_DEVICE_ID_PROC_BXTX_THERMAL 0x4A8C
44#define PCI_DEVICE_ID_PROC_BXTP_THERMAL 0x5A8C
45
46/* GeminiLake thermal reporting device */
47#define PCI_DEVICE_ID_PROC_GLK_THERMAL 0x318C
48
49struct power_config {
50 u32 index;
51 u32 min_uw;
52 u32 max_uw;
53 u32 tmin_us;
54 u32 tmax_us;
55 u32 step_uw;
56};
57
58struct proc_thermal_device {
59 struct device *dev;
60 struct acpi_device *adev;
61 struct power_config power_limits[2];
62 struct int34x_thermal_zone *int340x_zone;
63 struct intel_soc_dts_sensors *soc_dts;
64};
65
66enum proc_thermal_emum_mode_type {
67 PROC_THERMAL_NONE,
68 PROC_THERMAL_PCI,
69 PROC_THERMAL_PLATFORM_DEV
70};
71
72/*
73 * We can have only one type of enumeration, PCI or Platform,
74 * not both. So we don't need instance specific data.
75 */
76static enum proc_thermal_emum_mode_type proc_thermal_emum_mode =
77 PROC_THERMAL_NONE;
78
79#define POWER_LIMIT_SHOW(index, suffix) \
80static ssize_t power_limit_##index##_##suffix##_show(struct device *dev, \
81 struct device_attribute *attr, \
82 char *buf) \
83{ \
84 struct pci_dev *pci_dev; \
85 struct platform_device *pdev; \
86 struct proc_thermal_device *proc_dev; \
87\
88 if (proc_thermal_emum_mode == PROC_THERMAL_PLATFORM_DEV) { \
89 pdev = to_platform_device(dev); \
90 proc_dev = platform_get_drvdata(pdev); \
91 } else { \
92 pci_dev = to_pci_dev(dev); \
93 proc_dev = pci_get_drvdata(pci_dev); \
94 } \
95 return sprintf(buf, "%lu\n",\
96 (unsigned long)proc_dev->power_limits[index].suffix * 1000); \
97}
98
99POWER_LIMIT_SHOW(0, min_uw)
100POWER_LIMIT_SHOW(0, max_uw)
101POWER_LIMIT_SHOW(0, step_uw)
102POWER_LIMIT_SHOW(0, tmin_us)
103POWER_LIMIT_SHOW(0, tmax_us)
104
105POWER_LIMIT_SHOW(1, min_uw)
106POWER_LIMIT_SHOW(1, max_uw)
107POWER_LIMIT_SHOW(1, step_uw)
108POWER_LIMIT_SHOW(1, tmin_us)
109POWER_LIMIT_SHOW(1, tmax_us)
110
111static DEVICE_ATTR_RO(power_limit_0_min_uw);
112static DEVICE_ATTR_RO(power_limit_0_max_uw);
113static DEVICE_ATTR_RO(power_limit_0_step_uw);
114static DEVICE_ATTR_RO(power_limit_0_tmin_us);
115static DEVICE_ATTR_RO(power_limit_0_tmax_us);
116
117static DEVICE_ATTR_RO(power_limit_1_min_uw);
118static DEVICE_ATTR_RO(power_limit_1_max_uw);
119static DEVICE_ATTR_RO(power_limit_1_step_uw);
120static DEVICE_ATTR_RO(power_limit_1_tmin_us);
121static DEVICE_ATTR_RO(power_limit_1_tmax_us);
122
123static struct attribute *power_limit_attrs[] = {
124 &dev_attr_power_limit_0_min_uw.attr,
125 &dev_attr_power_limit_1_min_uw.attr,
126 &dev_attr_power_limit_0_max_uw.attr,
127 &dev_attr_power_limit_1_max_uw.attr,
128 &dev_attr_power_limit_0_step_uw.attr,
129 &dev_attr_power_limit_1_step_uw.attr,
130 &dev_attr_power_limit_0_tmin_us.attr,
131 &dev_attr_power_limit_1_tmin_us.attr,
132 &dev_attr_power_limit_0_tmax_us.attr,
133 &dev_attr_power_limit_1_tmax_us.attr,
134 NULL
135};
136
137static const struct attribute_group power_limit_attribute_group = {
138 .attrs = power_limit_attrs,
139 .name = "power_limits"
140};
141
142static int stored_tjmax; /* since it is fixed, we can have local storage */
143
144static int get_tjmax(void)
145{
146 u32 eax, edx;
147 u32 val;
148 int err;
149
150 err = rdmsr_safe(MSR_IA32_TEMPERATURE_TARGET, &eax, &edx);
151 if (err)
152 return err;
153
154 val = (eax >> 16) & 0xff;
155 if (val)
156 return val;
157
158 return -EINVAL;
159}
160
161static int read_temp_msr(int *temp)
162{
163 int cpu;
164 u32 eax, edx;
165 int err;
166 unsigned long curr_temp_off = 0;
167
168 *temp = 0;
169
170 for_each_online_cpu(cpu) {
171 err = rdmsr_safe_on_cpu(cpu, MSR_IA32_THERM_STATUS, &eax,
172 &edx);
173 if (err)
174 goto err_ret;
175 else {
176 if (eax & 0x80000000) {
177 curr_temp_off = (eax >> 16) & 0x7f;
178 if (!*temp || curr_temp_off < *temp)
179 *temp = curr_temp_off;
180 } else {
181 err = -EINVAL;
182 goto err_ret;
183 }
184 }
185 }
186
187 return 0;
188err_ret:
189 return err;
190}
191
192static int proc_thermal_get_zone_temp(struct thermal_zone_device *zone,
193 int *temp)
194{
195 int ret;
196
197 ret = read_temp_msr(temp);
198 if (!ret)
199 *temp = (stored_tjmax - *temp) * 1000;
200
201 return ret;
202}
203
204static struct thermal_zone_device_ops proc_thermal_local_ops = {
205 .get_temp = proc_thermal_get_zone_temp,
206};
207
208static int proc_thermal_read_ppcc(struct proc_thermal_device *proc_priv)
209{
210 int i;
211 acpi_status status;
212 struct acpi_buffer buf = { ACPI_ALLOCATE_BUFFER, NULL };
213 union acpi_object *elements, *ppcc;
214 union acpi_object *p;
215 int ret = 0;
216
217 status = acpi_evaluate_object(proc_priv->adev->handle, "PPCC",
218 NULL, &buf);
219 if (ACPI_FAILURE(status))
220 return -ENODEV;
221
222 p = buf.pointer;
223 if (!p || (p->type != ACPI_TYPE_PACKAGE)) {
224 dev_err(proc_priv->dev, "Invalid PPCC data\n");
225 ret = -EFAULT;
226 goto free_buffer;
227 }
228
229 if (!p->package.count) {
230 dev_err(proc_priv->dev, "Invalid PPCC package size\n");
231 ret = -EFAULT;
232 goto free_buffer;
233 }
234
235 for (i = 0; i < min((int)p->package.count - 1, 2); ++i) {
236 elements = &(p->package.elements[i+1]);
237 if (elements->type != ACPI_TYPE_PACKAGE ||
238 elements->package.count != 6) {
239 ret = -EFAULT;
240 goto free_buffer;
241 }
242 ppcc = elements->package.elements;
243 proc_priv->power_limits[i].index = ppcc[0].integer.value;
244 proc_priv->power_limits[i].min_uw = ppcc[1].integer.value;
245 proc_priv->power_limits[i].max_uw = ppcc[2].integer.value;
246 proc_priv->power_limits[i].tmin_us = ppcc[3].integer.value;
247 proc_priv->power_limits[i].tmax_us = ppcc[4].integer.value;
248 proc_priv->power_limits[i].step_uw = ppcc[5].integer.value;
249 }
250
251free_buffer:
252 kfree(buf.pointer);
253
254 return ret;
255}
256
257#define PROC_POWER_CAPABILITY_CHANGED 0x83
258static void proc_thermal_notify(acpi_handle handle, u32 event, void *data)
259{
260 struct proc_thermal_device *proc_priv = data;
261
262 if (!proc_priv)
263 return;
264
265 switch (event) {
266 case PROC_POWER_CAPABILITY_CHANGED:
267 proc_thermal_read_ppcc(proc_priv);
268 int340x_thermal_zone_device_update(proc_priv->int340x_zone,
269 THERMAL_DEVICE_POWER_CAPABILITY_CHANGED);
270 break;
271 default:
272 dev_err(proc_priv->dev, "Unsupported event [0x%x]\n", event);
273 break;
274 }
275}
276
277
278static int proc_thermal_add(struct device *dev,
279 struct proc_thermal_device **priv)
280{
281 struct proc_thermal_device *proc_priv;
282 struct acpi_device *adev;
283 acpi_status status;
284 unsigned long long tmp;
285 struct thermal_zone_device_ops *ops = NULL;
286 int ret;
287
288 adev = ACPI_COMPANION(dev);
289 if (!adev)
290 return -ENODEV;
291
292 proc_priv = devm_kzalloc(dev, sizeof(*proc_priv), GFP_KERNEL);
293 if (!proc_priv)
294 return -ENOMEM;
295
296 proc_priv->dev = dev;
297 proc_priv->adev = adev;
298 *priv = proc_priv;
299
300 ret = proc_thermal_read_ppcc(proc_priv);
301 if (!ret) {
302 ret = sysfs_create_group(&dev->kobj,
303 &power_limit_attribute_group);
304
305 }
306 if (ret)
307 return ret;
308
309 status = acpi_evaluate_integer(adev->handle, "_TMP", NULL, &tmp);
310 if (ACPI_FAILURE(status)) {
311 /* there is no _TMP method, add local method */
312 stored_tjmax = get_tjmax();
313 if (stored_tjmax > 0)
314 ops = &proc_thermal_local_ops;
315 }
316
317 proc_priv->int340x_zone = int340x_thermal_zone_add(adev, ops);
318 if (IS_ERR(proc_priv->int340x_zone)) {
319 ret = PTR_ERR(proc_priv->int340x_zone);
320 goto remove_group;
321 } else
322 ret = 0;
323
324 ret = acpi_install_notify_handler(adev->handle, ACPI_DEVICE_NOTIFY,
325 proc_thermal_notify,
326 (void *)proc_priv);
327 if (ret)
328 goto remove_zone;
329
330 return 0;
331
332remove_zone:
333 int340x_thermal_zone_remove(proc_priv->int340x_zone);
334remove_group:
335 sysfs_remove_group(&proc_priv->dev->kobj,
336 &power_limit_attribute_group);
337
338 return ret;
339}
340
341static void proc_thermal_remove(struct proc_thermal_device *proc_priv)
342{
343 acpi_remove_notify_handler(proc_priv->adev->handle,
344 ACPI_DEVICE_NOTIFY, proc_thermal_notify);
345 int340x_thermal_zone_remove(proc_priv->int340x_zone);
346 sysfs_remove_group(&proc_priv->dev->kobj,
347 &power_limit_attribute_group);
348}
349
350static int int3401_add(struct platform_device *pdev)
351{
352 struct proc_thermal_device *proc_priv;
353 int ret;
354
355 if (proc_thermal_emum_mode == PROC_THERMAL_PCI) {
356 dev_err(&pdev->dev, "error: enumerated as PCI dev\n");
357 return -ENODEV;
358 }
359
360 ret = proc_thermal_add(&pdev->dev, &proc_priv);
361 if (ret)
362 return ret;
363
364 platform_set_drvdata(pdev, proc_priv);
365 proc_thermal_emum_mode = PROC_THERMAL_PLATFORM_DEV;
366
367 return 0;
368}
369
370static int int3401_remove(struct platform_device *pdev)
371{
372 proc_thermal_remove(platform_get_drvdata(pdev));
373
374 return 0;
375}
376
377static irqreturn_t proc_thermal_pci_msi_irq(int irq, void *devid)
378{
379 struct proc_thermal_device *proc_priv;
380 struct pci_dev *pdev = devid;
381
382 proc_priv = pci_get_drvdata(pdev);
383
384 intel_soc_dts_iosf_interrupt_handler(proc_priv->soc_dts);
385
386 return IRQ_HANDLED;
387}
388
389static int proc_thermal_pci_probe(struct pci_dev *pdev,
390 const struct pci_device_id *unused)
391{
392 struct proc_thermal_device *proc_priv;
393 int ret;
394
395 if (proc_thermal_emum_mode == PROC_THERMAL_PLATFORM_DEV) {
396 dev_err(&pdev->dev, "error: enumerated as platform dev\n");
397 return -ENODEV;
398 }
399
400 ret = pci_enable_device(pdev);
401 if (ret < 0) {
402 dev_err(&pdev->dev, "error: could not enable device\n");
403 return ret;
404 }
405
406 ret = proc_thermal_add(&pdev->dev, &proc_priv);
407 if (ret) {
408 pci_disable_device(pdev);
409 return ret;
410 }
411
412 pci_set_drvdata(pdev, proc_priv);
413 proc_thermal_emum_mode = PROC_THERMAL_PCI;
414
415 if (pdev->device == PCI_DEVICE_ID_PROC_BSW_THERMAL) {
416 /*
417 * Enumerate additional DTS sensors available via IOSF.
418 * But we are not treating as a failure condition, if
419 * there are no aux DTSs enabled or fails. This driver
420 * already exposes sensors, which can be accessed via
421 * ACPI/MSR. So we don't want to fail for auxiliary DTSs.
422 */
423 proc_priv->soc_dts = intel_soc_dts_iosf_init(
424 INTEL_SOC_DTS_INTERRUPT_MSI, 2, 0);
425
426 if (proc_priv->soc_dts && pdev->irq) {
427 ret = pci_enable_msi(pdev);
428 if (!ret) {
429 ret = request_threaded_irq(pdev->irq, NULL,
430 proc_thermal_pci_msi_irq,
431 IRQF_ONESHOT, "proc_thermal",
432 pdev);
433 if (ret) {
434 intel_soc_dts_iosf_exit(
435 proc_priv->soc_dts);
436 pci_disable_msi(pdev);
437 proc_priv->soc_dts = NULL;
438 }
439 }
440 } else
441 dev_err(&pdev->dev, "No auxiliary DTSs enabled\n");
442 }
443
444 return 0;
445}
446
447static void proc_thermal_pci_remove(struct pci_dev *pdev)
448{
449 struct proc_thermal_device *proc_priv = pci_get_drvdata(pdev);
450
451 if (proc_priv->soc_dts) {
452 intel_soc_dts_iosf_exit(proc_priv->soc_dts);
453 if (pdev->irq) {
454 free_irq(pdev->irq, pdev);
455 pci_disable_msi(pdev);
456 }
457 }
458 proc_thermal_remove(proc_priv);
459 pci_disable_device(pdev);
460}
461
462static const struct pci_device_id proc_thermal_pci_ids[] = {
463 { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_PROC_BDW_THERMAL)},
464 { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_PROC_HSB_THERMAL)},
465 { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_PROC_SKL_THERMAL)},
466 { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_PROC_BSW_THERMAL)},
467 { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_PROC_BXT0_THERMAL)},
468 { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_PROC_BXT1_THERMAL)},
469 { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_PROC_BXTX_THERMAL)},
470 { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_PROC_BXTP_THERMAL)},
471 { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_PROC_CNL_THERMAL)},
472 { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_PROC_CFL_THERMAL)},
473 { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_PROC_GLK_THERMAL)},
474 { 0, },
475};
476
477MODULE_DEVICE_TABLE(pci, proc_thermal_pci_ids);
478
479static struct pci_driver proc_thermal_pci_driver = {
480 .name = "proc_thermal",
481 .probe = proc_thermal_pci_probe,
482 .remove = proc_thermal_pci_remove,
483 .id_table = proc_thermal_pci_ids,
484};
485
486static const struct acpi_device_id int3401_device_ids[] = {
487 {"INT3401", 0},
488 {"", 0},
489};
490MODULE_DEVICE_TABLE(acpi, int3401_device_ids);
491
492static struct platform_driver int3401_driver = {
493 .probe = int3401_add,
494 .remove = int3401_remove,
495 .driver = {
496 .name = "int3401 thermal",
497 .acpi_match_table = int3401_device_ids,
498 },
499};
500
501static int __init proc_thermal_init(void)
502{
503 int ret;
504
505 ret = platform_driver_register(&int3401_driver);
506 if (ret)
507 return ret;
508
509 ret = pci_register_driver(&proc_thermal_pci_driver);
510
511 return ret;
512}
513
514static void __exit proc_thermal_exit(void)
515{
516 platform_driver_unregister(&int3401_driver);
517 pci_unregister_driver(&proc_thermal_pci_driver);
518}
519
520module_init(proc_thermal_init);
521module_exit(proc_thermal_exit);
522
523MODULE_AUTHOR("Srinivas Pandruvada <srinivas.pandruvada@linux.intel.com>");
524MODULE_DESCRIPTION("Processor Thermal Reporting Device Driver");
525MODULE_LICENSE("GPL v2");
diff --git a/drivers/thermal/intel/intel_bxt_pmic_thermal.c b/drivers/thermal/intel/intel_bxt_pmic_thermal.c
new file mode 100644
index 000000000000..94cfd0064c43
--- /dev/null
+++ b/drivers/thermal/intel/intel_bxt_pmic_thermal.c
@@ -0,0 +1,299 @@
1/*
2 * Intel Broxton PMIC thermal driver
3 *
4 * Copyright (C) 2016 Intel Corporation. All rights reserved.
5 *
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU General Public License version
8 * 2 as published by the Free Software Foundation.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 */
16
17#include <linux/module.h>
18#include <linux/kernel.h>
19#include <linux/slab.h>
20#include <linux/delay.h>
21#include <linux/interrupt.h>
22#include <linux/device.h>
23#include <linux/thermal.h>
24#include <linux/platform_device.h>
25#include <linux/sched.h>
26#include <linux/mfd/intel_soc_pmic.h>
27
28#define BXTWC_THRM0IRQ 0x4E04
29#define BXTWC_THRM1IRQ 0x4E05
30#define BXTWC_THRM2IRQ 0x4E06
31#define BXTWC_MTHRM0IRQ 0x4E12
32#define BXTWC_MTHRM1IRQ 0x4E13
33#define BXTWC_MTHRM2IRQ 0x4E14
34#define BXTWC_STHRM0IRQ 0x4F19
35#define BXTWC_STHRM1IRQ 0x4F1A
36#define BXTWC_STHRM2IRQ 0x4F1B
37
38struct trip_config_map {
39 u16 irq_reg;
40 u16 irq_en;
41 u16 evt_stat;
42 u8 irq_mask;
43 u8 irq_en_mask;
44 u8 evt_mask;
45 u8 trip_num;
46};
47
48struct thermal_irq_map {
49 char handle[20];
50 int num_trips;
51 const struct trip_config_map *trip_config;
52};
53
54struct pmic_thermal_data {
55 const struct thermal_irq_map *maps;
56 int num_maps;
57};
58
59static const struct trip_config_map bxtwc_str0_trip_config[] = {
60 {
61 .irq_reg = BXTWC_THRM0IRQ,
62 .irq_mask = 0x01,
63 .irq_en = BXTWC_MTHRM0IRQ,
64 .irq_en_mask = 0x01,
65 .evt_stat = BXTWC_STHRM0IRQ,
66 .evt_mask = 0x01,
67 .trip_num = 0
68 },
69 {
70 .irq_reg = BXTWC_THRM0IRQ,
71 .irq_mask = 0x10,
72 .irq_en = BXTWC_MTHRM0IRQ,
73 .irq_en_mask = 0x10,
74 .evt_stat = BXTWC_STHRM0IRQ,
75 .evt_mask = 0x10,
76 .trip_num = 1
77 }
78};
79
80static const struct trip_config_map bxtwc_str1_trip_config[] = {
81 {
82 .irq_reg = BXTWC_THRM0IRQ,
83 .irq_mask = 0x02,
84 .irq_en = BXTWC_MTHRM0IRQ,
85 .irq_en_mask = 0x02,
86 .evt_stat = BXTWC_STHRM0IRQ,
87 .evt_mask = 0x02,
88 .trip_num = 0
89 },
90 {
91 .irq_reg = BXTWC_THRM0IRQ,
92 .irq_mask = 0x20,
93 .irq_en = BXTWC_MTHRM0IRQ,
94 .irq_en_mask = 0x20,
95 .evt_stat = BXTWC_STHRM0IRQ,
96 .evt_mask = 0x20,
97 .trip_num = 1
98 },
99};
100
101static const struct trip_config_map bxtwc_str2_trip_config[] = {
102 {
103 .irq_reg = BXTWC_THRM0IRQ,
104 .irq_mask = 0x04,
105 .irq_en = BXTWC_MTHRM0IRQ,
106 .irq_en_mask = 0x04,
107 .evt_stat = BXTWC_STHRM0IRQ,
108 .evt_mask = 0x04,
109 .trip_num = 0
110 },
111 {
112 .irq_reg = BXTWC_THRM0IRQ,
113 .irq_mask = 0x40,
114 .irq_en = BXTWC_MTHRM0IRQ,
115 .irq_en_mask = 0x40,
116 .evt_stat = BXTWC_STHRM0IRQ,
117 .evt_mask = 0x40,
118 .trip_num = 1
119 },
120};
121
122static const struct trip_config_map bxtwc_str3_trip_config[] = {
123 {
124 .irq_reg = BXTWC_THRM2IRQ,
125 .irq_mask = 0x10,
126 .irq_en = BXTWC_MTHRM2IRQ,
127 .irq_en_mask = 0x10,
128 .evt_stat = BXTWC_STHRM2IRQ,
129 .evt_mask = 0x10,
130 .trip_num = 0
131 },
132};
133
134static const struct thermal_irq_map bxtwc_thermal_irq_map[] = {
135 {
136 .handle = "STR0",
137 .trip_config = bxtwc_str0_trip_config,
138 .num_trips = ARRAY_SIZE(bxtwc_str0_trip_config),
139 },
140 {
141 .handle = "STR1",
142 .trip_config = bxtwc_str1_trip_config,
143 .num_trips = ARRAY_SIZE(bxtwc_str1_trip_config),
144 },
145 {
146 .handle = "STR2",
147 .trip_config = bxtwc_str2_trip_config,
148 .num_trips = ARRAY_SIZE(bxtwc_str2_trip_config),
149 },
150 {
151 .handle = "STR3",
152 .trip_config = bxtwc_str3_trip_config,
153 .num_trips = ARRAY_SIZE(bxtwc_str3_trip_config),
154 },
155};
156
157static const struct pmic_thermal_data bxtwc_thermal_data = {
158 .maps = bxtwc_thermal_irq_map,
159 .num_maps = ARRAY_SIZE(bxtwc_thermal_irq_map),
160};
161
162static irqreturn_t pmic_thermal_irq_handler(int irq, void *data)
163{
164 struct platform_device *pdev = data;
165 struct thermal_zone_device *tzd;
166 struct pmic_thermal_data *td;
167 struct intel_soc_pmic *pmic;
168 struct regmap *regmap;
169 u8 reg_val, mask, irq_stat;
170 u16 reg, evt_stat_reg;
171 int i, j, ret;
172
173 pmic = dev_get_drvdata(pdev->dev.parent);
174 regmap = pmic->regmap;
175 td = (struct pmic_thermal_data *)
176 platform_get_device_id(pdev)->driver_data;
177
178 /* Resolve thermal irqs */
179 for (i = 0; i < td->num_maps; i++) {
180 for (j = 0; j < td->maps[i].num_trips; j++) {
181 reg = td->maps[i].trip_config[j].irq_reg;
182 mask = td->maps[i].trip_config[j].irq_mask;
183 /*
184 * Read the irq register to resolve whether the
185 * interrupt was triggered for this sensor
186 */
187 if (regmap_read(regmap, reg, &ret))
188 return IRQ_HANDLED;
189
190 reg_val = (u8)ret;
191 irq_stat = ((u8)ret & mask);
192
193 if (!irq_stat)
194 continue;
195
196 /*
197 * Read the status register to find out what
198 * event occurred i.e a high or a low
199 */
200 evt_stat_reg = td->maps[i].trip_config[j].evt_stat;
201 if (regmap_read(regmap, evt_stat_reg, &ret))
202 return IRQ_HANDLED;
203
204 tzd = thermal_zone_get_zone_by_name(td->maps[i].handle);
205 if (!IS_ERR(tzd))
206 thermal_zone_device_update(tzd,
207 THERMAL_EVENT_UNSPECIFIED);
208
209 /* Clear the appropriate irq */
210 regmap_write(regmap, reg, reg_val & mask);
211 }
212 }
213
214 return IRQ_HANDLED;
215}
216
217static int pmic_thermal_probe(struct platform_device *pdev)
218{
219 struct regmap_irq_chip_data *regmap_irq_chip;
220 struct pmic_thermal_data *thermal_data;
221 int ret, irq, virq, i, j, pmic_irq_count;
222 struct intel_soc_pmic *pmic;
223 struct regmap *regmap;
224 struct device *dev;
225 u16 reg;
226 u8 mask;
227
228 dev = &pdev->dev;
229 pmic = dev_get_drvdata(pdev->dev.parent);
230 if (!pmic) {
231 dev_err(dev, "Failed to get struct intel_soc_pmic pointer\n");
232 return -ENODEV;
233 }
234
235 thermal_data = (struct pmic_thermal_data *)
236 platform_get_device_id(pdev)->driver_data;
237 if (!thermal_data) {
238 dev_err(dev, "No thermal data initialized!!\n");
239 return -ENODEV;
240 }
241
242 regmap = pmic->regmap;
243 regmap_irq_chip = pmic->irq_chip_data;
244
245 pmic_irq_count = 0;
246 while ((irq = platform_get_irq(pdev, pmic_irq_count)) != -ENXIO) {
247 virq = regmap_irq_get_virq(regmap_irq_chip, irq);
248 if (virq < 0) {
249 dev_err(dev, "failed to get virq by irq %d\n", irq);
250 return virq;
251 }
252
253 ret = devm_request_threaded_irq(&pdev->dev, virq,
254 NULL, pmic_thermal_irq_handler,
255 IRQF_ONESHOT, "pmic_thermal", pdev);
256
257 if (ret) {
258 dev_err(dev, "request irq(%d) failed: %d\n", virq, ret);
259 return ret;
260 }
261 pmic_irq_count++;
262 }
263
264 /* Enable thermal interrupts */
265 for (i = 0; i < thermal_data->num_maps; i++) {
266 for (j = 0; j < thermal_data->maps[i].num_trips; j++) {
267 reg = thermal_data->maps[i].trip_config[j].irq_en;
268 mask = thermal_data->maps[i].trip_config[j].irq_en_mask;
269 ret = regmap_update_bits(regmap, reg, mask, 0x00);
270 if (ret)
271 return ret;
272 }
273 }
274
275 return 0;
276}
277
278static const struct platform_device_id pmic_thermal_id_table[] = {
279 {
280 .name = "bxt_wcove_thermal",
281 .driver_data = (kernel_ulong_t)&bxtwc_thermal_data,
282 },
283 {},
284};
285
286static struct platform_driver pmic_thermal_driver = {
287 .probe = pmic_thermal_probe,
288 .driver = {
289 .name = "pmic_thermal",
290 },
291 .id_table = pmic_thermal_id_table,
292};
293
294MODULE_DEVICE_TABLE(platform, pmic_thermal_id_table);
295module_platform_driver(pmic_thermal_driver);
296
297MODULE_AUTHOR("Yegnesh S Iyer <yegnesh.s.iyer@intel.com>");
298MODULE_DESCRIPTION("Intel Broxton PMIC Thermal Driver");
299MODULE_LICENSE("GPL v2");
diff --git a/drivers/thermal/intel/intel_pch_thermal.c b/drivers/thermal/intel/intel_pch_thermal.c
new file mode 100644
index 000000000000..8a7f69b4b022
--- /dev/null
+++ b/drivers/thermal/intel/intel_pch_thermal.c
@@ -0,0 +1,432 @@
1/* intel_pch_thermal.c - Intel PCH Thermal driver
2 *
3 * Copyright (c) 2015, Intel Corporation.
4 *
5 * Authors:
6 * Tushar Dave <tushar.n.dave@intel.com>
7 *
8 * This program is free software; you can redistribute it and/or modify it
9 * under the terms and conditions of the GNU General Public License,
10 * version 2, as published by the Free Software Foundation.
11 *
12 * This program is distributed in the hope it will be useful, but WITHOUT
13 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
14 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
15 * more details.
16 *
17 */
18
19#include <linux/module.h>
20#include <linux/types.h>
21#include <linux/init.h>
22#include <linux/pci.h>
23#include <linux/acpi.h>
24#include <linux/thermal.h>
25#include <linux/pm.h>
26
27/* Intel PCH thermal Device IDs */
28#define PCH_THERMAL_DID_HSW_1 0x9C24 /* Haswell PCH */
29#define PCH_THERMAL_DID_HSW_2 0x8C24 /* Haswell PCH */
30#define PCH_THERMAL_DID_WPT 0x9CA4 /* Wildcat Point */
31#define PCH_THERMAL_DID_SKL 0x9D31 /* Skylake PCH */
32#define PCH_THERMAL_DID_SKL_H 0xA131 /* Skylake PCH 100 series */
33#define PCH_THERMAL_DID_CNL 0x9Df9 /* CNL PCH */
34#define PCH_THERMAL_DID_CNL_H 0xA379 /* CNL-H PCH */
35
36/* Wildcat Point-LP PCH Thermal registers */
37#define WPT_TEMP 0x0000 /* Temperature */
38#define WPT_TSC 0x04 /* Thermal Sensor Control */
39#define WPT_TSS 0x06 /* Thermal Sensor Status */
40#define WPT_TSEL 0x08 /* Thermal Sensor Enable and Lock */
41#define WPT_TSREL 0x0A /* Thermal Sensor Report Enable and Lock */
42#define WPT_TSMIC 0x0C /* Thermal Sensor SMI Control */
43#define WPT_CTT 0x0010 /* Catastrophic Trip Point */
44#define WPT_TAHV 0x0014 /* Thermal Alert High Value */
45#define WPT_TALV 0x0018 /* Thermal Alert Low Value */
46#define WPT_TL 0x00000040 /* Throttle Value */
47#define WPT_PHL 0x0060 /* PCH Hot Level */
48#define WPT_PHLC 0x62 /* PHL Control */
49#define WPT_TAS 0x80 /* Thermal Alert Status */
50#define WPT_TSPIEN 0x82 /* PCI Interrupt Event Enables */
51#define WPT_TSGPEN 0x84 /* General Purpose Event Enables */
52
53/* Wildcat Point-LP PCH Thermal Register bit definitions */
54#define WPT_TEMP_TSR 0x01ff /* Temp TS Reading */
55#define WPT_TSC_CPDE 0x01 /* Catastrophic Power-Down Enable */
56#define WPT_TSS_TSDSS 0x10 /* Thermal Sensor Dynamic Shutdown Status */
57#define WPT_TSS_GPES 0x08 /* GPE status */
58#define WPT_TSEL_ETS 0x01 /* Enable TS */
59#define WPT_TSEL_PLDB 0x80 /* TSEL Policy Lock-Down Bit */
60#define WPT_TL_TOL 0x000001FF /* T0 Level */
61#define WPT_TL_T1L 0x1ff00000 /* T1 Level */
62#define WPT_TL_TTEN 0x20000000 /* TT Enable */
63
64static char driver_name[] = "Intel PCH thermal driver";
65
66struct pch_thermal_device {
67 void __iomem *hw_base;
68 const struct pch_dev_ops *ops;
69 struct pci_dev *pdev;
70 struct thermal_zone_device *tzd;
71 int crt_trip_id;
72 unsigned long crt_temp;
73 int hot_trip_id;
74 unsigned long hot_temp;
75 int psv_trip_id;
76 unsigned long psv_temp;
77 bool bios_enabled;
78};
79
80#ifdef CONFIG_ACPI
81
82/*
83 * On some platforms, there is a companion ACPI device, which adds
84 * passive trip temperature using _PSV method. There is no specific
85 * passive temperature setting in MMIO interface of this PCI device.
86 */
87static void pch_wpt_add_acpi_psv_trip(struct pch_thermal_device *ptd,
88 int *nr_trips)
89{
90 struct acpi_device *adev;
91
92 ptd->psv_trip_id = -1;
93
94 adev = ACPI_COMPANION(&ptd->pdev->dev);
95 if (adev) {
96 unsigned long long r;
97 acpi_status status;
98
99 status = acpi_evaluate_integer(adev->handle, "_PSV", NULL,
100 &r);
101 if (ACPI_SUCCESS(status)) {
102 unsigned long trip_temp;
103
104 trip_temp = DECI_KELVIN_TO_MILLICELSIUS(r);
105 if (trip_temp) {
106 ptd->psv_temp = trip_temp;
107 ptd->psv_trip_id = *nr_trips;
108 ++(*nr_trips);
109 }
110 }
111 }
112}
113#else
114static void pch_wpt_add_acpi_psv_trip(struct pch_thermal_device *ptd,
115 int *nr_trips)
116{
117 ptd->psv_trip_id = -1;
118
119}
120#endif
121
122static int pch_wpt_init(struct pch_thermal_device *ptd, int *nr_trips)
123{
124 u8 tsel;
125 u16 trip_temp;
126
127 *nr_trips = 0;
128
129 /* Check if BIOS has already enabled thermal sensor */
130 if (WPT_TSEL_ETS & readb(ptd->hw_base + WPT_TSEL)) {
131 ptd->bios_enabled = true;
132 goto read_trips;
133 }
134
135 tsel = readb(ptd->hw_base + WPT_TSEL);
136 /*
137 * When TSEL's Policy Lock-Down bit is 1, TSEL become RO.
138 * If so, thermal sensor cannot enable. Bail out.
139 */
140 if (tsel & WPT_TSEL_PLDB) {
141 dev_err(&ptd->pdev->dev, "Sensor can't be enabled\n");
142 return -ENODEV;
143 }
144
145 writeb(tsel|WPT_TSEL_ETS, ptd->hw_base + WPT_TSEL);
146 if (!(WPT_TSEL_ETS & readb(ptd->hw_base + WPT_TSEL))) {
147 dev_err(&ptd->pdev->dev, "Sensor can't be enabled\n");
148 return -ENODEV;
149 }
150
151read_trips:
152 ptd->crt_trip_id = -1;
153 trip_temp = readw(ptd->hw_base + WPT_CTT);
154 trip_temp &= 0x1FF;
155 if (trip_temp) {
156 /* Resolution of 1/2 degree C and an offset of -50C */
157 ptd->crt_temp = trip_temp * 1000 / 2 - 50000;
158 ptd->crt_trip_id = 0;
159 ++(*nr_trips);
160 }
161
162 ptd->hot_trip_id = -1;
163 trip_temp = readw(ptd->hw_base + WPT_PHL);
164 trip_temp &= 0x1FF;
165 if (trip_temp) {
166 /* Resolution of 1/2 degree C and an offset of -50C */
167 ptd->hot_temp = trip_temp * 1000 / 2 - 50000;
168 ptd->hot_trip_id = *nr_trips;
169 ++(*nr_trips);
170 }
171
172 pch_wpt_add_acpi_psv_trip(ptd, nr_trips);
173
174 return 0;
175}
176
177static int pch_wpt_get_temp(struct pch_thermal_device *ptd, int *temp)
178{
179 u16 wpt_temp;
180
181 wpt_temp = WPT_TEMP_TSR & readw(ptd->hw_base + WPT_TEMP);
182
183 /* Resolution of 1/2 degree C and an offset of -50C */
184 *temp = (wpt_temp * 1000 / 2 - 50000);
185
186 return 0;
187}
188
189static int pch_wpt_suspend(struct pch_thermal_device *ptd)
190{
191 u8 tsel;
192
193 if (ptd->bios_enabled)
194 return 0;
195
196 tsel = readb(ptd->hw_base + WPT_TSEL);
197
198 writeb(tsel & 0xFE, ptd->hw_base + WPT_TSEL);
199
200 return 0;
201}
202
203static int pch_wpt_resume(struct pch_thermal_device *ptd)
204{
205 u8 tsel;
206
207 if (ptd->bios_enabled)
208 return 0;
209
210 tsel = readb(ptd->hw_base + WPT_TSEL);
211
212 writeb(tsel | WPT_TSEL_ETS, ptd->hw_base + WPT_TSEL);
213
214 return 0;
215}
216
217struct pch_dev_ops {
218 int (*hw_init)(struct pch_thermal_device *ptd, int *nr_trips);
219 int (*get_temp)(struct pch_thermal_device *ptd, int *temp);
220 int (*suspend)(struct pch_thermal_device *ptd);
221 int (*resume)(struct pch_thermal_device *ptd);
222};
223
224
225/* dev ops for Wildcat Point */
226static const struct pch_dev_ops pch_dev_ops_wpt = {
227 .hw_init = pch_wpt_init,
228 .get_temp = pch_wpt_get_temp,
229 .suspend = pch_wpt_suspend,
230 .resume = pch_wpt_resume,
231};
232
233static int pch_thermal_get_temp(struct thermal_zone_device *tzd, int *temp)
234{
235 struct pch_thermal_device *ptd = tzd->devdata;
236
237 return ptd->ops->get_temp(ptd, temp);
238}
239
240static int pch_get_trip_type(struct thermal_zone_device *tzd, int trip,
241 enum thermal_trip_type *type)
242{
243 struct pch_thermal_device *ptd = tzd->devdata;
244
245 if (ptd->crt_trip_id == trip)
246 *type = THERMAL_TRIP_CRITICAL;
247 else if (ptd->hot_trip_id == trip)
248 *type = THERMAL_TRIP_HOT;
249 else if (ptd->psv_trip_id == trip)
250 *type = THERMAL_TRIP_PASSIVE;
251 else
252 return -EINVAL;
253
254 return 0;
255}
256
257static int pch_get_trip_temp(struct thermal_zone_device *tzd, int trip, int *temp)
258{
259 struct pch_thermal_device *ptd = tzd->devdata;
260
261 if (ptd->crt_trip_id == trip)
262 *temp = ptd->crt_temp;
263 else if (ptd->hot_trip_id == trip)
264 *temp = ptd->hot_temp;
265 else if (ptd->psv_trip_id == trip)
266 *temp = ptd->psv_temp;
267 else
268 return -EINVAL;
269
270 return 0;
271}
272
273static struct thermal_zone_device_ops tzd_ops = {
274 .get_temp = pch_thermal_get_temp,
275 .get_trip_type = pch_get_trip_type,
276 .get_trip_temp = pch_get_trip_temp,
277};
278
279enum board_ids {
280 board_hsw,
281 board_wpt,
282 board_skl,
283 board_cnl,
284};
285
286static const struct board_info {
287 const char *name;
288 const struct pch_dev_ops *ops;
289} board_info[] = {
290 [board_hsw] = {
291 .name = "pch_haswell",
292 .ops = &pch_dev_ops_wpt,
293 },
294 [board_wpt] = {
295 .name = "pch_wildcat_point",
296 .ops = &pch_dev_ops_wpt,
297 },
298 [board_skl] = {
299 .name = "pch_skylake",
300 .ops = &pch_dev_ops_wpt,
301 },
302 [board_cnl] = {
303 .name = "pch_cannonlake",
304 .ops = &pch_dev_ops_wpt,
305 },
306};
307
308static int intel_pch_thermal_probe(struct pci_dev *pdev,
309 const struct pci_device_id *id)
310{
311 enum board_ids board_id = id->driver_data;
312 const struct board_info *bi = &board_info[board_id];
313 struct pch_thermal_device *ptd;
314 int err;
315 int nr_trips;
316
317 ptd = devm_kzalloc(&pdev->dev, sizeof(*ptd), GFP_KERNEL);
318 if (!ptd)
319 return -ENOMEM;
320
321 ptd->ops = bi->ops;
322
323 pci_set_drvdata(pdev, ptd);
324 ptd->pdev = pdev;
325
326 err = pci_enable_device(pdev);
327 if (err) {
328 dev_err(&pdev->dev, "failed to enable pci device\n");
329 return err;
330 }
331
332 err = pci_request_regions(pdev, driver_name);
333 if (err) {
334 dev_err(&pdev->dev, "failed to request pci region\n");
335 goto error_disable;
336 }
337
338 ptd->hw_base = pci_ioremap_bar(pdev, 0);
339 if (!ptd->hw_base) {
340 err = -ENOMEM;
341 dev_err(&pdev->dev, "failed to map mem base\n");
342 goto error_release;
343 }
344
345 err = ptd->ops->hw_init(ptd, &nr_trips);
346 if (err)
347 goto error_cleanup;
348
349 ptd->tzd = thermal_zone_device_register(bi->name, nr_trips, 0, ptd,
350 &tzd_ops, NULL, 0, 0);
351 if (IS_ERR(ptd->tzd)) {
352 dev_err(&pdev->dev, "Failed to register thermal zone %s\n",
353 bi->name);
354 err = PTR_ERR(ptd->tzd);
355 goto error_cleanup;
356 }
357
358 return 0;
359
360error_cleanup:
361 iounmap(ptd->hw_base);
362error_release:
363 pci_release_regions(pdev);
364error_disable:
365 pci_disable_device(pdev);
366 dev_err(&pdev->dev, "pci device failed to probe\n");
367 return err;
368}
369
370static void intel_pch_thermal_remove(struct pci_dev *pdev)
371{
372 struct pch_thermal_device *ptd = pci_get_drvdata(pdev);
373
374 thermal_zone_device_unregister(ptd->tzd);
375 iounmap(ptd->hw_base);
376 pci_set_drvdata(pdev, NULL);
377 pci_release_region(pdev, 0);
378 pci_disable_device(pdev);
379}
380
381static int intel_pch_thermal_suspend(struct device *device)
382{
383 struct pci_dev *pdev = to_pci_dev(device);
384 struct pch_thermal_device *ptd = pci_get_drvdata(pdev);
385
386 return ptd->ops->suspend(ptd);
387}
388
389static int intel_pch_thermal_resume(struct device *device)
390{
391 struct pci_dev *pdev = to_pci_dev(device);
392 struct pch_thermal_device *ptd = pci_get_drvdata(pdev);
393
394 return ptd->ops->resume(ptd);
395}
396
397static const struct pci_device_id intel_pch_thermal_id[] = {
398 { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCH_THERMAL_DID_HSW_1),
399 .driver_data = board_hsw, },
400 { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCH_THERMAL_DID_HSW_2),
401 .driver_data = board_hsw, },
402 { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCH_THERMAL_DID_WPT),
403 .driver_data = board_wpt, },
404 { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCH_THERMAL_DID_SKL),
405 .driver_data = board_skl, },
406 { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCH_THERMAL_DID_SKL_H),
407 .driver_data = board_skl, },
408 { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCH_THERMAL_DID_CNL),
409 .driver_data = board_cnl, },
410 { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCH_THERMAL_DID_CNL_H),
411 .driver_data = board_cnl, },
412 { 0, },
413};
414MODULE_DEVICE_TABLE(pci, intel_pch_thermal_id);
415
416static const struct dev_pm_ops intel_pch_pm_ops = {
417 .suspend = intel_pch_thermal_suspend,
418 .resume = intel_pch_thermal_resume,
419};
420
421static struct pci_driver intel_pch_thermal_driver = {
422 .name = "intel_pch_thermal",
423 .id_table = intel_pch_thermal_id,
424 .probe = intel_pch_thermal_probe,
425 .remove = intel_pch_thermal_remove,
426 .driver.pm = &intel_pch_pm_ops,
427};
428
429module_pci_driver(intel_pch_thermal_driver);
430
431MODULE_LICENSE("GPL v2");
432MODULE_DESCRIPTION("Intel PCH Thermal driver");
diff --git a/drivers/thermal/intel/intel_powerclamp.c b/drivers/thermal/intel/intel_powerclamp.c
new file mode 100644
index 000000000000..cde891c54cde
--- /dev/null
+++ b/drivers/thermal/intel/intel_powerclamp.c
@@ -0,0 +1,815 @@
1/*
2 * intel_powerclamp.c - package c-state idle injection
3 *
4 * Copyright (c) 2012, Intel Corporation.
5 *
6 * Authors:
7 * Arjan van de Ven <arjan@linux.intel.com>
8 * Jacob Pan <jacob.jun.pan@linux.intel.com>
9 *
10 * This program is free software; you can redistribute it and/or modify it
11 * under the terms and conditions of the GNU General Public License,
12 * version 2, as published by the Free Software Foundation.
13 *
14 * This program is distributed in the hope it will be useful, but WITHOUT
15 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
16 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
17 * more details.
18 *
19 * You should have received a copy of the GNU General Public License along with
20 * this program; if not, write to the Free Software Foundation, Inc.,
21 * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
22 *
23 *
24 * TODO:
25 * 1. better handle wakeup from external interrupts, currently a fixed
26 * compensation is added to clamping duration when excessive amount
27 * of wakeups are observed during idle time. the reason is that in
28 * case of external interrupts without need for ack, clamping down
29 * cpu in non-irq context does not reduce irq. for majority of the
30 * cases, clamping down cpu does help reduce irq as well, we should
31 * be able to differentiate the two cases and give a quantitative
32 * solution for the irqs that we can control. perhaps based on
33 * get_cpu_iowait_time_us()
34 *
35 * 2. synchronization with other hw blocks
36 *
37 *
38 */
39
40#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
41
42#include <linux/module.h>
43#include <linux/kernel.h>
44#include <linux/delay.h>
45#include <linux/kthread.h>
46#include <linux/cpu.h>
47#include <linux/thermal.h>
48#include <linux/slab.h>
49#include <linux/tick.h>
50#include <linux/debugfs.h>
51#include <linux/seq_file.h>
52#include <linux/sched/rt.h>
53#include <uapi/linux/sched/types.h>
54
55#include <asm/nmi.h>
56#include <asm/msr.h>
57#include <asm/mwait.h>
58#include <asm/cpu_device_id.h>
59#include <asm/hardirq.h>
60
61#define MAX_TARGET_RATIO (50U)
62/* For each undisturbed clamping period (no extra wake ups during idle time),
63 * we increment the confidence counter for the given target ratio.
64 * CONFIDENCE_OK defines the level where runtime calibration results are
65 * valid.
66 */
67#define CONFIDENCE_OK (3)
68/* Default idle injection duration, driver adjust sleep time to meet target
69 * idle ratio. Similar to frequency modulation.
70 */
71#define DEFAULT_DURATION_JIFFIES (6)
72
73static unsigned int target_mwait;
74static struct dentry *debug_dir;
75
76/* user selected target */
77static unsigned int set_target_ratio;
78static unsigned int current_ratio;
79static bool should_skip;
80static bool reduce_irq;
81static atomic_t idle_wakeup_counter;
82static unsigned int control_cpu; /* The cpu assigned to collect stat and update
83 * control parameters. default to BSP but BSP
84 * can be offlined.
85 */
86static bool clamping;
87
88static const struct sched_param sparam = {
89 .sched_priority = MAX_USER_RT_PRIO / 2,
90};
91struct powerclamp_worker_data {
92 struct kthread_worker *worker;
93 struct kthread_work balancing_work;
94 struct kthread_delayed_work idle_injection_work;
95 unsigned int cpu;
96 unsigned int count;
97 unsigned int guard;
98 unsigned int window_size_now;
99 unsigned int target_ratio;
100 unsigned int duration_jiffies;
101 bool clamping;
102};
103
104static struct powerclamp_worker_data * __percpu worker_data;
105static struct thermal_cooling_device *cooling_dev;
106static unsigned long *cpu_clamping_mask; /* bit map for tracking per cpu
107 * clamping kthread worker
108 */
109
110static unsigned int duration;
111static unsigned int pkg_cstate_ratio_cur;
112static unsigned int window_size;
113
114static int duration_set(const char *arg, const struct kernel_param *kp)
115{
116 int ret = 0;
117 unsigned long new_duration;
118
119 ret = kstrtoul(arg, 10, &new_duration);
120 if (ret)
121 goto exit;
122 if (new_duration > 25 || new_duration < 6) {
123 pr_err("Out of recommended range %lu, between 6-25ms\n",
124 new_duration);
125 ret = -EINVAL;
126 }
127
128 duration = clamp(new_duration, 6ul, 25ul);
129 smp_mb();
130
131exit:
132
133 return ret;
134}
135
136static const struct kernel_param_ops duration_ops = {
137 .set = duration_set,
138 .get = param_get_int,
139};
140
141
142module_param_cb(duration, &duration_ops, &duration, 0644);
143MODULE_PARM_DESC(duration, "forced idle time for each attempt in msec.");
144
145struct powerclamp_calibration_data {
146 unsigned long confidence; /* used for calibration, basically a counter
147 * gets incremented each time a clamping
148 * period is completed without extra wakeups
149 * once that counter is reached given level,
150 * compensation is deemed usable.
151 */
152 unsigned long steady_comp; /* steady state compensation used when
153 * no extra wakeups occurred.
154 */
155 unsigned long dynamic_comp; /* compensate excessive wakeup from idle
156 * mostly from external interrupts.
157 */
158};
159
160static struct powerclamp_calibration_data cal_data[MAX_TARGET_RATIO];
161
162static int window_size_set(const char *arg, const struct kernel_param *kp)
163{
164 int ret = 0;
165 unsigned long new_window_size;
166
167 ret = kstrtoul(arg, 10, &new_window_size);
168 if (ret)
169 goto exit_win;
170 if (new_window_size > 10 || new_window_size < 2) {
171 pr_err("Out of recommended window size %lu, between 2-10\n",
172 new_window_size);
173 ret = -EINVAL;
174 }
175
176 window_size = clamp(new_window_size, 2ul, 10ul);
177 smp_mb();
178
179exit_win:
180
181 return ret;
182}
183
184static const struct kernel_param_ops window_size_ops = {
185 .set = window_size_set,
186 .get = param_get_int,
187};
188
189module_param_cb(window_size, &window_size_ops, &window_size, 0644);
190MODULE_PARM_DESC(window_size, "sliding window in number of clamping cycles\n"
191 "\tpowerclamp controls idle ratio within this window. larger\n"
192 "\twindow size results in slower response time but more smooth\n"
193 "\tclamping results. default to 2.");
194
195static void find_target_mwait(void)
196{
197 unsigned int eax, ebx, ecx, edx;
198 unsigned int highest_cstate = 0;
199 unsigned int highest_subcstate = 0;
200 int i;
201
202 if (boot_cpu_data.cpuid_level < CPUID_MWAIT_LEAF)
203 return;
204
205 cpuid(CPUID_MWAIT_LEAF, &eax, &ebx, &ecx, &edx);
206
207 if (!(ecx & CPUID5_ECX_EXTENSIONS_SUPPORTED) ||
208 !(ecx & CPUID5_ECX_INTERRUPT_BREAK))
209 return;
210
211 edx >>= MWAIT_SUBSTATE_SIZE;
212 for (i = 0; i < 7 && edx; i++, edx >>= MWAIT_SUBSTATE_SIZE) {
213 if (edx & MWAIT_SUBSTATE_MASK) {
214 highest_cstate = i;
215 highest_subcstate = edx & MWAIT_SUBSTATE_MASK;
216 }
217 }
218 target_mwait = (highest_cstate << MWAIT_SUBSTATE_SIZE) |
219 (highest_subcstate - 1);
220
221}
222
223struct pkg_cstate_info {
224 bool skip;
225 int msr_index;
226 int cstate_id;
227};
228
229#define PKG_CSTATE_INIT(id) { \
230 .msr_index = MSR_PKG_C##id##_RESIDENCY, \
231 .cstate_id = id \
232 }
233
234static struct pkg_cstate_info pkg_cstates[] = {
235 PKG_CSTATE_INIT(2),
236 PKG_CSTATE_INIT(3),
237 PKG_CSTATE_INIT(6),
238 PKG_CSTATE_INIT(7),
239 PKG_CSTATE_INIT(8),
240 PKG_CSTATE_INIT(9),
241 PKG_CSTATE_INIT(10),
242 {NULL},
243};
244
245static bool has_pkg_state_counter(void)
246{
247 u64 val;
248 struct pkg_cstate_info *info = pkg_cstates;
249
250 /* check if any one of the counter msrs exists */
251 while (info->msr_index) {
252 if (!rdmsrl_safe(info->msr_index, &val))
253 return true;
254 info++;
255 }
256
257 return false;
258}
259
260static u64 pkg_state_counter(void)
261{
262 u64 val;
263 u64 count = 0;
264 struct pkg_cstate_info *info = pkg_cstates;
265
266 while (info->msr_index) {
267 if (!info->skip) {
268 if (!rdmsrl_safe(info->msr_index, &val))
269 count += val;
270 else
271 info->skip = true;
272 }
273 info++;
274 }
275
276 return count;
277}
278
279static unsigned int get_compensation(int ratio)
280{
281 unsigned int comp = 0;
282
283 /* we only use compensation if all adjacent ones are good */
284 if (ratio == 1 &&
285 cal_data[ratio].confidence >= CONFIDENCE_OK &&
286 cal_data[ratio + 1].confidence >= CONFIDENCE_OK &&
287 cal_data[ratio + 2].confidence >= CONFIDENCE_OK) {
288 comp = (cal_data[ratio].steady_comp +
289 cal_data[ratio + 1].steady_comp +
290 cal_data[ratio + 2].steady_comp) / 3;
291 } else if (ratio == MAX_TARGET_RATIO - 1 &&
292 cal_data[ratio].confidence >= CONFIDENCE_OK &&
293 cal_data[ratio - 1].confidence >= CONFIDENCE_OK &&
294 cal_data[ratio - 2].confidence >= CONFIDENCE_OK) {
295 comp = (cal_data[ratio].steady_comp +
296 cal_data[ratio - 1].steady_comp +
297 cal_data[ratio - 2].steady_comp) / 3;
298 } else if (cal_data[ratio].confidence >= CONFIDENCE_OK &&
299 cal_data[ratio - 1].confidence >= CONFIDENCE_OK &&
300 cal_data[ratio + 1].confidence >= CONFIDENCE_OK) {
301 comp = (cal_data[ratio].steady_comp +
302 cal_data[ratio - 1].steady_comp +
303 cal_data[ratio + 1].steady_comp) / 3;
304 }
305
306 /* REVISIT: simple penalty of double idle injection */
307 if (reduce_irq)
308 comp = ratio;
309 /* do not exceed limit */
310 if (comp + ratio >= MAX_TARGET_RATIO)
311 comp = MAX_TARGET_RATIO - ratio - 1;
312
313 return comp;
314}
315
316static void adjust_compensation(int target_ratio, unsigned int win)
317{
318 int delta;
319 struct powerclamp_calibration_data *d = &cal_data[target_ratio];
320
321 /*
322 * adjust compensations if confidence level has not been reached or
323 * there are too many wakeups during the last idle injection period, we
324 * cannot trust the data for compensation.
325 */
326 if (d->confidence >= CONFIDENCE_OK ||
327 atomic_read(&idle_wakeup_counter) >
328 win * num_online_cpus())
329 return;
330
331 delta = set_target_ratio - current_ratio;
332 /* filter out bad data */
333 if (delta >= 0 && delta <= (1+target_ratio/10)) {
334 if (d->steady_comp)
335 d->steady_comp =
336 roundup(delta+d->steady_comp, 2)/2;
337 else
338 d->steady_comp = delta;
339 d->confidence++;
340 }
341}
342
343static bool powerclamp_adjust_controls(unsigned int target_ratio,
344 unsigned int guard, unsigned int win)
345{
346 static u64 msr_last, tsc_last;
347 u64 msr_now, tsc_now;
348 u64 val64;
349
350 /* check result for the last window */
351 msr_now = pkg_state_counter();
352 tsc_now = rdtsc();
353
354 /* calculate pkg cstate vs tsc ratio */
355 if (!msr_last || !tsc_last)
356 current_ratio = 1;
357 else if (tsc_now-tsc_last) {
358 val64 = 100*(msr_now-msr_last);
359 do_div(val64, (tsc_now-tsc_last));
360 current_ratio = val64;
361 }
362
363 /* update record */
364 msr_last = msr_now;
365 tsc_last = tsc_now;
366
367 adjust_compensation(target_ratio, win);
368 /*
369 * too many external interrupts, set flag such
370 * that we can take measure later.
371 */
372 reduce_irq = atomic_read(&idle_wakeup_counter) >=
373 2 * win * num_online_cpus();
374
375 atomic_set(&idle_wakeup_counter, 0);
376 /* if we are above target+guard, skip */
377 return set_target_ratio + guard <= current_ratio;
378}
379
380static void clamp_balancing_func(struct kthread_work *work)
381{
382 struct powerclamp_worker_data *w_data;
383 int sleeptime;
384 unsigned long target_jiffies;
385 unsigned int compensated_ratio;
386 int interval; /* jiffies to sleep for each attempt */
387
388 w_data = container_of(work, struct powerclamp_worker_data,
389 balancing_work);
390
391 /*
392 * make sure user selected ratio does not take effect until
393 * the next round. adjust target_ratio if user has changed
394 * target such that we can converge quickly.
395 */
396 w_data->target_ratio = READ_ONCE(set_target_ratio);
397 w_data->guard = 1 + w_data->target_ratio / 20;
398 w_data->window_size_now = window_size;
399 w_data->duration_jiffies = msecs_to_jiffies(duration);
400 w_data->count++;
401
402 /*
403 * systems may have different ability to enter package level
404 * c-states, thus we need to compensate the injected idle ratio
405 * to achieve the actual target reported by the HW.
406 */
407 compensated_ratio = w_data->target_ratio +
408 get_compensation(w_data->target_ratio);
409 if (compensated_ratio <= 0)
410 compensated_ratio = 1;
411 interval = w_data->duration_jiffies * 100 / compensated_ratio;
412
413 /* align idle time */
414 target_jiffies = roundup(jiffies, interval);
415 sleeptime = target_jiffies - jiffies;
416 if (sleeptime <= 0)
417 sleeptime = 1;
418
419 if (clamping && w_data->clamping && cpu_online(w_data->cpu))
420 kthread_queue_delayed_work(w_data->worker,
421 &w_data->idle_injection_work,
422 sleeptime);
423}
424
425static void clamp_idle_injection_func(struct kthread_work *work)
426{
427 struct powerclamp_worker_data *w_data;
428
429 w_data = container_of(work, struct powerclamp_worker_data,
430 idle_injection_work.work);
431
432 /*
433 * only elected controlling cpu can collect stats and update
434 * control parameters.
435 */
436 if (w_data->cpu == control_cpu &&
437 !(w_data->count % w_data->window_size_now)) {
438 should_skip =
439 powerclamp_adjust_controls(w_data->target_ratio,
440 w_data->guard,
441 w_data->window_size_now);
442 smp_mb();
443 }
444
445 if (should_skip)
446 goto balance;
447
448 play_idle(jiffies_to_msecs(w_data->duration_jiffies));
449
450balance:
451 if (clamping && w_data->clamping && cpu_online(w_data->cpu))
452 kthread_queue_work(w_data->worker, &w_data->balancing_work);
453}
454
455/*
456 * 1 HZ polling while clamping is active, useful for userspace
457 * to monitor actual idle ratio.
458 */
459static void poll_pkg_cstate(struct work_struct *dummy);
460static DECLARE_DELAYED_WORK(poll_pkg_cstate_work, poll_pkg_cstate);
461static void poll_pkg_cstate(struct work_struct *dummy)
462{
463 static u64 msr_last;
464 static u64 tsc_last;
465
466 u64 msr_now;
467 u64 tsc_now;
468 u64 val64;
469
470 msr_now = pkg_state_counter();
471 tsc_now = rdtsc();
472
473 /* calculate pkg cstate vs tsc ratio */
474 if (!msr_last || !tsc_last)
475 pkg_cstate_ratio_cur = 1;
476 else {
477 if (tsc_now - tsc_last) {
478 val64 = 100 * (msr_now - msr_last);
479 do_div(val64, (tsc_now - tsc_last));
480 pkg_cstate_ratio_cur = val64;
481 }
482 }
483
484 /* update record */
485 msr_last = msr_now;
486 tsc_last = tsc_now;
487
488 if (true == clamping)
489 schedule_delayed_work(&poll_pkg_cstate_work, HZ);
490}
491
492static void start_power_clamp_worker(unsigned long cpu)
493{
494 struct powerclamp_worker_data *w_data = per_cpu_ptr(worker_data, cpu);
495 struct kthread_worker *worker;
496
497 worker = kthread_create_worker_on_cpu(cpu, 0, "kidle_inject/%ld", cpu);
498 if (IS_ERR(worker))
499 return;
500
501 w_data->worker = worker;
502 w_data->count = 0;
503 w_data->cpu = cpu;
504 w_data->clamping = true;
505 set_bit(cpu, cpu_clamping_mask);
506 sched_setscheduler(worker->task, SCHED_FIFO, &sparam);
507 kthread_init_work(&w_data->balancing_work, clamp_balancing_func);
508 kthread_init_delayed_work(&w_data->idle_injection_work,
509 clamp_idle_injection_func);
510 kthread_queue_work(w_data->worker, &w_data->balancing_work);
511}
512
513static void stop_power_clamp_worker(unsigned long cpu)
514{
515 struct powerclamp_worker_data *w_data = per_cpu_ptr(worker_data, cpu);
516
517 if (!w_data->worker)
518 return;
519
520 w_data->clamping = false;
521 /*
522 * Make sure that all works that get queued after this point see
523 * the clamping disabled. The counter part is not needed because
524 * there is an implicit memory barrier when the queued work
525 * is proceed.
526 */
527 smp_wmb();
528 kthread_cancel_work_sync(&w_data->balancing_work);
529 kthread_cancel_delayed_work_sync(&w_data->idle_injection_work);
530 /*
531 * The balancing work still might be queued here because
532 * the handling of the "clapming" variable, cancel, and queue
533 * operations are not synchronized via a lock. But it is not
534 * a big deal. The balancing work is fast and destroy kthread
535 * will wait for it.
536 */
537 clear_bit(w_data->cpu, cpu_clamping_mask);
538 kthread_destroy_worker(w_data->worker);
539
540 w_data->worker = NULL;
541}
542
543static int start_power_clamp(void)
544{
545 unsigned long cpu;
546
547 set_target_ratio = clamp(set_target_ratio, 0U, MAX_TARGET_RATIO - 1);
548 /* prevent cpu hotplug */
549 get_online_cpus();
550
551 /* prefer BSP */
552 control_cpu = 0;
553 if (!cpu_online(control_cpu))
554 control_cpu = smp_processor_id();
555
556 clamping = true;
557 schedule_delayed_work(&poll_pkg_cstate_work, 0);
558
559 /* start one kthread worker per online cpu */
560 for_each_online_cpu(cpu) {
561 start_power_clamp_worker(cpu);
562 }
563 put_online_cpus();
564
565 return 0;
566}
567
568static void end_power_clamp(void)
569{
570 int i;
571
572 /*
573 * Block requeuing in all the kthread workers. They will flush and
574 * stop faster.
575 */
576 clamping = false;
577 if (bitmap_weight(cpu_clamping_mask, num_possible_cpus())) {
578 for_each_set_bit(i, cpu_clamping_mask, num_possible_cpus()) {
579 pr_debug("clamping worker for cpu %d alive, destroy\n",
580 i);
581 stop_power_clamp_worker(i);
582 }
583 }
584}
585
586static int powerclamp_cpu_online(unsigned int cpu)
587{
588 if (clamping == false)
589 return 0;
590 start_power_clamp_worker(cpu);
591 /* prefer BSP as controlling CPU */
592 if (cpu == 0) {
593 control_cpu = 0;
594 smp_mb();
595 }
596 return 0;
597}
598
599static int powerclamp_cpu_predown(unsigned int cpu)
600{
601 if (clamping == false)
602 return 0;
603
604 stop_power_clamp_worker(cpu);
605 if (cpu != control_cpu)
606 return 0;
607
608 control_cpu = cpumask_first(cpu_online_mask);
609 if (control_cpu == cpu)
610 control_cpu = cpumask_next(cpu, cpu_online_mask);
611 smp_mb();
612 return 0;
613}
614
615static int powerclamp_get_max_state(struct thermal_cooling_device *cdev,
616 unsigned long *state)
617{
618 *state = MAX_TARGET_RATIO;
619
620 return 0;
621}
622
623static int powerclamp_get_cur_state(struct thermal_cooling_device *cdev,
624 unsigned long *state)
625{
626 if (true == clamping)
627 *state = pkg_cstate_ratio_cur;
628 else
629 /* to save power, do not poll idle ratio while not clamping */
630 *state = -1; /* indicates invalid state */
631
632 return 0;
633}
634
635static int powerclamp_set_cur_state(struct thermal_cooling_device *cdev,
636 unsigned long new_target_ratio)
637{
638 int ret = 0;
639
640 new_target_ratio = clamp(new_target_ratio, 0UL,
641 (unsigned long) (MAX_TARGET_RATIO-1));
642 if (set_target_ratio == 0 && new_target_ratio > 0) {
643 pr_info("Start idle injection to reduce power\n");
644 set_target_ratio = new_target_ratio;
645 ret = start_power_clamp();
646 goto exit_set;
647 } else if (set_target_ratio > 0 && new_target_ratio == 0) {
648 pr_info("Stop forced idle injection\n");
649 end_power_clamp();
650 set_target_ratio = 0;
651 } else /* adjust currently running */ {
652 set_target_ratio = new_target_ratio;
653 /* make new set_target_ratio visible to other cpus */
654 smp_mb();
655 }
656
657exit_set:
658 return ret;
659}
660
661/* bind to generic thermal layer as cooling device*/
662static struct thermal_cooling_device_ops powerclamp_cooling_ops = {
663 .get_max_state = powerclamp_get_max_state,
664 .get_cur_state = powerclamp_get_cur_state,
665 .set_cur_state = powerclamp_set_cur_state,
666};
667
668static const struct x86_cpu_id __initconst intel_powerclamp_ids[] = {
669 { X86_VENDOR_INTEL, X86_FAMILY_ANY, X86_MODEL_ANY, X86_FEATURE_MWAIT },
670 {}
671};
672MODULE_DEVICE_TABLE(x86cpu, intel_powerclamp_ids);
673
674static int __init powerclamp_probe(void)
675{
676
677 if (!x86_match_cpu(intel_powerclamp_ids)) {
678 pr_err("CPU does not support MWAIT\n");
679 return -ENODEV;
680 }
681
682 /* The goal for idle time alignment is to achieve package cstate. */
683 if (!has_pkg_state_counter()) {
684 pr_info("No package C-state available\n");
685 return -ENODEV;
686 }
687
688 /* find the deepest mwait value */
689 find_target_mwait();
690
691 return 0;
692}
693
694static int powerclamp_debug_show(struct seq_file *m, void *unused)
695{
696 int i = 0;
697
698 seq_printf(m, "controlling cpu: %d\n", control_cpu);
699 seq_printf(m, "pct confidence steady dynamic (compensation)\n");
700 for (i = 0; i < MAX_TARGET_RATIO; i++) {
701 seq_printf(m, "%d\t%lu\t%lu\t%lu\n",
702 i,
703 cal_data[i].confidence,
704 cal_data[i].steady_comp,
705 cal_data[i].dynamic_comp);
706 }
707
708 return 0;
709}
710
711static int powerclamp_debug_open(struct inode *inode,
712 struct file *file)
713{
714 return single_open(file, powerclamp_debug_show, inode->i_private);
715}
716
717static const struct file_operations powerclamp_debug_fops = {
718 .open = powerclamp_debug_open,
719 .read = seq_read,
720 .llseek = seq_lseek,
721 .release = single_release,
722 .owner = THIS_MODULE,
723};
724
725static inline void powerclamp_create_debug_files(void)
726{
727 debug_dir = debugfs_create_dir("intel_powerclamp", NULL);
728 if (!debug_dir)
729 return;
730
731 if (!debugfs_create_file("powerclamp_calib", S_IRUGO, debug_dir,
732 cal_data, &powerclamp_debug_fops))
733 goto file_error;
734
735 return;
736
737file_error:
738 debugfs_remove_recursive(debug_dir);
739}
740
741static enum cpuhp_state hp_state;
742
743static int __init powerclamp_init(void)
744{
745 int retval;
746 int bitmap_size;
747
748 bitmap_size = BITS_TO_LONGS(num_possible_cpus()) * sizeof(long);
749 cpu_clamping_mask = kzalloc(bitmap_size, GFP_KERNEL);
750 if (!cpu_clamping_mask)
751 return -ENOMEM;
752
753 /* probe cpu features and ids here */
754 retval = powerclamp_probe();
755 if (retval)
756 goto exit_free;
757
758 /* set default limit, maybe adjusted during runtime based on feedback */
759 window_size = 2;
760 retval = cpuhp_setup_state_nocalls(CPUHP_AP_ONLINE_DYN,
761 "thermal/intel_powerclamp:online",
762 powerclamp_cpu_online,
763 powerclamp_cpu_predown);
764 if (retval < 0)
765 goto exit_free;
766
767 hp_state = retval;
768
769 worker_data = alloc_percpu(struct powerclamp_worker_data);
770 if (!worker_data) {
771 retval = -ENOMEM;
772 goto exit_unregister;
773 }
774
775 cooling_dev = thermal_cooling_device_register("intel_powerclamp", NULL,
776 &powerclamp_cooling_ops);
777 if (IS_ERR(cooling_dev)) {
778 retval = -ENODEV;
779 goto exit_free_thread;
780 }
781
782 if (!duration)
783 duration = jiffies_to_msecs(DEFAULT_DURATION_JIFFIES);
784
785 powerclamp_create_debug_files();
786
787 return 0;
788
789exit_free_thread:
790 free_percpu(worker_data);
791exit_unregister:
792 cpuhp_remove_state_nocalls(hp_state);
793exit_free:
794 kfree(cpu_clamping_mask);
795 return retval;
796}
797module_init(powerclamp_init);
798
799static void __exit powerclamp_exit(void)
800{
801 end_power_clamp();
802 cpuhp_remove_state_nocalls(hp_state);
803 free_percpu(worker_data);
804 thermal_cooling_device_unregister(cooling_dev);
805 kfree(cpu_clamping_mask);
806
807 cancel_delayed_work_sync(&poll_pkg_cstate_work);
808 debugfs_remove_recursive(debug_dir);
809}
810module_exit(powerclamp_exit);
811
812MODULE_LICENSE("GPL");
813MODULE_AUTHOR("Arjan van de Ven <arjan@linux.intel.com>");
814MODULE_AUTHOR("Jacob Pan <jacob.jun.pan@linux.intel.com>");
815MODULE_DESCRIPTION("Package Level C-state Idle Injection for Intel CPUs");
diff --git a/drivers/thermal/intel/intel_quark_dts_thermal.c b/drivers/thermal/intel/intel_quark_dts_thermal.c
new file mode 100644
index 000000000000..5d33b350da1c
--- /dev/null
+++ b/drivers/thermal/intel/intel_quark_dts_thermal.c
@@ -0,0 +1,471 @@
1/*
2 * intel_quark_dts_thermal.c
3 *
4 * This file is provided under a dual BSD/GPLv2 license. When using or
5 * redistributing this file, you may do so under either license.
6 *
7 * GPL LICENSE SUMMARY
8 *
9 * Copyright(c) 2015 Intel Corporation.
10 *
11 * This program is free software; you can redistribute it and/or modify
12 * it under the terms of version 2 of the GNU General Public License as
13 * published by the Free Software Foundation.
14 *
15 * This program is distributed in the hope that it will be useful, but
16 * WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
18 * General Public License for more details.
19 *
20 * Contact Information:
21 * Ong Boon Leong <boon.leong.ong@intel.com>
22 * Intel Malaysia, Penang
23 *
24 * BSD LICENSE
25 *
26 * Copyright(c) 2015 Intel Corporation.
27 *
28 * Redistribution and use in source and binary forms, with or without
29 * modification, are permitted provided that the following conditions
30 * are met:
31 *
32 * * Redistributions of source code must retain the above copyright
33 * notice, this list of conditions and the following disclaimer.
34 * * Redistributions in binary form must reproduce the above copyright
35 * notice, this list of conditions and the following disclaimer in
36 * the documentation and/or other materials provided with the
37 * distribution.
38 * * Neither the name of Intel Corporation nor the names of its
39 * contributors may be used to endorse or promote products derived
40 * from this software without specific prior written permission.
41 *
42 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
43 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
44 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
45 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
46 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
47 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
48 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
49 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
50 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
51 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
52 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
53 *
54 * Quark DTS thermal driver is implemented by referencing
55 * intel_soc_dts_thermal.c.
56 */
57
58#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
59
60#include <linux/module.h>
61#include <linux/slab.h>
62#include <linux/interrupt.h>
63#include <linux/thermal.h>
64#include <asm/cpu_device_id.h>
65#include <asm/iosf_mbi.h>
66
67#define X86_FAMILY_QUARK 0x5
68#define X86_MODEL_QUARK_X1000 0x9
69
70/* DTS reset is programmed via QRK_MBI_UNIT_SOC */
71#define QRK_DTS_REG_OFFSET_RESET 0x34
72#define QRK_DTS_RESET_BIT BIT(0)
73
74/* DTS enable is programmed via QRK_MBI_UNIT_RMU */
75#define QRK_DTS_REG_OFFSET_ENABLE 0xB0
76#define QRK_DTS_ENABLE_BIT BIT(15)
77
78/* Temperature Register is read via QRK_MBI_UNIT_RMU */
79#define QRK_DTS_REG_OFFSET_TEMP 0xB1
80#define QRK_DTS_MASK_TEMP 0xFF
81#define QRK_DTS_OFFSET_TEMP 0
82#define QRK_DTS_OFFSET_REL_TEMP 16
83#define QRK_DTS_TEMP_BASE 50
84
85/* Programmable Trip Point Register is configured via QRK_MBI_UNIT_RMU */
86#define QRK_DTS_REG_OFFSET_PTPS 0xB2
87#define QRK_DTS_MASK_TP_THRES 0xFF
88#define QRK_DTS_SHIFT_TP 8
89#define QRK_DTS_ID_TP_CRITICAL 0
90#define QRK_DTS_SAFE_TP_THRES 105
91
92/* Thermal Sensor Register Lock */
93#define QRK_DTS_REG_OFFSET_LOCK 0x71
94#define QRK_DTS_LOCK_BIT BIT(5)
95
96/* Quark DTS has 2 trip points: hot & catastrophic */
97#define QRK_MAX_DTS_TRIPS 2
98/* If DTS not locked, all trip points are configurable */
99#define QRK_DTS_WR_MASK_SET 0x3
100/* If DTS locked, all trip points are not configurable */
101#define QRK_DTS_WR_MASK_CLR 0
102
103#define DEFAULT_POLL_DELAY 2000
104
105struct soc_sensor_entry {
106 bool locked;
107 u32 store_ptps;
108 u32 store_dts_enable;
109 enum thermal_device_mode mode;
110 struct thermal_zone_device *tzone;
111};
112
113static struct soc_sensor_entry *soc_dts;
114
115static int polling_delay = DEFAULT_POLL_DELAY;
116module_param(polling_delay, int, 0644);
117MODULE_PARM_DESC(polling_delay,
118 "Polling interval for checking trip points (in milliseconds)");
119
120static DEFINE_MUTEX(dts_update_mutex);
121
122static int soc_dts_enable(struct thermal_zone_device *tzd)
123{
124 u32 out;
125 struct soc_sensor_entry *aux_entry = tzd->devdata;
126 int ret;
127
128 ret = iosf_mbi_read(QRK_MBI_UNIT_RMU, MBI_REG_READ,
129 QRK_DTS_REG_OFFSET_ENABLE, &out);
130 if (ret)
131 return ret;
132
133 if (out & QRK_DTS_ENABLE_BIT) {
134 aux_entry->mode = THERMAL_DEVICE_ENABLED;
135 return 0;
136 }
137
138 if (!aux_entry->locked) {
139 out |= QRK_DTS_ENABLE_BIT;
140 ret = iosf_mbi_write(QRK_MBI_UNIT_RMU, MBI_REG_WRITE,
141 QRK_DTS_REG_OFFSET_ENABLE, out);
142 if (ret)
143 return ret;
144
145 aux_entry->mode = THERMAL_DEVICE_ENABLED;
146 } else {
147 aux_entry->mode = THERMAL_DEVICE_DISABLED;
148 pr_info("DTS is locked. Cannot enable DTS\n");
149 ret = -EPERM;
150 }
151
152 return ret;
153}
154
155static int soc_dts_disable(struct thermal_zone_device *tzd)
156{
157 u32 out;
158 struct soc_sensor_entry *aux_entry = tzd->devdata;
159 int ret;
160
161 ret = iosf_mbi_read(QRK_MBI_UNIT_RMU, MBI_REG_READ,
162 QRK_DTS_REG_OFFSET_ENABLE, &out);
163 if (ret)
164 return ret;
165
166 if (!(out & QRK_DTS_ENABLE_BIT)) {
167 aux_entry->mode = THERMAL_DEVICE_DISABLED;
168 return 0;
169 }
170
171 if (!aux_entry->locked) {
172 out &= ~QRK_DTS_ENABLE_BIT;
173 ret = iosf_mbi_write(QRK_MBI_UNIT_RMU, MBI_REG_WRITE,
174 QRK_DTS_REG_OFFSET_ENABLE, out);
175
176 if (ret)
177 return ret;
178
179 aux_entry->mode = THERMAL_DEVICE_DISABLED;
180 } else {
181 aux_entry->mode = THERMAL_DEVICE_ENABLED;
182 pr_info("DTS is locked. Cannot disable DTS\n");
183 ret = -EPERM;
184 }
185
186 return ret;
187}
188
189static int _get_trip_temp(int trip, int *temp)
190{
191 int status;
192 u32 out;
193
194 mutex_lock(&dts_update_mutex);
195 status = iosf_mbi_read(QRK_MBI_UNIT_RMU, MBI_REG_READ,
196 QRK_DTS_REG_OFFSET_PTPS, &out);
197 mutex_unlock(&dts_update_mutex);
198
199 if (status)
200 return status;
201
202 /*
203 * Thermal Sensor Programmable Trip Point Register has 8-bit
204 * fields for critical (catastrophic) and hot set trip point
205 * thresholds. The threshold value is always offset by its
206 * temperature base (50 degree Celsius).
207 */
208 *temp = (out >> (trip * QRK_DTS_SHIFT_TP)) & QRK_DTS_MASK_TP_THRES;
209 *temp -= QRK_DTS_TEMP_BASE;
210
211 return 0;
212}
213
214static inline int sys_get_trip_temp(struct thermal_zone_device *tzd,
215 int trip, int *temp)
216{
217 return _get_trip_temp(trip, temp);
218}
219
220static inline int sys_get_crit_temp(struct thermal_zone_device *tzd, int *temp)
221{
222 return _get_trip_temp(QRK_DTS_ID_TP_CRITICAL, temp);
223}
224
225static int update_trip_temp(struct soc_sensor_entry *aux_entry,
226 int trip, int temp)
227{
228 u32 out;
229 u32 temp_out;
230 u32 store_ptps;
231 int ret;
232
233 mutex_lock(&dts_update_mutex);
234 if (aux_entry->locked) {
235 ret = -EPERM;
236 goto failed;
237 }
238
239 ret = iosf_mbi_read(QRK_MBI_UNIT_RMU, MBI_REG_READ,
240 QRK_DTS_REG_OFFSET_PTPS, &store_ptps);
241 if (ret)
242 goto failed;
243
244 /*
245 * Protection against unsafe trip point thresdhold value.
246 * As Quark X1000 data-sheet does not provide any recommendation
247 * regarding the safe trip point threshold value to use, we choose
248 * the safe value according to the threshold value set by UEFI BIOS.
249 */
250 if (temp > QRK_DTS_SAFE_TP_THRES)
251 temp = QRK_DTS_SAFE_TP_THRES;
252
253 /*
254 * Thermal Sensor Programmable Trip Point Register has 8-bit
255 * fields for critical (catastrophic) and hot set trip point
256 * thresholds. The threshold value is always offset by its
257 * temperature base (50 degree Celsius).
258 */
259 temp_out = temp + QRK_DTS_TEMP_BASE;
260 out = (store_ptps & ~(QRK_DTS_MASK_TP_THRES <<
261 (trip * QRK_DTS_SHIFT_TP)));
262 out |= (temp_out & QRK_DTS_MASK_TP_THRES) <<
263 (trip * QRK_DTS_SHIFT_TP);
264
265 ret = iosf_mbi_write(QRK_MBI_UNIT_RMU, MBI_REG_WRITE,
266 QRK_DTS_REG_OFFSET_PTPS, out);
267
268failed:
269 mutex_unlock(&dts_update_mutex);
270 return ret;
271}
272
273static inline int sys_set_trip_temp(struct thermal_zone_device *tzd, int trip,
274 int temp)
275{
276 return update_trip_temp(tzd->devdata, trip, temp);
277}
278
279static int sys_get_trip_type(struct thermal_zone_device *thermal,
280 int trip, enum thermal_trip_type *type)
281{
282 if (trip)
283 *type = THERMAL_TRIP_HOT;
284 else
285 *type = THERMAL_TRIP_CRITICAL;
286
287 return 0;
288}
289
290static int sys_get_curr_temp(struct thermal_zone_device *tzd,
291 int *temp)
292{
293 u32 out;
294 int ret;
295
296 mutex_lock(&dts_update_mutex);
297 ret = iosf_mbi_read(QRK_MBI_UNIT_RMU, MBI_REG_READ,
298 QRK_DTS_REG_OFFSET_TEMP, &out);
299 mutex_unlock(&dts_update_mutex);
300
301 if (ret)
302 return ret;
303
304 /*
305 * Thermal Sensor Temperature Register has 8-bit field
306 * for temperature value (offset by temperature base
307 * 50 degree Celsius).
308 */
309 out = (out >> QRK_DTS_OFFSET_TEMP) & QRK_DTS_MASK_TEMP;
310 *temp = out - QRK_DTS_TEMP_BASE;
311
312 return 0;
313}
314
315static int sys_get_mode(struct thermal_zone_device *tzd,
316 enum thermal_device_mode *mode)
317{
318 struct soc_sensor_entry *aux_entry = tzd->devdata;
319 *mode = aux_entry->mode;
320 return 0;
321}
322
323static int sys_set_mode(struct thermal_zone_device *tzd,
324 enum thermal_device_mode mode)
325{
326 int ret;
327
328 mutex_lock(&dts_update_mutex);
329 if (mode == THERMAL_DEVICE_ENABLED)
330 ret = soc_dts_enable(tzd);
331 else
332 ret = soc_dts_disable(tzd);
333 mutex_unlock(&dts_update_mutex);
334
335 return ret;
336}
337
338static struct thermal_zone_device_ops tzone_ops = {
339 .get_temp = sys_get_curr_temp,
340 .get_trip_temp = sys_get_trip_temp,
341 .get_trip_type = sys_get_trip_type,
342 .set_trip_temp = sys_set_trip_temp,
343 .get_crit_temp = sys_get_crit_temp,
344 .get_mode = sys_get_mode,
345 .set_mode = sys_set_mode,
346};
347
348static void free_soc_dts(struct soc_sensor_entry *aux_entry)
349{
350 if (aux_entry) {
351 if (!aux_entry->locked) {
352 mutex_lock(&dts_update_mutex);
353 iosf_mbi_write(QRK_MBI_UNIT_RMU, MBI_REG_WRITE,
354 QRK_DTS_REG_OFFSET_ENABLE,
355 aux_entry->store_dts_enable);
356
357 iosf_mbi_write(QRK_MBI_UNIT_RMU, MBI_REG_WRITE,
358 QRK_DTS_REG_OFFSET_PTPS,
359 aux_entry->store_ptps);
360 mutex_unlock(&dts_update_mutex);
361 }
362 thermal_zone_device_unregister(aux_entry->tzone);
363 kfree(aux_entry);
364 }
365}
366
367static struct soc_sensor_entry *alloc_soc_dts(void)
368{
369 struct soc_sensor_entry *aux_entry;
370 int err;
371 u32 out;
372 int wr_mask;
373
374 aux_entry = kzalloc(sizeof(*aux_entry), GFP_KERNEL);
375 if (!aux_entry) {
376 err = -ENOMEM;
377 return ERR_PTR(-ENOMEM);
378 }
379
380 /* Check if DTS register is locked */
381 err = iosf_mbi_read(QRK_MBI_UNIT_RMU, MBI_REG_READ,
382 QRK_DTS_REG_OFFSET_LOCK, &out);
383 if (err)
384 goto err_ret;
385
386 if (out & QRK_DTS_LOCK_BIT) {
387 aux_entry->locked = true;
388 wr_mask = QRK_DTS_WR_MASK_CLR;
389 } else {
390 aux_entry->locked = false;
391 wr_mask = QRK_DTS_WR_MASK_SET;
392 }
393
394 /* Store DTS default state if DTS registers are not locked */
395 if (!aux_entry->locked) {
396 /* Store DTS default enable for restore on exit */
397 err = iosf_mbi_read(QRK_MBI_UNIT_RMU, MBI_REG_READ,
398 QRK_DTS_REG_OFFSET_ENABLE,
399 &aux_entry->store_dts_enable);
400 if (err)
401 goto err_ret;
402
403 /* Store DTS default PTPS register for restore on exit */
404 err = iosf_mbi_read(QRK_MBI_UNIT_RMU, MBI_REG_READ,
405 QRK_DTS_REG_OFFSET_PTPS,
406 &aux_entry->store_ptps);
407 if (err)
408 goto err_ret;
409 }
410
411 aux_entry->tzone = thermal_zone_device_register("quark_dts",
412 QRK_MAX_DTS_TRIPS,
413 wr_mask,
414 aux_entry, &tzone_ops, NULL, 0, polling_delay);
415 if (IS_ERR(aux_entry->tzone)) {
416 err = PTR_ERR(aux_entry->tzone);
417 goto err_ret;
418 }
419
420 mutex_lock(&dts_update_mutex);
421 err = soc_dts_enable(aux_entry->tzone);
422 mutex_unlock(&dts_update_mutex);
423 if (err)
424 goto err_aux_status;
425
426 return aux_entry;
427
428err_aux_status:
429 thermal_zone_device_unregister(aux_entry->tzone);
430err_ret:
431 kfree(aux_entry);
432 return ERR_PTR(err);
433}
434
435static const struct x86_cpu_id qrk_thermal_ids[] __initconst = {
436 { X86_VENDOR_INTEL, X86_FAMILY_QUARK, X86_MODEL_QUARK_X1000 },
437 {}
438};
439MODULE_DEVICE_TABLE(x86cpu, qrk_thermal_ids);
440
441static int __init intel_quark_thermal_init(void)
442{
443 int err = 0;
444
445 if (!x86_match_cpu(qrk_thermal_ids) || !iosf_mbi_available())
446 return -ENODEV;
447
448 soc_dts = alloc_soc_dts();
449 if (IS_ERR(soc_dts)) {
450 err = PTR_ERR(soc_dts);
451 goto err_free;
452 }
453
454 return 0;
455
456err_free:
457 free_soc_dts(soc_dts);
458 return err;
459}
460
461static void __exit intel_quark_thermal_exit(void)
462{
463 free_soc_dts(soc_dts);
464}
465
466module_init(intel_quark_thermal_init)
467module_exit(intel_quark_thermal_exit)
468
469MODULE_DESCRIPTION("Intel Quark DTS Thermal Driver");
470MODULE_AUTHOR("Ong Boon Leong <boon.leong.ong@intel.com>");
471MODULE_LICENSE("Dual BSD/GPL");
diff --git a/drivers/thermal/intel/intel_soc_dts_iosf.c b/drivers/thermal/intel/intel_soc_dts_iosf.c
new file mode 100644
index 000000000000..e0813dfaa278
--- /dev/null
+++ b/drivers/thermal/intel/intel_soc_dts_iosf.c
@@ -0,0 +1,478 @@
1/*
2 * intel_soc_dts_iosf.c
3 * Copyright (c) 2015, 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 <asm/iosf_mbi.h>
22#include "intel_soc_dts_iosf.h"
23
24#define SOC_DTS_OFFSET_ENABLE 0xB0
25#define SOC_DTS_OFFSET_TEMP 0xB1
26
27#define SOC_DTS_OFFSET_PTPS 0xB2
28#define SOC_DTS_OFFSET_PTTS 0xB3
29#define SOC_DTS_OFFSET_PTTSS 0xB4
30#define SOC_DTS_OFFSET_PTMC 0x80
31#define SOC_DTS_TE_AUX0 0xB5
32#define SOC_DTS_TE_AUX1 0xB6
33
34#define SOC_DTS_AUX0_ENABLE_BIT BIT(0)
35#define SOC_DTS_AUX1_ENABLE_BIT BIT(1)
36#define SOC_DTS_CPU_MODULE0_ENABLE_BIT BIT(16)
37#define SOC_DTS_CPU_MODULE1_ENABLE_BIT BIT(17)
38#define SOC_DTS_TE_SCI_ENABLE BIT(9)
39#define SOC_DTS_TE_SMI_ENABLE BIT(10)
40#define SOC_DTS_TE_MSI_ENABLE BIT(11)
41#define SOC_DTS_TE_APICA_ENABLE BIT(14)
42#define SOC_DTS_PTMC_APIC_DEASSERT_BIT BIT(4)
43
44/* DTS encoding for TJ MAX temperature */
45#define SOC_DTS_TJMAX_ENCODING 0x7F
46
47/* Only 2 out of 4 is allowed for OSPM */
48#define SOC_MAX_DTS_TRIPS 2
49
50/* Mask for two trips in status bits */
51#define SOC_DTS_TRIP_MASK 0x03
52
53/* DTS0 and DTS 1 */
54#define SOC_MAX_DTS_SENSORS 2
55
56static int get_tj_max(u32 *tj_max)
57{
58 u32 eax, edx;
59 u32 val;
60 int err;
61
62 err = rdmsr_safe(MSR_IA32_TEMPERATURE_TARGET, &eax, &edx);
63 if (err)
64 goto err_ret;
65 else {
66 val = (eax >> 16) & 0xff;
67 if (val)
68 *tj_max = val * 1000;
69 else {
70 err = -EINVAL;
71 goto err_ret;
72 }
73 }
74
75 return 0;
76err_ret:
77 *tj_max = 0;
78
79 return err;
80}
81
82static int sys_get_trip_temp(struct thermal_zone_device *tzd, int trip,
83 int *temp)
84{
85 int status;
86 u32 out;
87 struct intel_soc_dts_sensor_entry *dts;
88 struct intel_soc_dts_sensors *sensors;
89
90 dts = tzd->devdata;
91 sensors = dts->sensors;
92 mutex_lock(&sensors->dts_update_lock);
93 status = iosf_mbi_read(BT_MBI_UNIT_PMC, MBI_REG_READ,
94 SOC_DTS_OFFSET_PTPS, &out);
95 mutex_unlock(&sensors->dts_update_lock);
96 if (status)
97 return status;
98
99 out = (out >> (trip * 8)) & SOC_DTS_TJMAX_ENCODING;
100 if (!out)
101 *temp = 0;
102 else
103 *temp = sensors->tj_max - out * 1000;
104
105 return 0;
106}
107
108static int update_trip_temp(struct intel_soc_dts_sensor_entry *dts,
109 int thres_index, int temp,
110 enum thermal_trip_type trip_type)
111{
112 int status;
113 u32 temp_out;
114 u32 out;
115 u32 store_ptps;
116 u32 store_ptmc;
117 u32 store_te_out;
118 u32 te_out;
119 u32 int_enable_bit = SOC_DTS_TE_APICA_ENABLE;
120 struct intel_soc_dts_sensors *sensors = dts->sensors;
121
122 if (sensors->intr_type == INTEL_SOC_DTS_INTERRUPT_MSI)
123 int_enable_bit |= SOC_DTS_TE_MSI_ENABLE;
124
125 temp_out = (sensors->tj_max - temp) / 1000;
126
127 status = iosf_mbi_read(BT_MBI_UNIT_PMC, MBI_REG_READ,
128 SOC_DTS_OFFSET_PTPS, &store_ptps);
129 if (status)
130 return status;
131
132 out = (store_ptps & ~(0xFF << (thres_index * 8)));
133 out |= (temp_out & 0xFF) << (thres_index * 8);
134 status = iosf_mbi_write(BT_MBI_UNIT_PMC, MBI_REG_WRITE,
135 SOC_DTS_OFFSET_PTPS, out);
136 if (status)
137 return status;
138
139 pr_debug("update_trip_temp PTPS = %x\n", out);
140 status = iosf_mbi_read(BT_MBI_UNIT_PMC, MBI_REG_READ,
141 SOC_DTS_OFFSET_PTMC, &out);
142 if (status)
143 goto err_restore_ptps;
144
145 store_ptmc = out;
146
147 status = iosf_mbi_read(BT_MBI_UNIT_PMC, MBI_REG_READ,
148 SOC_DTS_TE_AUX0 + thres_index,
149 &te_out);
150 if (status)
151 goto err_restore_ptmc;
152
153 store_te_out = te_out;
154 /* Enable for CPU module 0 and module 1 */
155 out |= (SOC_DTS_CPU_MODULE0_ENABLE_BIT |
156 SOC_DTS_CPU_MODULE1_ENABLE_BIT);
157 if (temp) {
158 if (thres_index)
159 out |= SOC_DTS_AUX1_ENABLE_BIT;
160 else
161 out |= SOC_DTS_AUX0_ENABLE_BIT;
162 te_out |= int_enable_bit;
163 } else {
164 if (thres_index)
165 out &= ~SOC_DTS_AUX1_ENABLE_BIT;
166 else
167 out &= ~SOC_DTS_AUX0_ENABLE_BIT;
168 te_out &= ~int_enable_bit;
169 }
170 status = iosf_mbi_write(BT_MBI_UNIT_PMC, MBI_REG_WRITE,
171 SOC_DTS_OFFSET_PTMC, out);
172 if (status)
173 goto err_restore_te_out;
174
175 status = iosf_mbi_write(BT_MBI_UNIT_PMC, MBI_REG_WRITE,
176 SOC_DTS_TE_AUX0 + thres_index,
177 te_out);
178 if (status)
179 goto err_restore_te_out;
180
181 dts->trip_types[thres_index] = trip_type;
182
183 return 0;
184err_restore_te_out:
185 iosf_mbi_write(BT_MBI_UNIT_PMC, MBI_REG_WRITE,
186 SOC_DTS_OFFSET_PTMC, store_te_out);
187err_restore_ptmc:
188 iosf_mbi_write(BT_MBI_UNIT_PMC, MBI_REG_WRITE,
189 SOC_DTS_OFFSET_PTMC, store_ptmc);
190err_restore_ptps:
191 iosf_mbi_write(BT_MBI_UNIT_PMC, MBI_REG_WRITE,
192 SOC_DTS_OFFSET_PTPS, store_ptps);
193 /* Nothing we can do if restore fails */
194
195 return status;
196}
197
198static int sys_set_trip_temp(struct thermal_zone_device *tzd, int trip,
199 int temp)
200{
201 struct intel_soc_dts_sensor_entry *dts = tzd->devdata;
202 struct intel_soc_dts_sensors *sensors = dts->sensors;
203 int status;
204
205 if (temp > sensors->tj_max)
206 return -EINVAL;
207
208 mutex_lock(&sensors->dts_update_lock);
209 status = update_trip_temp(tzd->devdata, trip, temp,
210 dts->trip_types[trip]);
211 mutex_unlock(&sensors->dts_update_lock);
212
213 return status;
214}
215
216static int sys_get_trip_type(struct thermal_zone_device *tzd,
217 int trip, enum thermal_trip_type *type)
218{
219 struct intel_soc_dts_sensor_entry *dts;
220
221 dts = tzd->devdata;
222
223 *type = dts->trip_types[trip];
224
225 return 0;
226}
227
228static int sys_get_curr_temp(struct thermal_zone_device *tzd,
229 int *temp)
230{
231 int status;
232 u32 out;
233 struct intel_soc_dts_sensor_entry *dts;
234 struct intel_soc_dts_sensors *sensors;
235
236 dts = tzd->devdata;
237 sensors = dts->sensors;
238 status = iosf_mbi_read(BT_MBI_UNIT_PMC, MBI_REG_READ,
239 SOC_DTS_OFFSET_TEMP, &out);
240 if (status)
241 return status;
242
243 out = (out & dts->temp_mask) >> dts->temp_shift;
244 out -= SOC_DTS_TJMAX_ENCODING;
245 *temp = sensors->tj_max - out * 1000;
246
247 return 0;
248}
249
250static struct thermal_zone_device_ops tzone_ops = {
251 .get_temp = sys_get_curr_temp,
252 .get_trip_temp = sys_get_trip_temp,
253 .get_trip_type = sys_get_trip_type,
254 .set_trip_temp = sys_set_trip_temp,
255};
256
257static int soc_dts_enable(int id)
258{
259 u32 out;
260 int ret;
261
262 ret = iosf_mbi_read(BT_MBI_UNIT_PMC, MBI_REG_READ,
263 SOC_DTS_OFFSET_ENABLE, &out);
264 if (ret)
265 return ret;
266
267 if (!(out & BIT(id))) {
268 out |= BIT(id);
269 ret = iosf_mbi_write(BT_MBI_UNIT_PMC, MBI_REG_WRITE,
270 SOC_DTS_OFFSET_ENABLE, out);
271 if (ret)
272 return ret;
273 }
274
275 return ret;
276}
277
278static void remove_dts_thermal_zone(struct intel_soc_dts_sensor_entry *dts)
279{
280 if (dts) {
281 iosf_mbi_write(BT_MBI_UNIT_PMC, MBI_REG_WRITE,
282 SOC_DTS_OFFSET_ENABLE, dts->store_status);
283 thermal_zone_device_unregister(dts->tzone);
284 }
285}
286
287static int add_dts_thermal_zone(int id, struct intel_soc_dts_sensor_entry *dts,
288 bool notification_support, int trip_cnt,
289 int read_only_trip_cnt)
290{
291 char name[10];
292 int trip_count = 0;
293 int trip_mask = 0;
294 u32 store_ptps;
295 int ret;
296 int i;
297
298 /* Store status to restor on exit */
299 ret = iosf_mbi_read(BT_MBI_UNIT_PMC, MBI_REG_READ,
300 SOC_DTS_OFFSET_ENABLE, &dts->store_status);
301 if (ret)
302 goto err_ret;
303
304 dts->id = id;
305 dts->temp_mask = 0x00FF << (id * 8);
306 dts->temp_shift = id * 8;
307 if (notification_support) {
308 trip_count = min(SOC_MAX_DTS_TRIPS, trip_cnt);
309 trip_mask = BIT(trip_count - read_only_trip_cnt) - 1;
310 }
311
312 /* Check if the writable trip we provide is not used by BIOS */
313 ret = iosf_mbi_read(BT_MBI_UNIT_PMC, MBI_REG_READ,
314 SOC_DTS_OFFSET_PTPS, &store_ptps);
315 if (ret)
316 trip_mask = 0;
317 else {
318 for (i = 0; i < trip_count; ++i) {
319 if (trip_mask & BIT(i))
320 if (store_ptps & (0xff << (i * 8)))
321 trip_mask &= ~BIT(i);
322 }
323 }
324 dts->trip_mask = trip_mask;
325 dts->trip_count = trip_count;
326 snprintf(name, sizeof(name), "soc_dts%d", id);
327 dts->tzone = thermal_zone_device_register(name,
328 trip_count,
329 trip_mask,
330 dts, &tzone_ops,
331 NULL, 0, 0);
332 if (IS_ERR(dts->tzone)) {
333 ret = PTR_ERR(dts->tzone);
334 goto err_ret;
335 }
336
337 ret = soc_dts_enable(id);
338 if (ret)
339 goto err_enable;
340
341 return 0;
342err_enable:
343 thermal_zone_device_unregister(dts->tzone);
344err_ret:
345 return ret;
346}
347
348int intel_soc_dts_iosf_add_read_only_critical_trip(
349 struct intel_soc_dts_sensors *sensors, int critical_offset)
350{
351 int i, j;
352
353 for (i = 0; i < SOC_MAX_DTS_SENSORS; ++i) {
354 for (j = 0; j < sensors->soc_dts[i].trip_count; ++j) {
355 if (!(sensors->soc_dts[i].trip_mask & BIT(j))) {
356 return update_trip_temp(&sensors->soc_dts[i], j,
357 sensors->tj_max - critical_offset,
358 THERMAL_TRIP_CRITICAL);
359 }
360 }
361 }
362
363 return -EINVAL;
364}
365EXPORT_SYMBOL_GPL(intel_soc_dts_iosf_add_read_only_critical_trip);
366
367void intel_soc_dts_iosf_interrupt_handler(struct intel_soc_dts_sensors *sensors)
368{
369 u32 sticky_out;
370 int status;
371 u32 ptmc_out;
372 unsigned long flags;
373
374 spin_lock_irqsave(&sensors->intr_notify_lock, flags);
375
376 status = iosf_mbi_read(BT_MBI_UNIT_PMC, MBI_REG_READ,
377 SOC_DTS_OFFSET_PTMC, &ptmc_out);
378 ptmc_out |= SOC_DTS_PTMC_APIC_DEASSERT_BIT;
379 status = iosf_mbi_write(BT_MBI_UNIT_PMC, MBI_REG_WRITE,
380 SOC_DTS_OFFSET_PTMC, ptmc_out);
381
382 status = iosf_mbi_read(BT_MBI_UNIT_PMC, MBI_REG_READ,
383 SOC_DTS_OFFSET_PTTSS, &sticky_out);
384 pr_debug("status %d PTTSS %x\n", status, sticky_out);
385 if (sticky_out & SOC_DTS_TRIP_MASK) {
386 int i;
387 /* reset sticky bit */
388 status = iosf_mbi_write(BT_MBI_UNIT_PMC, MBI_REG_WRITE,
389 SOC_DTS_OFFSET_PTTSS, sticky_out);
390 spin_unlock_irqrestore(&sensors->intr_notify_lock, flags);
391
392 for (i = 0; i < SOC_MAX_DTS_SENSORS; ++i) {
393 pr_debug("TZD update for zone %d\n", i);
394 thermal_zone_device_update(sensors->soc_dts[i].tzone,
395 THERMAL_EVENT_UNSPECIFIED);
396 }
397 } else
398 spin_unlock_irqrestore(&sensors->intr_notify_lock, flags);
399}
400EXPORT_SYMBOL_GPL(intel_soc_dts_iosf_interrupt_handler);
401
402struct intel_soc_dts_sensors *intel_soc_dts_iosf_init(
403 enum intel_soc_dts_interrupt_type intr_type, int trip_count,
404 int read_only_trip_count)
405{
406 struct intel_soc_dts_sensors *sensors;
407 bool notification;
408 u32 tj_max;
409 int ret;
410 int i;
411
412 if (!iosf_mbi_available())
413 return ERR_PTR(-ENODEV);
414
415 if (!trip_count || read_only_trip_count > trip_count)
416 return ERR_PTR(-EINVAL);
417
418 if (get_tj_max(&tj_max))
419 return ERR_PTR(-EINVAL);
420
421 sensors = kzalloc(sizeof(*sensors), GFP_KERNEL);
422 if (!sensors)
423 return ERR_PTR(-ENOMEM);
424
425 spin_lock_init(&sensors->intr_notify_lock);
426 mutex_init(&sensors->dts_update_lock);
427 sensors->intr_type = intr_type;
428 sensors->tj_max = tj_max;
429 if (intr_type == INTEL_SOC_DTS_INTERRUPT_NONE)
430 notification = false;
431 else
432 notification = true;
433 for (i = 0; i < SOC_MAX_DTS_SENSORS; ++i) {
434 sensors->soc_dts[i].sensors = sensors;
435 ret = add_dts_thermal_zone(i, &sensors->soc_dts[i],
436 notification, trip_count,
437 read_only_trip_count);
438 if (ret)
439 goto err_free;
440 }
441
442 for (i = 0; i < SOC_MAX_DTS_SENSORS; ++i) {
443 ret = update_trip_temp(&sensors->soc_dts[i], 0, 0,
444 THERMAL_TRIP_PASSIVE);
445 if (ret)
446 goto err_remove_zone;
447
448 ret = update_trip_temp(&sensors->soc_dts[i], 1, 0,
449 THERMAL_TRIP_PASSIVE);
450 if (ret)
451 goto err_remove_zone;
452 }
453
454 return sensors;
455err_remove_zone:
456 for (i = 0; i < SOC_MAX_DTS_SENSORS; ++i)
457 remove_dts_thermal_zone(&sensors->soc_dts[i]);
458
459err_free:
460 kfree(sensors);
461 return ERR_PTR(ret);
462}
463EXPORT_SYMBOL_GPL(intel_soc_dts_iosf_init);
464
465void intel_soc_dts_iosf_exit(struct intel_soc_dts_sensors *sensors)
466{
467 int i;
468
469 for (i = 0; i < SOC_MAX_DTS_SENSORS; ++i) {
470 update_trip_temp(&sensors->soc_dts[i], 0, 0, 0);
471 update_trip_temp(&sensors->soc_dts[i], 1, 0, 0);
472 remove_dts_thermal_zone(&sensors->soc_dts[i]);
473 }
474 kfree(sensors);
475}
476EXPORT_SYMBOL_GPL(intel_soc_dts_iosf_exit);
477
478MODULE_LICENSE("GPL v2");
diff --git a/drivers/thermal/intel/intel_soc_dts_iosf.h b/drivers/thermal/intel/intel_soc_dts_iosf.h
new file mode 100644
index 000000000000..625e37bf93dc
--- /dev/null
+++ b/drivers/thermal/intel/intel_soc_dts_iosf.h
@@ -0,0 +1,62 @@
1/*
2 * intel_soc_dts_iosf.h
3 * Copyright (c) 2015, 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#ifndef _INTEL_SOC_DTS_IOSF_CORE_H
17#define _INTEL_SOC_DTS_IOSF_CORE_H
18
19#include <linux/thermal.h>
20
21/* DTS0 and DTS 1 */
22#define SOC_MAX_DTS_SENSORS 2
23
24enum intel_soc_dts_interrupt_type {
25 INTEL_SOC_DTS_INTERRUPT_NONE,
26 INTEL_SOC_DTS_INTERRUPT_APIC,
27 INTEL_SOC_DTS_INTERRUPT_MSI,
28 INTEL_SOC_DTS_INTERRUPT_SCI,
29 INTEL_SOC_DTS_INTERRUPT_SMI,
30};
31
32struct intel_soc_dts_sensors;
33
34struct intel_soc_dts_sensor_entry {
35 int id;
36 u32 temp_mask;
37 u32 temp_shift;
38 u32 store_status;
39 u32 trip_mask;
40 u32 trip_count;
41 enum thermal_trip_type trip_types[2];
42 struct thermal_zone_device *tzone;
43 struct intel_soc_dts_sensors *sensors;
44};
45
46struct intel_soc_dts_sensors {
47 u32 tj_max;
48 spinlock_t intr_notify_lock;
49 struct mutex dts_update_lock;
50 enum intel_soc_dts_interrupt_type intr_type;
51 struct intel_soc_dts_sensor_entry soc_dts[SOC_MAX_DTS_SENSORS];
52};
53
54struct intel_soc_dts_sensors *intel_soc_dts_iosf_init(
55 enum intel_soc_dts_interrupt_type intr_type, int trip_count,
56 int read_only_trip_count);
57void intel_soc_dts_iosf_exit(struct intel_soc_dts_sensors *sensors);
58void intel_soc_dts_iosf_interrupt_handler(
59 struct intel_soc_dts_sensors *sensors);
60int intel_soc_dts_iosf_add_read_only_critical_trip(
61 struct intel_soc_dts_sensors *sensors, int critical_offset);
62#endif
diff --git a/drivers/thermal/intel/intel_soc_dts_thermal.c b/drivers/thermal/intel/intel_soc_dts_thermal.c
new file mode 100644
index 000000000000..d748527d7a38
--- /dev/null
+++ b/drivers/thermal/intel/intel_soc_dts_thermal.c
@@ -0,0 +1,132 @@
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/acpi.h>
19#include <linux/module.h>
20#include <linux/interrupt.h>
21#include <asm/cpu_device_id.h>
22#include <asm/intel-family.h>
23#include "intel_soc_dts_iosf.h"
24
25#define CRITICAL_OFFSET_FROM_TJ_MAX 5000
26
27static int crit_offset = CRITICAL_OFFSET_FROM_TJ_MAX;
28module_param(crit_offset, int, 0644);
29MODULE_PARM_DESC(crit_offset,
30 "Critical Temperature offset from tj max in millidegree Celsius.");
31
32/* IRQ 86 is a fixed APIC interrupt for BYT DTS Aux threshold notifications */
33#define BYT_SOC_DTS_APIC_IRQ 86
34
35static int soc_dts_thres_gsi;
36static int soc_dts_thres_irq;
37static struct intel_soc_dts_sensors *soc_dts;
38
39static irqreturn_t soc_irq_thread_fn(int irq, void *dev_data)
40{
41 pr_debug("proc_thermal_interrupt\n");
42 intel_soc_dts_iosf_interrupt_handler(soc_dts);
43
44 return IRQ_HANDLED;
45}
46
47static const struct x86_cpu_id soc_thermal_ids[] = {
48 { X86_VENDOR_INTEL, 6, INTEL_FAM6_ATOM_SILVERMONT, 0,
49 BYT_SOC_DTS_APIC_IRQ},
50 {}
51};
52MODULE_DEVICE_TABLE(x86cpu, soc_thermal_ids);
53
54static int __init intel_soc_thermal_init(void)
55{
56 int err = 0;
57 const struct x86_cpu_id *match_cpu;
58
59 match_cpu = x86_match_cpu(soc_thermal_ids);
60 if (!match_cpu)
61 return -ENODEV;
62
63 /* Create a zone with 2 trips with marked as read only */
64 soc_dts = intel_soc_dts_iosf_init(INTEL_SOC_DTS_INTERRUPT_APIC, 2, 1);
65 if (IS_ERR(soc_dts)) {
66 err = PTR_ERR(soc_dts);
67 return err;
68 }
69
70 soc_dts_thres_gsi = (int)match_cpu->driver_data;
71 if (soc_dts_thres_gsi) {
72 /*
73 * Note the flags here MUST match the firmware defaults, rather
74 * then the request_irq flags, otherwise we get an EBUSY error.
75 */
76 soc_dts_thres_irq = acpi_register_gsi(NULL, soc_dts_thres_gsi,
77 ACPI_LEVEL_SENSITIVE,
78 ACPI_ACTIVE_LOW);
79 if (soc_dts_thres_irq < 0) {
80 pr_warn("intel_soc_dts: Could not get IRQ for GSI %d, err %d\n",
81 soc_dts_thres_gsi, soc_dts_thres_irq);
82 soc_dts_thres_irq = 0;
83 }
84 }
85
86 if (soc_dts_thres_irq) {
87 err = request_threaded_irq(soc_dts_thres_irq, NULL,
88 soc_irq_thread_fn,
89 IRQF_TRIGGER_RISING | IRQF_ONESHOT,
90 "soc_dts", soc_dts);
91 if (err) {
92 /*
93 * Do not just error out because the user space thermal
94 * daemon such as DPTF may use polling instead of being
95 * interrupt driven.
96 */
97 pr_warn("request_threaded_irq ret %d\n", err);
98 }
99 }
100
101 err = intel_soc_dts_iosf_add_read_only_critical_trip(soc_dts,
102 crit_offset);
103 if (err)
104 goto error_trips;
105
106 return 0;
107
108error_trips:
109 if (soc_dts_thres_irq) {
110 free_irq(soc_dts_thres_irq, soc_dts);
111 acpi_unregister_gsi(soc_dts_thres_gsi);
112 }
113 intel_soc_dts_iosf_exit(soc_dts);
114
115 return err;
116}
117
118static void __exit intel_soc_thermal_exit(void)
119{
120 if (soc_dts_thres_irq) {
121 free_irq(soc_dts_thres_irq, soc_dts);
122 acpi_unregister_gsi(soc_dts_thres_gsi);
123 }
124 intel_soc_dts_iosf_exit(soc_dts);
125}
126
127module_init(intel_soc_thermal_init)
128module_exit(intel_soc_thermal_exit)
129
130MODULE_DESCRIPTION("Intel SoC DTS Thermal Driver");
131MODULE_AUTHOR("Srinivas Pandruvada <srinivas.pandruvada@linux.intel.com>");
132MODULE_LICENSE("GPL v2");
diff --git a/drivers/thermal/intel/x86_pkg_temp_thermal.c b/drivers/thermal/intel/x86_pkg_temp_thermal.c
new file mode 100644
index 000000000000..1ef937d799e4
--- /dev/null
+++ b/drivers/thermal/intel/x86_pkg_temp_thermal.c
@@ -0,0 +1,558 @@
1/*
2 * x86_pkg_temp_thermal driver
3 * Copyright (c) 2013, 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 * You should have received a copy of the GNU General Public License along with
15 * this program; if not, write to the Free Software Foundation, Inc.
16 *
17 */
18#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
19
20#include <linux/module.h>
21#include <linux/init.h>
22#include <linux/err.h>
23#include <linux/param.h>
24#include <linux/device.h>
25#include <linux/platform_device.h>
26#include <linux/cpu.h>
27#include <linux/smp.h>
28#include <linux/slab.h>
29#include <linux/pm.h>
30#include <linux/thermal.h>
31#include <linux/debugfs.h>
32#include <asm/cpu_device_id.h>
33#include <asm/mce.h>
34
35/*
36* Rate control delay: Idea is to introduce denounce effect
37* This should be long enough to avoid reduce events, when
38* threshold is set to a temperature, which is constantly
39* violated, but at the short enough to take any action.
40* The action can be remove threshold or change it to next
41* interesting setting. Based on experiments, in around
42* every 5 seconds under load will give us a significant
43* temperature change.
44*/
45#define PKG_TEMP_THERMAL_NOTIFY_DELAY 5000
46static int notify_delay_ms = PKG_TEMP_THERMAL_NOTIFY_DELAY;
47module_param(notify_delay_ms, int, 0644);
48MODULE_PARM_DESC(notify_delay_ms,
49 "User space notification delay in milli seconds.");
50
51/* Number of trip points in thermal zone. Currently it can't
52* be more than 2. MSR can allow setting and getting notifications
53* for only 2 thresholds. This define enforces this, if there
54* is some wrong values returned by cpuid for number of thresholds.
55*/
56#define MAX_NUMBER_OF_TRIPS 2
57
58struct pkg_device {
59 int cpu;
60 bool work_scheduled;
61 u32 tj_max;
62 u32 msr_pkg_therm_low;
63 u32 msr_pkg_therm_high;
64 struct delayed_work work;
65 struct thermal_zone_device *tzone;
66 struct cpumask cpumask;
67};
68
69static struct thermal_zone_params pkg_temp_tz_params = {
70 .no_hwmon = true,
71};
72
73/* Keep track of how many package pointers we allocated in init() */
74static int max_packages __read_mostly;
75/* Array of package pointers */
76static struct pkg_device **packages;
77/* Serializes interrupt notification, work and hotplug */
78static DEFINE_SPINLOCK(pkg_temp_lock);
79/* Protects zone operation in the work function against hotplug removal */
80static DEFINE_MUTEX(thermal_zone_mutex);
81
82/* The dynamically assigned cpu hotplug state for module_exit() */
83static enum cpuhp_state pkg_thermal_hp_state __read_mostly;
84
85/* Debug counters to show using debugfs */
86static struct dentry *debugfs;
87static unsigned int pkg_interrupt_cnt;
88static unsigned int pkg_work_cnt;
89
90static int pkg_temp_debugfs_init(void)
91{
92 struct dentry *d;
93
94 debugfs = debugfs_create_dir("pkg_temp_thermal", NULL);
95 if (!debugfs)
96 return -ENOENT;
97
98 d = debugfs_create_u32("pkg_thres_interrupt", S_IRUGO, debugfs,
99 &pkg_interrupt_cnt);
100 if (!d)
101 goto err_out;
102
103 d = debugfs_create_u32("pkg_thres_work", S_IRUGO, debugfs,
104 &pkg_work_cnt);
105 if (!d)
106 goto err_out;
107
108 return 0;
109
110err_out:
111 debugfs_remove_recursive(debugfs);
112 return -ENOENT;
113}
114
115/*
116 * Protection:
117 *
118 * - cpu hotplug: Read serialized by cpu hotplug lock
119 * Write must hold pkg_temp_lock
120 *
121 * - Other callsites: Must hold pkg_temp_lock
122 */
123static struct pkg_device *pkg_temp_thermal_get_dev(unsigned int cpu)
124{
125 int pkgid = topology_logical_package_id(cpu);
126
127 if (pkgid >= 0 && pkgid < max_packages)
128 return packages[pkgid];
129 return NULL;
130}
131
132/*
133* tj-max is is interesting because threshold is set relative to this
134* temperature.
135*/
136static int get_tj_max(int cpu, u32 *tj_max)
137{
138 u32 eax, edx, val;
139 int err;
140
141 err = rdmsr_safe_on_cpu(cpu, MSR_IA32_TEMPERATURE_TARGET, &eax, &edx);
142 if (err)
143 return err;
144
145 val = (eax >> 16) & 0xff;
146 *tj_max = val * 1000;
147
148 return val ? 0 : -EINVAL;
149}
150
151static int sys_get_curr_temp(struct thermal_zone_device *tzd, int *temp)
152{
153 struct pkg_device *pkgdev = tzd->devdata;
154 u32 eax, edx;
155
156 rdmsr_on_cpu(pkgdev->cpu, MSR_IA32_PACKAGE_THERM_STATUS, &eax, &edx);
157 if (eax & 0x80000000) {
158 *temp = pkgdev->tj_max - ((eax >> 16) & 0x7f) * 1000;
159 pr_debug("sys_get_curr_temp %d\n", *temp);
160 return 0;
161 }
162 return -EINVAL;
163}
164
165static int sys_get_trip_temp(struct thermal_zone_device *tzd,
166 int trip, int *temp)
167{
168 struct pkg_device *pkgdev = tzd->devdata;
169 unsigned long thres_reg_value;
170 u32 mask, shift, eax, edx;
171 int ret;
172
173 if (trip >= MAX_NUMBER_OF_TRIPS)
174 return -EINVAL;
175
176 if (trip) {
177 mask = THERM_MASK_THRESHOLD1;
178 shift = THERM_SHIFT_THRESHOLD1;
179 } else {
180 mask = THERM_MASK_THRESHOLD0;
181 shift = THERM_SHIFT_THRESHOLD0;
182 }
183
184 ret = rdmsr_on_cpu(pkgdev->cpu, MSR_IA32_PACKAGE_THERM_INTERRUPT,
185 &eax, &edx);
186 if (ret < 0)
187 return ret;
188
189 thres_reg_value = (eax & mask) >> shift;
190 if (thres_reg_value)
191 *temp = pkgdev->tj_max - thres_reg_value * 1000;
192 else
193 *temp = 0;
194 pr_debug("sys_get_trip_temp %d\n", *temp);
195
196 return 0;
197}
198
199static int
200sys_set_trip_temp(struct thermal_zone_device *tzd, int trip, int temp)
201{
202 struct pkg_device *pkgdev = tzd->devdata;
203 u32 l, h, mask, shift, intr;
204 int ret;
205
206 if (trip >= MAX_NUMBER_OF_TRIPS || temp >= pkgdev->tj_max)
207 return -EINVAL;
208
209 ret = rdmsr_on_cpu(pkgdev->cpu, MSR_IA32_PACKAGE_THERM_INTERRUPT,
210 &l, &h);
211 if (ret < 0)
212 return ret;
213
214 if (trip) {
215 mask = THERM_MASK_THRESHOLD1;
216 shift = THERM_SHIFT_THRESHOLD1;
217 intr = THERM_INT_THRESHOLD1_ENABLE;
218 } else {
219 mask = THERM_MASK_THRESHOLD0;
220 shift = THERM_SHIFT_THRESHOLD0;
221 intr = THERM_INT_THRESHOLD0_ENABLE;
222 }
223 l &= ~mask;
224 /*
225 * When users space sets a trip temperature == 0, which is indication
226 * that, it is no longer interested in receiving notifications.
227 */
228 if (!temp) {
229 l &= ~intr;
230 } else {
231 l |= (pkgdev->tj_max - temp)/1000 << shift;
232 l |= intr;
233 }
234
235 return wrmsr_on_cpu(pkgdev->cpu, MSR_IA32_PACKAGE_THERM_INTERRUPT, l, h);
236}
237
238static int sys_get_trip_type(struct thermal_zone_device *thermal, int trip,
239 enum thermal_trip_type *type)
240{
241 *type = THERMAL_TRIP_PASSIVE;
242 return 0;
243}
244
245/* Thermal zone callback registry */
246static struct thermal_zone_device_ops tzone_ops = {
247 .get_temp = sys_get_curr_temp,
248 .get_trip_temp = sys_get_trip_temp,
249 .get_trip_type = sys_get_trip_type,
250 .set_trip_temp = sys_set_trip_temp,
251};
252
253static bool pkg_thermal_rate_control(void)
254{
255 return true;
256}
257
258/* Enable threshold interrupt on local package/cpu */
259static inline void enable_pkg_thres_interrupt(void)
260{
261 u8 thres_0, thres_1;
262 u32 l, h;
263
264 rdmsr(MSR_IA32_PACKAGE_THERM_INTERRUPT, l, h);
265 /* only enable/disable if it had valid threshold value */
266 thres_0 = (l & THERM_MASK_THRESHOLD0) >> THERM_SHIFT_THRESHOLD0;
267 thres_1 = (l & THERM_MASK_THRESHOLD1) >> THERM_SHIFT_THRESHOLD1;
268 if (thres_0)
269 l |= THERM_INT_THRESHOLD0_ENABLE;
270 if (thres_1)
271 l |= THERM_INT_THRESHOLD1_ENABLE;
272 wrmsr(MSR_IA32_PACKAGE_THERM_INTERRUPT, l, h);
273}
274
275/* Disable threshold interrupt on local package/cpu */
276static inline void disable_pkg_thres_interrupt(void)
277{
278 u32 l, h;
279
280 rdmsr(MSR_IA32_PACKAGE_THERM_INTERRUPT, l, h);
281
282 l &= ~(THERM_INT_THRESHOLD0_ENABLE | THERM_INT_THRESHOLD1_ENABLE);
283 wrmsr(MSR_IA32_PACKAGE_THERM_INTERRUPT, l, h);
284}
285
286static void pkg_temp_thermal_threshold_work_fn(struct work_struct *work)
287{
288 struct thermal_zone_device *tzone = NULL;
289 int cpu = smp_processor_id();
290 struct pkg_device *pkgdev;
291 u64 msr_val, wr_val;
292
293 mutex_lock(&thermal_zone_mutex);
294 spin_lock_irq(&pkg_temp_lock);
295 ++pkg_work_cnt;
296
297 pkgdev = pkg_temp_thermal_get_dev(cpu);
298 if (!pkgdev) {
299 spin_unlock_irq(&pkg_temp_lock);
300 mutex_unlock(&thermal_zone_mutex);
301 return;
302 }
303 pkgdev->work_scheduled = false;
304
305 rdmsrl(MSR_IA32_PACKAGE_THERM_STATUS, msr_val);
306 wr_val = msr_val & ~(THERM_LOG_THRESHOLD0 | THERM_LOG_THRESHOLD1);
307 if (wr_val != msr_val) {
308 wrmsrl(MSR_IA32_PACKAGE_THERM_STATUS, wr_val);
309 tzone = pkgdev->tzone;
310 }
311
312 enable_pkg_thres_interrupt();
313 spin_unlock_irq(&pkg_temp_lock);
314
315 /*
316 * If tzone is not NULL, then thermal_zone_mutex will prevent the
317 * concurrent removal in the cpu offline callback.
318 */
319 if (tzone)
320 thermal_zone_device_update(tzone, THERMAL_EVENT_UNSPECIFIED);
321
322 mutex_unlock(&thermal_zone_mutex);
323}
324
325static void pkg_thermal_schedule_work(int cpu, struct delayed_work *work)
326{
327 unsigned long ms = msecs_to_jiffies(notify_delay_ms);
328
329 schedule_delayed_work_on(cpu, work, ms);
330}
331
332static int pkg_thermal_notify(u64 msr_val)
333{
334 int cpu = smp_processor_id();
335 struct pkg_device *pkgdev;
336 unsigned long flags;
337
338 spin_lock_irqsave(&pkg_temp_lock, flags);
339 ++pkg_interrupt_cnt;
340
341 disable_pkg_thres_interrupt();
342
343 /* Work is per package, so scheduling it once is enough. */
344 pkgdev = pkg_temp_thermal_get_dev(cpu);
345 if (pkgdev && !pkgdev->work_scheduled) {
346 pkgdev->work_scheduled = true;
347 pkg_thermal_schedule_work(pkgdev->cpu, &pkgdev->work);
348 }
349
350 spin_unlock_irqrestore(&pkg_temp_lock, flags);
351 return 0;
352}
353
354static int pkg_temp_thermal_device_add(unsigned int cpu)
355{
356 int pkgid = topology_logical_package_id(cpu);
357 u32 tj_max, eax, ebx, ecx, edx;
358 struct pkg_device *pkgdev;
359 int thres_count, err;
360
361 if (pkgid >= max_packages)
362 return -ENOMEM;
363
364 cpuid(6, &eax, &ebx, &ecx, &edx);
365 thres_count = ebx & 0x07;
366 if (!thres_count)
367 return -ENODEV;
368
369 thres_count = clamp_val(thres_count, 0, MAX_NUMBER_OF_TRIPS);
370
371 err = get_tj_max(cpu, &tj_max);
372 if (err)
373 return err;
374
375 pkgdev = kzalloc(sizeof(*pkgdev), GFP_KERNEL);
376 if (!pkgdev)
377 return -ENOMEM;
378
379 INIT_DELAYED_WORK(&pkgdev->work, pkg_temp_thermal_threshold_work_fn);
380 pkgdev->cpu = cpu;
381 pkgdev->tj_max = tj_max;
382 pkgdev->tzone = thermal_zone_device_register("x86_pkg_temp",
383 thres_count,
384 (thres_count == MAX_NUMBER_OF_TRIPS) ? 0x03 : 0x01,
385 pkgdev, &tzone_ops, &pkg_temp_tz_params, 0, 0);
386 if (IS_ERR(pkgdev->tzone)) {
387 err = PTR_ERR(pkgdev->tzone);
388 kfree(pkgdev);
389 return err;
390 }
391 /* Store MSR value for package thermal interrupt, to restore at exit */
392 rdmsr(MSR_IA32_PACKAGE_THERM_INTERRUPT, pkgdev->msr_pkg_therm_low,
393 pkgdev->msr_pkg_therm_high);
394
395 cpumask_set_cpu(cpu, &pkgdev->cpumask);
396 spin_lock_irq(&pkg_temp_lock);
397 packages[pkgid] = pkgdev;
398 spin_unlock_irq(&pkg_temp_lock);
399 return 0;
400}
401
402static int pkg_thermal_cpu_offline(unsigned int cpu)
403{
404 struct pkg_device *pkgdev = pkg_temp_thermal_get_dev(cpu);
405 bool lastcpu, was_target;
406 int target;
407
408 if (!pkgdev)
409 return 0;
410
411 target = cpumask_any_but(&pkgdev->cpumask, cpu);
412 cpumask_clear_cpu(cpu, &pkgdev->cpumask);
413 lastcpu = target >= nr_cpu_ids;
414 /*
415 * Remove the sysfs files, if this is the last cpu in the package
416 * before doing further cleanups.
417 */
418 if (lastcpu) {
419 struct thermal_zone_device *tzone = pkgdev->tzone;
420
421 /*
422 * We must protect against a work function calling
423 * thermal_zone_update, after/while unregister. We null out
424 * the pointer under the zone mutex, so the worker function
425 * won't try to call.
426 */
427 mutex_lock(&thermal_zone_mutex);
428 pkgdev->tzone = NULL;
429 mutex_unlock(&thermal_zone_mutex);
430
431 thermal_zone_device_unregister(tzone);
432 }
433
434 /* Protect against work and interrupts */
435 spin_lock_irq(&pkg_temp_lock);
436
437 /*
438 * Check whether this cpu was the current target and store the new
439 * one. When we drop the lock, then the interrupt notify function
440 * will see the new target.
441 */
442 was_target = pkgdev->cpu == cpu;
443 pkgdev->cpu = target;
444
445 /*
446 * If this is the last CPU in the package remove the package
447 * reference from the array and restore the interrupt MSR. When we
448 * drop the lock neither the interrupt notify function nor the
449 * worker will see the package anymore.
450 */
451 if (lastcpu) {
452 packages[topology_logical_package_id(cpu)] = NULL;
453 /* After this point nothing touches the MSR anymore. */
454 wrmsr(MSR_IA32_PACKAGE_THERM_INTERRUPT,
455 pkgdev->msr_pkg_therm_low, pkgdev->msr_pkg_therm_high);
456 }
457
458 /*
459 * Check whether there is work scheduled and whether the work is
460 * targeted at the outgoing CPU.
461 */
462 if (pkgdev->work_scheduled && was_target) {
463 /*
464 * To cancel the work we need to drop the lock, otherwise
465 * we might deadlock if the work needs to be flushed.
466 */
467 spin_unlock_irq(&pkg_temp_lock);
468 cancel_delayed_work_sync(&pkgdev->work);
469 spin_lock_irq(&pkg_temp_lock);
470 /*
471 * If this is not the last cpu in the package and the work
472 * did not run after we dropped the lock above, then we
473 * need to reschedule the work, otherwise the interrupt
474 * stays disabled forever.
475 */
476 if (!lastcpu && pkgdev->work_scheduled)
477 pkg_thermal_schedule_work(target, &pkgdev->work);
478 }
479
480 spin_unlock_irq(&pkg_temp_lock);
481
482 /* Final cleanup if this is the last cpu */
483 if (lastcpu)
484 kfree(pkgdev);
485 return 0;
486}
487
488static int pkg_thermal_cpu_online(unsigned int cpu)
489{
490 struct pkg_device *pkgdev = pkg_temp_thermal_get_dev(cpu);
491 struct cpuinfo_x86 *c = &cpu_data(cpu);
492
493 /* Paranoia check */
494 if (!cpu_has(c, X86_FEATURE_DTHERM) || !cpu_has(c, X86_FEATURE_PTS))
495 return -ENODEV;
496
497 /* If the package exists, nothing to do */
498 if (pkgdev) {
499 cpumask_set_cpu(cpu, &pkgdev->cpumask);
500 return 0;
501 }
502 return pkg_temp_thermal_device_add(cpu);
503}
504
505static const struct x86_cpu_id __initconst pkg_temp_thermal_ids[] = {
506 { X86_VENDOR_INTEL, X86_FAMILY_ANY, X86_MODEL_ANY, X86_FEATURE_PTS },
507 {}
508};
509MODULE_DEVICE_TABLE(x86cpu, pkg_temp_thermal_ids);
510
511static int __init pkg_temp_thermal_init(void)
512{
513 int ret;
514
515 if (!x86_match_cpu(pkg_temp_thermal_ids))
516 return -ENODEV;
517
518 max_packages = topology_max_packages();
519 packages = kcalloc(max_packages, sizeof(struct pkg_device *),
520 GFP_KERNEL);
521 if (!packages)
522 return -ENOMEM;
523
524 ret = cpuhp_setup_state(CPUHP_AP_ONLINE_DYN, "thermal/x86_pkg:online",
525 pkg_thermal_cpu_online, pkg_thermal_cpu_offline);
526 if (ret < 0)
527 goto err;
528
529 /* Store the state for module exit */
530 pkg_thermal_hp_state = ret;
531
532 platform_thermal_package_notify = pkg_thermal_notify;
533 platform_thermal_package_rate_control = pkg_thermal_rate_control;
534
535 /* Don't care if it fails */
536 pkg_temp_debugfs_init();
537 return 0;
538
539err:
540 kfree(packages);
541 return ret;
542}
543module_init(pkg_temp_thermal_init)
544
545static void __exit pkg_temp_thermal_exit(void)
546{
547 platform_thermal_package_notify = NULL;
548 platform_thermal_package_rate_control = NULL;
549
550 cpuhp_remove_state(pkg_thermal_hp_state);
551 debugfs_remove_recursive(debugfs);
552 kfree(packages);
553}
554module_exit(pkg_temp_thermal_exit)
555
556MODULE_DESCRIPTION("X86 PKG TEMP Thermal Driver");
557MODULE_AUTHOR("Srinivas Pandruvada <srinivas.pandruvada@linux.intel.com>");
558MODULE_LICENSE("GPL v2");