1 // SPDX-License-Identifier: GPL-2.0-only
2 /* acpi_thermal_rel.c driver for exporting ACPI thermal relationship
4 * Copyright (c) 2014 Intel Corp
8 * Two functionalities included:
9 * 1. Export _TRT, _ART, via misc device interface to the userspace.
10 * 2. Provide parsing result to kernel drivers
13 #include <linux/init.h>
14 #include <linux/export.h>
15 #include <linux/module.h>
16 #include <linux/device.h>
17 #include <linux/platform_device.h>
19 #include <linux/acpi.h>
20 #include <linux/uaccess.h>
21 #include <linux/miscdevice.h>
23 #include "acpi_thermal_rel.h"
25 static acpi_handle acpi_thermal_rel_handle
;
26 static DEFINE_SPINLOCK(acpi_thermal_rel_chrdev_lock
);
27 static int acpi_thermal_rel_chrdev_count
; /* #times opened */
28 static int acpi_thermal_rel_chrdev_exclu
; /* already open exclusive? */
30 static int acpi_thermal_rel_open(struct inode
*inode
, struct file
*file
)
32 spin_lock(&acpi_thermal_rel_chrdev_lock
);
33 if (acpi_thermal_rel_chrdev_exclu
||
34 (acpi_thermal_rel_chrdev_count
&& (file
->f_flags
& O_EXCL
))) {
35 spin_unlock(&acpi_thermal_rel_chrdev_lock
);
39 if (file
->f_flags
& O_EXCL
)
40 acpi_thermal_rel_chrdev_exclu
= 1;
41 acpi_thermal_rel_chrdev_count
++;
43 spin_unlock(&acpi_thermal_rel_chrdev_lock
);
45 return nonseekable_open(inode
, file
);
48 static int acpi_thermal_rel_release(struct inode
*inode
, struct file
*file
)
50 spin_lock(&acpi_thermal_rel_chrdev_lock
);
51 acpi_thermal_rel_chrdev_count
--;
52 acpi_thermal_rel_chrdev_exclu
= 0;
53 spin_unlock(&acpi_thermal_rel_chrdev_lock
);
59 * acpi_parse_trt - Thermal Relationship Table _TRT for passive cooling
61 * @handle: ACPI handle of the device contains _TRT
62 * @trt_count: the number of valid entries resulted from parsing _TRT
63 * @trtp: pointer to pointer of array of _TRT entries in parsing result
64 * @create_dev: whether to create platform devices for target and source
67 int acpi_parse_trt(acpi_handle handle
, int *trt_count
, struct trt
**trtp
,
73 int nr_bad_entries
= 0;
76 struct acpi_buffer buffer
= { ACPI_ALLOCATE_BUFFER
, NULL
};
77 struct acpi_buffer element
= { 0, NULL
};
78 struct acpi_buffer trt_format
= { sizeof("RRNNNNNN"), "RRNNNNNN" };
80 status
= acpi_evaluate_object(handle
, "_TRT", NULL
, &buffer
);
81 if (ACPI_FAILURE(status
))
85 if (!p
|| (p
->type
!= ACPI_TYPE_PACKAGE
)) {
86 pr_err("Invalid _TRT data\n");
91 *trt_count
= p
->package
.count
;
92 trts
= kcalloc(*trt_count
, sizeof(struct trt
), GFP_KERNEL
);
98 for (i
= 0; i
< *trt_count
; i
++) {
99 struct trt
*trt
= &trts
[i
- nr_bad_entries
];
101 element
.length
= sizeof(struct trt
);
102 element
.pointer
= trt
;
104 status
= acpi_extract_package(&(p
->package
.elements
[i
]),
105 &trt_format
, &element
);
106 if (ACPI_FAILURE(status
)) {
108 pr_warn("_TRT package %d is invalid, ignored\n", i
);
114 if (!acpi_fetch_acpi_dev(trt
->source
))
115 pr_warn("Failed to get source ACPI device\n");
117 if (!acpi_fetch_acpi_dev(trt
->target
))
118 pr_warn("Failed to get target ACPI device\n");
124 /* don't count bad entries */
125 *trt_count
-= nr_bad_entries
;
127 kfree(buffer
.pointer
);
130 EXPORT_SYMBOL(acpi_parse_trt
);
133 * acpi_parse_art - Parse Active Relationship Table _ART
135 * @handle: ACPI handle of the device contains _ART
136 * @art_count: the number of valid entries resulted from parsing _ART
137 * @artp: pointer to pointer of array of art entries in parsing result
138 * @create_dev: whether to create platform devices for target and source
141 int acpi_parse_art(acpi_handle handle
, int *art_count
, struct art
**artp
,
147 int nr_bad_entries
= 0;
149 union acpi_object
*p
;
150 struct acpi_buffer buffer
= { ACPI_ALLOCATE_BUFFER
, NULL
};
151 struct acpi_buffer element
= { 0, NULL
};
152 struct acpi_buffer art_format
= {
153 sizeof("RRNNNNNNNNNNN"), "RRNNNNNNNNNNN" };
155 status
= acpi_evaluate_object(handle
, "_ART", NULL
, &buffer
);
156 if (ACPI_FAILURE(status
))
160 if (!p
|| (p
->type
!= ACPI_TYPE_PACKAGE
)) {
161 pr_err("Invalid _ART data\n");
166 /* ignore p->package.elements[0], as this is _ART Revision field */
167 *art_count
= p
->package
.count
- 1;
168 arts
= kcalloc(*art_count
, sizeof(struct art
), GFP_KERNEL
);
174 for (i
= 0; i
< *art_count
; i
++) {
175 struct art
*art
= &arts
[i
- nr_bad_entries
];
177 element
.length
= sizeof(struct art
);
178 element
.pointer
= art
;
180 status
= acpi_extract_package(&(p
->package
.elements
[i
+ 1]),
181 &art_format
, &element
);
182 if (ACPI_FAILURE(status
)) {
183 pr_warn("_ART package %d is invalid, ignored", i
);
190 if (!acpi_fetch_acpi_dev(art
->source
))
191 pr_warn("Failed to get source ACPI device\n");
193 if (!acpi_fetch_acpi_dev(art
->target
))
194 pr_warn("Failed to get target ACPI device\n");
198 /* don't count bad entries */
199 *art_count
-= nr_bad_entries
;
201 kfree(buffer
.pointer
);
204 EXPORT_SYMBOL(acpi_parse_art
);
207 * acpi_parse_psvt - Passive Table (PSVT) for passive cooling
209 * @handle: ACPI handle of the device which contains PSVT
210 * @psvt_count: the number of valid entries resulted from parsing PSVT
211 * @psvtp: pointer to array of psvt entries
214 static int acpi_parse_psvt(acpi_handle handle
, int *psvt_count
, struct psvt
**psvtp
)
216 struct acpi_buffer buffer
= { ACPI_ALLOCATE_BUFFER
, NULL
};
217 int nr_bad_entries
= 0, revision
= 0;
218 union acpi_object
*p
;
223 if (!acpi_has_method(handle
, "PSVT"))
226 status
= acpi_evaluate_object(handle
, "PSVT", NULL
, &buffer
);
227 if (ACPI_FAILURE(status
))
231 if (!p
|| (p
->type
!= ACPI_TYPE_PACKAGE
)) {
236 /* first package is the revision number */
237 if (p
->package
.count
> 0) {
238 union acpi_object
*prev
= &(p
->package
.elements
[0]);
240 if (prev
->type
== ACPI_TYPE_INTEGER
)
241 revision
= (int)prev
->integer
.value
;
247 /* Support only version 2 */
253 *psvt_count
= p
->package
.count
- 1;
259 psvts
= kcalloc(*psvt_count
, sizeof(*psvts
), GFP_KERNEL
);
265 /* Start index is 1 because the first package is the revision number */
266 for (i
= 1; i
< p
->package
.count
; i
++) {
267 struct acpi_buffer psvt_int_format
= { sizeof("RRNNNNNNNNNN"), "RRNNNNNNNNNN" };
268 struct acpi_buffer psvt_str_format
= { sizeof("RRNNNNNSNNNN"), "RRNNNNNSNNNN" };
269 union acpi_object
*package
= &(p
->package
.elements
[i
]);
270 struct psvt
*psvt
= &psvts
[i
- 1 - nr_bad_entries
];
271 struct acpi_buffer
*psvt_format
= &psvt_int_format
;
272 struct acpi_buffer element
= { 0, NULL
};
273 union acpi_object
*knob
;
274 struct acpi_device
*res
;
275 struct psvt
*psvt_ptr
;
277 element
.length
= ACPI_ALLOCATE_BUFFER
;
278 element
.pointer
= NULL
;
280 if (package
->package
.count
>= ACPI_NR_PSVT_ELEMENTS
) {
281 knob
= &(package
->package
.elements
[ACPI_PSVT_CONTROL_KNOB
]);
284 pr_info("PSVT package %d is invalid, ignored\n", i
);
288 if (knob
->type
== ACPI_TYPE_STRING
) {
289 psvt_format
= &psvt_str_format
;
290 if (knob
->string
.length
> ACPI_LIMIT_STR_MAX_LEN
- 1) {
291 pr_info("PSVT package %d limit string len exceeds max\n", i
);
292 knob
->string
.length
= ACPI_LIMIT_STR_MAX_LEN
- 1;
296 status
= acpi_extract_package(&(p
->package
.elements
[i
]), psvt_format
, &element
);
297 if (ACPI_FAILURE(status
)) {
299 pr_info("PSVT package %d is invalid, ignored\n", i
);
303 psvt_ptr
= (struct psvt
*)element
.pointer
;
305 memcpy(psvt
, psvt_ptr
, sizeof(*psvt
));
307 /* The limit element can be string or U64 */
308 psvt
->control_knob_type
= (u64
)knob
->type
;
310 if (knob
->type
== ACPI_TYPE_STRING
) {
311 memset(&psvt
->limit
, 0, sizeof(u64
));
312 strscpy(psvt
->limit
.string
, psvt_ptr
->limit
.str_ptr
, ACPI_LIMIT_STR_MAX_LEN
);
314 psvt
->limit
.integer
= psvt_ptr
->limit
.integer
;
317 kfree(element
.pointer
);
319 res
= acpi_fetch_acpi_dev(psvt
->source
);
322 pr_info("Failed to get source ACPI device\n");
326 res
= acpi_fetch_acpi_dev(psvt
->target
);
329 pr_info("Failed to get target ACPI device\n");
334 /* don't count bad entries */
335 *psvt_count
-= nr_bad_entries
;
348 kfree(buffer
.pointer
);
352 /* get device name from acpi handle */
353 static void get_single_name(acpi_handle handle
, char *name
)
355 struct acpi_buffer buffer
= {ACPI_ALLOCATE_BUFFER
};
357 if (ACPI_FAILURE(acpi_get_name(handle
, ACPI_SINGLE_NAME
, &buffer
)))
358 pr_warn("Failed to get device name from acpi handle\n");
360 memcpy(name
, buffer
.pointer
, ACPI_NAMESEG_SIZE
);
361 kfree(buffer
.pointer
);
365 static int fill_art(char __user
*ubuf
)
371 struct art
*arts
= NULL
;
372 union art_object
*art_user
;
374 ret
= acpi_parse_art(acpi_thermal_rel_handle
, &count
, &arts
, false);
377 art_len
= count
* sizeof(union art_object
);
378 art_user
= kzalloc(art_len
, GFP_KERNEL
);
383 /* now fill in user art data */
384 for (i
= 0; i
< count
; i
++) {
385 /* userspace art needs device name instead of acpi reference */
386 get_single_name(arts
[i
].source
, art_user
[i
].source_device
);
387 get_single_name(arts
[i
].target
, art_user
[i
].target_device
);
388 /* copy the rest int data in addition to source and target */
389 BUILD_BUG_ON(sizeof(art_user
[i
].data
) !=
390 sizeof(u64
) * (ACPI_NR_ART_ELEMENTS
- 2));
391 memcpy(&art_user
[i
].data
, &arts
[i
].data
, sizeof(art_user
[i
].data
));
394 if (copy_to_user(ubuf
, art_user
, art_len
))
402 static int fill_trt(char __user
*ubuf
)
408 struct trt
*trts
= NULL
;
409 union trt_object
*trt_user
;
411 ret
= acpi_parse_trt(acpi_thermal_rel_handle
, &count
, &trts
, false);
414 trt_len
= count
* sizeof(union trt_object
);
415 trt_user
= kzalloc(trt_len
, GFP_KERNEL
);
420 /* now fill in user trt data */
421 for (i
= 0; i
< count
; i
++) {
422 /* userspace trt needs device name instead of acpi reference */
423 get_single_name(trts
[i
].source
, trt_user
[i
].source_device
);
424 get_single_name(trts
[i
].target
, trt_user
[i
].target_device
);
425 trt_user
[i
].sample_period
= trts
[i
].sample_period
;
426 trt_user
[i
].influence
= trts
[i
].influence
;
429 if (copy_to_user(ubuf
, trt_user
, trt_len
))
437 static int fill_psvt(char __user
*ubuf
)
439 int i
, ret
, count
, psvt_len
;
440 union psvt_object
*psvt_user
;
443 ret
= acpi_parse_psvt(acpi_thermal_rel_handle
, &count
, &psvts
);
447 psvt_len
= count
* sizeof(*psvt_user
);
449 psvt_user
= kzalloc(psvt_len
, GFP_KERNEL
);
455 /* now fill in user psvt data */
456 for (i
= 0; i
< count
; i
++) {
457 /* userspace psvt needs device name instead of acpi reference */
458 get_single_name(psvts
[i
].source
, psvt_user
[i
].source_device
);
459 get_single_name(psvts
[i
].target
, psvt_user
[i
].target_device
);
461 psvt_user
[i
].priority
= psvts
[i
].priority
;
462 psvt_user
[i
].sample_period
= psvts
[i
].sample_period
;
463 psvt_user
[i
].passive_temp
= psvts
[i
].passive_temp
;
464 psvt_user
[i
].source_domain
= psvts
[i
].source_domain
;
465 psvt_user
[i
].control_knob
= psvts
[i
].control_knob
;
466 psvt_user
[i
].step_size
= psvts
[i
].step_size
;
467 psvt_user
[i
].limit_coeff
= psvts
[i
].limit_coeff
;
468 psvt_user
[i
].unlimit_coeff
= psvts
[i
].unlimit_coeff
;
469 psvt_user
[i
].control_knob_type
= psvts
[i
].control_knob_type
;
470 if (psvt_user
[i
].control_knob_type
== ACPI_TYPE_STRING
)
471 strscpy(psvt_user
[i
].limit
.string
, psvts
[i
].limit
.string
,
472 ACPI_LIMIT_STR_MAX_LEN
);
474 psvt_user
[i
].limit
.integer
= psvts
[i
].limit
.integer
;
478 if (copy_to_user(ubuf
, psvt_user
, psvt_len
))
488 static long acpi_thermal_rel_ioctl(struct file
*f
, unsigned int cmd
,
492 unsigned long length
= 0;
494 char __user
*arg
= (void __user
*)__arg
;
495 struct trt
*trts
= NULL
;
496 struct art
*arts
= NULL
;
500 case ACPI_THERMAL_GET_TRT_COUNT
:
501 ret
= acpi_parse_trt(acpi_thermal_rel_handle
, &count
,
505 return put_user(count
, (unsigned long __user
*)__arg
);
507 case ACPI_THERMAL_GET_TRT_LEN
:
508 ret
= acpi_parse_trt(acpi_thermal_rel_handle
, &count
,
511 length
= count
* sizeof(union trt_object
);
513 return put_user(length
, (unsigned long __user
*)__arg
);
515 case ACPI_THERMAL_GET_TRT
:
516 return fill_trt(arg
);
517 case ACPI_THERMAL_GET_ART_COUNT
:
518 ret
= acpi_parse_art(acpi_thermal_rel_handle
, &count
,
522 return put_user(count
, (unsigned long __user
*)__arg
);
524 case ACPI_THERMAL_GET_ART_LEN
:
525 ret
= acpi_parse_art(acpi_thermal_rel_handle
, &count
,
528 length
= count
* sizeof(union art_object
);
530 return put_user(length
, (unsigned long __user
*)__arg
);
533 case ACPI_THERMAL_GET_ART
:
534 return fill_art(arg
);
536 case ACPI_THERMAL_GET_PSVT_COUNT
:
537 ret
= acpi_parse_psvt(acpi_thermal_rel_handle
, &count
, &psvts
);
540 return put_user(count
, (unsigned long __user
*)__arg
);
544 case ACPI_THERMAL_GET_PSVT_LEN
:
545 /* total length of the data retrieved (count * PSVT entry size) */
546 ret
= acpi_parse_psvt(acpi_thermal_rel_handle
, &count
, &psvts
);
547 length
= count
* sizeof(union psvt_object
);
550 return put_user(length
, (unsigned long __user
*)__arg
);
554 case ACPI_THERMAL_GET_PSVT
:
555 return fill_psvt(arg
);
562 static const struct file_operations acpi_thermal_rel_fops
= {
563 .owner
= THIS_MODULE
,
564 .open
= acpi_thermal_rel_open
,
565 .release
= acpi_thermal_rel_release
,
566 .unlocked_ioctl
= acpi_thermal_rel_ioctl
,
569 static struct miscdevice acpi_thermal_rel_misc_device
= {
570 .minor
= MISC_DYNAMIC_MINOR
,
572 &acpi_thermal_rel_fops
575 int acpi_thermal_rel_misc_device_add(acpi_handle handle
)
577 acpi_thermal_rel_handle
= handle
;
579 return misc_register(&acpi_thermal_rel_misc_device
);
581 EXPORT_SYMBOL(acpi_thermal_rel_misc_device_add
);
583 int acpi_thermal_rel_misc_device_remove(acpi_handle handle
)
585 misc_deregister(&acpi_thermal_rel_misc_device
);
589 EXPORT_SYMBOL(acpi_thermal_rel_misc_device_remove
);
591 MODULE_AUTHOR("Zhang Rui <rui.zhang@intel.com>");
592 MODULE_AUTHOR("Jacob Pan <jacob.jun.pan@intel.com");
593 MODULE_DESCRIPTION("Intel acpi thermal rel misc dev driver");
594 MODULE_LICENSE("GPL v2");