Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-only
0002 /* acpi_thermal_rel.c driver for exporting ACPI thermal relationship
0003  *
0004  * Copyright (c) 2014 Intel Corp
0005  */
0006 
0007 /*
0008  * Two functionalities included:
0009  * 1. Export _TRT, _ART, via misc device interface to the userspace.
0010  * 2. Provide parsing result to kernel drivers
0011  *
0012  */
0013 #include <linux/init.h>
0014 #include <linux/export.h>
0015 #include <linux/module.h>
0016 #include <linux/device.h>
0017 #include <linux/platform_device.h>
0018 #include <linux/io.h>
0019 #include <linux/acpi.h>
0020 #include <linux/uaccess.h>
0021 #include <linux/miscdevice.h>
0022 #include <linux/fs.h>
0023 #include "acpi_thermal_rel.h"
0024 
0025 static acpi_handle acpi_thermal_rel_handle;
0026 static DEFINE_SPINLOCK(acpi_thermal_rel_chrdev_lock);
0027 static int acpi_thermal_rel_chrdev_count;   /* #times opened */
0028 static int acpi_thermal_rel_chrdev_exclu;   /* already open exclusive? */
0029 
0030 static int acpi_thermal_rel_open(struct inode *inode, struct file *file)
0031 {
0032     spin_lock(&acpi_thermal_rel_chrdev_lock);
0033     if (acpi_thermal_rel_chrdev_exclu ||
0034         (acpi_thermal_rel_chrdev_count && (file->f_flags & O_EXCL))) {
0035         spin_unlock(&acpi_thermal_rel_chrdev_lock);
0036         return -EBUSY;
0037     }
0038 
0039     if (file->f_flags & O_EXCL)
0040         acpi_thermal_rel_chrdev_exclu = 1;
0041     acpi_thermal_rel_chrdev_count++;
0042 
0043     spin_unlock(&acpi_thermal_rel_chrdev_lock);
0044 
0045     return nonseekable_open(inode, file);
0046 }
0047 
0048 static int acpi_thermal_rel_release(struct inode *inode, struct file *file)
0049 {
0050     spin_lock(&acpi_thermal_rel_chrdev_lock);
0051     acpi_thermal_rel_chrdev_count--;
0052     acpi_thermal_rel_chrdev_exclu = 0;
0053     spin_unlock(&acpi_thermal_rel_chrdev_lock);
0054 
0055     return 0;
0056 }
0057 
0058 /**
0059  * acpi_parse_trt - Thermal Relationship Table _TRT for passive cooling
0060  *
0061  * @handle: ACPI handle of the device contains _TRT
0062  * @trt_count: the number of valid entries resulted from parsing _TRT
0063  * @trtp: pointer to pointer of array of _TRT entries in parsing result
0064  * @create_dev: whether to create platform devices for target and source
0065  *
0066  */
0067 int acpi_parse_trt(acpi_handle handle, int *trt_count, struct trt **trtp,
0068         bool create_dev)
0069 {
0070     acpi_status status;
0071     int result = 0;
0072     int i;
0073     int nr_bad_entries = 0;
0074     struct trt *trts;
0075     union acpi_object *p;
0076     struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL };
0077     struct acpi_buffer element = { 0, NULL };
0078     struct acpi_buffer trt_format = { sizeof("RRNNNNNN"), "RRNNNNNN" };
0079 
0080     status = acpi_evaluate_object(handle, "_TRT", NULL, &buffer);
0081     if (ACPI_FAILURE(status))
0082         return -ENODEV;
0083 
0084     p = buffer.pointer;
0085     if (!p || (p->type != ACPI_TYPE_PACKAGE)) {
0086         pr_err("Invalid _TRT data\n");
0087         result = -EFAULT;
0088         goto end;
0089     }
0090 
0091     *trt_count = p->package.count;
0092     trts = kcalloc(*trt_count, sizeof(struct trt), GFP_KERNEL);
0093     if (!trts) {
0094         result = -ENOMEM;
0095         goto end;
0096     }
0097 
0098     for (i = 0; i < *trt_count; i++) {
0099         struct trt *trt = &trts[i - nr_bad_entries];
0100 
0101         element.length = sizeof(struct trt);
0102         element.pointer = trt;
0103 
0104         status = acpi_extract_package(&(p->package.elements[i]),
0105                           &trt_format, &element);
0106         if (ACPI_FAILURE(status)) {
0107             nr_bad_entries++;
0108             pr_warn("_TRT package %d is invalid, ignored\n", i);
0109             continue;
0110         }
0111         if (!create_dev)
0112             continue;
0113 
0114         if (!acpi_fetch_acpi_dev(trt->source))
0115             pr_warn("Failed to get source ACPI device\n");
0116 
0117         if (!acpi_fetch_acpi_dev(trt->target))
0118             pr_warn("Failed to get target ACPI device\n");
0119     }
0120 
0121     result = 0;
0122 
0123     *trtp = trts;
0124     /* don't count bad entries */
0125     *trt_count -= nr_bad_entries;
0126 end:
0127     kfree(buffer.pointer);
0128     return result;
0129 }
0130 EXPORT_SYMBOL(acpi_parse_trt);
0131 
0132 /**
0133  * acpi_parse_art - Parse Active Relationship Table _ART
0134  *
0135  * @handle: ACPI handle of the device contains _ART
0136  * @art_count: the number of valid entries resulted from parsing _ART
0137  * @artp: pointer to pointer of array of art entries in parsing result
0138  * @create_dev: whether to create platform devices for target and source
0139  *
0140  */
0141 int acpi_parse_art(acpi_handle handle, int *art_count, struct art **artp,
0142         bool create_dev)
0143 {
0144     acpi_status status;
0145     int result = 0;
0146     int i;
0147     int nr_bad_entries = 0;
0148     struct art *arts;
0149     union acpi_object *p;
0150     struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL };
0151     struct acpi_buffer element = { 0, NULL };
0152     struct acpi_buffer art_format = {
0153         sizeof("RRNNNNNNNNNNN"), "RRNNNNNNNNNNN" };
0154 
0155     status = acpi_evaluate_object(handle, "_ART", NULL, &buffer);
0156     if (ACPI_FAILURE(status))
0157         return -ENODEV;
0158 
0159     p = buffer.pointer;
0160     if (!p || (p->type != ACPI_TYPE_PACKAGE)) {
0161         pr_err("Invalid _ART data\n");
0162         result = -EFAULT;
0163         goto end;
0164     }
0165 
0166     /* ignore p->package.elements[0], as this is _ART Revision field */
0167     *art_count = p->package.count - 1;
0168     arts = kcalloc(*art_count, sizeof(struct art), GFP_KERNEL);
0169     if (!arts) {
0170         result = -ENOMEM;
0171         goto end;
0172     }
0173 
0174     for (i = 0; i < *art_count; i++) {
0175         struct art *art = &arts[i - nr_bad_entries];
0176 
0177         element.length = sizeof(struct art);
0178         element.pointer = art;
0179 
0180         status = acpi_extract_package(&(p->package.elements[i + 1]),
0181                           &art_format, &element);
0182         if (ACPI_FAILURE(status)) {
0183             pr_warn("_ART package %d is invalid, ignored", i);
0184             nr_bad_entries++;
0185             continue;
0186         }
0187         if (!create_dev)
0188             continue;
0189 
0190         if (!acpi_fetch_acpi_dev(art->source))
0191             pr_warn("Failed to get source ACPI device\n");
0192 
0193         if (!acpi_fetch_acpi_dev(art->target))
0194             pr_warn("Failed to get target ACPI device\n");
0195     }
0196 
0197     *artp = arts;
0198     /* don't count bad entries */
0199     *art_count -= nr_bad_entries;
0200 end:
0201     kfree(buffer.pointer);
0202     return result;
0203 }
0204 EXPORT_SYMBOL(acpi_parse_art);
0205 
0206 
0207 /* get device name from acpi handle */
0208 static void get_single_name(acpi_handle handle, char *name)
0209 {
0210     struct acpi_buffer buffer = {ACPI_ALLOCATE_BUFFER};
0211 
0212     if (ACPI_FAILURE(acpi_get_name(handle, ACPI_SINGLE_NAME, &buffer)))
0213         pr_warn("Failed to get device name from acpi handle\n");
0214     else {
0215         memcpy(name, buffer.pointer, ACPI_NAMESEG_SIZE);
0216         kfree(buffer.pointer);
0217     }
0218 }
0219 
0220 static int fill_art(char __user *ubuf)
0221 {
0222     int i;
0223     int ret;
0224     int count;
0225     int art_len;
0226     struct art *arts = NULL;
0227     union art_object *art_user;
0228 
0229     ret = acpi_parse_art(acpi_thermal_rel_handle, &count, &arts, false);
0230     if (ret)
0231         goto free_art;
0232     art_len = count * sizeof(union art_object);
0233     art_user = kzalloc(art_len, GFP_KERNEL);
0234     if (!art_user) {
0235         ret = -ENOMEM;
0236         goto free_art;
0237     }
0238     /* now fill in user art data */
0239     for (i = 0; i < count; i++) {
0240         /* userspace art needs device name instead of acpi reference */
0241         get_single_name(arts[i].source, art_user[i].source_device);
0242         get_single_name(arts[i].target, art_user[i].target_device);
0243         /* copy the rest int data in addition to source and target */
0244         BUILD_BUG_ON(sizeof(art_user[i].data) !=
0245                  sizeof(u64) * (ACPI_NR_ART_ELEMENTS - 2));
0246         memcpy(&art_user[i].data, &arts[i].data, sizeof(art_user[i].data));
0247     }
0248 
0249     if (copy_to_user(ubuf, art_user, art_len))
0250         ret = -EFAULT;
0251     kfree(art_user);
0252 free_art:
0253     kfree(arts);
0254     return ret;
0255 }
0256 
0257 static int fill_trt(char __user *ubuf)
0258 {
0259     int i;
0260     int ret;
0261     int count;
0262     int trt_len;
0263     struct trt *trts = NULL;
0264     union trt_object *trt_user;
0265 
0266     ret = acpi_parse_trt(acpi_thermal_rel_handle, &count, &trts, false);
0267     if (ret)
0268         goto free_trt;
0269     trt_len = count * sizeof(union trt_object);
0270     trt_user = kzalloc(trt_len, GFP_KERNEL);
0271     if (!trt_user) {
0272         ret = -ENOMEM;
0273         goto free_trt;
0274     }
0275     /* now fill in user trt data */
0276     for (i = 0; i < count; i++) {
0277         /* userspace trt needs device name instead of acpi reference */
0278         get_single_name(trts[i].source, trt_user[i].source_device);
0279         get_single_name(trts[i].target, trt_user[i].target_device);
0280         trt_user[i].sample_period = trts[i].sample_period;
0281         trt_user[i].influence = trts[i].influence;
0282     }
0283 
0284     if (copy_to_user(ubuf, trt_user, trt_len))
0285         ret = -EFAULT;
0286     kfree(trt_user);
0287 free_trt:
0288     kfree(trts);
0289     return ret;
0290 }
0291 
0292 static long acpi_thermal_rel_ioctl(struct file *f, unsigned int cmd,
0293                    unsigned long __arg)
0294 {
0295     int ret = 0;
0296     unsigned long length = 0;
0297     int count = 0;
0298     char __user *arg = (void __user *)__arg;
0299     struct trt *trts = NULL;
0300     struct art *arts = NULL;
0301 
0302     switch (cmd) {
0303     case ACPI_THERMAL_GET_TRT_COUNT:
0304         ret = acpi_parse_trt(acpi_thermal_rel_handle, &count,
0305                 &trts, false);
0306         kfree(trts);
0307         if (!ret)
0308             return put_user(count, (unsigned long __user *)__arg);
0309         return ret;
0310     case ACPI_THERMAL_GET_TRT_LEN:
0311         ret = acpi_parse_trt(acpi_thermal_rel_handle, &count,
0312                 &trts, false);
0313         kfree(trts);
0314         length = count * sizeof(union trt_object);
0315         if (!ret)
0316             return put_user(length, (unsigned long __user *)__arg);
0317         return ret;
0318     case ACPI_THERMAL_GET_TRT:
0319         return fill_trt(arg);
0320     case ACPI_THERMAL_GET_ART_COUNT:
0321         ret = acpi_parse_art(acpi_thermal_rel_handle, &count,
0322                 &arts, false);
0323         kfree(arts);
0324         if (!ret)
0325             return put_user(count, (unsigned long __user *)__arg);
0326         return ret;
0327     case ACPI_THERMAL_GET_ART_LEN:
0328         ret = acpi_parse_art(acpi_thermal_rel_handle, &count,
0329                 &arts, false);
0330         kfree(arts);
0331         length = count * sizeof(union art_object);
0332         if (!ret)
0333             return put_user(length, (unsigned long __user *)__arg);
0334         return ret;
0335 
0336     case ACPI_THERMAL_GET_ART:
0337         return fill_art(arg);
0338 
0339     default:
0340         return -ENOTTY;
0341     }
0342 }
0343 
0344 static const struct file_operations acpi_thermal_rel_fops = {
0345     .owner      = THIS_MODULE,
0346     .open       = acpi_thermal_rel_open,
0347     .release    = acpi_thermal_rel_release,
0348     .unlocked_ioctl = acpi_thermal_rel_ioctl,
0349     .llseek     = no_llseek,
0350 };
0351 
0352 static struct miscdevice acpi_thermal_rel_misc_device = {
0353     .minor  = MISC_DYNAMIC_MINOR,
0354     "acpi_thermal_rel",
0355     &acpi_thermal_rel_fops
0356 };
0357 
0358 int acpi_thermal_rel_misc_device_add(acpi_handle handle)
0359 {
0360     acpi_thermal_rel_handle = handle;
0361 
0362     return misc_register(&acpi_thermal_rel_misc_device);
0363 }
0364 EXPORT_SYMBOL(acpi_thermal_rel_misc_device_add);
0365 
0366 int acpi_thermal_rel_misc_device_remove(acpi_handle handle)
0367 {
0368     misc_deregister(&acpi_thermal_rel_misc_device);
0369 
0370     return 0;
0371 }
0372 EXPORT_SYMBOL(acpi_thermal_rel_misc_device_remove);
0373 
0374 MODULE_AUTHOR("Zhang Rui <rui.zhang@intel.com>");
0375 MODULE_AUTHOR("Jacob Pan <jacob.jun.pan@intel.com");
0376 MODULE_DESCRIPTION("Intel acpi thermal rel misc dev driver");
0377 MODULE_LICENSE("GPL v2");