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