| 1 | |
| 2 | |
| 3 | * Copyright 2024, Intel Corporation
|
| 4 | *
|
| 5 | * Author: Rafael J. Wysocki <[email protected]>
|
| 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 workqueue_struct *tt_wq __ro_after_init;
|
| 90 |
|
| 91 | struct dentry *d_testing __ro_after_init;
|
| 92 | static struct dentry *d_command __ro_after_init;
|
| 93 |
|
| 94 | #define TT_COMMAND_SIZE 16
|
| 95 |
|
| 96 | enum tt_commands {
|
| 97 | TT_CMD_ADDTZ,
|
| 98 | TT_CMD_DELTZ,
|
| 99 | TT_CMD_TZADDTRIP,
|
| 100 | TT_CMD_TZREG,
|
| 101 | TT_CMD_TZUNREG,
|
| 102 | };
|
| 103 |
|
| 104 | static const char *tt_command_strings[] = {
|
| 105 | [TT_CMD_ADDTZ] = "addtz",
|
| 106 | [TT_CMD_DELTZ] = "deltz",
|
| 107 | [TT_CMD_TZADDTRIP] = "tzaddtrip",
|
| 108 | [TT_CMD_TZREG] = "tzreg",
|
| 109 | [TT_CMD_TZUNREG] = "tzunreg",
|
| 110 | };
|
| 111 |
|
| 112 | static int tt_command_exec(int index, const char *arg)
|
| 113 | {
|
| 114 | int ret;
|
| 115 |
|
| 116 | switch (index) {
|
| 117 | case TT_CMD_ADDTZ:
|
| 118 | ret = tt_add_tz();
|
| 119 | break;
|
| 120 |
|
| 121 | case TT_CMD_DELTZ:
|
| 122 | if (!arg || !*arg)
|
| 123 | return -EINVAL;
|
| 124 |
|
| 125 | ret = tt_del_tz(arg);
|
| 126 | break;
|
| 127 |
|
| 128 | case TT_CMD_TZADDTRIP:
|
| 129 | if (!arg || !*arg)
|
| 130 | return -EINVAL;
|
| 131 |
|
| 132 | ret = tt_zone_add_trip(arg);
|
| 133 | break;
|
| 134 |
|
| 135 | case TT_CMD_TZREG:
|
| 136 | if (!arg || !*arg)
|
| 137 | return -EINVAL;
|
| 138 |
|
| 139 | ret = tt_zone_reg(arg);
|
| 140 | break;
|
| 141 |
|
| 142 | case TT_CMD_TZUNREG:
|
| 143 | if (!arg || !*arg)
|
| 144 | return -EINVAL;
|
| 145 |
|
| 146 | ret = tt_zone_unreg(arg);
|
| 147 | break;
|
| 148 |
|
| 149 | default:
|
| 150 | ret = -EINVAL;
|
| 151 | break;
|
| 152 | }
|
| 153 |
|
| 154 | return ret;
|
| 155 | }
|
| 156 |
|
| 157 | static ssize_t tt_command_process(char *s)
|
| 158 | {
|
| 159 | char *arg;
|
| 160 | int i;
|
| 161 |
|
| 162 | strim(s);
|
| 163 |
|
| 164 | arg = strchr(s, ':');
|
| 165 | if (arg) {
|
| 166 | *arg = '\0';
|
| 167 | arg++;
|
| 168 | }
|
| 169 |
|
| 170 | for (i = 0; i < ARRAY_SIZE(tt_command_strings); i++) {
|
| 171 | if (!strcmp(s, tt_command_strings[i]))
|
| 172 | return tt_command_exec(i, arg);
|
| 173 | }
|
| 174 |
|
| 175 | return -EINVAL;
|
| 176 | }
|
| 177 |
|
| 178 | static ssize_t tt_command_write(struct file *file, const char __user *user_buf,
|
| 179 | size_t count, loff_t *ppos)
|
| 180 | {
|
| 181 | char buf[TT_COMMAND_SIZE];
|
| 182 | ssize_t ret;
|
| 183 |
|
| 184 | if (*ppos)
|
| 185 | return -EINVAL;
|
| 186 |
|
| 187 | if (count > TT_COMMAND_SIZE - 1)
|
| 188 | return -E2BIG;
|
| 189 |
|
| 190 | if (copy_from_user(buf, user_buf, count))
|
| 191 | return -EFAULT;
|
| 192 | buf[count] = '\0';
|
| 193 |
|
| 194 | ret = tt_command_process(buf);
|
| 195 | if (ret)
|
| 196 | return ret;
|
| 197 |
|
| 198 | return count;
|
| 199 | }
|
| 200 |
|
| 201 | static const struct file_operations tt_command_fops = {
|
| 202 | .write = tt_command_write,
|
| 203 | .open = simple_open,
|
| 204 | .llseek = default_llseek,
|
| 205 | };
|
| 206 |
|
| 207 | static int __init thermal_testing_init(void)
|
| 208 | {
|
| 209 | int error;
|
| 210 |
|
| 211 | tt_wq = alloc_workqueue("thermal_testing", WQ_UNBOUND, 0);
|
| 212 | if (!tt_wq)
|
| 213 | return -ENOMEM;
|
| 214 |
|
| 215 | d_testing = debugfs_create_dir("thermal-testing", NULL);
|
| 216 | if (IS_ERR(d_testing)) {
|
| 217 | error = PTR_ERR(d_testing);
|
| 218 | goto destroy_wq;
|
| 219 | }
|
| 220 |
|
| 221 | d_command = debugfs_create_file("command", 0200, d_testing, NULL, &tt_command_fops);
|
| 222 | if (IS_ERR(d_command)) {
|
| 223 | error = PTR_ERR(d_command);
|
| 224 | goto remove_d_testing;
|
| 225 | }
|
| 226 |
|
| 227 | return 0;
|
| 228 |
|
| 229 | remove_d_testing:
|
| 230 | debugfs_remove(d_testing);
|
| 231 | destroy_wq:
|
| 232 | destroy_workqueue(tt_wq);
|
| 233 | return error;
|
| 234 | }
|
| 235 | module_init(thermal_testing_init);
|
| 236 |
|
| 237 | static void __exit thermal_testing_exit(void)
|
| 238 | {
|
| 239 |
|
| 240 | debugfs_remove(d_command);
|
| 241 |
|
| 242 | flush_workqueue(tt_wq);
|
| 243 | destroy_workqueue(tt_wq);
|
| 244 |
|
| 245 | debugfs_remove(d_testing);
|
| 246 | tt_zone_cleanup();
|
| 247 | }
|
| 248 | module_exit(thermal_testing_exit);
|
| 249 |
|
| 250 | MODULE_DESCRIPTION("Thermal core testing facility");
|
| 251 | MODULE_LICENSE("GPL v2");
|