1d2912cb1SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only 23e8c4d31SAmit Kucheria /* acpi_thermal_rel.c driver for exporting ACPI thermal relationship 33e8c4d31SAmit Kucheria * 43e8c4d31SAmit Kucheria * Copyright (c) 2014 Intel Corp 53e8c4d31SAmit Kucheria */ 63e8c4d31SAmit Kucheria 73e8c4d31SAmit Kucheria /* 83e8c4d31SAmit Kucheria * Two functionalities included: 93e8c4d31SAmit Kucheria * 1. Export _TRT, _ART, via misc device interface to the userspace. 103e8c4d31SAmit Kucheria * 2. Provide parsing result to kernel drivers 113e8c4d31SAmit Kucheria * 123e8c4d31SAmit Kucheria */ 133e8c4d31SAmit Kucheria #include <linux/init.h> 143e8c4d31SAmit Kucheria #include <linux/export.h> 153e8c4d31SAmit Kucheria #include <linux/module.h> 163e8c4d31SAmit Kucheria #include <linux/device.h> 173e8c4d31SAmit Kucheria #include <linux/platform_device.h> 183e8c4d31SAmit Kucheria #include <linux/io.h> 193e8c4d31SAmit Kucheria #include <linux/acpi.h> 203e8c4d31SAmit Kucheria #include <linux/uaccess.h> 213e8c4d31SAmit Kucheria #include <linux/miscdevice.h> 22df23e2beSPeter Zijlstra #include <linux/fs.h> 233e8c4d31SAmit Kucheria #include "acpi_thermal_rel.h" 243e8c4d31SAmit Kucheria 253e8c4d31SAmit Kucheria static acpi_handle acpi_thermal_rel_handle; 263e8c4d31SAmit Kucheria static DEFINE_SPINLOCK(acpi_thermal_rel_chrdev_lock); 273e8c4d31SAmit Kucheria static int acpi_thermal_rel_chrdev_count; /* #times opened */ 283e8c4d31SAmit Kucheria static int acpi_thermal_rel_chrdev_exclu; /* already open exclusive? */ 293e8c4d31SAmit Kucheria 303e8c4d31SAmit Kucheria static int acpi_thermal_rel_open(struct inode *inode, struct file *file) 313e8c4d31SAmit Kucheria { 323e8c4d31SAmit Kucheria spin_lock(&acpi_thermal_rel_chrdev_lock); 333e8c4d31SAmit Kucheria if (acpi_thermal_rel_chrdev_exclu || 343e8c4d31SAmit Kucheria (acpi_thermal_rel_chrdev_count && (file->f_flags & O_EXCL))) { 353e8c4d31SAmit Kucheria spin_unlock(&acpi_thermal_rel_chrdev_lock); 363e8c4d31SAmit Kucheria return -EBUSY; 373e8c4d31SAmit Kucheria } 383e8c4d31SAmit Kucheria 393e8c4d31SAmit Kucheria if (file->f_flags & O_EXCL) 403e8c4d31SAmit Kucheria acpi_thermal_rel_chrdev_exclu = 1; 413e8c4d31SAmit Kucheria acpi_thermal_rel_chrdev_count++; 423e8c4d31SAmit Kucheria 433e8c4d31SAmit Kucheria spin_unlock(&acpi_thermal_rel_chrdev_lock); 443e8c4d31SAmit Kucheria 453e8c4d31SAmit Kucheria return nonseekable_open(inode, file); 463e8c4d31SAmit Kucheria } 473e8c4d31SAmit Kucheria 483e8c4d31SAmit Kucheria static int acpi_thermal_rel_release(struct inode *inode, struct file *file) 493e8c4d31SAmit Kucheria { 503e8c4d31SAmit Kucheria spin_lock(&acpi_thermal_rel_chrdev_lock); 513e8c4d31SAmit Kucheria acpi_thermal_rel_chrdev_count--; 523e8c4d31SAmit Kucheria acpi_thermal_rel_chrdev_exclu = 0; 533e8c4d31SAmit Kucheria spin_unlock(&acpi_thermal_rel_chrdev_lock); 543e8c4d31SAmit Kucheria 553e8c4d31SAmit Kucheria return 0; 563e8c4d31SAmit Kucheria } 573e8c4d31SAmit Kucheria 583e8c4d31SAmit Kucheria /** 593e8c4d31SAmit Kucheria * acpi_parse_trt - Thermal Relationship Table _TRT for passive cooling 603e8c4d31SAmit Kucheria * 613e8c4d31SAmit Kucheria * @handle: ACPI handle of the device contains _TRT 623e8c4d31SAmit Kucheria * @trt_count: the number of valid entries resulted from parsing _TRT 633e8c4d31SAmit Kucheria * @trtp: pointer to pointer of array of _TRT entries in parsing result 643e8c4d31SAmit Kucheria * @create_dev: whether to create platform devices for target and source 653e8c4d31SAmit Kucheria * 663e8c4d31SAmit Kucheria */ 673e8c4d31SAmit Kucheria int acpi_parse_trt(acpi_handle handle, int *trt_count, struct trt **trtp, 683e8c4d31SAmit Kucheria bool create_dev) 693e8c4d31SAmit Kucheria { 703e8c4d31SAmit Kucheria acpi_status status; 713e8c4d31SAmit Kucheria int result = 0; 723e8c4d31SAmit Kucheria int i; 733e8c4d31SAmit Kucheria int nr_bad_entries = 0; 743e8c4d31SAmit Kucheria struct trt *trts; 753e8c4d31SAmit Kucheria struct acpi_device *adev; 763e8c4d31SAmit Kucheria union acpi_object *p; 773e8c4d31SAmit Kucheria struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL }; 783e8c4d31SAmit Kucheria struct acpi_buffer element = { 0, NULL }; 793e8c4d31SAmit Kucheria struct acpi_buffer trt_format = { sizeof("RRNNNNNN"), "RRNNNNNN" }; 803e8c4d31SAmit Kucheria 813e8c4d31SAmit Kucheria status = acpi_evaluate_object(handle, "_TRT", NULL, &buffer); 823e8c4d31SAmit Kucheria if (ACPI_FAILURE(status)) 833e8c4d31SAmit Kucheria return -ENODEV; 843e8c4d31SAmit Kucheria 853e8c4d31SAmit Kucheria p = buffer.pointer; 863e8c4d31SAmit Kucheria if (!p || (p->type != ACPI_TYPE_PACKAGE)) { 873e8c4d31SAmit Kucheria pr_err("Invalid _TRT data\n"); 883e8c4d31SAmit Kucheria result = -EFAULT; 893e8c4d31SAmit Kucheria goto end; 903e8c4d31SAmit Kucheria } 913e8c4d31SAmit Kucheria 923e8c4d31SAmit Kucheria *trt_count = p->package.count; 933e8c4d31SAmit Kucheria trts = kcalloc(*trt_count, sizeof(struct trt), GFP_KERNEL); 943e8c4d31SAmit Kucheria if (!trts) { 953e8c4d31SAmit Kucheria result = -ENOMEM; 963e8c4d31SAmit Kucheria goto end; 973e8c4d31SAmit Kucheria } 983e8c4d31SAmit Kucheria 993e8c4d31SAmit Kucheria for (i = 0; i < *trt_count; i++) { 1003e8c4d31SAmit Kucheria struct trt *trt = &trts[i - nr_bad_entries]; 1013e8c4d31SAmit Kucheria 1023e8c4d31SAmit Kucheria element.length = sizeof(struct trt); 1033e8c4d31SAmit Kucheria element.pointer = trt; 1043e8c4d31SAmit Kucheria 1053e8c4d31SAmit Kucheria status = acpi_extract_package(&(p->package.elements[i]), 1063e8c4d31SAmit Kucheria &trt_format, &element); 1073e8c4d31SAmit Kucheria if (ACPI_FAILURE(status)) { 1083e8c4d31SAmit Kucheria nr_bad_entries++; 1093e8c4d31SAmit Kucheria pr_warn("_TRT package %d is invalid, ignored\n", i); 1103e8c4d31SAmit Kucheria continue; 1113e8c4d31SAmit Kucheria } 1123e8c4d31SAmit Kucheria if (!create_dev) 1133e8c4d31SAmit Kucheria continue; 1143e8c4d31SAmit Kucheria 1153e8c4d31SAmit Kucheria result = acpi_bus_get_device(trt->source, &adev); 1163e8c4d31SAmit Kucheria if (result) 1173e8c4d31SAmit Kucheria pr_warn("Failed to get source ACPI device\n"); 1183e8c4d31SAmit Kucheria 1193e8c4d31SAmit Kucheria result = acpi_bus_get_device(trt->target, &adev); 1203e8c4d31SAmit Kucheria if (result) 1213e8c4d31SAmit Kucheria pr_warn("Failed to get target ACPI device\n"); 1223e8c4d31SAmit Kucheria } 1233e8c4d31SAmit Kucheria 1243e8c4d31SAmit Kucheria result = 0; 1253e8c4d31SAmit Kucheria 1263e8c4d31SAmit Kucheria *trtp = trts; 1273e8c4d31SAmit Kucheria /* don't count bad entries */ 1283e8c4d31SAmit Kucheria *trt_count -= nr_bad_entries; 1293e8c4d31SAmit Kucheria end: 1303e8c4d31SAmit Kucheria kfree(buffer.pointer); 1313e8c4d31SAmit Kucheria return result; 1323e8c4d31SAmit Kucheria } 1333e8c4d31SAmit Kucheria EXPORT_SYMBOL(acpi_parse_trt); 1343e8c4d31SAmit Kucheria 1353e8c4d31SAmit Kucheria /** 1363e8c4d31SAmit Kucheria * acpi_parse_art - Parse Active Relationship Table _ART 1373e8c4d31SAmit Kucheria * 1383e8c4d31SAmit Kucheria * @handle: ACPI handle of the device contains _ART 1393e8c4d31SAmit Kucheria * @art_count: the number of valid entries resulted from parsing _ART 1403e8c4d31SAmit Kucheria * @artp: pointer to pointer of array of art entries in parsing result 1413e8c4d31SAmit Kucheria * @create_dev: whether to create platform devices for target and source 1423e8c4d31SAmit Kucheria * 1433e8c4d31SAmit Kucheria */ 1443e8c4d31SAmit Kucheria int acpi_parse_art(acpi_handle handle, int *art_count, struct art **artp, 1453e8c4d31SAmit Kucheria bool create_dev) 1463e8c4d31SAmit Kucheria { 1473e8c4d31SAmit Kucheria acpi_status status; 1483e8c4d31SAmit Kucheria int result = 0; 1493e8c4d31SAmit Kucheria int i; 1503e8c4d31SAmit Kucheria int nr_bad_entries = 0; 1513e8c4d31SAmit Kucheria struct art *arts; 1523e8c4d31SAmit Kucheria struct acpi_device *adev; 1533e8c4d31SAmit Kucheria union acpi_object *p; 1543e8c4d31SAmit Kucheria struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL }; 1553e8c4d31SAmit Kucheria struct acpi_buffer element = { 0, NULL }; 1563e8c4d31SAmit Kucheria struct acpi_buffer art_format = { 1573e8c4d31SAmit Kucheria sizeof("RRNNNNNNNNNNN"), "RRNNNNNNNNNNN" }; 1583e8c4d31SAmit Kucheria 1593e8c4d31SAmit Kucheria status = acpi_evaluate_object(handle, "_ART", NULL, &buffer); 1603e8c4d31SAmit Kucheria if (ACPI_FAILURE(status)) 1613e8c4d31SAmit Kucheria return -ENODEV; 1623e8c4d31SAmit Kucheria 1633e8c4d31SAmit Kucheria p = buffer.pointer; 1643e8c4d31SAmit Kucheria if (!p || (p->type != ACPI_TYPE_PACKAGE)) { 1653e8c4d31SAmit Kucheria pr_err("Invalid _ART data\n"); 1663e8c4d31SAmit Kucheria result = -EFAULT; 1673e8c4d31SAmit Kucheria goto end; 1683e8c4d31SAmit Kucheria } 1693e8c4d31SAmit Kucheria 1703e8c4d31SAmit Kucheria /* ignore p->package.elements[0], as this is _ART Revision field */ 1713e8c4d31SAmit Kucheria *art_count = p->package.count - 1; 1723e8c4d31SAmit Kucheria arts = kcalloc(*art_count, sizeof(struct art), GFP_KERNEL); 1733e8c4d31SAmit Kucheria if (!arts) { 1743e8c4d31SAmit Kucheria result = -ENOMEM; 1753e8c4d31SAmit Kucheria goto end; 1763e8c4d31SAmit Kucheria } 1773e8c4d31SAmit Kucheria 1783e8c4d31SAmit Kucheria for (i = 0; i < *art_count; i++) { 1793e8c4d31SAmit Kucheria struct art *art = &arts[i - nr_bad_entries]; 1803e8c4d31SAmit Kucheria 1813e8c4d31SAmit Kucheria element.length = sizeof(struct art); 1823e8c4d31SAmit Kucheria element.pointer = art; 1833e8c4d31SAmit Kucheria 1843e8c4d31SAmit Kucheria status = acpi_extract_package(&(p->package.elements[i + 1]), 1853e8c4d31SAmit Kucheria &art_format, &element); 1863e8c4d31SAmit Kucheria if (ACPI_FAILURE(status)) { 1873e8c4d31SAmit Kucheria pr_warn("_ART package %d is invalid, ignored", i); 1883e8c4d31SAmit Kucheria nr_bad_entries++; 1893e8c4d31SAmit Kucheria continue; 1903e8c4d31SAmit Kucheria } 1913e8c4d31SAmit Kucheria if (!create_dev) 1923e8c4d31SAmit Kucheria continue; 1933e8c4d31SAmit Kucheria 1943e8c4d31SAmit Kucheria if (art->source) { 1953e8c4d31SAmit Kucheria result = acpi_bus_get_device(art->source, &adev); 1963e8c4d31SAmit Kucheria if (result) 1973e8c4d31SAmit Kucheria pr_warn("Failed to get source ACPI device\n"); 1983e8c4d31SAmit Kucheria } 1993e8c4d31SAmit Kucheria if (art->target) { 2003e8c4d31SAmit Kucheria result = acpi_bus_get_device(art->target, &adev); 2013e8c4d31SAmit Kucheria if (result) 2023e8c4d31SAmit Kucheria pr_warn("Failed to get target ACPI device\n"); 2033e8c4d31SAmit Kucheria } 2043e8c4d31SAmit Kucheria } 2053e8c4d31SAmit Kucheria 2063e8c4d31SAmit Kucheria *artp = arts; 2073e8c4d31SAmit Kucheria /* don't count bad entries */ 2083e8c4d31SAmit Kucheria *art_count -= nr_bad_entries; 2093e8c4d31SAmit Kucheria end: 2103e8c4d31SAmit Kucheria kfree(buffer.pointer); 2113e8c4d31SAmit Kucheria return result; 2123e8c4d31SAmit Kucheria } 2133e8c4d31SAmit Kucheria EXPORT_SYMBOL(acpi_parse_art); 2143e8c4d31SAmit Kucheria 2153e8c4d31SAmit Kucheria 2163e8c4d31SAmit Kucheria /* get device name from acpi handle */ 2173e8c4d31SAmit Kucheria static void get_single_name(acpi_handle handle, char *name) 2183e8c4d31SAmit Kucheria { 2193e8c4d31SAmit Kucheria struct acpi_buffer buffer = {ACPI_ALLOCATE_BUFFER}; 2203e8c4d31SAmit Kucheria 2213e8c4d31SAmit Kucheria if (ACPI_FAILURE(acpi_get_name(handle, ACPI_SINGLE_NAME, &buffer))) 2223e8c4d31SAmit Kucheria pr_warn("Failed to get device name from acpi handle\n"); 2233e8c4d31SAmit Kucheria else { 22432786755SBob Moore memcpy(name, buffer.pointer, ACPI_NAMESEG_SIZE); 2253e8c4d31SAmit Kucheria kfree(buffer.pointer); 2263e8c4d31SAmit Kucheria } 2273e8c4d31SAmit Kucheria } 2283e8c4d31SAmit Kucheria 2293e8c4d31SAmit Kucheria static int fill_art(char __user *ubuf) 2303e8c4d31SAmit Kucheria { 2313e8c4d31SAmit Kucheria int i; 2323e8c4d31SAmit Kucheria int ret; 2333e8c4d31SAmit Kucheria int count; 2343e8c4d31SAmit Kucheria int art_len; 2353e8c4d31SAmit Kucheria struct art *arts = NULL; 2363e8c4d31SAmit Kucheria union art_object *art_user; 2373e8c4d31SAmit Kucheria 2383e8c4d31SAmit Kucheria ret = acpi_parse_art(acpi_thermal_rel_handle, &count, &arts, false); 2393e8c4d31SAmit Kucheria if (ret) 2403e8c4d31SAmit Kucheria goto free_art; 2413e8c4d31SAmit Kucheria art_len = count * sizeof(union art_object); 2423e8c4d31SAmit Kucheria art_user = kzalloc(art_len, GFP_KERNEL); 2433e8c4d31SAmit Kucheria if (!art_user) { 2443e8c4d31SAmit Kucheria ret = -ENOMEM; 2453e8c4d31SAmit Kucheria goto free_art; 2463e8c4d31SAmit Kucheria } 2473e8c4d31SAmit Kucheria /* now fill in user art data */ 2483e8c4d31SAmit Kucheria for (i = 0; i < count; i++) { 2493e8c4d31SAmit Kucheria /* userspace art needs device name instead of acpi reference */ 2503e8c4d31SAmit Kucheria get_single_name(arts[i].source, art_user[i].source_device); 2513e8c4d31SAmit Kucheria get_single_name(arts[i].target, art_user[i].target_device); 2523e8c4d31SAmit Kucheria /* copy the rest int data in addition to source and target */ 2533e8c4d31SAmit Kucheria memcpy(&art_user[i].weight, &arts[i].weight, 2543e8c4d31SAmit Kucheria sizeof(u64) * (ACPI_NR_ART_ELEMENTS - 2)); 2553e8c4d31SAmit Kucheria } 2563e8c4d31SAmit Kucheria 2573e8c4d31SAmit Kucheria if (copy_to_user(ubuf, art_user, art_len)) 2583e8c4d31SAmit Kucheria ret = -EFAULT; 2593e8c4d31SAmit Kucheria kfree(art_user); 2603e8c4d31SAmit Kucheria free_art: 2613e8c4d31SAmit Kucheria kfree(arts); 2623e8c4d31SAmit Kucheria return ret; 2633e8c4d31SAmit Kucheria } 2643e8c4d31SAmit Kucheria 2653e8c4d31SAmit Kucheria static int fill_trt(char __user *ubuf) 2663e8c4d31SAmit Kucheria { 2673e8c4d31SAmit Kucheria int i; 2683e8c4d31SAmit Kucheria int ret; 2693e8c4d31SAmit Kucheria int count; 2703e8c4d31SAmit Kucheria int trt_len; 2713e8c4d31SAmit Kucheria struct trt *trts = NULL; 2723e8c4d31SAmit Kucheria union trt_object *trt_user; 2733e8c4d31SAmit Kucheria 2743e8c4d31SAmit Kucheria ret = acpi_parse_trt(acpi_thermal_rel_handle, &count, &trts, false); 2753e8c4d31SAmit Kucheria if (ret) 2763e8c4d31SAmit Kucheria goto free_trt; 2773e8c4d31SAmit Kucheria trt_len = count * sizeof(union trt_object); 2783e8c4d31SAmit Kucheria trt_user = kzalloc(trt_len, GFP_KERNEL); 2793e8c4d31SAmit Kucheria if (!trt_user) { 2803e8c4d31SAmit Kucheria ret = -ENOMEM; 2813e8c4d31SAmit Kucheria goto free_trt; 2823e8c4d31SAmit Kucheria } 2833e8c4d31SAmit Kucheria /* now fill in user trt data */ 2843e8c4d31SAmit Kucheria for (i = 0; i < count; i++) { 2853e8c4d31SAmit Kucheria /* userspace trt needs device name instead of acpi reference */ 2863e8c4d31SAmit Kucheria get_single_name(trts[i].source, trt_user[i].source_device); 2873e8c4d31SAmit Kucheria get_single_name(trts[i].target, trt_user[i].target_device); 2883e8c4d31SAmit Kucheria trt_user[i].sample_period = trts[i].sample_period; 2893e8c4d31SAmit Kucheria trt_user[i].influence = trts[i].influence; 2903e8c4d31SAmit Kucheria } 2913e8c4d31SAmit Kucheria 2923e8c4d31SAmit Kucheria if (copy_to_user(ubuf, trt_user, trt_len)) 2933e8c4d31SAmit Kucheria ret = -EFAULT; 2943e8c4d31SAmit Kucheria kfree(trt_user); 2953e8c4d31SAmit Kucheria free_trt: 2963e8c4d31SAmit Kucheria kfree(trts); 2973e8c4d31SAmit Kucheria return ret; 2983e8c4d31SAmit Kucheria } 2993e8c4d31SAmit Kucheria 3003e8c4d31SAmit Kucheria static long acpi_thermal_rel_ioctl(struct file *f, unsigned int cmd, 3013e8c4d31SAmit Kucheria unsigned long __arg) 3023e8c4d31SAmit Kucheria { 3033e8c4d31SAmit Kucheria int ret = 0; 3043e8c4d31SAmit Kucheria unsigned long length = 0; 3053e8c4d31SAmit Kucheria int count = 0; 3063e8c4d31SAmit Kucheria char __user *arg = (void __user *)__arg; 3073e8c4d31SAmit Kucheria struct trt *trts = NULL; 3083e8c4d31SAmit Kucheria struct art *arts = NULL; 3093e8c4d31SAmit Kucheria 3103e8c4d31SAmit Kucheria switch (cmd) { 3113e8c4d31SAmit Kucheria case ACPI_THERMAL_GET_TRT_COUNT: 3123e8c4d31SAmit Kucheria ret = acpi_parse_trt(acpi_thermal_rel_handle, &count, 3133e8c4d31SAmit Kucheria &trts, false); 3143e8c4d31SAmit Kucheria kfree(trts); 3153e8c4d31SAmit Kucheria if (!ret) 3163e8c4d31SAmit Kucheria return put_user(count, (unsigned long __user *)__arg); 3173e8c4d31SAmit Kucheria return ret; 3183e8c4d31SAmit Kucheria case ACPI_THERMAL_GET_TRT_LEN: 3193e8c4d31SAmit Kucheria ret = acpi_parse_trt(acpi_thermal_rel_handle, &count, 3203e8c4d31SAmit Kucheria &trts, false); 3213e8c4d31SAmit Kucheria kfree(trts); 3223e8c4d31SAmit Kucheria length = count * sizeof(union trt_object); 3233e8c4d31SAmit Kucheria if (!ret) 3243e8c4d31SAmit Kucheria return put_user(length, (unsigned long __user *)__arg); 3253e8c4d31SAmit Kucheria return ret; 3263e8c4d31SAmit Kucheria case ACPI_THERMAL_GET_TRT: 3273e8c4d31SAmit Kucheria return fill_trt(arg); 3283e8c4d31SAmit Kucheria case ACPI_THERMAL_GET_ART_COUNT: 3293e8c4d31SAmit Kucheria ret = acpi_parse_art(acpi_thermal_rel_handle, &count, 3303e8c4d31SAmit Kucheria &arts, false); 3313e8c4d31SAmit Kucheria kfree(arts); 3323e8c4d31SAmit Kucheria if (!ret) 3333e8c4d31SAmit Kucheria return put_user(count, (unsigned long __user *)__arg); 3343e8c4d31SAmit Kucheria return ret; 3353e8c4d31SAmit Kucheria case ACPI_THERMAL_GET_ART_LEN: 3363e8c4d31SAmit Kucheria ret = acpi_parse_art(acpi_thermal_rel_handle, &count, 3373e8c4d31SAmit Kucheria &arts, false); 3383e8c4d31SAmit Kucheria kfree(arts); 3393e8c4d31SAmit Kucheria length = count * sizeof(union art_object); 3403e8c4d31SAmit Kucheria if (!ret) 3413e8c4d31SAmit Kucheria return put_user(length, (unsigned long __user *)__arg); 3423e8c4d31SAmit Kucheria return ret; 3433e8c4d31SAmit Kucheria 3443e8c4d31SAmit Kucheria case ACPI_THERMAL_GET_ART: 3453e8c4d31SAmit Kucheria return fill_art(arg); 3463e8c4d31SAmit Kucheria 3473e8c4d31SAmit Kucheria default: 3483e8c4d31SAmit Kucheria return -ENOTTY; 3493e8c4d31SAmit Kucheria } 3503e8c4d31SAmit Kucheria } 3513e8c4d31SAmit Kucheria 3523e8c4d31SAmit Kucheria static const struct file_operations acpi_thermal_rel_fops = { 3533e8c4d31SAmit Kucheria .owner = THIS_MODULE, 3543e8c4d31SAmit Kucheria .open = acpi_thermal_rel_open, 3553e8c4d31SAmit Kucheria .release = acpi_thermal_rel_release, 3563e8c4d31SAmit Kucheria .unlocked_ioctl = acpi_thermal_rel_ioctl, 3573e8c4d31SAmit Kucheria .llseek = no_llseek, 3583e8c4d31SAmit Kucheria }; 3593e8c4d31SAmit Kucheria 3603e8c4d31SAmit Kucheria static struct miscdevice acpi_thermal_rel_misc_device = { 3613e8c4d31SAmit Kucheria .minor = MISC_DYNAMIC_MINOR, 3623e8c4d31SAmit Kucheria "acpi_thermal_rel", 3633e8c4d31SAmit Kucheria &acpi_thermal_rel_fops 3643e8c4d31SAmit Kucheria }; 3653e8c4d31SAmit Kucheria 3663e8c4d31SAmit Kucheria int acpi_thermal_rel_misc_device_add(acpi_handle handle) 3673e8c4d31SAmit Kucheria { 3683e8c4d31SAmit Kucheria acpi_thermal_rel_handle = handle; 3693e8c4d31SAmit Kucheria 3703e8c4d31SAmit Kucheria return misc_register(&acpi_thermal_rel_misc_device); 3713e8c4d31SAmit Kucheria } 3723e8c4d31SAmit Kucheria EXPORT_SYMBOL(acpi_thermal_rel_misc_device_add); 3733e8c4d31SAmit Kucheria 3743e8c4d31SAmit Kucheria int acpi_thermal_rel_misc_device_remove(acpi_handle handle) 3753e8c4d31SAmit Kucheria { 3763e8c4d31SAmit Kucheria misc_deregister(&acpi_thermal_rel_misc_device); 3773e8c4d31SAmit Kucheria 3783e8c4d31SAmit Kucheria return 0; 3793e8c4d31SAmit Kucheria } 3803e8c4d31SAmit Kucheria EXPORT_SYMBOL(acpi_thermal_rel_misc_device_remove); 3813e8c4d31SAmit Kucheria 3823e8c4d31SAmit Kucheria MODULE_AUTHOR("Zhang Rui <rui.zhang@intel.com>"); 3833e8c4d31SAmit Kucheria MODULE_AUTHOR("Jacob Pan <jacob.jun.pan@intel.com"); 3843e8c4d31SAmit Kucheria MODULE_DESCRIPTION("Intel acpi thermal rel misc dev driver"); 3853e8c4d31SAmit Kucheria MODULE_LICENSE("GPL v2"); 386