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	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			strncpy(psvt->limit.string, psvt_ptr->limit.str_ptr, knob->string.length);
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			strncpy(psvt_user[i].limit.string, psvts[i].limit.string,
472				ACPI_LIMIT_STR_MAX_LEN);
473		else
474			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	.llseek		= no_llseek,
568};
569
570static struct miscdevice acpi_thermal_rel_misc_device = {
571	.minor	= MISC_DYNAMIC_MINOR,
572	"acpi_thermal_rel",
573	&acpi_thermal_rel_fops
574};
575
576int acpi_thermal_rel_misc_device_add(acpi_handle handle)
577{
578	acpi_thermal_rel_handle = handle;
579
580	return misc_register(&acpi_thermal_rel_misc_device);
581}
582EXPORT_SYMBOL(acpi_thermal_rel_misc_device_add);
583
584int acpi_thermal_rel_misc_device_remove(acpi_handle handle)
585{
586	misc_deregister(&acpi_thermal_rel_misc_device);
587
588	return 0;
589}
590EXPORT_SYMBOL(acpi_thermal_rel_misc_device_remove);
591
592MODULE_AUTHOR("Zhang Rui <rui.zhang@intel.com>");
593MODULE_AUTHOR("Jacob Pan <jacob.jun.pan@intel.com");
594MODULE_DESCRIPTION("Intel acpi thermal rel misc dev driver");
595MODULE_LICENSE("GPL v2");
596