aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJacob Pan <jacob.jun.pan@linux.intel.com>2014-09-03 03:14:23 -0400
committerZhang Rui <rui.zhang@intel.com>2014-10-10 21:35:48 -0400
commit52b1c69d7e3cd8bdba0e55bde24093f0779bb29d (patch)
tree6b1a0e0da03403896e2b014226b62e8331ff6c9c
parent4384b8fe162d8aa03905d02073707bcf364cc7ce (diff)
Thermal: int340x_thermal: expose acpi thermal relationship tables
ACPI 4.0 introduced two thermal relationship tables via _ART (active cooling) and _TRT (passive cooling) objects. These tables contain many to many relationships among thermal sensors and cooling devices. This patch parses _ART and _TRT and makes the result available to the userspace via an misc device interface. At the same time, kernel drivers can also request parsing results from internal kernel APIs. The results include source and target devices, influence, and sampling rate in case of _TRT. For _ART, the result shows source device, target device, and weight percentage. Signed-off-by: Jacob Pan <jacob.jun.pan@linux.intel.com> Signed-off-by: Zhang Rui <rui.zhang@intel.com>
-rw-r--r--drivers/thermal/Kconfig5
-rw-r--r--drivers/thermal/int340x_thermal/Makefile1
-rw-r--r--drivers/thermal/int340x_thermal/acpi_thermal_rel.c400
-rw-r--r--drivers/thermal/int340x_thermal/acpi_thermal_rel.h84
4 files changed, 490 insertions, 0 deletions
diff --git a/drivers/thermal/Kconfig b/drivers/thermal/Kconfig
index 6f93e5c4e1f2..3a8929222cab 100644
--- a/drivers/thermal/Kconfig
+++ b/drivers/thermal/Kconfig
@@ -223,6 +223,7 @@ config INT340X_THERMAL
223 tristate "ACPI INT340X thermal drivers" 223 tristate "ACPI INT340X thermal drivers"
224 depends on X86 && ACPI 224 depends on X86 && ACPI
225 select THERMAL_GOV_USER_SPACE 225 select THERMAL_GOV_USER_SPACE
226 select ACPI_THERMAL_REL
226 help 227 help
227 Newer laptops and tablets that use ACPI may have thermal sensors and 228 Newer laptops and tablets that use ACPI may have thermal sensors and
228 other devices with thermal control capabilities outside the core 229 other devices with thermal control capabilities outside the core
@@ -237,6 +238,10 @@ config INT340X_THERMAL
237 information to allow the user to select his laptop to run without 238 information to allow the user to select his laptop to run without
238 turning on the fans. 239 turning on the fans.
239 240
241config ACPI_THERMAL_REL
242 tristate
243 depends on ACPI
244
240menu "Texas Instruments thermal drivers" 245menu "Texas Instruments thermal drivers"
241source "drivers/thermal/ti-soc-thermal/Kconfig" 246source "drivers/thermal/ti-soc-thermal/Kconfig"
242endmenu 247endmenu
diff --git a/drivers/thermal/int340x_thermal/Makefile b/drivers/thermal/int340x_thermal/Makefile
index c4a5c50d7ee4..ffe40bffaf1a 100644
--- a/drivers/thermal/int340x_thermal/Makefile
+++ b/drivers/thermal/int340x_thermal/Makefile
@@ -1,3 +1,4 @@
1obj-$(CONFIG_INT340X_THERMAL) += int3400_thermal.o 1obj-$(CONFIG_INT340X_THERMAL) += int3400_thermal.o
2obj-$(CONFIG_INT340X_THERMAL) += int3402_thermal.o 2obj-$(CONFIG_INT340X_THERMAL) += int3402_thermal.o
3obj-$(CONFIG_INT340X_THERMAL) += int3403_thermal.o 3obj-$(CONFIG_INT340X_THERMAL) += int3403_thermal.o
4obj-$(CONFIG_ACPI_THERMAL_REL) += acpi_thermal_rel.o
diff --git a/drivers/thermal/int340x_thermal/acpi_thermal_rel.c b/drivers/thermal/int340x_thermal/acpi_thermal_rel.c
new file mode 100644
index 000000000000..0d8db808f0ae
--- /dev/null
+++ b/drivers/thermal/int340x_thermal/acpi_thermal_rel.c
@@ -0,0 +1,400 @@
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 * @art_count: the number of valid entries resulted from parsing _TRT
66 * @artp: pointer to pointer of array of art 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 0;
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 = kzalloc(*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 acpi_create_platform_device(adev);
124 else
125 pr_warn("Failed to get source ACPI device\n");
126
127 result = acpi_bus_get_device(trt->target, &adev);
128 if (!result)
129 acpi_create_platform_device(adev);
130 else
131 pr_warn("Failed to get target ACPI device\n");
132 }
133
134 *trtp = trts;
135 /* don't count bad entries */
136 *trt_count -= nr_bad_entries;
137end:
138 kfree(buffer.pointer);
139 return result;
140}
141EXPORT_SYMBOL(acpi_parse_trt);
142
143/**
144 * acpi_parse_art - Parse Active Relationship Table _ART
145 *
146 * @handle: ACPI handle of the device contains _ART
147 * @art_count: the number of valid entries resulted from parsing _ART
148 * @artp: pointer to pointer of array of art entries in parsing result
149 * @create_dev: whether to create platform devices for target and source
150 *
151 */
152int acpi_parse_art(acpi_handle handle, int *art_count, struct art **artp,
153 bool create_dev)
154{
155 acpi_status status;
156 int result = 0;
157 int i;
158 int nr_bad_entries = 0;
159 struct art *arts;
160 struct acpi_device *adev;
161 union acpi_object *p;
162 struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL };
163 struct acpi_buffer element = { 0, NULL };
164 struct acpi_buffer art_format = {
165 sizeof("RRNNNNNNNNNNN"), "RRNNNNNNNNNNN" };
166
167 if (!acpi_has_method(handle, "_ART"))
168 return 0;
169
170 status = acpi_evaluate_object(handle, "_ART", NULL, &buffer);
171 if (ACPI_FAILURE(status))
172 return -ENODEV;
173
174 p = buffer.pointer;
175 if (!p || (p->type != ACPI_TYPE_PACKAGE)) {
176 pr_err("Invalid _ART data\n");
177 result = -EFAULT;
178 goto end;
179 }
180
181 /* ignore p->package.elements[0], as this is _ART Revision field */
182 *art_count = p->package.count - 1;
183 arts = kzalloc(*art_count * sizeof(struct art), GFP_KERNEL);
184 if (!arts) {
185 result = -ENOMEM;
186 goto end;
187 }
188
189 for (i = 0; i < *art_count; i++) {
190 struct art *art = &arts[i - nr_bad_entries];
191
192 element.length = sizeof(struct art);
193 element.pointer = art;
194
195 status = acpi_extract_package(&(p->package.elements[i + 1]),
196 &art_format, &element);
197 if (ACPI_FAILURE(status)) {
198 pr_warn("_ART package %d is invalid, ignored", i);
199 nr_bad_entries++;
200 continue;
201 }
202 if (!create_dev)
203 continue;
204
205 if (art->source) {
206 result = acpi_bus_get_device(art->source, &adev);
207 if (!result)
208 acpi_create_platform_device(adev);
209 else
210 pr_warn("Failed to get source ACPI device\n");
211 }
212 if (art->target) {
213 result = acpi_bus_get_device(art->target, &adev);
214 if (!result)
215 acpi_create_platform_device(adev);
216 else
217 pr_warn("Failed to get source ACPI device\n");
218 }
219 }
220
221 *artp = arts;
222 /* don't count bad entries */
223 *art_count -= nr_bad_entries;
224end:
225 kfree(buffer.pointer);
226 return result;
227}
228EXPORT_SYMBOL(acpi_parse_art);
229
230
231/* get device name from acpi handle */
232static void get_single_name(acpi_handle handle, char *name)
233{
234 struct acpi_buffer buffer = {ACPI_ALLOCATE_BUFFER};
235
236 if (ACPI_FAILURE(acpi_get_name(handle, ACPI_SINGLE_NAME, &buffer)))
237 pr_warn("Failed get name from handle\n");
238 else {
239 memcpy(name, buffer.pointer, ACPI_NAME_SIZE);
240 kfree(buffer.pointer);
241 }
242}
243
244static int fill_art(char __user *ubuf)
245{
246 int i;
247 int ret;
248 int count;
249 int art_len;
250 struct art *arts = NULL;
251 union art_object *art_user;
252
253 ret = acpi_parse_art(acpi_thermal_rel_handle, &count, &arts, false);
254 if (ret)
255 goto free_art;
256 art_len = count * sizeof(union art_object);
257 art_user = kzalloc(art_len, GFP_KERNEL);
258 if (!art_user) {
259 ret = -ENOMEM;
260 goto free_art;
261 }
262 /* now fill in user art data */
263 for (i = 0; i < count; i++) {
264 /* userspace art needs device name instead of acpi reference */
265 get_single_name(arts[i].source, art_user[i].source_device);
266 get_single_name(arts[i].target, art_user[i].target_device);
267 /* copy the rest int data in addition to source and target */
268 memcpy(&art_user[i].weight, &arts[i].weight,
269 sizeof(u64) * (ACPI_NR_ART_ELEMENTS - 2));
270 }
271
272 if (copy_to_user(ubuf, art_user, art_len))
273 ret = -EFAULT;
274 kfree(art_user);
275free_art:
276 kfree(arts);
277 return ret;
278}
279
280static int fill_trt(char __user *ubuf)
281{
282 int i;
283 int ret;
284 int count;
285 int trt_len;
286 struct trt *trts = NULL;
287 union trt_object *trt_user;
288
289 ret = acpi_parse_trt(acpi_thermal_rel_handle, &count, &trts, false);
290 if (ret)
291 goto free_trt;
292 trt_len = count * sizeof(union trt_object);
293 trt_user = kzalloc(trt_len, GFP_KERNEL);
294 if (!trt_user) {
295 ret = -ENOMEM;
296 goto free_trt;
297 }
298 /* now fill in user trt data */
299 for (i = 0; i < count; i++) {
300 /* userspace trt needs device name instead of acpi reference */
301 get_single_name(trts[i].source, trt_user[i].source_device);
302 get_single_name(trts[i].target, trt_user[i].target_device);
303 trt_user[i].sample_period = trts[i].sample_period;
304 trt_user[i].influence = trts[i].influence;
305 }
306
307 if (copy_to_user(ubuf, trt_user, trt_len))
308 ret = -EFAULT;
309 kfree(trt_user);
310free_trt:
311 kfree(trts);
312 return ret;
313}
314
315static long acpi_thermal_rel_ioctl(struct file *f, unsigned int cmd,
316 unsigned long __arg)
317{
318 int ret = 0;
319 unsigned long length = 0;
320 unsigned long count = 0;
321 char __user *arg = (void __user *)__arg;
322 struct trt *trts;
323 struct art *arts;
324
325 switch (cmd) {
326 case ACPI_THERMAL_GET_TRT_COUNT:
327 ret = acpi_parse_trt(acpi_thermal_rel_handle, (int *)&count,
328 &trts, false);
329 kfree(trts);
330 if (!ret)
331 return put_user(count, (unsigned long __user *)__arg);
332 return ret;
333 case ACPI_THERMAL_GET_TRT_LEN:
334 ret = acpi_parse_trt(acpi_thermal_rel_handle, (int *)&count,
335 &trts, false);
336 kfree(trts);
337 length = count * sizeof(union trt_object);
338 if (!ret)
339 return put_user(length, (unsigned long __user *)__arg);
340 return ret;
341 case ACPI_THERMAL_GET_TRT:
342 return fill_trt(arg);
343 case ACPI_THERMAL_GET_ART_COUNT:
344 ret = acpi_parse_art(acpi_thermal_rel_handle, (int *)&count,
345 &arts, false);
346 kfree(arts);
347 if (!ret)
348 return put_user(count, (unsigned long __user *)__arg);
349 return ret;
350 case ACPI_THERMAL_GET_ART_LEN:
351 ret = acpi_parse_art(acpi_thermal_rel_handle, (int *)&count,
352 &arts, false);
353 kfree(arts);
354 length = count * sizeof(union art_object);
355 if (!ret)
356 return put_user(length, (unsigned long __user *)__arg);
357 return ret;
358
359 case ACPI_THERMAL_GET_ART:
360 return fill_art(arg);
361
362 default:
363 return -ENOTTY;
364 }
365}
366
367static const struct file_operations acpi_thermal_rel_fops = {
368 .owner = THIS_MODULE,
369 .open = acpi_thermal_rel_open,
370 .release = acpi_thermal_rel_release,
371 .unlocked_ioctl = acpi_thermal_rel_ioctl,
372 .llseek = no_llseek,
373};
374
375static struct miscdevice acpi_thermal_rel_misc_device = {
376 .minor = MISC_DYNAMIC_MINOR,
377 "acpi_thermal_rel",
378 &acpi_thermal_rel_fops
379};
380
381int acpi_thermal_rel_misc_device_add(acpi_handle handle)
382{
383 acpi_thermal_rel_handle = handle;
384
385 return misc_register(&acpi_thermal_rel_misc_device);
386}
387EXPORT_SYMBOL(acpi_thermal_rel_misc_device_add);
388
389int acpi_thermal_rel_misc_device_remove(acpi_handle handle)
390{
391 misc_deregister(&acpi_thermal_rel_misc_device);
392
393 return 0;
394}
395EXPORT_SYMBOL(acpi_thermal_rel_misc_device_remove);
396
397MODULE_AUTHOR("Zhang Rui <rui.zhang@intel.com>");
398MODULE_AUTHOR("Jacob Pan <jacob.jun.pan@intel.com");
399MODULE_DESCRIPTION("Intel acpi thermal rel misc dev driver");
400MODULE_LICENSE("GPL v2");
diff --git a/drivers/thermal/int340x_thermal/acpi_thermal_rel.h b/drivers/thermal/int340x_thermal/acpi_thermal_rel.h
new file mode 100644
index 000000000000..f00700bc9d79
--- /dev/null
+++ b/drivers/thermal/int340x_thermal/acpi_thermal_rel.h
@@ -0,0 +1,84 @@
1#ifndef __ACPI_ACPI_THERMAL_H
2#define __ACPI_ACPI_THERMAL_H
3
4#include <asm/ioctl.h>
5
6#define ACPI_THERMAL_MAGIC 's'
7
8#define ACPI_THERMAL_GET_TRT_LEN _IOR(ACPI_THERMAL_MAGIC, 1, unsigned long)
9#define ACPI_THERMAL_GET_ART_LEN _IOR(ACPI_THERMAL_MAGIC, 2, unsigned long)
10#define ACPI_THERMAL_GET_TRT_COUNT _IOR(ACPI_THERMAL_MAGIC, 3, unsigned long)
11#define ACPI_THERMAL_GET_ART_COUNT _IOR(ACPI_THERMAL_MAGIC, 4, unsigned long)
12
13#define ACPI_THERMAL_GET_TRT _IOR(ACPI_THERMAL_MAGIC, 5, unsigned long)
14#define ACPI_THERMAL_GET_ART _IOR(ACPI_THERMAL_MAGIC, 6, unsigned long)
15
16struct art {
17 acpi_handle source;
18 acpi_handle target;
19 u64 weight;
20 u64 ac0_max;
21 u64 ac1_max;
22 u64 ac2_max;
23 u64 ac3_max;
24 u64 ac4_max;
25 u64 ac5_max;
26 u64 ac6_max;
27 u64 ac7_max;
28 u64 ac8_max;
29 u64 ac9_max;
30} __packed;
31
32struct trt {
33 acpi_handle source;
34 acpi_handle target;
35 u64 influence;
36 u64 sample_period;
37 u64 reverved1;
38 u64 reverved2;
39 u64 reverved3;
40 u64 reverved4;
41} __packed;
42
43#define ACPI_NR_ART_ELEMENTS 13
44/* for usrspace */
45union art_object {
46 struct {
47 char source_device[8]; /* ACPI single name */
48 char target_device[8]; /* ACPI single name */
49 u64 weight;
50 u64 ac0_max_level;
51 u64 ac1_max_level;
52 u64 ac2_max_level;
53 u64 ac3_max_level;
54 u64 ac4_max_level;
55 u64 ac5_max_level;
56 u64 ac6_max_level;
57 u64 ac7_max_level;
58 u64 ac8_max_level;
59 u64 ac9_max_level;
60 };
61 u64 __data[ACPI_NR_ART_ELEMENTS];
62};
63
64union trt_object {
65 struct {
66 char source_device[8]; /* ACPI single name */
67 char target_device[8]; /* ACPI single name */
68 u64 influence;
69 u64 sample_period;
70 u64 reserved[4];
71 };
72 u64 __data[8];
73};
74
75#ifdef __KERNEL__
76int acpi_thermal_rel_misc_device_add(acpi_handle handle);
77int acpi_thermal_rel_misc_device_remove(acpi_handle handle);
78int acpi_parse_art(acpi_handle handle, int *art_count, struct art **arts,
79 bool create_dev);
80int acpi_parse_trt(acpi_handle handle, int *trt_count, struct trt **trts,
81 bool create_dev);
82#endif
83
84#endif /* __ACPI_ACPI_THERMAL_H */