root/drivers/thermal/intel/int340x_thermal/acpi_thermal_rel.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. acpi_thermal_rel_open
  2. acpi_thermal_rel_release
  3. acpi_parse_trt
  4. acpi_parse_art
  5. get_single_name
  6. fill_art
  7. fill_trt
  8. acpi_thermal_rel_ioctl
  9. acpi_thermal_rel_misc_device_add
  10. acpi_thermal_rel_misc_device_remove

   1 // SPDX-License-Identifier: GPL-2.0-only
   2 /* acpi_thermal_rel.c driver for exporting ACPI thermal relationship
   3  *
   4  * Copyright (c) 2014 Intel Corp
   5  */
   6 
   7 /*
   8  * Two functionalities included:
   9  * 1. Export _TRT, _ART, via misc device interface to the userspace.
  10  * 2. Provide parsing result to kernel drivers
  11  *
  12  */
  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>
  18 #include <linux/io.h>
  19 #include <linux/acpi.h>
  20 #include <linux/uaccess.h>
  21 #include <linux/miscdevice.h>
  22 #include "acpi_thermal_rel.h"
  23 
  24 static acpi_handle acpi_thermal_rel_handle;
  25 static DEFINE_SPINLOCK(acpi_thermal_rel_chrdev_lock);
  26 static int acpi_thermal_rel_chrdev_count;       /* #times opened */
  27 static int acpi_thermal_rel_chrdev_exclu;       /* already open exclusive? */
  28 
  29 static int acpi_thermal_rel_open(struct inode *inode, struct file *file)
  30 {
  31         spin_lock(&acpi_thermal_rel_chrdev_lock);
  32         if (acpi_thermal_rel_chrdev_exclu ||
  33             (acpi_thermal_rel_chrdev_count && (file->f_flags & O_EXCL))) {
  34                 spin_unlock(&acpi_thermal_rel_chrdev_lock);
  35                 return -EBUSY;
  36         }
  37 
  38         if (file->f_flags & O_EXCL)
  39                 acpi_thermal_rel_chrdev_exclu = 1;
  40         acpi_thermal_rel_chrdev_count++;
  41 
  42         spin_unlock(&acpi_thermal_rel_chrdev_lock);
  43 
  44         return nonseekable_open(inode, file);
  45 }
  46 
  47 static int acpi_thermal_rel_release(struct inode *inode, struct file *file)
  48 {
  49         spin_lock(&acpi_thermal_rel_chrdev_lock);
  50         acpi_thermal_rel_chrdev_count--;
  51         acpi_thermal_rel_chrdev_exclu = 0;
  52         spin_unlock(&acpi_thermal_rel_chrdev_lock);
  53 
  54         return 0;
  55 }
  56 
  57 /**
  58  * acpi_parse_trt - Thermal Relationship Table _TRT for passive cooling
  59  *
  60  * @handle: ACPI handle of the device contains _TRT
  61  * @trt_count: the number of valid entries resulted from parsing _TRT
  62  * @trtp: pointer to pointer of array of _TRT entries in parsing result
  63  * @create_dev: whether to create platform devices for target and source
  64  *
  65  */
  66 int acpi_parse_trt(acpi_handle handle, int *trt_count, struct trt **trtp,
  67                 bool create_dev)
  68 {
  69         acpi_status status;
  70         int result = 0;
  71         int i;
  72         int nr_bad_entries = 0;
  73         struct trt *trts;
  74         struct acpi_device *adev;
  75         union acpi_object *p;
  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" };
  79 
  80         status = acpi_evaluate_object(handle, "_TRT", NULL, &buffer);
  81         if (ACPI_FAILURE(status))
  82                 return -ENODEV;
  83 
  84         p = buffer.pointer;
  85         if (!p || (p->type != ACPI_TYPE_PACKAGE)) {
  86                 pr_err("Invalid _TRT data\n");
  87                 result = -EFAULT;
  88                 goto end;
  89         }
  90 
  91         *trt_count = p->package.count;
  92         trts = kcalloc(*trt_count, sizeof(struct trt), GFP_KERNEL);
  93         if (!trts) {
  94                 result = -ENOMEM;
  95                 goto end;
  96         }
  97 
  98         for (i = 0; i < *trt_count; i++) {
  99                 struct trt *trt = &trts[i - nr_bad_entries];
 100 
 101                 element.length = sizeof(struct trt);
 102                 element.pointer = trt;
 103 
 104                 status = acpi_extract_package(&(p->package.elements[i]),
 105                                               &trt_format, &element);
 106                 if (ACPI_FAILURE(status)) {
 107                         nr_bad_entries++;
 108                         pr_warn("_TRT package %d is invalid, ignored\n", i);
 109                         continue;
 110                 }
 111                 if (!create_dev)
 112                         continue;
 113 
 114                 result = acpi_bus_get_device(trt->source, &adev);
 115                 if (result)
 116                         pr_warn("Failed to get source ACPI device\n");
 117 
 118                 result = acpi_bus_get_device(trt->target, &adev);
 119                 if (result)
 120                         pr_warn("Failed to get target ACPI device\n");
 121         }
 122 
 123         result = 0;
 124 
 125         *trtp = trts;
 126         /* don't count bad entries */
 127         *trt_count -= nr_bad_entries;
 128 end:
 129         kfree(buffer.pointer);
 130         return result;
 131 }
 132 EXPORT_SYMBOL(acpi_parse_trt);
 133 
 134 /**
 135  * acpi_parse_art - Parse Active Relationship Table _ART
 136  *
 137  * @handle: ACPI handle of the device contains _ART
 138  * @art_count: the number of valid entries resulted from parsing _ART
 139  * @artp: pointer to pointer of array of art entries in parsing result
 140  * @create_dev: whether to create platform devices for target and source
 141  *
 142  */
 143 int acpi_parse_art(acpi_handle handle, int *art_count, struct art **artp,
 144                 bool create_dev)
 145 {
 146         acpi_status status;
 147         int result = 0;
 148         int i;
 149         int nr_bad_entries = 0;
 150         struct art *arts;
 151         struct acpi_device *adev;
 152         union acpi_object *p;
 153         struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL };
 154         struct acpi_buffer element = { 0, NULL };
 155         struct acpi_buffer art_format = {
 156                 sizeof("RRNNNNNNNNNNN"), "RRNNNNNNNNNNN" };
 157 
 158         status = acpi_evaluate_object(handle, "_ART", NULL, &buffer);
 159         if (ACPI_FAILURE(status))
 160                 return -ENODEV;
 161 
 162         p = buffer.pointer;
 163         if (!p || (p->type != ACPI_TYPE_PACKAGE)) {
 164                 pr_err("Invalid _ART data\n");
 165                 result = -EFAULT;
 166                 goto end;
 167         }
 168 
 169         /* ignore p->package.elements[0], as this is _ART Revision field */
 170         *art_count = p->package.count - 1;
 171         arts = kcalloc(*art_count, sizeof(struct art), GFP_KERNEL);
 172         if (!arts) {
 173                 result = -ENOMEM;
 174                 goto end;
 175         }
 176 
 177         for (i = 0; i < *art_count; i++) {
 178                 struct art *art = &arts[i - nr_bad_entries];
 179 
 180                 element.length = sizeof(struct art);
 181                 element.pointer = art;
 182 
 183                 status = acpi_extract_package(&(p->package.elements[i + 1]),
 184                                               &art_format, &element);
 185                 if (ACPI_FAILURE(status)) {
 186                         pr_warn("_ART package %d is invalid, ignored", i);
 187                         nr_bad_entries++;
 188                         continue;
 189                 }
 190                 if (!create_dev)
 191                         continue;
 192 
 193                 if (art->source) {
 194                         result = acpi_bus_get_device(art->source, &adev);
 195                         if (result)
 196                                 pr_warn("Failed to get source ACPI device\n");
 197                 }
 198                 if (art->target) {
 199                         result = acpi_bus_get_device(art->target, &adev);
 200                         if (result)
 201                                 pr_warn("Failed to get target ACPI device\n");
 202                 }
 203         }
 204 
 205         *artp = arts;
 206         /* don't count bad entries */
 207         *art_count -= nr_bad_entries;
 208 end:
 209         kfree(buffer.pointer);
 210         return result;
 211 }
 212 EXPORT_SYMBOL(acpi_parse_art);
 213 
 214 
 215 /* get device name from acpi handle */
 216 static void get_single_name(acpi_handle handle, char *name)
 217 {
 218         struct acpi_buffer buffer = {ACPI_ALLOCATE_BUFFER};
 219 
 220         if (ACPI_FAILURE(acpi_get_name(handle, ACPI_SINGLE_NAME, &buffer)))
 221                 pr_warn("Failed to get device name from acpi handle\n");
 222         else {
 223                 memcpy(name, buffer.pointer, ACPI_NAMESEG_SIZE);
 224                 kfree(buffer.pointer);
 225         }
 226 }
 227 
 228 static int fill_art(char __user *ubuf)
 229 {
 230         int i;
 231         int ret;
 232         int count;
 233         int art_len;
 234         struct art *arts = NULL;
 235         union art_object *art_user;
 236 
 237         ret = acpi_parse_art(acpi_thermal_rel_handle, &count, &arts, false);
 238         if (ret)
 239                 goto free_art;
 240         art_len = count * sizeof(union art_object);
 241         art_user = kzalloc(art_len, GFP_KERNEL);
 242         if (!art_user) {
 243                 ret = -ENOMEM;
 244                 goto free_art;
 245         }
 246         /* now fill in user art data */
 247         for (i = 0; i < count; i++) {
 248                 /* userspace art needs device name instead of acpi reference */
 249                 get_single_name(arts[i].source, art_user[i].source_device);
 250                 get_single_name(arts[i].target, art_user[i].target_device);
 251                 /* copy the rest int data in addition to source and target */
 252                 memcpy(&art_user[i].weight, &arts[i].weight,
 253                         sizeof(u64) * (ACPI_NR_ART_ELEMENTS - 2));
 254         }
 255 
 256         if (copy_to_user(ubuf, art_user, art_len))
 257                 ret = -EFAULT;
 258         kfree(art_user);
 259 free_art:
 260         kfree(arts);
 261         return ret;
 262 }
 263 
 264 static int fill_trt(char __user *ubuf)
 265 {
 266         int i;
 267         int ret;
 268         int count;
 269         int trt_len;
 270         struct trt *trts = NULL;
 271         union trt_object *trt_user;
 272 
 273         ret = acpi_parse_trt(acpi_thermal_rel_handle, &count, &trts, false);
 274         if (ret)
 275                 goto free_trt;
 276         trt_len = count * sizeof(union trt_object);
 277         trt_user = kzalloc(trt_len, GFP_KERNEL);
 278         if (!trt_user) {
 279                 ret = -ENOMEM;
 280                 goto free_trt;
 281         }
 282         /* now fill in user trt data */
 283         for (i = 0; i < count; i++) {
 284                 /* userspace trt needs device name instead of acpi reference */
 285                 get_single_name(trts[i].source, trt_user[i].source_device);
 286                 get_single_name(trts[i].target, trt_user[i].target_device);
 287                 trt_user[i].sample_period = trts[i].sample_period;
 288                 trt_user[i].influence = trts[i].influence;
 289         }
 290 
 291         if (copy_to_user(ubuf, trt_user, trt_len))
 292                 ret = -EFAULT;
 293         kfree(trt_user);
 294 free_trt:
 295         kfree(trts);
 296         return ret;
 297 }
 298 
 299 static long acpi_thermal_rel_ioctl(struct file *f, unsigned int cmd,
 300                                    unsigned long __arg)
 301 {
 302         int ret = 0;
 303         unsigned long length = 0;
 304         int count = 0;
 305         char __user *arg = (void __user *)__arg;
 306         struct trt *trts = NULL;
 307         struct art *arts = NULL;
 308 
 309         switch (cmd) {
 310         case ACPI_THERMAL_GET_TRT_COUNT:
 311                 ret = acpi_parse_trt(acpi_thermal_rel_handle, &count,
 312                                 &trts, false);
 313                 kfree(trts);
 314                 if (!ret)
 315                         return put_user(count, (unsigned long __user *)__arg);
 316                 return ret;
 317         case ACPI_THERMAL_GET_TRT_LEN:
 318                 ret = acpi_parse_trt(acpi_thermal_rel_handle, &count,
 319                                 &trts, false);
 320                 kfree(trts);
 321                 length = count * sizeof(union trt_object);
 322                 if (!ret)
 323                         return put_user(length, (unsigned long __user *)__arg);
 324                 return ret;
 325         case ACPI_THERMAL_GET_TRT:
 326                 return fill_trt(arg);
 327         case ACPI_THERMAL_GET_ART_COUNT:
 328                 ret = acpi_parse_art(acpi_thermal_rel_handle, &count,
 329                                 &arts, false);
 330                 kfree(arts);
 331                 if (!ret)
 332                         return put_user(count, (unsigned long __user *)__arg);
 333                 return ret;
 334         case ACPI_THERMAL_GET_ART_LEN:
 335                 ret = acpi_parse_art(acpi_thermal_rel_handle, &count,
 336                                 &arts, false);
 337                 kfree(arts);
 338                 length = count * sizeof(union art_object);
 339                 if (!ret)
 340                         return put_user(length, (unsigned long __user *)__arg);
 341                 return ret;
 342 
 343         case ACPI_THERMAL_GET_ART:
 344                 return fill_art(arg);
 345 
 346         default:
 347                 return -ENOTTY;
 348         }
 349 }
 350 
 351 static const struct file_operations acpi_thermal_rel_fops = {
 352         .owner          = THIS_MODULE,
 353         .open           = acpi_thermal_rel_open,
 354         .release        = acpi_thermal_rel_release,
 355         .unlocked_ioctl = acpi_thermal_rel_ioctl,
 356         .llseek         = no_llseek,
 357 };
 358 
 359 static struct miscdevice acpi_thermal_rel_misc_device = {
 360         .minor  = MISC_DYNAMIC_MINOR,
 361         "acpi_thermal_rel",
 362         &acpi_thermal_rel_fops
 363 };
 364 
 365 int acpi_thermal_rel_misc_device_add(acpi_handle handle)
 366 {
 367         acpi_thermal_rel_handle = handle;
 368 
 369         return misc_register(&acpi_thermal_rel_misc_device);
 370 }
 371 EXPORT_SYMBOL(acpi_thermal_rel_misc_device_add);
 372 
 373 int acpi_thermal_rel_misc_device_remove(acpi_handle handle)
 374 {
 375         misc_deregister(&acpi_thermal_rel_misc_device);
 376 
 377         return 0;
 378 }
 379 EXPORT_SYMBOL(acpi_thermal_rel_misc_device_remove);
 380 
 381 MODULE_AUTHOR("Zhang Rui <rui.zhang@intel.com>");
 382 MODULE_AUTHOR("Jacob Pan <jacob.jun.pan@intel.com");
 383 MODULE_DESCRIPTION("Intel acpi thermal rel misc dev driver");
 384 MODULE_LICENSE("GPL v2");

/* [<][>][^][v][top][bottom][index][help] */