File Index
Files and directories
Sign in
FILE

acpi_thermal_rel.c

File preview with syntax highlighting.

linux-6.19.14/drivers/thermal/intel/int340x_thermal/acpi_thermal_rel.c C
  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 <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 cooling
 60 *
 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
 65 *
 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 _ART
134 *
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
139 *
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 cooling
208 *
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
212 *
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	status = acpi_evaluate_object(handle, "PSVT", NULL, &buffer);
224	if (ACPI_FAILURE(status))
225		return -ENODEV;
226
227	p = buffer.pointer;
228	if (!p || (p->type != ACPI_TYPE_PACKAGE)) {
229		result = -EFAULT;
230		goto end;
231	}
232
233	/* first package is the revision number */
234	if (p->package.count > 0) {
235		union acpi_object *prev = &(p->package.elements[0]);
236
237		if (prev->type == ACPI_TYPE_INTEGER)
238			revision = (int)prev->integer.value;
239	} else {
240		result = -EFAULT;
241		goto end;
242	}
243
244	/* Support only version 2 */
245	if (revision != 2) {
246		result = -EFAULT;
247		goto end;
248	}
249
250	*psvt_count = p->package.count - 1;
251	if (!*psvt_count) {
252		result = -EFAULT;
253		goto end;
254	}
255
256	psvts = kcalloc(*psvt_count, sizeof(*psvts), GFP_KERNEL);
257	if (!psvts) {
258		result = -ENOMEM;
259		goto end;
260	}
261
262	/* Start index is 1 because the first package is the revision number */
263	for (i = 1; i < p->package.count; i++) {
264		struct acpi_buffer psvt_int_format = { sizeof("RRNNNNNNNNNN"), "RRNNNNNNNNNN" };
265		struct acpi_buffer psvt_str_format = { sizeof("RRNNNNNSNNNN"), "RRNNNNNSNNNN" };
266		union acpi_object *package = &(p->package.elements[i]);
267		struct psvt *psvt = &psvts[i - 1 - nr_bad_entries];
268		struct acpi_buffer *psvt_format = &psvt_int_format;
269		struct acpi_buffer element = { 0, NULL };
270		union acpi_object *knob;
271		struct acpi_device *res;
272		struct psvt *psvt_ptr;
273
274		element.length = ACPI_ALLOCATE_BUFFER;
275		element.pointer = NULL;
276
277		if (package->package.count >= ACPI_NR_PSVT_ELEMENTS) {
278			knob = &(package->package.elements[ACPI_PSVT_CONTROL_KNOB]);
279		} else {
280			nr_bad_entries++;
281			pr_info("PSVT package %d is invalid, ignored\n", i);
282			continue;
283		}
284
285		if (knob->type == ACPI_TYPE_STRING) {
286			psvt_format = &psvt_str_format;
287			if (knob->string.length > ACPI_LIMIT_STR_MAX_LEN - 1) {
288				pr_info("PSVT package %d limit string len exceeds max\n", i);
289				knob->string.length = ACPI_LIMIT_STR_MAX_LEN - 1;
290			}
291		}
292
293		status = acpi_extract_package(&(p->package.elements[i]), psvt_format, &element);
294		if (ACPI_FAILURE(status)) {
295			nr_bad_entries++;
296			pr_info("PSVT package %d is invalid, ignored\n", i);
297			continue;
298		}
299
300		psvt_ptr = (struct psvt *)element.pointer;
301
302		memcpy(psvt, psvt_ptr, sizeof(*psvt));
303
304		/* The limit element can be string or U64 */
305		psvt->control_knob_type = (u64)knob->type;
306
307		if (knob->type == ACPI_TYPE_STRING) {
308			memset(&psvt->limit, 0, sizeof(u64));
309			strscpy(psvt->limit.string, psvt_ptr->limit.str_ptr, ACPI_LIMIT_STR_MAX_LEN);
310		} else {
311			psvt->limit.integer = psvt_ptr->limit.integer;
312		}
313
314		kfree(element.pointer);
315
316		res = acpi_fetch_acpi_dev(psvt->source);
317		if (!res) {
318			nr_bad_entries++;
319			pr_info("Failed to get source ACPI device\n");
320			continue;
321		}
322
323		res = acpi_fetch_acpi_dev(psvt->target);
324		if (!res) {
325			nr_bad_entries++;
326			pr_info("Failed to get target ACPI device\n");
327			continue;
328		}
329	}
330
331	/* don't count bad entries */
332	*psvt_count -= nr_bad_entries;
333
334	if (!*psvt_count) {
335		result = -EFAULT;
336		kfree(psvts);
337		goto end;
338	}
339
340	*psvtp = psvts;
341
342	return 0;
343
344end:
345	kfree(buffer.pointer);
346	return result;
347}
348
349/* get device name from acpi handle */
350static void get_single_name(acpi_handle handle, char *name)
351{
352	struct acpi_buffer buffer = {ACPI_ALLOCATE_BUFFER};
353
354	if (ACPI_FAILURE(acpi_get_name(handle, ACPI_SINGLE_NAME, &buffer)))
355		pr_warn("Failed to get device name from acpi handle\n");
356	else {
357		memcpy(name, buffer.pointer, ACPI_NAMESEG_SIZE);
358		kfree(buffer.pointer);
359	}
360}
361
362static int fill_art(char __user *ubuf)
363{
364	int i;
365	int ret;
366	int count;
367	int art_len;
368	struct art *arts = NULL;
369	union art_object *art_user;
370
371	ret = acpi_parse_art(acpi_thermal_rel_handle, &count, &arts, false);
372	if (ret)
373		goto free_art;
374	art_len = count * sizeof(union art_object);
375	art_user = kzalloc(art_len, GFP_KERNEL);
376	if (!art_user) {
377		ret = -ENOMEM;
378		goto free_art;
379	}
380	/* now fill in user art data */
381	for (i = 0; i < count; i++) {
382		/* userspace art needs device name instead of acpi reference */
383		get_single_name(arts[i].source, art_user[i].source_device);
384		get_single_name(arts[i].target, art_user[i].target_device);
385		/* copy the rest int data in addition to source and target */
386		BUILD_BUG_ON(sizeof(art_user[i].data) !=
387			     sizeof(u64) * (ACPI_NR_ART_ELEMENTS - 2));
388		memcpy(&art_user[i].data, &arts[i].data, sizeof(art_user[i].data));
389	}
390
391	if (copy_to_user(ubuf, art_user, art_len))
392		ret = -EFAULT;
393	kfree(art_user);
394free_art:
395	kfree(arts);
396	return ret;
397}
398
399static int fill_trt(char __user *ubuf)
400{
401	int i;
402	int ret;
403	int count;
404	int trt_len;
405	struct trt *trts = NULL;
406	union trt_object *trt_user;
407
408	ret = acpi_parse_trt(acpi_thermal_rel_handle, &count, &trts, false);
409	if (ret)
410		goto free_trt;
411	trt_len = count * sizeof(union trt_object);
412	trt_user = kzalloc(trt_len, GFP_KERNEL);
413	if (!trt_user) {
414		ret = -ENOMEM;
415		goto free_trt;
416	}
417	/* now fill in user trt data */
418	for (i = 0; i < count; i++) {
419		/* userspace trt needs device name instead of acpi reference */
420		get_single_name(trts[i].source, trt_user[i].source_device);
421		get_single_name(trts[i].target, trt_user[i].target_device);
422		trt_user[i].sample_period = trts[i].sample_period;
423		trt_user[i].influence = trts[i].influence;
424	}
425
426	if (copy_to_user(ubuf, trt_user, trt_len))
427		ret = -EFAULT;
428	kfree(trt_user);
429free_trt:
430	kfree(trts);
431	return ret;
432}
433
434static int fill_psvt(char __user *ubuf)
435{
436	int i, ret, count, psvt_len;
437	union psvt_object *psvt_user;
438	struct psvt *psvts;
439
440	ret = acpi_parse_psvt(acpi_thermal_rel_handle, &count, &psvts);
441	if (ret)
442		return ret;
443
444	psvt_len = count * sizeof(*psvt_user);
445
446	psvt_user = kzalloc(psvt_len, GFP_KERNEL);
447	if (!psvt_user) {
448		ret = -ENOMEM;
449		goto free_psvt;
450	}
451
452	/* now fill in user psvt data */
453	for (i = 0; i < count; i++) {
454		/* userspace psvt needs device name instead of acpi reference */
455		get_single_name(psvts[i].source, psvt_user[i].source_device);
456		get_single_name(psvts[i].target, psvt_user[i].target_device);
457
458		psvt_user[i].priority = psvts[i].priority;
459		psvt_user[i].sample_period = psvts[i].sample_period;
460		psvt_user[i].passive_temp = psvts[i].passive_temp;
461		psvt_user[i].source_domain = psvts[i].source_domain;
462		psvt_user[i].control_knob = psvts[i].control_knob;
463		psvt_user[i].step_size = psvts[i].step_size;
464		psvt_user[i].limit_coeff = psvts[i].limit_coeff;
465		psvt_user[i].unlimit_coeff = psvts[i].unlimit_coeff;
466		psvt_user[i].control_knob_type = psvts[i].control_knob_type;
467		if (psvt_user[i].control_knob_type == ACPI_TYPE_STRING)
468			strscpy(psvt_user[i].limit.string, psvts[i].limit.string,
469				ACPI_LIMIT_STR_MAX_LEN);
470		else
471			psvt_user[i].limit.integer = psvts[i].limit.integer;
472
473	}
474
475	if (copy_to_user(ubuf, psvt_user, psvt_len))
476		ret = -EFAULT;
477
478	kfree(psvt_user);
479
480free_psvt:
481	kfree(psvts);
482	return ret;
483}
484
485static long acpi_thermal_rel_ioctl(struct file *f, unsigned int cmd,
486				   unsigned long __arg)
487{
488	int ret = 0;
489	unsigned long length = 0;
490	int count = 0;
491	char __user *arg = (void __user *)__arg;
492	struct trt *trts = NULL;
493	struct art *arts = NULL;
494	struct psvt *psvts;
495
496	switch (cmd) {
497	case ACPI_THERMAL_GET_TRT_COUNT:
498		ret = acpi_parse_trt(acpi_thermal_rel_handle, &count,
499				&trts, false);
500		kfree(trts);
501		if (!ret)
502			return put_user(count, (unsigned long __user *)__arg);
503		return ret;
504	case ACPI_THERMAL_GET_TRT_LEN:
505		ret = acpi_parse_trt(acpi_thermal_rel_handle, &count,
506				&trts, false);
507		kfree(trts);
508		length = count * sizeof(union trt_object);
509		if (!ret)
510			return put_user(length, (unsigned long __user *)__arg);
511		return ret;
512	case ACPI_THERMAL_GET_TRT:
513		return fill_trt(arg);
514	case ACPI_THERMAL_GET_ART_COUNT:
515		ret = acpi_parse_art(acpi_thermal_rel_handle, &count,
516				&arts, false);
517		kfree(arts);
518		if (!ret)
519			return put_user(count, (unsigned long __user *)__arg);
520		return ret;
521	case ACPI_THERMAL_GET_ART_LEN:
522		ret = acpi_parse_art(acpi_thermal_rel_handle, &count,
523				&arts, false);
524		kfree(arts);
525		length = count * sizeof(union art_object);
526		if (!ret)
527			return put_user(length, (unsigned long __user *)__arg);
528		return ret;
529
530	case ACPI_THERMAL_GET_ART:
531		return fill_art(arg);
532
533	case ACPI_THERMAL_GET_PSVT_COUNT:
534		ret = acpi_parse_psvt(acpi_thermal_rel_handle, &count, &psvts);
535		if (!ret) {
536			kfree(psvts);
537			return put_user(count, (unsigned long __user *)__arg);
538		}
539		return ret;
540
541	case ACPI_THERMAL_GET_PSVT_LEN:
542		/* total length of the data retrieved (count * PSVT entry size) */
543		ret = acpi_parse_psvt(acpi_thermal_rel_handle, &count, &psvts);
544		length = count * sizeof(union psvt_object);
545		if (!ret) {
546			kfree(psvts);
547			return put_user(length, (unsigned long __user *)__arg);
548		}
549		return ret;
550
551	case ACPI_THERMAL_GET_PSVT:
552		return fill_psvt(arg);
553
554	default:
555		return -ENOTTY;
556	}
557}
558
559static const struct file_operations acpi_thermal_rel_fops = {
560	.owner		= THIS_MODULE,
561	.open		= acpi_thermal_rel_open,
562	.release	= acpi_thermal_rel_release,
563	.unlocked_ioctl	= acpi_thermal_rel_ioctl,
564};
565
566static struct miscdevice acpi_thermal_rel_misc_device = {
567	.minor	= MISC_DYNAMIC_MINOR,
568	"acpi_thermal_rel",
569	&acpi_thermal_rel_fops
570};
571
572int acpi_thermal_rel_misc_device_add(acpi_handle handle)
573{
574	acpi_thermal_rel_handle = handle;
575
576	return misc_register(&acpi_thermal_rel_misc_device);
577}
578EXPORT_SYMBOL(acpi_thermal_rel_misc_device_add);
579
580int acpi_thermal_rel_misc_device_remove(acpi_handle handle)
581{
582	misc_deregister(&acpi_thermal_rel_misc_device);
583
584	return 0;
585}
586EXPORT_SYMBOL(acpi_thermal_rel_misc_device_remove);
587
588MODULE_AUTHOR("Zhang Rui <rui.zhang@intel.com>");
589MODULE_AUTHOR("Jacob Pan <jacob.jun.pan@intel.com");
590MODULE_DESCRIPTION("Intel acpi thermal rel misc dev driver");
591MODULE_LICENSE("GPL v2");
~/ Read-only file preview.