brintos

brintos / linux-shallow public Read only

0
0
Text · 15.1 KiB · cb149bc Raw
595 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/* acpi_thermal_rel.c driver for exporting ACPI thermal relationship3 *4 * Copyright (c) 2014 Intel Corp5 */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 drivers11 *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 <linux/fs.h>23#include "acpi_thermal_rel.h"24 25static acpi_handle acpi_thermal_rel_handle;26static DEFINE_SPINLOCK(acpi_thermal_rel_chrdev_lock);27static int acpi_thermal_rel_chrdev_count;	/* #times opened */28static int acpi_thermal_rel_chrdev_exclu;	/* already open exclusive? */29 30static int acpi_thermal_rel_open(struct inode *inode, struct file *file)31{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);36		return -EBUSY;37	}38 39	if (file->f_flags & O_EXCL)40		acpi_thermal_rel_chrdev_exclu = 1;41	acpi_thermal_rel_chrdev_count++;42 43	spin_unlock(&acpi_thermal_rel_chrdev_lock);44 45	return nonseekable_open(inode, file);46}47 48static int acpi_thermal_rel_release(struct inode *inode, struct file *file)49{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);54 55	return 0;56}57 58/**59 * acpi_parse_trt - Thermal Relationship Table _TRT for passive cooling60 *61 * @handle: ACPI handle of the device contains _TRT62 * @trt_count: the number of valid entries resulted from parsing _TRT63 * @trtp: pointer to pointer of array of _TRT entries in parsing result64 * @create_dev: whether to create platform devices for target and source65 *66 */67int acpi_parse_trt(acpi_handle handle, int *trt_count, struct trt **trtp,68		bool create_dev)69{70	acpi_status status;71	int result = 0;72	int i;73	int nr_bad_entries = 0;74	struct trt *trts;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		if (!acpi_fetch_acpi_dev(trt->source))115			pr_warn("Failed to get source ACPI device\n");116 117		if (!acpi_fetch_acpi_dev(trt->target))118			pr_warn("Failed to get target ACPI device\n");119	}120 121	result = 0;122 123	*trtp = trts;124	/* don't count bad entries */125	*trt_count -= nr_bad_entries;126end:127	kfree(buffer.pointer);128	return result;129}130EXPORT_SYMBOL(acpi_parse_trt);131 132/**133 * acpi_parse_art - Parse Active Relationship Table _ART134 *135 * @handle: ACPI handle of the device contains _ART136 * @art_count: the number of valid entries resulted from parsing _ART137 * @artp: pointer to pointer of array of art entries in parsing result138 * @create_dev: whether to create platform devices for target and source139 *140 */141int acpi_parse_art(acpi_handle handle, int *art_count, struct art **artp,142		bool create_dev)143{144	acpi_status status;145	int result = 0;146	int i;147	int nr_bad_entries = 0;148	struct art *arts;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" };154 155	status = acpi_evaluate_object(handle, "_ART", NULL, &buffer);156	if (ACPI_FAILURE(status))157		return -ENODEV;158 159	p = buffer.pointer;160	if (!p || (p->type != ACPI_TYPE_PACKAGE)) {161		pr_err("Invalid _ART data\n");162		result = -EFAULT;163		goto end;164	}165 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);169	if (!arts) {170		result = -ENOMEM;171		goto end;172	}173 174	for (i = 0; i < *art_count; i++) {175		struct art *art = &arts[i - nr_bad_entries];176 177		element.length = sizeof(struct art);178		element.pointer = art;179 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);184			nr_bad_entries++;185			continue;186		}187		if (!create_dev)188			continue;189 190		if (!acpi_fetch_acpi_dev(art->source))191			pr_warn("Failed to get source ACPI device\n");192 193		if (!acpi_fetch_acpi_dev(art->target))194			pr_warn("Failed to get target ACPI device\n");195	}196 197	*artp = arts;198	/* don't count bad entries */199	*art_count -= nr_bad_entries;200end:201	kfree(buffer.pointer);202	return result;203}204EXPORT_SYMBOL(acpi_parse_art);205 206/*207 * acpi_parse_psvt - Passive Table (PSVT) for passive cooling208 *209 * @handle: ACPI handle of the device which contains PSVT210 * @psvt_count: the number of valid entries resulted from parsing PSVT211 * @psvtp: pointer to array of psvt entries212 *213 */214static int acpi_parse_psvt(acpi_handle handle, int *psvt_count, struct psvt **psvtp)215{216	struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL };217	int nr_bad_entries = 0, revision = 0;218	union acpi_object *p;219	acpi_status status;220	int i, result = 0;221	struct psvt *psvts;222 223	if (!acpi_has_method(handle, "PSVT"))224		return -ENODEV;225 226	status = acpi_evaluate_object(handle, "PSVT", NULL, &buffer);227	if (ACPI_FAILURE(status))228		return -ENODEV;229 230	p = buffer.pointer;231	if (!p || (p->type != ACPI_TYPE_PACKAGE)) {232		result = -EFAULT;233		goto end;234	}235 236	/* first package is the revision number */237	if (p->package.count > 0) {238		union acpi_object *prev = &(p->package.elements[0]);239 240		if (prev->type == ACPI_TYPE_INTEGER)241			revision = (int)prev->integer.value;242	} else {243		result = -EFAULT;244		goto end;245	}246 247	/* Support only version 2 */248	if (revision != 2) {249		result = -EFAULT;250		goto end;251	}252 253	*psvt_count = p->package.count - 1;254	if (!*psvt_count) {255		result = -EFAULT;256		goto end;257	}258 259	psvts = kcalloc(*psvt_count, sizeof(*psvts), GFP_KERNEL);260	if (!psvts) {261		result = -ENOMEM;262		goto end;263	}264 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;276 277		element.length = ACPI_ALLOCATE_BUFFER;278		element.pointer = NULL;279 280		if (package->package.count >= ACPI_NR_PSVT_ELEMENTS) {281			knob = &(package->package.elements[ACPI_PSVT_CONTROL_KNOB]);282		} else {283			nr_bad_entries++;284			pr_info("PSVT package %d is invalid, ignored\n", i);285			continue;286		}287 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;293			}294		}295 296		status = acpi_extract_package(&(p->package.elements[i]), psvt_format, &element);297		if (ACPI_FAILURE(status)) {298			nr_bad_entries++;299			pr_info("PSVT package %d is invalid, ignored\n", i);300			continue;301		}302 303		psvt_ptr = (struct psvt *)element.pointer;304 305		memcpy(psvt, psvt_ptr, sizeof(*psvt));306 307		/* The limit element can be string or U64 */308		psvt->control_knob_type = (u64)knob->type;309 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);313		} else {314			psvt->limit.integer = psvt_ptr->limit.integer;315		}316 317		kfree(element.pointer);318 319		res = acpi_fetch_acpi_dev(psvt->source);320		if (!res) {321			nr_bad_entries++;322			pr_info("Failed to get source ACPI device\n");323			continue;324		}325 326		res = acpi_fetch_acpi_dev(psvt->target);327		if (!res) {328			nr_bad_entries++;329			pr_info("Failed to get target ACPI device\n");330			continue;331		}332	}333 334	/* don't count bad entries */335	*psvt_count -= nr_bad_entries;336 337	if (!*psvt_count) {338		result = -EFAULT;339		kfree(psvts);340		goto end;341	}342 343	*psvtp = psvts;344 345	return 0;346 347end:348	kfree(buffer.pointer);349	return result;350}351 352/* get device name from acpi handle */353static void get_single_name(acpi_handle handle, char *name)354{355	struct acpi_buffer buffer = {ACPI_ALLOCATE_BUFFER};356 357	if (ACPI_FAILURE(acpi_get_name(handle, ACPI_SINGLE_NAME, &buffer)))358		pr_warn("Failed to get device name from acpi handle\n");359	else {360		memcpy(name, buffer.pointer, ACPI_NAMESEG_SIZE);361		kfree(buffer.pointer);362	}363}364 365static int fill_art(char __user *ubuf)366{367	int i;368	int ret;369	int count;370	int art_len;371	struct art *arts = NULL;372	union art_object *art_user;373 374	ret = acpi_parse_art(acpi_thermal_rel_handle, &count, &arts, false);375	if (ret)376		goto free_art;377	art_len = count * sizeof(union art_object);378	art_user = kzalloc(art_len, GFP_KERNEL);379	if (!art_user) {380		ret = -ENOMEM;381		goto free_art;382	}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));392	}393 394	if (copy_to_user(ubuf, art_user, art_len))395		ret = -EFAULT;396	kfree(art_user);397free_art:398	kfree(arts);399	return ret;400}401 402static int fill_trt(char __user *ubuf)403{404	int i;405	int ret;406	int count;407	int trt_len;408	struct trt *trts = NULL;409	union trt_object *trt_user;410 411	ret = acpi_parse_trt(acpi_thermal_rel_handle, &count, &trts, false);412	if (ret)413		goto free_trt;414	trt_len = count * sizeof(union trt_object);415	trt_user = kzalloc(trt_len, GFP_KERNEL);416	if (!trt_user) {417		ret = -ENOMEM;418		goto free_trt;419	}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;427	}428 429	if (copy_to_user(ubuf, trt_user, trt_len))430		ret = -EFAULT;431	kfree(trt_user);432free_trt:433	kfree(trts);434	return ret;435}436 437static int fill_psvt(char __user *ubuf)438{439	int i, ret, count, psvt_len;440	union psvt_object *psvt_user;441	struct psvt *psvts;442 443	ret = acpi_parse_psvt(acpi_thermal_rel_handle, &count, &psvts);444	if (ret)445		return ret;446 447	psvt_len = count * sizeof(*psvt_user);448 449	psvt_user = kzalloc(psvt_len, GFP_KERNEL);450	if (!psvt_user) {451		ret = -ENOMEM;452		goto free_psvt;453	}454 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);460 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);473		else474			psvt_user[i].limit.integer = psvts[i].limit.integer;475 476	}477 478	if (copy_to_user(ubuf, psvt_user, psvt_len))479		ret = -EFAULT;480 481	kfree(psvt_user);482 483free_psvt:484	kfree(psvts);485	return ret;486}487 488static long acpi_thermal_rel_ioctl(struct file *f, unsigned int cmd,489				   unsigned long __arg)490{491	int ret = 0;492	unsigned long length = 0;493	int count = 0;494	char __user *arg = (void __user *)__arg;495	struct trt *trts = NULL;496	struct art *arts = NULL;497	struct psvt *psvts;498 499	switch (cmd) {500	case ACPI_THERMAL_GET_TRT_COUNT:501		ret = acpi_parse_trt(acpi_thermal_rel_handle, &count,502				&trts, false);503		kfree(trts);504		if (!ret)505			return put_user(count, (unsigned long __user *)__arg);506		return ret;507	case ACPI_THERMAL_GET_TRT_LEN:508		ret = acpi_parse_trt(acpi_thermal_rel_handle, &count,509				&trts, false);510		kfree(trts);511		length = count * sizeof(union trt_object);512		if (!ret)513			return put_user(length, (unsigned long __user *)__arg);514		return ret;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,519				&arts, false);520		kfree(arts);521		if (!ret)522			return put_user(count, (unsigned long __user *)__arg);523		return ret;524	case ACPI_THERMAL_GET_ART_LEN:525		ret = acpi_parse_art(acpi_thermal_rel_handle, &count,526				&arts, false);527		kfree(arts);528		length = count * sizeof(union art_object);529		if (!ret)530			return put_user(length, (unsigned long __user *)__arg);531		return ret;532 533	case ACPI_THERMAL_GET_ART:534		return fill_art(arg);535 536	case ACPI_THERMAL_GET_PSVT_COUNT:537		ret = acpi_parse_psvt(acpi_thermal_rel_handle, &count, &psvts);538		if (!ret) {539			kfree(psvts);540			return put_user(count, (unsigned long __user *)__arg);541		}542		return ret;543 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);548		if (!ret) {549			kfree(psvts);550			return put_user(length, (unsigned long __user *)__arg);551		}552		return ret;553 554	case ACPI_THERMAL_GET_PSVT:555		return fill_psvt(arg);556 557	default:558		return -ENOTTY;559	}560}561 562static 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,567};568 569static struct miscdevice acpi_thermal_rel_misc_device = {570	.minor	= MISC_DYNAMIC_MINOR,571	"acpi_thermal_rel",572	&acpi_thermal_rel_fops573};574 575int acpi_thermal_rel_misc_device_add(acpi_handle handle)576{577	acpi_thermal_rel_handle = handle;578 579	return misc_register(&acpi_thermal_rel_misc_device);580}581EXPORT_SYMBOL(acpi_thermal_rel_misc_device_add);582 583int acpi_thermal_rel_misc_device_remove(acpi_handle handle)584{585	misc_deregister(&acpi_thermal_rel_misc_device);586 587	return 0;588}589EXPORT_SYMBOL(acpi_thermal_rel_misc_device_remove);590 591MODULE_AUTHOR("Zhang Rui <rui.zhang@intel.com>");592MODULE_AUTHOR("Jacob Pan <jacob.jun.pan@intel.com");593MODULE_DESCRIPTION("Intel acpi thermal rel misc dev driver");594MODULE_LICENSE("GPL v2");595