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
tt_command_exec(int index,const char * arg)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 ret = tt_del_tz(arg);
120 break;
121
122 case TT_CMD_TZADDTRIP:
123 ret = tt_zone_add_trip(arg);
124 break;
125
126 case TT_CMD_TZREG:
127 ret = tt_zone_reg(arg);
128 break;
129
130 case TT_CMD_TZUNREG:
131 ret = tt_zone_unreg(arg);
132 break;
133
134 default:
135 ret = -EINVAL;
136 break;
137 }
138
139 return ret;
140 }
141
tt_command_process(char * s)142 static ssize_t tt_command_process(char *s)
143 {
144 char *arg;
145 int i;
146
147 strim(s);
148
149 arg = strchr(s, ':');
150 if (arg) {
151 *arg = '\0';
152 arg++;
153 }
154
155 for (i = 0; i < ARRAY_SIZE(tt_command_strings); i++) {
156 if (!strcmp(s, tt_command_strings[i]))
157 return tt_command_exec(i, arg);
158 }
159
160 return -EINVAL;
161 }
162
tt_command_write(struct file * file,const char __user * user_buf,size_t count,loff_t * ppos)163 static ssize_t tt_command_write(struct file *file, const char __user *user_buf,
164 size_t count, loff_t *ppos)
165 {
166 char buf[TT_COMMAND_SIZE];
167 ssize_t ret;
168
169 if (*ppos)
170 return -EINVAL;
171
172 if (count > TT_COMMAND_SIZE - 1)
173 return -E2BIG;
174
175 if (copy_from_user(buf, user_buf, count))
176 return -EFAULT;
177 buf[count] = '\0';
178
179 ret = tt_command_process(buf);
180 if (ret)
181 return ret;
182
183 return count;
184 }
185
186 static const struct file_operations tt_command_fops = {
187 .write = tt_command_write,
188 .open = simple_open,
189 .llseek = default_llseek,
190 };
191
thermal_testing_init(void)192 static int __init thermal_testing_init(void)
193 {
194 d_testing = debugfs_create_dir("thermal-testing", NULL);
195 if (!IS_ERR(d_testing))
196 debugfs_create_file("command", 0200, d_testing, NULL,
197 &tt_command_fops);
198
199 return 0;
200 }
201 module_init(thermal_testing_init);
202
thermal_testing_exit(void)203 static void __exit thermal_testing_exit(void)
204 {
205 debugfs_remove(d_testing);
206 tt_zone_cleanup();
207 }
208 module_exit(thermal_testing_exit);
209
210 MODULE_DESCRIPTION("Thermal core testing facility");
211 MODULE_LICENSE("GPL v2");
212