1*f6a034f2SRafael J. Wysocki // SPDX-License-Identifier: GPL-2.0
2*f6a034f2SRafael J. Wysocki /*
3*f6a034f2SRafael J. Wysocki  * Copyright 2024, Intel Corporation
4*f6a034f2SRafael J. Wysocki  *
5*f6a034f2SRafael J. Wysocki  * Author: Rafael J. Wysocki <[email protected]>
6*f6a034f2SRafael J. Wysocki  *
7*f6a034f2SRafael J. Wysocki  * Thermal subsystem testing facility.
8*f6a034f2SRafael J. Wysocki  *
9*f6a034f2SRafael J. Wysocki  * This facility allows the thermal core functionality to be exercised in a
10*f6a034f2SRafael J. Wysocki  * controlled way in order to verify its behavior.
11*f6a034f2SRafael J. Wysocki  *
12*f6a034f2SRafael J. Wysocki  * It resides in the "thermal-testing" directory under the debugfs root and
13*f6a034f2SRafael J. Wysocki  * starts with a single file called "command" which can be written a string
14*f6a034f2SRafael J. Wysocki  * representing a thermal testing facility command.
15*f6a034f2SRafael J. Wysocki  *
16*f6a034f2SRafael J. Wysocki  * The currently supported commands are listed in the tt_commands enum below.
17*f6a034f2SRafael J. Wysocki  *
18*f6a034f2SRafael J. Wysocki  * The "addtz" command causes a new test thermal zone template to be created,
19*f6a034f2SRafael J. Wysocki  * for example:
20*f6a034f2SRafael J. Wysocki  *
21*f6a034f2SRafael J. Wysocki  * # echo addtz > /sys/kernel/debug/thermal-testing/command
22*f6a034f2SRafael J. Wysocki  *
23*f6a034f2SRafael J. Wysocki  * That template will be represented as a subdirectory in the "thermal-testing"
24*f6a034f2SRafael J. Wysocki  * directory, for example
25*f6a034f2SRafael J. Wysocki  *
26*f6a034f2SRafael J. Wysocki  * # ls /sys/kernel/debug/thermal-testing/
27*f6a034f2SRafael J. Wysocki  * command tz0
28*f6a034f2SRafael J. Wysocki  *
29*f6a034f2SRafael J. Wysocki  * The thermal zone template can be populated with trip points with the help of
30*f6a034f2SRafael J. Wysocki  * the "tzaddtrip" command, for example:
31*f6a034f2SRafael J. Wysocki  *
32*f6a034f2SRafael J. Wysocki  * # echo tzaddtrip:0 > /sys/kernel/debug/thermal-testing/command
33*f6a034f2SRafael J. Wysocki  *
34*f6a034f2SRafael J. Wysocki  * which causes a trip point template to be added to the test thermal zone
35*f6a034f2SRafael J. Wysocki  * template 0 (represented by the tz0 subdirectory in "thermal-testing").
36*f6a034f2SRafael J. Wysocki  *
37*f6a034f2SRafael J. Wysocki  * # ls /sys/kernel/debug/thermal-testing/tz0
38*f6a034f2SRafael J. Wysocki  * init_temp temp trip_0_temp trip_0_hyst
39*f6a034f2SRafael J. Wysocki  *
40*f6a034f2SRafael J. Wysocki  * The temperature of a trip point template is initially THERMAL_TEMP_INVALID
41*f6a034f2SRafael J. Wysocki  * and its hysteresis is initially 0.  They can be adjusted by writing to the
42*f6a034f2SRafael J. Wysocki  * "trip_x_temp" and "trip_x_hyst" files correspoinding to that trip point
43*f6a034f2SRafael J. Wysocki  * template, respectively.
44*f6a034f2SRafael J. Wysocki  *
45*f6a034f2SRafael J. Wysocki  * The initial temperature of a thermal zone based on a template can be set by
46*f6a034f2SRafael J. Wysocki  * writing to the "init_temp" file in its directory under "thermal-testing", for
47*f6a034f2SRafael J. Wysocki  * example:
48*f6a034f2SRafael J. Wysocki  *
49*f6a034f2SRafael J. Wysocki  * echo 50000 > /sys/kernel/debug/thermal-testing/tz0/init_temp
50*f6a034f2SRafael J. Wysocki  *
51*f6a034f2SRafael J. Wysocki  * When ready, "tzreg" command can be used for registering and enabling a
52*f6a034f2SRafael J. Wysocki  * thermal zone based on a given template with the thermal core, for example
53*f6a034f2SRafael J. Wysocki  *
54*f6a034f2SRafael J. Wysocki  * # echo tzreg:0 > /sys/kernel/debug/thermal-testing/command
55*f6a034f2SRafael J. Wysocki  *
56*f6a034f2SRafael J. Wysocki  * In this case, test thermal zone template 0 is used for registering a new
57*f6a034f2SRafael J. Wysocki  * thermal zone and the set of trip point templates associated with it is used
58*f6a034f2SRafael J. Wysocki  * for populating the new thermal zone's trip points table.  The type of the new
59*f6a034f2SRafael J. Wysocki  * thermal zone is "test_tz".
60*f6a034f2SRafael J. Wysocki  *
61*f6a034f2SRafael J. Wysocki  * The temperature and hysteresis of all of the trip points in that new thermal
62*f6a034f2SRafael J. Wysocki  * zone are adjustable via sysfs, so they can be updated at any time.
63*f6a034f2SRafael J. Wysocki  *
64*f6a034f2SRafael J. Wysocki  * The current temperature of the new thermal zone can be set by writing to the
65*f6a034f2SRafael J. Wysocki  * "temp" file in the corresponding thermal zone template's directory under
66*f6a034f2SRafael J. Wysocki  * "thermal-testing", for example
67*f6a034f2SRafael J. Wysocki  *
68*f6a034f2SRafael J. Wysocki  * echo 10000 > /sys/kernel/debug/thermal-testing/tz0/temp
69*f6a034f2SRafael J. Wysocki  *
70*f6a034f2SRafael J. Wysocki  * which will also trigger a temperature update for this zone in the thermal
71*f6a034f2SRafael J. Wysocki  * core, including checking its trip points, sending notifications to user space
72*f6a034f2SRafael J. Wysocki  * if any of them have been crossed and so on.
73*f6a034f2SRafael J. Wysocki  *
74*f6a034f2SRafael J. Wysocki  * When it is not needed any more, a test thermal zone template can be deleted
75*f6a034f2SRafael J. Wysocki  * with the help of the "deltz" command, for example
76*f6a034f2SRafael J. Wysocki  *
77*f6a034f2SRafael J. Wysocki  * # echo deltz:0 > /sys/kernel/debug/thermal-testing/command
78*f6a034f2SRafael J. Wysocki  *
79*f6a034f2SRafael J. Wysocki  * which will also unregister the thermal zone based on it, if present.
80*f6a034f2SRafael J. Wysocki  */
81*f6a034f2SRafael J. Wysocki 
82*f6a034f2SRafael J. Wysocki #define pr_fmt(fmt) "thermal-testing: " fmt
83*f6a034f2SRafael J. Wysocki 
84*f6a034f2SRafael J. Wysocki #include <linux/debugfs.h>
85*f6a034f2SRafael J. Wysocki #include <linux/module.h>
86*f6a034f2SRafael J. Wysocki 
87*f6a034f2SRafael J. Wysocki #include "thermal_testing.h"
88*f6a034f2SRafael J. Wysocki 
89*f6a034f2SRafael J. Wysocki struct dentry *d_testing;
90*f6a034f2SRafael J. Wysocki 
91*f6a034f2SRafael J. Wysocki #define TT_COMMAND_SIZE		16
92*f6a034f2SRafael J. Wysocki 
93*f6a034f2SRafael J. Wysocki enum tt_commands {
94*f6a034f2SRafael J. Wysocki 	TT_CMD_ADDTZ,
95*f6a034f2SRafael J. Wysocki 	TT_CMD_DELTZ,
96*f6a034f2SRafael J. Wysocki 	TT_CMD_TZADDTRIP,
97*f6a034f2SRafael J. Wysocki 	TT_CMD_TZREG,
98*f6a034f2SRafael J. Wysocki 	TT_CMD_TZUNREG,
99*f6a034f2SRafael J. Wysocki };
100*f6a034f2SRafael J. Wysocki 
101*f6a034f2SRafael J. Wysocki static const char *tt_command_strings[] = {
102*f6a034f2SRafael J. Wysocki 	[TT_CMD_ADDTZ] = "addtz",
103*f6a034f2SRafael J. Wysocki 	[TT_CMD_DELTZ] = "deltz",
104*f6a034f2SRafael J. Wysocki 	[TT_CMD_TZADDTRIP] = "tzaddtrip",
105*f6a034f2SRafael J. Wysocki 	[TT_CMD_TZREG] = "tzreg",
106*f6a034f2SRafael J. Wysocki 	[TT_CMD_TZUNREG] = "tzunreg",
107*f6a034f2SRafael J. Wysocki };
108*f6a034f2SRafael J. Wysocki 
tt_command_exec(int index,const char * arg)109*f6a034f2SRafael J. Wysocki static int tt_command_exec(int index, const char *arg)
110*f6a034f2SRafael J. Wysocki {
111*f6a034f2SRafael J. Wysocki 	int ret;
112*f6a034f2SRafael J. Wysocki 
113*f6a034f2SRafael J. Wysocki 	switch (index) {
114*f6a034f2SRafael J. Wysocki 	case TT_CMD_ADDTZ:
115*f6a034f2SRafael J. Wysocki 		ret = tt_add_tz();
116*f6a034f2SRafael J. Wysocki 		break;
117*f6a034f2SRafael J. Wysocki 
118*f6a034f2SRafael J. Wysocki 	case TT_CMD_DELTZ:
119*f6a034f2SRafael J. Wysocki 		ret = tt_del_tz(arg);
120*f6a034f2SRafael J. Wysocki 		break;
121*f6a034f2SRafael J. Wysocki 
122*f6a034f2SRafael J. Wysocki 	case TT_CMD_TZADDTRIP:
123*f6a034f2SRafael J. Wysocki 		ret = tt_zone_add_trip(arg);
124*f6a034f2SRafael J. Wysocki 		break;
125*f6a034f2SRafael J. Wysocki 
126*f6a034f2SRafael J. Wysocki 	case TT_CMD_TZREG:
127*f6a034f2SRafael J. Wysocki 		ret = tt_zone_reg(arg);
128*f6a034f2SRafael J. Wysocki 		break;
129*f6a034f2SRafael J. Wysocki 
130*f6a034f2SRafael J. Wysocki 	case TT_CMD_TZUNREG:
131*f6a034f2SRafael J. Wysocki 		ret = tt_zone_unreg(arg);
132*f6a034f2SRafael J. Wysocki 		break;
133*f6a034f2SRafael J. Wysocki 
134*f6a034f2SRafael J. Wysocki 	default:
135*f6a034f2SRafael J. Wysocki 		ret = -EINVAL;
136*f6a034f2SRafael J. Wysocki 		break;
137*f6a034f2SRafael J. Wysocki 	}
138*f6a034f2SRafael J. Wysocki 
139*f6a034f2SRafael J. Wysocki 	return ret;
140*f6a034f2SRafael J. Wysocki }
141*f6a034f2SRafael J. Wysocki 
tt_command_process(struct dentry * dentry,const char __user * user_buf,size_t count)142*f6a034f2SRafael J. Wysocki static ssize_t tt_command_process(struct dentry *dentry, const char __user *user_buf,
143*f6a034f2SRafael J. Wysocki 				  size_t count)
144*f6a034f2SRafael J. Wysocki {
145*f6a034f2SRafael J. Wysocki 	char *buf __free(kfree);
146*f6a034f2SRafael J. Wysocki 	char *arg;
147*f6a034f2SRafael J. Wysocki 	int i;
148*f6a034f2SRafael J. Wysocki 
149*f6a034f2SRafael J. Wysocki 	buf = kmalloc(count + 1, GFP_KERNEL);
150*f6a034f2SRafael J. Wysocki 	if (!buf)
151*f6a034f2SRafael J. Wysocki 		return -ENOMEM;
152*f6a034f2SRafael J. Wysocki 
153*f6a034f2SRafael J. Wysocki 	if (copy_from_user(buf, user_buf, count))
154*f6a034f2SRafael J. Wysocki 		return -EFAULT;
155*f6a034f2SRafael J. Wysocki 
156*f6a034f2SRafael J. Wysocki 	buf[count] = '\0';
157*f6a034f2SRafael J. Wysocki 	strim(buf);
158*f6a034f2SRafael J. Wysocki 
159*f6a034f2SRafael J. Wysocki 	arg = strstr(buf, ":");
160*f6a034f2SRafael J. Wysocki 	if (arg) {
161*f6a034f2SRafael J. Wysocki 		*arg = '\0';
162*f6a034f2SRafael J. Wysocki 		arg++;
163*f6a034f2SRafael J. Wysocki 	}
164*f6a034f2SRafael J. Wysocki 
165*f6a034f2SRafael J. Wysocki 	for (i = 0; i < ARRAY_SIZE(tt_command_strings); i++) {
166*f6a034f2SRafael J. Wysocki 		if (!strcmp(buf, tt_command_strings[i]))
167*f6a034f2SRafael J. Wysocki 			return tt_command_exec(i, arg);
168*f6a034f2SRafael J. Wysocki 	}
169*f6a034f2SRafael J. Wysocki 
170*f6a034f2SRafael J. Wysocki 	return -EINVAL;
171*f6a034f2SRafael J. Wysocki }
172*f6a034f2SRafael J. Wysocki 
tt_command_write(struct file * file,const char __user * user_buf,size_t count,loff_t * ppos)173*f6a034f2SRafael J. Wysocki static ssize_t tt_command_write(struct file *file, const char __user *user_buf,
174*f6a034f2SRafael J. Wysocki 				size_t count, loff_t *ppos)
175*f6a034f2SRafael J. Wysocki {
176*f6a034f2SRafael J. Wysocki 	struct dentry *dentry = file->f_path.dentry;
177*f6a034f2SRafael J. Wysocki 	ssize_t ret;
178*f6a034f2SRafael J. Wysocki 
179*f6a034f2SRafael J. Wysocki 	if (*ppos)
180*f6a034f2SRafael J. Wysocki 		return -EINVAL;
181*f6a034f2SRafael J. Wysocki 
182*f6a034f2SRafael J. Wysocki 	if (count + 1 > TT_COMMAND_SIZE)
183*f6a034f2SRafael J. Wysocki 		return -E2BIG;
184*f6a034f2SRafael J. Wysocki 
185*f6a034f2SRafael J. Wysocki 	ret = debugfs_file_get(dentry);
186*f6a034f2SRafael J. Wysocki 	if (unlikely(ret))
187*f6a034f2SRafael J. Wysocki 		return ret;
188*f6a034f2SRafael J. Wysocki 
189*f6a034f2SRafael J. Wysocki 	ret = tt_command_process(dentry, user_buf, count);
190*f6a034f2SRafael J. Wysocki 	if (ret)
191*f6a034f2SRafael J. Wysocki 		return ret;
192*f6a034f2SRafael J. Wysocki 
193*f6a034f2SRafael J. Wysocki 	return count;
194*f6a034f2SRafael J. Wysocki }
195*f6a034f2SRafael J. Wysocki 
196*f6a034f2SRafael J. Wysocki static const struct file_operations tt_command_fops = {
197*f6a034f2SRafael J. Wysocki 	.write = tt_command_write,
198*f6a034f2SRafael J. Wysocki 	.open =	 simple_open,
199*f6a034f2SRafael J. Wysocki 	.llseek = default_llseek,
200*f6a034f2SRafael J. Wysocki };
201*f6a034f2SRafael J. Wysocki 
thermal_testing_init(void)202*f6a034f2SRafael J. Wysocki static int __init thermal_testing_init(void)
203*f6a034f2SRafael J. Wysocki {
204*f6a034f2SRafael J. Wysocki 	d_testing = debugfs_create_dir("thermal-testing", NULL);
205*f6a034f2SRafael J. Wysocki 	if (!IS_ERR(d_testing))
206*f6a034f2SRafael J. Wysocki 		debugfs_create_file("command", 0200, d_testing, NULL,
207*f6a034f2SRafael J. Wysocki 				    &tt_command_fops);
208*f6a034f2SRafael J. Wysocki 
209*f6a034f2SRafael J. Wysocki 	return 0;
210*f6a034f2SRafael J. Wysocki }
211*f6a034f2SRafael J. Wysocki module_init(thermal_testing_init);
212*f6a034f2SRafael J. Wysocki 
thermal_testing_exit(void)213*f6a034f2SRafael J. Wysocki static void __exit thermal_testing_exit(void)
214*f6a034f2SRafael J. Wysocki {
215*f6a034f2SRafael J. Wysocki 	debugfs_remove(d_testing);
216*f6a034f2SRafael J. Wysocki 	tt_zone_cleanup();
217*f6a034f2SRafael J. Wysocki }
218*f6a034f2SRafael J. Wysocki module_exit(thermal_testing_exit);
219*f6a034f2SRafael J. Wysocki 
220*f6a034f2SRafael J. Wysocki MODULE_DESCRIPTION("Thermal core testing facility");
221*f6a034f2SRafael J. Wysocki MODULE_LICENSE("GPL v2");
222