| 1 | // SPDX-License-Identifier: GPL-2.0 | 
|---|
| 2 | /* | 
|---|
| 3 | *  thermal.c - Generic Thermal Management Sysfs support. | 
|---|
| 4 | * | 
|---|
| 5 | *  Copyright (C) 2008 Intel Corp | 
|---|
| 6 | *  Copyright (C) 2008 Zhang Rui <rui.zhang@intel.com> | 
|---|
| 7 | *  Copyright (C) 2008 Sujith Thomas <sujith.thomas@intel.com> | 
|---|
| 8 | */ | 
|---|
| 9 |  | 
|---|
| 10 | #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt | 
|---|
| 11 |  | 
|---|
| 12 | #include <linux/device.h> | 
|---|
| 13 | #include <linux/err.h> | 
|---|
| 14 | #include <linux/export.h> | 
|---|
| 15 | #include <linux/slab.h> | 
|---|
| 16 | #include <linux/kdev_t.h> | 
|---|
| 17 | #include <linux/idr.h> | 
|---|
| 18 | #include <linux/thermal.h> | 
|---|
| 19 | #include <linux/reboot.h> | 
|---|
| 20 | #include <linux/string.h> | 
|---|
| 21 | #include <linux/of.h> | 
|---|
| 22 | #include <linux/suspend.h> | 
|---|
| 23 |  | 
|---|
| 24 | #define CREATE_TRACE_POINTS | 
|---|
| 25 | #include "thermal_trace.h" | 
|---|
| 26 |  | 
|---|
| 27 | #include "thermal_core.h" | 
|---|
| 28 | #include "thermal_hwmon.h" | 
|---|
| 29 |  | 
|---|
| 30 | static DEFINE_IDA(thermal_tz_ida); | 
|---|
| 31 | static DEFINE_IDA(thermal_cdev_ida); | 
|---|
| 32 |  | 
|---|
| 33 | static LIST_HEAD(thermal_tz_list); | 
|---|
| 34 | static LIST_HEAD(thermal_cdev_list); | 
|---|
| 35 | static LIST_HEAD(thermal_governor_list); | 
|---|
| 36 |  | 
|---|
| 37 | static DEFINE_MUTEX(thermal_list_lock); | 
|---|
| 38 | static DEFINE_MUTEX(thermal_governor_lock); | 
|---|
| 39 |  | 
|---|
| 40 | static struct thermal_governor *def_governor; | 
|---|
| 41 |  | 
|---|
| 42 | static bool thermal_pm_suspended; | 
|---|
| 43 |  | 
|---|
| 44 | /* | 
|---|
| 45 | * Governor section: set of functions to handle thermal governors | 
|---|
| 46 | * | 
|---|
| 47 | * Functions to help in the life cycle of thermal governors within | 
|---|
| 48 | * the thermal core and by the thermal governor code. | 
|---|
| 49 | */ | 
|---|
| 50 |  | 
|---|
| 51 | static struct thermal_governor *__find_governor(const char *name) | 
|---|
| 52 | { | 
|---|
| 53 | struct thermal_governor *pos; | 
|---|
| 54 |  | 
|---|
| 55 | if (!name || !name[0]) | 
|---|
| 56 | return def_governor; | 
|---|
| 57 |  | 
|---|
| 58 | list_for_each_entry(pos, &thermal_governor_list, governor_list) | 
|---|
| 59 | if (!strncasecmp(s1: name, s2: pos->name, THERMAL_NAME_LENGTH)) | 
|---|
| 60 | return pos; | 
|---|
| 61 |  | 
|---|
| 62 | return NULL; | 
|---|
| 63 | } | 
|---|
| 64 |  | 
|---|
| 65 | /** | 
|---|
| 66 | * bind_previous_governor() - bind the previous governor of the thermal zone | 
|---|
| 67 | * @tz:		a valid pointer to a struct thermal_zone_device | 
|---|
| 68 | * @failed_gov_name:	the name of the governor that failed to register | 
|---|
| 69 | * | 
|---|
| 70 | * Register the previous governor of the thermal zone after a new | 
|---|
| 71 | * governor has failed to be bound. | 
|---|
| 72 | */ | 
|---|
| 73 | static void bind_previous_governor(struct thermal_zone_device *tz, | 
|---|
| 74 | const char *failed_gov_name) | 
|---|
| 75 | { | 
|---|
| 76 | if (tz->governor && tz->governor->bind_to_tz) { | 
|---|
| 77 | if (tz->governor->bind_to_tz(tz)) { | 
|---|
| 78 | dev_err(&tz->device, | 
|---|
| 79 | "governor %s failed to bind and the previous one (%s) failed to bind again, thermal zone %s has no governor\n", | 
|---|
| 80 | failed_gov_name, tz->governor->name, tz->type); | 
|---|
| 81 | tz->governor = NULL; | 
|---|
| 82 | } | 
|---|
| 83 | } | 
|---|
| 84 | } | 
|---|
| 85 |  | 
|---|
| 86 | /** | 
|---|
| 87 | * thermal_set_governor() - Switch to another governor | 
|---|
| 88 | * @tz:		a valid pointer to a struct thermal_zone_device | 
|---|
| 89 | * @new_gov:	pointer to the new governor | 
|---|
| 90 | * | 
|---|
| 91 | * Change the governor of thermal zone @tz. | 
|---|
| 92 | * | 
|---|
| 93 | * Return: 0 on success, an error if the new governor's bind_to_tz() failed. | 
|---|
| 94 | */ | 
|---|
| 95 | static int thermal_set_governor(struct thermal_zone_device *tz, | 
|---|
| 96 | struct thermal_governor *new_gov) | 
|---|
| 97 | { | 
|---|
| 98 | int ret = 0; | 
|---|
| 99 |  | 
|---|
| 100 | if (tz->governor && tz->governor->unbind_from_tz) | 
|---|
| 101 | tz->governor->unbind_from_tz(tz); | 
|---|
| 102 |  | 
|---|
| 103 | if (new_gov && new_gov->bind_to_tz) { | 
|---|
| 104 | ret = new_gov->bind_to_tz(tz); | 
|---|
| 105 | if (ret) { | 
|---|
| 106 | bind_previous_governor(tz, failed_gov_name: new_gov->name); | 
|---|
| 107 |  | 
|---|
| 108 | return ret; | 
|---|
| 109 | } | 
|---|
| 110 | } | 
|---|
| 111 |  | 
|---|
| 112 | tz->governor = new_gov; | 
|---|
| 113 |  | 
|---|
| 114 | return ret; | 
|---|
| 115 | } | 
|---|
| 116 |  | 
|---|
| 117 | int thermal_register_governor(struct thermal_governor *governor) | 
|---|
| 118 | { | 
|---|
| 119 | int err; | 
|---|
| 120 | const char *name; | 
|---|
| 121 | struct thermal_zone_device *pos; | 
|---|
| 122 |  | 
|---|
| 123 | if (!governor) | 
|---|
| 124 | return -EINVAL; | 
|---|
| 125 |  | 
|---|
| 126 | guard(mutex)(T: &thermal_governor_lock); | 
|---|
| 127 |  | 
|---|
| 128 | err = -EBUSY; | 
|---|
| 129 | if (!__find_governor(name: governor->name)) { | 
|---|
| 130 | bool match_default; | 
|---|
| 131 |  | 
|---|
| 132 | err = 0; | 
|---|
| 133 | list_add(new: &governor->governor_list, head: &thermal_governor_list); | 
|---|
| 134 | match_default = !strncmp(governor->name, | 
|---|
| 135 | DEFAULT_THERMAL_GOVERNOR, | 
|---|
| 136 | THERMAL_NAME_LENGTH); | 
|---|
| 137 |  | 
|---|
| 138 | if (!def_governor && match_default) | 
|---|
| 139 | def_governor = governor; | 
|---|
| 140 | } | 
|---|
| 141 |  | 
|---|
| 142 | guard(mutex)(T: &thermal_list_lock); | 
|---|
| 143 |  | 
|---|
| 144 | list_for_each_entry(pos, &thermal_tz_list, node) { | 
|---|
| 145 | /* | 
|---|
| 146 | * only thermal zones with specified tz->tzp->governor_name | 
|---|
| 147 | * may run with tz->govenor unset | 
|---|
| 148 | */ | 
|---|
| 149 | if (pos->governor) | 
|---|
| 150 | continue; | 
|---|
| 151 |  | 
|---|
| 152 | name = pos->tzp->governor_name; | 
|---|
| 153 |  | 
|---|
| 154 | if (!strncasecmp(s1: name, s2: governor->name, THERMAL_NAME_LENGTH)) { | 
|---|
| 155 | int ret; | 
|---|
| 156 |  | 
|---|
| 157 | ret = thermal_set_governor(tz: pos, new_gov: governor); | 
|---|
| 158 | if (ret) | 
|---|
| 159 | dev_err(&pos->device, | 
|---|
| 160 | "Failed to set governor %s for thermal zone %s: %d\n", | 
|---|
| 161 | governor->name, pos->type, ret); | 
|---|
| 162 | } | 
|---|
| 163 | } | 
|---|
| 164 |  | 
|---|
| 165 | return err; | 
|---|
| 166 | } | 
|---|
| 167 |  | 
|---|
| 168 | void thermal_unregister_governor(struct thermal_governor *governor) | 
|---|
| 169 | { | 
|---|
| 170 | struct thermal_zone_device *pos; | 
|---|
| 171 |  | 
|---|
| 172 | if (!governor) | 
|---|
| 173 | return; | 
|---|
| 174 |  | 
|---|
| 175 | guard(mutex)(T: &thermal_governor_lock); | 
|---|
| 176 |  | 
|---|
| 177 | if (!__find_governor(name: governor->name)) | 
|---|
| 178 | return; | 
|---|
| 179 |  | 
|---|
| 180 | list_del(entry: &governor->governor_list); | 
|---|
| 181 |  | 
|---|
| 182 | guard(mutex)(T: &thermal_list_lock); | 
|---|
| 183 |  | 
|---|
| 184 | list_for_each_entry(pos, &thermal_tz_list, node) { | 
|---|
| 185 | if (!strncasecmp(s1: pos->governor->name, s2: governor->name, | 
|---|
| 186 | THERMAL_NAME_LENGTH)) | 
|---|
| 187 | thermal_set_governor(tz: pos, NULL); | 
|---|
| 188 | } | 
|---|
| 189 | } | 
|---|
| 190 |  | 
|---|
| 191 | int thermal_zone_device_set_policy(struct thermal_zone_device *tz, | 
|---|
| 192 | char *policy) | 
|---|
| 193 | { | 
|---|
| 194 | struct thermal_governor *gov; | 
|---|
| 195 | int ret = -EINVAL; | 
|---|
| 196 |  | 
|---|
| 197 | guard(mutex)(T: &thermal_governor_lock); | 
|---|
| 198 | guard(thermal_zone)(T: tz); | 
|---|
| 199 |  | 
|---|
| 200 | gov = __find_governor(name: strim(policy)); | 
|---|
| 201 | if (gov) | 
|---|
| 202 | ret = thermal_set_governor(tz, new_gov: gov); | 
|---|
| 203 |  | 
|---|
| 204 | thermal_notify_tz_gov_change(tz, name: policy); | 
|---|
| 205 |  | 
|---|
| 206 | return ret; | 
|---|
| 207 | } | 
|---|
| 208 |  | 
|---|
| 209 | int thermal_build_list_of_policies(char *buf) | 
|---|
| 210 | { | 
|---|
| 211 | struct thermal_governor *pos; | 
|---|
| 212 | ssize_t count = 0; | 
|---|
| 213 |  | 
|---|
| 214 | guard(mutex)(T: &thermal_governor_lock); | 
|---|
| 215 |  | 
|---|
| 216 | list_for_each_entry(pos, &thermal_governor_list, governor_list) { | 
|---|
| 217 | count += sysfs_emit_at(buf, at: count, fmt: "%s ", pos->name); | 
|---|
| 218 | } | 
|---|
| 219 | count += sysfs_emit_at(buf, at: count, fmt: "\n"); | 
|---|
| 220 |  | 
|---|
| 221 | return count; | 
|---|
| 222 | } | 
|---|
| 223 |  | 
|---|
| 224 | static void __init thermal_unregister_governors(void) | 
|---|
| 225 | { | 
|---|
| 226 | struct thermal_governor **governor; | 
|---|
| 227 |  | 
|---|
| 228 | for_each_governor_table(governor) | 
|---|
| 229 | thermal_unregister_governor(governor: *governor); | 
|---|
| 230 | } | 
|---|
| 231 |  | 
|---|
| 232 | static int __init thermal_register_governors(void) | 
|---|
| 233 | { | 
|---|
| 234 | int ret = 0; | 
|---|
| 235 | struct thermal_governor **governor; | 
|---|
| 236 |  | 
|---|
| 237 | for_each_governor_table(governor) { | 
|---|
| 238 | ret = thermal_register_governor(governor: *governor); | 
|---|
| 239 | if (ret) { | 
|---|
| 240 | pr_err( "Failed to register governor: '%s'", | 
|---|
| 241 | (*governor)->name); | 
|---|
| 242 | break; | 
|---|
| 243 | } | 
|---|
| 244 |  | 
|---|
| 245 | pr_info( "Registered thermal governor '%s'", | 
|---|
| 246 | (*governor)->name); | 
|---|
| 247 | } | 
|---|
| 248 |  | 
|---|
| 249 | if (ret) { | 
|---|
| 250 | struct thermal_governor **gov; | 
|---|
| 251 |  | 
|---|
| 252 | for_each_governor_table(gov) { | 
|---|
| 253 | if (gov == governor) | 
|---|
| 254 | break; | 
|---|
| 255 | thermal_unregister_governor(governor: *gov); | 
|---|
| 256 | } | 
|---|
| 257 | } | 
|---|
| 258 |  | 
|---|
| 259 | return ret; | 
|---|
| 260 | } | 
|---|
| 261 |  | 
|---|
| 262 | static int __thermal_zone_device_set_mode(struct thermal_zone_device *tz, | 
|---|
| 263 | enum thermal_device_mode mode) | 
|---|
| 264 | { | 
|---|
| 265 | if (tz->ops.change_mode) { | 
|---|
| 266 | int ret; | 
|---|
| 267 |  | 
|---|
| 268 | ret = tz->ops.change_mode(tz, mode); | 
|---|
| 269 | if (ret) | 
|---|
| 270 | return ret; | 
|---|
| 271 | } | 
|---|
| 272 |  | 
|---|
| 273 | tz->mode = mode; | 
|---|
| 274 |  | 
|---|
| 275 | return 0; | 
|---|
| 276 | } | 
|---|
| 277 |  | 
|---|
| 278 | static void thermal_zone_broken_disable(struct thermal_zone_device *tz) | 
|---|
| 279 | { | 
|---|
| 280 | struct thermal_trip_desc *td; | 
|---|
| 281 |  | 
|---|
| 282 | dev_err(&tz->device, "Unable to get temperature, disabling!\n"); | 
|---|
| 283 | /* | 
|---|
| 284 | * This function only runs for enabled thermal zones, so no need to | 
|---|
| 285 | * check for the current mode. | 
|---|
| 286 | */ | 
|---|
| 287 | __thermal_zone_device_set_mode(tz, mode: THERMAL_DEVICE_DISABLED); | 
|---|
| 288 | thermal_notify_tz_disable(tz); | 
|---|
| 289 |  | 
|---|
| 290 | for_each_trip_desc(tz, td) { | 
|---|
| 291 | if (td->trip.type == THERMAL_TRIP_CRITICAL && | 
|---|
| 292 | td->trip.temperature > THERMAL_TEMP_INVALID) { | 
|---|
| 293 | dev_crit(&tz->device, | 
|---|
| 294 | "Disabled thermal zone with critical trip point\n"); | 
|---|
| 295 | return; | 
|---|
| 296 | } | 
|---|
| 297 | } | 
|---|
| 298 | } | 
|---|
| 299 |  | 
|---|
| 300 | /* | 
|---|
| 301 | * Zone update section: main control loop applied to each zone while monitoring | 
|---|
| 302 | * in polling mode. The monitoring is done using a workqueue. | 
|---|
| 303 | * Same update may be done on a zone by calling thermal_zone_device_update(). | 
|---|
| 304 | * | 
|---|
| 305 | * An update means: | 
|---|
| 306 | * - Non-critical trips will invoke the governor responsible for that zone; | 
|---|
| 307 | * - Hot trips will produce a notification to userspace; | 
|---|
| 308 | * - Critical trip point will cause a system shutdown. | 
|---|
| 309 | */ | 
|---|
| 310 | static void thermal_zone_device_set_polling(struct thermal_zone_device *tz, | 
|---|
| 311 | unsigned long delay) | 
|---|
| 312 | { | 
|---|
| 313 | if (delay > HZ) | 
|---|
| 314 | delay = round_jiffies_relative(j: delay); | 
|---|
| 315 |  | 
|---|
| 316 | mod_delayed_work(wq: system_freezable_power_efficient_wq, dwork: &tz->poll_queue, delay); | 
|---|
| 317 | } | 
|---|
| 318 |  | 
|---|
| 319 | static void thermal_zone_recheck(struct thermal_zone_device *tz, int error) | 
|---|
| 320 | { | 
|---|
| 321 | if (error == -EAGAIN) { | 
|---|
| 322 | thermal_zone_device_set_polling(tz, THERMAL_RECHECK_DELAY); | 
|---|
| 323 | return; | 
|---|
| 324 | } | 
|---|
| 325 |  | 
|---|
| 326 | /* | 
|---|
| 327 | * Print the message once to reduce log noise.  It will be followed by | 
|---|
| 328 | * another one if the temperature cannot be determined after multiple | 
|---|
| 329 | * attempts. | 
|---|
| 330 | */ | 
|---|
| 331 | if (tz->recheck_delay_jiffies == THERMAL_RECHECK_DELAY) | 
|---|
| 332 | dev_info(&tz->device, "Temperature check failed (%d)\n", error); | 
|---|
| 333 |  | 
|---|
| 334 | thermal_zone_device_set_polling(tz, delay: tz->recheck_delay_jiffies); | 
|---|
| 335 |  | 
|---|
| 336 | tz->recheck_delay_jiffies += max(tz->recheck_delay_jiffies >> 1, 1ULL); | 
|---|
| 337 | if (tz->recheck_delay_jiffies > THERMAL_MAX_RECHECK_DELAY) { | 
|---|
| 338 | thermal_zone_broken_disable(tz); | 
|---|
| 339 | /* | 
|---|
| 340 | * Restore the original recheck delay value to allow the thermal | 
|---|
| 341 | * zone to try to recover when it is reenabled by user space. | 
|---|
| 342 | */ | 
|---|
| 343 | tz->recheck_delay_jiffies = THERMAL_RECHECK_DELAY; | 
|---|
| 344 | } | 
|---|
| 345 | } | 
|---|
| 346 |  | 
|---|
| 347 | static void monitor_thermal_zone(struct thermal_zone_device *tz) | 
|---|
| 348 | { | 
|---|
| 349 | if (tz->passive > 0 && tz->passive_delay_jiffies) | 
|---|
| 350 | thermal_zone_device_set_polling(tz, delay: tz->passive_delay_jiffies); | 
|---|
| 351 | else if (tz->polling_delay_jiffies) | 
|---|
| 352 | thermal_zone_device_set_polling(tz, delay: tz->polling_delay_jiffies); | 
|---|
| 353 | } | 
|---|
| 354 |  | 
|---|
| 355 | static struct thermal_governor *thermal_get_tz_governor(struct thermal_zone_device *tz) | 
|---|
| 356 | { | 
|---|
| 357 | if (tz->governor) | 
|---|
| 358 | return tz->governor; | 
|---|
| 359 |  | 
|---|
| 360 | return def_governor; | 
|---|
| 361 | } | 
|---|
| 362 |  | 
|---|
| 363 | void thermal_governor_update_tz(struct thermal_zone_device *tz, | 
|---|
| 364 | enum thermal_notify_event reason) | 
|---|
| 365 | { | 
|---|
| 366 | if (!tz->governor || !tz->governor->update_tz) | 
|---|
| 367 | return; | 
|---|
| 368 |  | 
|---|
| 369 | tz->governor->update_tz(tz, reason); | 
|---|
| 370 | } | 
|---|
| 371 |  | 
|---|
| 372 | static void thermal_zone_device_halt(struct thermal_zone_device *tz, | 
|---|
| 373 | enum hw_protection_action action) | 
|---|
| 374 | { | 
|---|
| 375 | /* | 
|---|
| 376 | * poweroff_delay_ms must be a carefully profiled positive value. | 
|---|
| 377 | * Its a must for forced_emergency_poweroff_work to be scheduled. | 
|---|
| 378 | */ | 
|---|
| 379 | int poweroff_delay_ms = CONFIG_THERMAL_EMERGENCY_POWEROFF_DELAY_MS; | 
|---|
| 380 | const char *msg = "Temperature too high"; | 
|---|
| 381 |  | 
|---|
| 382 | dev_emerg(&tz->device, "%s: critical temperature reached\n", tz->type); | 
|---|
| 383 |  | 
|---|
| 384 | __hw_protection_trigger(reason: msg, ms_until_forced: poweroff_delay_ms, action); | 
|---|
| 385 | } | 
|---|
| 386 |  | 
|---|
| 387 | void thermal_zone_device_critical(struct thermal_zone_device *tz) | 
|---|
| 388 | { | 
|---|
| 389 | thermal_zone_device_halt(tz, action: HWPROT_ACT_DEFAULT); | 
|---|
| 390 | } | 
|---|
| 391 | EXPORT_SYMBOL(thermal_zone_device_critical); | 
|---|
| 392 |  | 
|---|
| 393 | void thermal_zone_device_critical_shutdown(struct thermal_zone_device *tz) | 
|---|
| 394 | { | 
|---|
| 395 | thermal_zone_device_halt(tz, action: HWPROT_ACT_SHUTDOWN); | 
|---|
| 396 | } | 
|---|
| 397 |  | 
|---|
| 398 | void thermal_zone_device_critical_reboot(struct thermal_zone_device *tz) | 
|---|
| 399 | { | 
|---|
| 400 | thermal_zone_device_halt(tz, action: HWPROT_ACT_REBOOT); | 
|---|
| 401 | } | 
|---|
| 402 |  | 
|---|
| 403 | static void handle_critical_trips(struct thermal_zone_device *tz, | 
|---|
| 404 | const struct thermal_trip *trip) | 
|---|
| 405 | { | 
|---|
| 406 | trace_thermal_zone_trip(tz, trip: thermal_zone_trip_id(tz, trip), trip_type: trip->type); | 
|---|
| 407 |  | 
|---|
| 408 | if (trip->type == THERMAL_TRIP_CRITICAL) | 
|---|
| 409 | tz->ops.critical(tz); | 
|---|
| 410 | else if (tz->ops.hot) | 
|---|
| 411 | tz->ops.hot(tz); | 
|---|
| 412 | } | 
|---|
| 413 |  | 
|---|
| 414 | static void move_trip_to_sorted_list(struct thermal_trip_desc *td, | 
|---|
| 415 | struct list_head *list) | 
|---|
| 416 | { | 
|---|
| 417 | struct thermal_trip_desc *entry; | 
|---|
| 418 |  | 
|---|
| 419 | /* | 
|---|
| 420 | * Delete upfront and then add to make relocation within the same list | 
|---|
| 421 | * work. | 
|---|
| 422 | */ | 
|---|
| 423 | list_del(entry: &td->list_node); | 
|---|
| 424 |  | 
|---|
| 425 | /* Assume that the new entry is likely to be the last one. */ | 
|---|
| 426 | list_for_each_entry_reverse(entry, list, list_node) { | 
|---|
| 427 | if (entry->threshold <= td->threshold) { | 
|---|
| 428 | list_add(new: &td->list_node, head: &entry->list_node); | 
|---|
| 429 | return; | 
|---|
| 430 | } | 
|---|
| 431 | } | 
|---|
| 432 | list_add(new: &td->list_node, head: list); | 
|---|
| 433 | } | 
|---|
| 434 |  | 
|---|
| 435 | static void move_to_trips_high(struct thermal_zone_device *tz, | 
|---|
| 436 | struct thermal_trip_desc *td) | 
|---|
| 437 | { | 
|---|
| 438 | td->threshold = td->trip.temperature; | 
|---|
| 439 | move_trip_to_sorted_list(td, list: &tz->trips_high); | 
|---|
| 440 | } | 
|---|
| 441 |  | 
|---|
| 442 | static void move_to_trips_reached(struct thermal_zone_device *tz, | 
|---|
| 443 | struct thermal_trip_desc *td) | 
|---|
| 444 | { | 
|---|
| 445 | td->threshold = td->trip.temperature - td->trip.hysteresis; | 
|---|
| 446 | move_trip_to_sorted_list(td, list: &tz->trips_reached); | 
|---|
| 447 | } | 
|---|
| 448 |  | 
|---|
| 449 | static void move_to_trips_invalid(struct thermal_zone_device *tz, | 
|---|
| 450 | struct thermal_trip_desc *td) | 
|---|
| 451 | { | 
|---|
| 452 | td->threshold = INT_MAX; | 
|---|
| 453 | list_move(list: &td->list_node, head: &tz->trips_invalid); | 
|---|
| 454 | } | 
|---|
| 455 |  | 
|---|
| 456 | static void thermal_governor_trip_crossed(struct thermal_governor *governor, | 
|---|
| 457 | struct thermal_zone_device *tz, | 
|---|
| 458 | const struct thermal_trip *trip, | 
|---|
| 459 | bool upward) | 
|---|
| 460 | { | 
|---|
| 461 | if (trip->type == THERMAL_TRIP_HOT || trip->type == THERMAL_TRIP_CRITICAL) | 
|---|
| 462 | return; | 
|---|
| 463 |  | 
|---|
| 464 | if (governor->trip_crossed) | 
|---|
| 465 | governor->trip_crossed(tz, trip, upward); | 
|---|
| 466 | } | 
|---|
| 467 |  | 
|---|
| 468 | static void thermal_trip_crossed(struct thermal_zone_device *tz, | 
|---|
| 469 | struct thermal_trip_desc *td, | 
|---|
| 470 | struct thermal_governor *governor, | 
|---|
| 471 | bool upward) | 
|---|
| 472 | { | 
|---|
| 473 | const struct thermal_trip *trip = &td->trip; | 
|---|
| 474 |  | 
|---|
| 475 | if (upward) { | 
|---|
| 476 | if (trip->type == THERMAL_TRIP_PASSIVE) | 
|---|
| 477 | tz->passive++; | 
|---|
| 478 | else if (trip->type == THERMAL_TRIP_CRITICAL || | 
|---|
| 479 | trip->type == THERMAL_TRIP_HOT) | 
|---|
| 480 | handle_critical_trips(tz, trip); | 
|---|
| 481 |  | 
|---|
| 482 | thermal_notify_tz_trip_up(tz, trip); | 
|---|
| 483 | thermal_debug_tz_trip_up(tz, trip); | 
|---|
| 484 | } else { | 
|---|
| 485 | if (trip->type == THERMAL_TRIP_PASSIVE) { | 
|---|
| 486 | tz->passive--; | 
|---|
| 487 | WARN_ON(tz->passive < 0); | 
|---|
| 488 | } | 
|---|
| 489 | thermal_notify_tz_trip_down(tz, trip); | 
|---|
| 490 | thermal_debug_tz_trip_down(tz, trip); | 
|---|
| 491 | } | 
|---|
| 492 | thermal_governor_trip_crossed(governor, tz, trip, upward); | 
|---|
| 493 | } | 
|---|
| 494 |  | 
|---|
| 495 | void thermal_zone_set_trip_hyst(struct thermal_zone_device *tz, | 
|---|
| 496 | struct thermal_trip *trip, int hyst) | 
|---|
| 497 | { | 
|---|
| 498 | struct thermal_trip_desc *td = trip_to_trip_desc(trip); | 
|---|
| 499 |  | 
|---|
| 500 | WRITE_ONCE(trip->hysteresis, hyst); | 
|---|
| 501 | thermal_notify_tz_trip_change(tz, trip); | 
|---|
| 502 | /* | 
|---|
| 503 | * If the zone temperature is above or at the trip tmperature, the trip | 
|---|
| 504 | * is in the trips_reached list and its threshold is equal to its low | 
|---|
| 505 | * temperature.  It needs to stay in that list, but its threshold needs | 
|---|
| 506 | * to be updated and the list ordering may need to be restored. | 
|---|
| 507 | */ | 
|---|
| 508 | if (tz->temperature >= td->threshold) | 
|---|
| 509 | move_to_trips_reached(tz, td); | 
|---|
| 510 | } | 
|---|
| 511 |  | 
|---|
| 512 | void thermal_zone_set_trip_temp(struct thermal_zone_device *tz, | 
|---|
| 513 | struct thermal_trip *trip, int temp) | 
|---|
| 514 | { | 
|---|
| 515 | struct thermal_trip_desc *td = trip_to_trip_desc(trip); | 
|---|
| 516 | int old_temp = trip->temperature; | 
|---|
| 517 |  | 
|---|
| 518 | if (old_temp == temp) | 
|---|
| 519 | return; | 
|---|
| 520 |  | 
|---|
| 521 | WRITE_ONCE(trip->temperature, temp); | 
|---|
| 522 | thermal_notify_tz_trip_change(tz, trip); | 
|---|
| 523 |  | 
|---|
| 524 | if (old_temp == THERMAL_TEMP_INVALID) { | 
|---|
| 525 | /* | 
|---|
| 526 | * The trip was invalid before the change, so move it to the | 
|---|
| 527 | * trips_high list regardless of the new temperature value | 
|---|
| 528 | * because there is no mitigation under way for it.  If a | 
|---|
| 529 | * mitigation needs to be started, the trip will be moved to the | 
|---|
| 530 | * trips_reached list later. | 
|---|
| 531 | */ | 
|---|
| 532 | move_to_trips_high(tz, td); | 
|---|
| 533 | return; | 
|---|
| 534 | } | 
|---|
| 535 |  | 
|---|
| 536 | if (temp == THERMAL_TEMP_INVALID) { | 
|---|
| 537 | /* | 
|---|
| 538 | * If the trip is in the trips_reached list, mitigation is under | 
|---|
| 539 | * way for it and it needs to be stopped because the trip is | 
|---|
| 540 | * effectively going away. | 
|---|
| 541 | */ | 
|---|
| 542 | if (tz->temperature >= td->threshold) | 
|---|
| 543 | thermal_trip_crossed(tz, td, governor: thermal_get_tz_governor(tz), upward: false); | 
|---|
| 544 |  | 
|---|
| 545 | move_to_trips_invalid(tz, td); | 
|---|
| 546 | return; | 
|---|
| 547 | } | 
|---|
| 548 |  | 
|---|
| 549 | /* | 
|---|
| 550 | * The trip stays on its current list, but its threshold needs to be | 
|---|
| 551 | * updated due to the temperature change and the list ordering may need | 
|---|
| 552 | * to be restored. | 
|---|
| 553 | */ | 
|---|
| 554 | if (tz->temperature >= td->threshold) | 
|---|
| 555 | move_to_trips_reached(tz, td); | 
|---|
| 556 | else | 
|---|
| 557 | move_to_trips_high(tz, td); | 
|---|
| 558 | } | 
|---|
| 559 | EXPORT_SYMBOL_GPL(thermal_zone_set_trip_temp); | 
|---|
| 560 |  | 
|---|
| 561 | static void thermal_zone_handle_trips(struct thermal_zone_device *tz, | 
|---|
| 562 | struct thermal_governor *governor, | 
|---|
| 563 | int *low, int *high) | 
|---|
| 564 | { | 
|---|
| 565 | struct thermal_trip_desc *td, *next; | 
|---|
| 566 | LIST_HEAD(way_down_list); | 
|---|
| 567 |  | 
|---|
| 568 | /* Check the trips that were below or at the zone temperature. */ | 
|---|
| 569 | list_for_each_entry_safe_reverse(td, next, &tz->trips_reached, list_node) { | 
|---|
| 570 | if (td->threshold <= tz->temperature) | 
|---|
| 571 | break; | 
|---|
| 572 |  | 
|---|
| 573 | thermal_trip_crossed(tz, td, governor, upward: false); | 
|---|
| 574 | /* | 
|---|
| 575 | * The current trips_high list needs to be processed before | 
|---|
| 576 | * adding new entries to it, so put them on a temporary list. | 
|---|
| 577 | */ | 
|---|
| 578 | list_move(list: &td->list_node, head: &way_down_list); | 
|---|
| 579 | } | 
|---|
| 580 | /* Check the trips that were previously above the zone temperature. */ | 
|---|
| 581 | list_for_each_entry_safe(td, next, &tz->trips_high, list_node) { | 
|---|
| 582 | if (td->threshold > tz->temperature) | 
|---|
| 583 | break; | 
|---|
| 584 |  | 
|---|
| 585 | thermal_trip_crossed(tz, td, governor, upward: true); | 
|---|
| 586 | move_to_trips_reached(tz, td); | 
|---|
| 587 | } | 
|---|
| 588 | /* Move all of the trips from the temporary list to trips_high. */ | 
|---|
| 589 | list_for_each_entry_safe(td, next, &way_down_list, list_node) | 
|---|
| 590 | move_to_trips_high(tz, td); | 
|---|
| 591 |  | 
|---|
| 592 | if (!list_empty(head: &tz->trips_reached)) { | 
|---|
| 593 | td = list_last_entry(&tz->trips_reached, | 
|---|
| 594 | struct thermal_trip_desc, list_node); | 
|---|
| 595 | /* | 
|---|
| 596 | * Set the "low" value below the current trip threshold in case | 
|---|
| 597 | * the zone temperature is at that threshold and stays there, | 
|---|
| 598 | * which would trigger a new interrupt immediately in vain. | 
|---|
| 599 | */ | 
|---|
| 600 | *low = td->threshold - 1; | 
|---|
| 601 | } | 
|---|
| 602 | if (!list_empty(head: &tz->trips_high)) { | 
|---|
| 603 | td = list_first_entry(&tz->trips_high, | 
|---|
| 604 | struct thermal_trip_desc, list_node); | 
|---|
| 605 | *high = td->threshold; | 
|---|
| 606 | } | 
|---|
| 607 | } | 
|---|
| 608 |  | 
|---|
| 609 | void __thermal_zone_device_update(struct thermal_zone_device *tz, | 
|---|
| 610 | enum thermal_notify_event event) | 
|---|
| 611 | { | 
|---|
| 612 | struct thermal_governor *governor = thermal_get_tz_governor(tz); | 
|---|
| 613 | int low = -INT_MAX, high = INT_MAX; | 
|---|
| 614 | int temp, ret; | 
|---|
| 615 |  | 
|---|
| 616 | if (tz->state != TZ_STATE_READY || tz->mode != THERMAL_DEVICE_ENABLED) | 
|---|
| 617 | return; | 
|---|
| 618 |  | 
|---|
| 619 | ret = __thermal_zone_get_temp(tz, temp: &temp); | 
|---|
| 620 | if (ret) { | 
|---|
| 621 | thermal_zone_recheck(tz, error: ret); | 
|---|
| 622 | return; | 
|---|
| 623 | } else if (temp <= THERMAL_TEMP_INVALID) { | 
|---|
| 624 | /* | 
|---|
| 625 | * Special case: No valid temperature value is available, but | 
|---|
| 626 | * the zone owner does not want the core to do anything about | 
|---|
| 627 | * it.  Continue regular zone polling if needed, so that this | 
|---|
| 628 | * function can be called again, but skip everything else. | 
|---|
| 629 | */ | 
|---|
| 630 | goto monitor; | 
|---|
| 631 | } | 
|---|
| 632 |  | 
|---|
| 633 | tz->recheck_delay_jiffies = THERMAL_RECHECK_DELAY; | 
|---|
| 634 |  | 
|---|
| 635 | tz->last_temperature = tz->temperature; | 
|---|
| 636 | tz->temperature = temp; | 
|---|
| 637 |  | 
|---|
| 638 | trace_thermal_temperature(tz); | 
|---|
| 639 |  | 
|---|
| 640 | thermal_genl_sampling_temp(id: tz->id, temp); | 
|---|
| 641 |  | 
|---|
| 642 | tz->notify_event = event; | 
|---|
| 643 |  | 
|---|
| 644 | thermal_zone_handle_trips(tz, governor, low: &low, high: &high); | 
|---|
| 645 |  | 
|---|
| 646 | thermal_thresholds_handle(tz, low: &low, high: &high); | 
|---|
| 647 |  | 
|---|
| 648 | thermal_zone_set_trips(tz, low, high); | 
|---|
| 649 |  | 
|---|
| 650 | if (governor->manage) | 
|---|
| 651 | governor->manage(tz); | 
|---|
| 652 |  | 
|---|
| 653 | thermal_debug_update_trip_stats(tz); | 
|---|
| 654 |  | 
|---|
| 655 | monitor: | 
|---|
| 656 | monitor_thermal_zone(tz); | 
|---|
| 657 | } | 
|---|
| 658 |  | 
|---|
| 659 | static int thermal_zone_device_set_mode(struct thermal_zone_device *tz, | 
|---|
| 660 | enum thermal_device_mode mode) | 
|---|
| 661 | { | 
|---|
| 662 | int ret; | 
|---|
| 663 |  | 
|---|
| 664 | guard(thermal_zone)(T: tz); | 
|---|
| 665 |  | 
|---|
| 666 | /* do nothing if mode isn't changing */ | 
|---|
| 667 | if (mode == tz->mode) | 
|---|
| 668 | return 0; | 
|---|
| 669 |  | 
|---|
| 670 | ret = __thermal_zone_device_set_mode(tz, mode); | 
|---|
| 671 | if (ret) | 
|---|
| 672 | return ret; | 
|---|
| 673 |  | 
|---|
| 674 | __thermal_zone_device_update(tz, event: THERMAL_EVENT_UNSPECIFIED); | 
|---|
| 675 |  | 
|---|
| 676 | if (mode == THERMAL_DEVICE_ENABLED) | 
|---|
| 677 | thermal_notify_tz_enable(tz); | 
|---|
| 678 | else | 
|---|
| 679 | thermal_notify_tz_disable(tz); | 
|---|
| 680 |  | 
|---|
| 681 | return 0; | 
|---|
| 682 | } | 
|---|
| 683 |  | 
|---|
| 684 | int thermal_zone_device_enable(struct thermal_zone_device *tz) | 
|---|
| 685 | { | 
|---|
| 686 | return thermal_zone_device_set_mode(tz, mode: THERMAL_DEVICE_ENABLED); | 
|---|
| 687 | } | 
|---|
| 688 | EXPORT_SYMBOL_GPL(thermal_zone_device_enable); | 
|---|
| 689 |  | 
|---|
| 690 | int thermal_zone_device_disable(struct thermal_zone_device *tz) | 
|---|
| 691 | { | 
|---|
| 692 | return thermal_zone_device_set_mode(tz, mode: THERMAL_DEVICE_DISABLED); | 
|---|
| 693 | } | 
|---|
| 694 | EXPORT_SYMBOL_GPL(thermal_zone_device_disable); | 
|---|
| 695 |  | 
|---|
| 696 | static bool thermal_zone_is_present(struct thermal_zone_device *tz) | 
|---|
| 697 | { | 
|---|
| 698 | return !list_empty(head: &tz->node); | 
|---|
| 699 | } | 
|---|
| 700 |  | 
|---|
| 701 | void thermal_zone_device_update(struct thermal_zone_device *tz, | 
|---|
| 702 | enum thermal_notify_event event) | 
|---|
| 703 | { | 
|---|
| 704 | guard(thermal_zone)(T: tz); | 
|---|
| 705 |  | 
|---|
| 706 | if (thermal_zone_is_present(tz)) | 
|---|
| 707 | __thermal_zone_device_update(tz, event); | 
|---|
| 708 | } | 
|---|
| 709 | EXPORT_SYMBOL_GPL(thermal_zone_device_update); | 
|---|
| 710 |  | 
|---|
| 711 | int for_each_thermal_governor(int (*cb)(struct thermal_governor *, void *), | 
|---|
| 712 | void *data) | 
|---|
| 713 | { | 
|---|
| 714 | struct thermal_governor *gov; | 
|---|
| 715 |  | 
|---|
| 716 | guard(mutex)(T: &thermal_governor_lock); | 
|---|
| 717 |  | 
|---|
| 718 | list_for_each_entry(gov, &thermal_governor_list, governor_list) { | 
|---|
| 719 | int ret; | 
|---|
| 720 |  | 
|---|
| 721 | ret = cb(gov, data); | 
|---|
| 722 | if (ret) | 
|---|
| 723 | return ret; | 
|---|
| 724 | } | 
|---|
| 725 |  | 
|---|
| 726 | return 0; | 
|---|
| 727 | } | 
|---|
| 728 |  | 
|---|
| 729 | int for_each_thermal_cooling_device(int (*cb)(struct thermal_cooling_device *, | 
|---|
| 730 | void *), void *data) | 
|---|
| 731 | { | 
|---|
| 732 | struct thermal_cooling_device *cdev; | 
|---|
| 733 |  | 
|---|
| 734 | guard(mutex)(T: &thermal_list_lock); | 
|---|
| 735 |  | 
|---|
| 736 | list_for_each_entry(cdev, &thermal_cdev_list, node) { | 
|---|
| 737 | int ret; | 
|---|
| 738 |  | 
|---|
| 739 | ret = cb(cdev, data); | 
|---|
| 740 | if (ret) | 
|---|
| 741 | return ret; | 
|---|
| 742 | } | 
|---|
| 743 |  | 
|---|
| 744 | return 0; | 
|---|
| 745 | } | 
|---|
| 746 |  | 
|---|
| 747 | int for_each_thermal_zone(int (*cb)(struct thermal_zone_device *, void *), | 
|---|
| 748 | void *data) | 
|---|
| 749 | { | 
|---|
| 750 | struct thermal_zone_device *tz; | 
|---|
| 751 |  | 
|---|
| 752 | guard(mutex)(T: &thermal_list_lock); | 
|---|
| 753 |  | 
|---|
| 754 | list_for_each_entry(tz, &thermal_tz_list, node) { | 
|---|
| 755 | int ret; | 
|---|
| 756 |  | 
|---|
| 757 | ret = cb(tz, data); | 
|---|
| 758 | if (ret) | 
|---|
| 759 | return ret; | 
|---|
| 760 | } | 
|---|
| 761 |  | 
|---|
| 762 | return 0; | 
|---|
| 763 | } | 
|---|
| 764 |  | 
|---|
| 765 | struct thermal_zone_device *thermal_zone_get_by_id(int id) | 
|---|
| 766 | { | 
|---|
| 767 | struct thermal_zone_device *tz; | 
|---|
| 768 |  | 
|---|
| 769 | guard(mutex)(T: &thermal_list_lock); | 
|---|
| 770 |  | 
|---|
| 771 | list_for_each_entry(tz, &thermal_tz_list, node) { | 
|---|
| 772 | if (tz->id == id) { | 
|---|
| 773 | get_device(dev: &tz->device); | 
|---|
| 774 | return tz; | 
|---|
| 775 | } | 
|---|
| 776 | } | 
|---|
| 777 |  | 
|---|
| 778 | return NULL; | 
|---|
| 779 | } | 
|---|
| 780 |  | 
|---|
| 781 | /* | 
|---|
| 782 | * Device management section: cooling devices, zones devices, and binding | 
|---|
| 783 | * | 
|---|
| 784 | * Set of functions provided by the thermal core for: | 
|---|
| 785 | * - cooling devices lifecycle: registration, unregistration, | 
|---|
| 786 | *				binding, and unbinding. | 
|---|
| 787 | * - thermal zone devices lifecycle: registration, unregistration, | 
|---|
| 788 | *				     binding, and unbinding. | 
|---|
| 789 | */ | 
|---|
| 790 |  | 
|---|
| 791 | static int thermal_instance_add(struct thermal_instance *new_instance, | 
|---|
| 792 | struct thermal_cooling_device *cdev, | 
|---|
| 793 | struct thermal_trip_desc *td) | 
|---|
| 794 | { | 
|---|
| 795 | struct thermal_instance *instance; | 
|---|
| 796 |  | 
|---|
| 797 | list_for_each_entry(instance, &td->thermal_instances, trip_node) { | 
|---|
| 798 | if (instance->cdev == cdev) | 
|---|
| 799 | return -EEXIST; | 
|---|
| 800 | } | 
|---|
| 801 |  | 
|---|
| 802 | list_add_tail(new: &new_instance->trip_node, head: &td->thermal_instances); | 
|---|
| 803 |  | 
|---|
| 804 | guard(cooling_dev)(T: cdev); | 
|---|
| 805 |  | 
|---|
| 806 | list_add_tail(new: &new_instance->cdev_node, head: &cdev->thermal_instances); | 
|---|
| 807 |  | 
|---|
| 808 | return 0; | 
|---|
| 809 | } | 
|---|
| 810 |  | 
|---|
| 811 | /** | 
|---|
| 812 | * thermal_bind_cdev_to_trip - bind a cooling device to a thermal zone | 
|---|
| 813 | * @tz:		pointer to struct thermal_zone_device | 
|---|
| 814 | * @td:		descriptor of the trip point to bind @cdev to | 
|---|
| 815 | * @cdev:	pointer to struct thermal_cooling_device | 
|---|
| 816 | * @cool_spec:	cooling specification for the trip point and @cdev | 
|---|
| 817 | * | 
|---|
| 818 | * This interface function bind a thermal cooling device to the certain trip | 
|---|
| 819 | * point of a thermal zone device. | 
|---|
| 820 | * This function is usually called in the thermal zone device .bind callback. | 
|---|
| 821 | * | 
|---|
| 822 | * Return: 0 on success, the proper error value otherwise. | 
|---|
| 823 | */ | 
|---|
| 824 | static int thermal_bind_cdev_to_trip(struct thermal_zone_device *tz, | 
|---|
| 825 | struct thermal_trip_desc *td, | 
|---|
| 826 | struct thermal_cooling_device *cdev, | 
|---|
| 827 | struct cooling_spec *cool_spec) | 
|---|
| 828 | { | 
|---|
| 829 | struct thermal_instance *dev; | 
|---|
| 830 | bool upper_no_limit; | 
|---|
| 831 | int result; | 
|---|
| 832 |  | 
|---|
| 833 | /* lower default 0, upper default max_state */ | 
|---|
| 834 | if (cool_spec->lower == THERMAL_NO_LIMIT) | 
|---|
| 835 | cool_spec->lower = 0; | 
|---|
| 836 |  | 
|---|
| 837 | if (cool_spec->upper == THERMAL_NO_LIMIT) { | 
|---|
| 838 | cool_spec->upper = cdev->max_state; | 
|---|
| 839 | upper_no_limit = true; | 
|---|
| 840 | } else { | 
|---|
| 841 | upper_no_limit = false; | 
|---|
| 842 | } | 
|---|
| 843 |  | 
|---|
| 844 | if (cool_spec->lower > cool_spec->upper || cool_spec->upper > cdev->max_state) | 
|---|
| 845 | return -EINVAL; | 
|---|
| 846 |  | 
|---|
| 847 | dev = kzalloc(sizeof(*dev), GFP_KERNEL); | 
|---|
| 848 | if (!dev) | 
|---|
| 849 | return -ENOMEM; | 
|---|
| 850 |  | 
|---|
| 851 | dev->cdev = cdev; | 
|---|
| 852 | dev->trip = &td->trip; | 
|---|
| 853 | dev->upper = cool_spec->upper; | 
|---|
| 854 | dev->upper_no_limit = upper_no_limit; | 
|---|
| 855 | dev->lower = cool_spec->lower; | 
|---|
| 856 | dev->target = THERMAL_NO_TARGET; | 
|---|
| 857 | dev->weight = cool_spec->weight; | 
|---|
| 858 |  | 
|---|
| 859 | result = ida_alloc(ida: &tz->ida, GFP_KERNEL); | 
|---|
| 860 | if (result < 0) | 
|---|
| 861 | goto free_mem; | 
|---|
| 862 |  | 
|---|
| 863 | dev->id = result; | 
|---|
| 864 | sprintf(buf: dev->name, fmt: "cdev%d", dev->id); | 
|---|
| 865 | result = | 
|---|
| 866 | sysfs_create_link(kobj: &tz->device.kobj, target: &cdev->device.kobj, name: dev->name); | 
|---|
| 867 | if (result) | 
|---|
| 868 | goto release_ida; | 
|---|
| 869 |  | 
|---|
| 870 | snprintf(buf: dev->attr_name, size: sizeof(dev->attr_name), fmt: "cdev%d_trip_point", | 
|---|
| 871 | dev->id); | 
|---|
| 872 | sysfs_attr_init(&dev->attr.attr); | 
|---|
| 873 | dev->attr.attr.name = dev->attr_name; | 
|---|
| 874 | dev->attr.attr.mode = 0444; | 
|---|
| 875 | dev->attr.show = trip_point_show; | 
|---|
| 876 | result = device_create_file(device: &tz->device, entry: &dev->attr); | 
|---|
| 877 | if (result) | 
|---|
| 878 | goto remove_symbol_link; | 
|---|
| 879 |  | 
|---|
| 880 | snprintf(buf: dev->weight_attr_name, size: sizeof(dev->weight_attr_name), | 
|---|
| 881 | fmt: "cdev%d_weight", dev->id); | 
|---|
| 882 | sysfs_attr_init(&dev->weight_attr.attr); | 
|---|
| 883 | dev->weight_attr.attr.name = dev->weight_attr_name; | 
|---|
| 884 | dev->weight_attr.attr.mode = S_IWUSR | S_IRUGO; | 
|---|
| 885 | dev->weight_attr.show = weight_show; | 
|---|
| 886 | dev->weight_attr.store = weight_store; | 
|---|
| 887 | result = device_create_file(device: &tz->device, entry: &dev->weight_attr); | 
|---|
| 888 | if (result) | 
|---|
| 889 | goto remove_trip_file; | 
|---|
| 890 |  | 
|---|
| 891 | result = thermal_instance_add(new_instance: dev, cdev, td); | 
|---|
| 892 | if (result) | 
|---|
| 893 | goto remove_weight_file; | 
|---|
| 894 |  | 
|---|
| 895 | thermal_governor_update_tz(tz, reason: THERMAL_TZ_BIND_CDEV); | 
|---|
| 896 |  | 
|---|
| 897 | return 0; | 
|---|
| 898 |  | 
|---|
| 899 | remove_weight_file: | 
|---|
| 900 | device_remove_file(dev: &tz->device, attr: &dev->weight_attr); | 
|---|
| 901 | remove_trip_file: | 
|---|
| 902 | device_remove_file(dev: &tz->device, attr: &dev->attr); | 
|---|
| 903 | remove_symbol_link: | 
|---|
| 904 | sysfs_remove_link(kobj: &tz->device.kobj, name: dev->name); | 
|---|
| 905 | release_ida: | 
|---|
| 906 | ida_free(&tz->ida, id: dev->id); | 
|---|
| 907 | free_mem: | 
|---|
| 908 | kfree(objp: dev); | 
|---|
| 909 | return result; | 
|---|
| 910 | } | 
|---|
| 911 |  | 
|---|
| 912 | static void thermal_instance_delete(struct thermal_instance *instance) | 
|---|
| 913 | { | 
|---|
| 914 | list_del(entry: &instance->trip_node); | 
|---|
| 915 |  | 
|---|
| 916 | guard(cooling_dev)(T: instance->cdev); | 
|---|
| 917 |  | 
|---|
| 918 | list_del(entry: &instance->cdev_node); | 
|---|
| 919 | } | 
|---|
| 920 |  | 
|---|
| 921 | /** | 
|---|
| 922 | * thermal_unbind_cdev_from_trip - unbind a cooling device from a thermal zone. | 
|---|
| 923 | * @tz:		pointer to a struct thermal_zone_device. | 
|---|
| 924 | * @td:		descriptor of the trip point to unbind @cdev from | 
|---|
| 925 | * @cdev:	pointer to a struct thermal_cooling_device. | 
|---|
| 926 | * | 
|---|
| 927 | * This interface function unbind a thermal cooling device from the certain | 
|---|
| 928 | * trip point of a thermal zone device. | 
|---|
| 929 | * This function is usually called in the thermal zone device .unbind callback. | 
|---|
| 930 | */ | 
|---|
| 931 | static void thermal_unbind_cdev_from_trip(struct thermal_zone_device *tz, | 
|---|
| 932 | struct thermal_trip_desc *td, | 
|---|
| 933 | struct thermal_cooling_device *cdev) | 
|---|
| 934 | { | 
|---|
| 935 | struct thermal_instance *pos, *next; | 
|---|
| 936 |  | 
|---|
| 937 | list_for_each_entry_safe(pos, next, &td->thermal_instances, trip_node) { | 
|---|
| 938 | if (pos->cdev == cdev) { | 
|---|
| 939 | thermal_instance_delete(instance: pos); | 
|---|
| 940 | goto unbind; | 
|---|
| 941 | } | 
|---|
| 942 | } | 
|---|
| 943 |  | 
|---|
| 944 | return; | 
|---|
| 945 |  | 
|---|
| 946 | unbind: | 
|---|
| 947 | thermal_governor_update_tz(tz, reason: THERMAL_TZ_UNBIND_CDEV); | 
|---|
| 948 |  | 
|---|
| 949 | device_remove_file(dev: &tz->device, attr: &pos->weight_attr); | 
|---|
| 950 | device_remove_file(dev: &tz->device, attr: &pos->attr); | 
|---|
| 951 | sysfs_remove_link(kobj: &tz->device.kobj, name: pos->name); | 
|---|
| 952 | ida_free(&tz->ida, id: pos->id); | 
|---|
| 953 | kfree(objp: pos); | 
|---|
| 954 | } | 
|---|
| 955 |  | 
|---|
| 956 | static void thermal_release(struct device *dev) | 
|---|
| 957 | { | 
|---|
| 958 | struct thermal_zone_device *tz; | 
|---|
| 959 | struct thermal_cooling_device *cdev; | 
|---|
| 960 |  | 
|---|
| 961 | if (!strncmp(dev_name(dev), "thermal_zone", | 
|---|
| 962 | sizeof( "thermal_zone") - 1)) { | 
|---|
| 963 | tz = to_thermal_zone(dev); | 
|---|
| 964 | thermal_zone_destroy_device_groups(tz); | 
|---|
| 965 | mutex_destroy(lock: &tz->lock); | 
|---|
| 966 | complete(&tz->removal); | 
|---|
| 967 | } else if (!strncmp(dev_name(dev), "cooling_device", | 
|---|
| 968 | sizeof( "cooling_device") - 1)) { | 
|---|
| 969 | cdev = to_cooling_device(dev); | 
|---|
| 970 | thermal_cooling_device_destroy_sysfs(cdev); | 
|---|
| 971 | kfree_const(x: cdev->type); | 
|---|
| 972 | ida_free(&thermal_cdev_ida, id: cdev->id); | 
|---|
| 973 | kfree(objp: cdev); | 
|---|
| 974 | } | 
|---|
| 975 | } | 
|---|
| 976 |  | 
|---|
| 977 | static struct class *thermal_class; | 
|---|
| 978 |  | 
|---|
| 979 | static inline | 
|---|
| 980 | void print_bind_err_msg(struct thermal_zone_device *tz, | 
|---|
| 981 | const struct thermal_trip_desc *td, | 
|---|
| 982 | struct thermal_cooling_device *cdev, int ret) | 
|---|
| 983 | { | 
|---|
| 984 | dev_err(&tz->device, "binding cdev %s to trip %d failed: %d\n", | 
|---|
| 985 | cdev->type, thermal_zone_trip_id(tz, &td->trip), ret); | 
|---|
| 986 | } | 
|---|
| 987 |  | 
|---|
| 988 | static bool __thermal_zone_cdev_bind(struct thermal_zone_device *tz, | 
|---|
| 989 | struct thermal_cooling_device *cdev) | 
|---|
| 990 | { | 
|---|
| 991 | struct thermal_trip_desc *td; | 
|---|
| 992 | bool update_tz = false; | 
|---|
| 993 |  | 
|---|
| 994 | if (!tz->ops.should_bind) | 
|---|
| 995 | return false; | 
|---|
| 996 |  | 
|---|
| 997 | for_each_trip_desc(tz, td) { | 
|---|
| 998 | struct cooling_spec c = { | 
|---|
| 999 | .upper = THERMAL_NO_LIMIT, | 
|---|
| 1000 | .lower = THERMAL_NO_LIMIT, | 
|---|
| 1001 | .weight = THERMAL_WEIGHT_DEFAULT | 
|---|
| 1002 | }; | 
|---|
| 1003 | int ret; | 
|---|
| 1004 |  | 
|---|
| 1005 | if (!tz->ops.should_bind(tz, &td->trip, cdev, &c)) | 
|---|
| 1006 | continue; | 
|---|
| 1007 |  | 
|---|
| 1008 | ret = thermal_bind_cdev_to_trip(tz, td, cdev, cool_spec: &c); | 
|---|
| 1009 | if (ret) { | 
|---|
| 1010 | print_bind_err_msg(tz, td, cdev, ret); | 
|---|
| 1011 | continue; | 
|---|
| 1012 | } | 
|---|
| 1013 |  | 
|---|
| 1014 | update_tz = true; | 
|---|
| 1015 | } | 
|---|
| 1016 |  | 
|---|
| 1017 | return update_tz; | 
|---|
| 1018 | } | 
|---|
| 1019 |  | 
|---|
| 1020 | static void thermal_zone_cdev_bind(struct thermal_zone_device *tz, | 
|---|
| 1021 | struct thermal_cooling_device *cdev) | 
|---|
| 1022 | { | 
|---|
| 1023 | guard(thermal_zone)(T: tz); | 
|---|
| 1024 |  | 
|---|
| 1025 | if (__thermal_zone_cdev_bind(tz, cdev)) | 
|---|
| 1026 | __thermal_zone_device_update(tz, event: THERMAL_EVENT_UNSPECIFIED); | 
|---|
| 1027 | } | 
|---|
| 1028 |  | 
|---|
| 1029 | static void thermal_cooling_device_init_complete(struct thermal_cooling_device *cdev) | 
|---|
| 1030 | { | 
|---|
| 1031 | struct thermal_zone_device *tz; | 
|---|
| 1032 |  | 
|---|
| 1033 | guard(mutex)(T: &thermal_list_lock); | 
|---|
| 1034 |  | 
|---|
| 1035 | list_add(new: &cdev->node, head: &thermal_cdev_list); | 
|---|
| 1036 |  | 
|---|
| 1037 | list_for_each_entry(tz, &thermal_tz_list, node) | 
|---|
| 1038 | thermal_zone_cdev_bind(tz, cdev); | 
|---|
| 1039 | } | 
|---|
| 1040 |  | 
|---|
| 1041 | /** | 
|---|
| 1042 | * __thermal_cooling_device_register() - register a new thermal cooling device | 
|---|
| 1043 | * @np:		a pointer to a device tree node. | 
|---|
| 1044 | * @type:	the thermal cooling device type. | 
|---|
| 1045 | * @devdata:	device private data. | 
|---|
| 1046 | * @ops:		standard thermal cooling devices callbacks. | 
|---|
| 1047 | * | 
|---|
| 1048 | * This interface function adds a new thermal cooling device (fan/processor/...) | 
|---|
| 1049 | * to /sys/class/thermal/ folder as cooling_device[0-*]. It tries to bind itself | 
|---|
| 1050 | * to all the thermal zone devices registered at the same time. | 
|---|
| 1051 | * It also gives the opportunity to link the cooling device to a device tree | 
|---|
| 1052 | * node, so that it can be bound to a thermal zone created out of device tree. | 
|---|
| 1053 | * | 
|---|
| 1054 | * Return: a pointer to the created struct thermal_cooling_device or an | 
|---|
| 1055 | * ERR_PTR. Caller must check return value with IS_ERR*() helpers. | 
|---|
| 1056 | */ | 
|---|
| 1057 | static struct thermal_cooling_device * | 
|---|
| 1058 | __thermal_cooling_device_register(struct device_node *np, | 
|---|
| 1059 | const char *type, void *devdata, | 
|---|
| 1060 | const struct thermal_cooling_device_ops *ops) | 
|---|
| 1061 | { | 
|---|
| 1062 | struct thermal_cooling_device *cdev; | 
|---|
| 1063 | unsigned long current_state; | 
|---|
| 1064 | int id, ret; | 
|---|
| 1065 |  | 
|---|
| 1066 | if (!ops || !ops->get_max_state || !ops->get_cur_state || | 
|---|
| 1067 | !ops->set_cur_state) | 
|---|
| 1068 | return ERR_PTR(error: -EINVAL); | 
|---|
| 1069 |  | 
|---|
| 1070 | if (!thermal_class) | 
|---|
| 1071 | return ERR_PTR(error: -ENODEV); | 
|---|
| 1072 |  | 
|---|
| 1073 | cdev = kzalloc(sizeof(*cdev), GFP_KERNEL); | 
|---|
| 1074 | if (!cdev) | 
|---|
| 1075 | return ERR_PTR(error: -ENOMEM); | 
|---|
| 1076 |  | 
|---|
| 1077 | ret = ida_alloc(ida: &thermal_cdev_ida, GFP_KERNEL); | 
|---|
| 1078 | if (ret < 0) | 
|---|
| 1079 | goto out_kfree_cdev; | 
|---|
| 1080 | cdev->id = ret; | 
|---|
| 1081 | id = ret; | 
|---|
| 1082 |  | 
|---|
| 1083 | cdev->type = kstrdup_const(s: type ? type : "", GFP_KERNEL); | 
|---|
| 1084 | if (!cdev->type) { | 
|---|
| 1085 | ret = -ENOMEM; | 
|---|
| 1086 | goto out_ida_remove; | 
|---|
| 1087 | } | 
|---|
| 1088 |  | 
|---|
| 1089 | mutex_init(&cdev->lock); | 
|---|
| 1090 | INIT_LIST_HEAD(list: &cdev->thermal_instances); | 
|---|
| 1091 | cdev->np = np; | 
|---|
| 1092 | cdev->ops = ops; | 
|---|
| 1093 | cdev->updated = false; | 
|---|
| 1094 | cdev->device.class = thermal_class; | 
|---|
| 1095 | cdev->devdata = devdata; | 
|---|
| 1096 |  | 
|---|
| 1097 | ret = cdev->ops->get_max_state(cdev, &cdev->max_state); | 
|---|
| 1098 | if (ret) | 
|---|
| 1099 | goto out_cdev_type; | 
|---|
| 1100 |  | 
|---|
| 1101 | /* | 
|---|
| 1102 | * The cooling device's current state is only needed for debug | 
|---|
| 1103 | * initialization below, so a failure to get it does not cause | 
|---|
| 1104 | * the entire cooling device initialization to fail.  However, | 
|---|
| 1105 | * the debug will not work for the device if its initial state | 
|---|
| 1106 | * cannot be determined and drivers are responsible for ensuring | 
|---|
| 1107 | * that this will not happen. | 
|---|
| 1108 | */ | 
|---|
| 1109 | ret = cdev->ops->get_cur_state(cdev, ¤t_state); | 
|---|
| 1110 | if (ret) | 
|---|
| 1111 | current_state = ULONG_MAX; | 
|---|
| 1112 |  | 
|---|
| 1113 | thermal_cooling_device_setup_sysfs(cdev); | 
|---|
| 1114 |  | 
|---|
| 1115 | ret = dev_set_name(dev: &cdev->device, name: "cooling_device%d", cdev->id); | 
|---|
| 1116 | if (ret) | 
|---|
| 1117 | goto out_cooling_dev; | 
|---|
| 1118 |  | 
|---|
| 1119 | ret = device_register(dev: &cdev->device); | 
|---|
| 1120 | if (ret) { | 
|---|
| 1121 | /* thermal_release() handles rest of the cleanup */ | 
|---|
| 1122 | put_device(dev: &cdev->device); | 
|---|
| 1123 | return ERR_PTR(error: ret); | 
|---|
| 1124 | } | 
|---|
| 1125 |  | 
|---|
| 1126 | if (current_state <= cdev->max_state) | 
|---|
| 1127 | thermal_debug_cdev_add(cdev, state: current_state); | 
|---|
| 1128 |  | 
|---|
| 1129 | thermal_cooling_device_init_complete(cdev); | 
|---|
| 1130 |  | 
|---|
| 1131 | return cdev; | 
|---|
| 1132 |  | 
|---|
| 1133 | out_cooling_dev: | 
|---|
| 1134 | thermal_cooling_device_destroy_sysfs(cdev); | 
|---|
| 1135 | out_cdev_type: | 
|---|
| 1136 | kfree_const(x: cdev->type); | 
|---|
| 1137 | out_ida_remove: | 
|---|
| 1138 | ida_free(&thermal_cdev_ida, id); | 
|---|
| 1139 | out_kfree_cdev: | 
|---|
| 1140 | kfree(objp: cdev); | 
|---|
| 1141 | return ERR_PTR(error: ret); | 
|---|
| 1142 | } | 
|---|
| 1143 |  | 
|---|
| 1144 | /** | 
|---|
| 1145 | * thermal_cooling_device_register() - register a new thermal cooling device | 
|---|
| 1146 | * @type:	the thermal cooling device type. | 
|---|
| 1147 | * @devdata:	device private data. | 
|---|
| 1148 | * @ops:		standard thermal cooling devices callbacks. | 
|---|
| 1149 | * | 
|---|
| 1150 | * This interface function adds a new thermal cooling device (fan/processor/...) | 
|---|
| 1151 | * to /sys/class/thermal/ folder as cooling_device[0-*]. It tries to bind itself | 
|---|
| 1152 | * to all the thermal zone devices registered at the same time. | 
|---|
| 1153 | * | 
|---|
| 1154 | * Return: a pointer to the created struct thermal_cooling_device or an | 
|---|
| 1155 | * ERR_PTR. Caller must check return value with IS_ERR*() helpers. | 
|---|
| 1156 | */ | 
|---|
| 1157 | struct thermal_cooling_device * | 
|---|
| 1158 | thermal_cooling_device_register(const char *type, void *devdata, | 
|---|
| 1159 | const struct thermal_cooling_device_ops *ops) | 
|---|
| 1160 | { | 
|---|
| 1161 | return __thermal_cooling_device_register(NULL, type, devdata, ops); | 
|---|
| 1162 | } | 
|---|
| 1163 | EXPORT_SYMBOL_GPL(thermal_cooling_device_register); | 
|---|
| 1164 |  | 
|---|
| 1165 | /** | 
|---|
| 1166 | * thermal_of_cooling_device_register() - register an OF thermal cooling device | 
|---|
| 1167 | * @np:		a pointer to a device tree node. | 
|---|
| 1168 | * @type:	the thermal cooling device type. | 
|---|
| 1169 | * @devdata:	device private data. | 
|---|
| 1170 | * @ops:		standard thermal cooling devices callbacks. | 
|---|
| 1171 | * | 
|---|
| 1172 | * This function will register a cooling device with device tree node reference. | 
|---|
| 1173 | * This interface function adds a new thermal cooling device (fan/processor/...) | 
|---|
| 1174 | * to /sys/class/thermal/ folder as cooling_device[0-*]. It tries to bind itself | 
|---|
| 1175 | * to all the thermal zone devices registered at the same time. | 
|---|
| 1176 | * | 
|---|
| 1177 | * Return: a pointer to the created struct thermal_cooling_device or an | 
|---|
| 1178 | * ERR_PTR. Caller must check return value with IS_ERR*() helpers. | 
|---|
| 1179 | */ | 
|---|
| 1180 | struct thermal_cooling_device * | 
|---|
| 1181 | thermal_of_cooling_device_register(struct device_node *np, | 
|---|
| 1182 | const char *type, void *devdata, | 
|---|
| 1183 | const struct thermal_cooling_device_ops *ops) | 
|---|
| 1184 | { | 
|---|
| 1185 | return __thermal_cooling_device_register(np, type, devdata, ops); | 
|---|
| 1186 | } | 
|---|
| 1187 | EXPORT_SYMBOL_GPL(thermal_of_cooling_device_register); | 
|---|
| 1188 |  | 
|---|
| 1189 | static void thermal_cooling_device_release(struct device *dev, void *res) | 
|---|
| 1190 | { | 
|---|
| 1191 | thermal_cooling_device_unregister( | 
|---|
| 1192 | *(struct thermal_cooling_device **)res); | 
|---|
| 1193 | } | 
|---|
| 1194 |  | 
|---|
| 1195 | /** | 
|---|
| 1196 | * devm_thermal_of_cooling_device_register() - register an OF thermal cooling | 
|---|
| 1197 | *					       device | 
|---|
| 1198 | * @dev:	a valid struct device pointer of a sensor device. | 
|---|
| 1199 | * @np:		a pointer to a device tree node. | 
|---|
| 1200 | * @type:	the thermal cooling device type. | 
|---|
| 1201 | * @devdata:	device private data. | 
|---|
| 1202 | * @ops:	standard thermal cooling devices callbacks. | 
|---|
| 1203 | * | 
|---|
| 1204 | * This function will register a cooling device with device tree node reference. | 
|---|
| 1205 | * This interface function adds a new thermal cooling device (fan/processor/...) | 
|---|
| 1206 | * to /sys/class/thermal/ folder as cooling_device[0-*]. It tries to bind itself | 
|---|
| 1207 | * to all the thermal zone devices registered at the same time. | 
|---|
| 1208 | * | 
|---|
| 1209 | * Return: a pointer to the created struct thermal_cooling_device or an | 
|---|
| 1210 | * ERR_PTR. Caller must check return value with IS_ERR*() helpers. | 
|---|
| 1211 | */ | 
|---|
| 1212 | struct thermal_cooling_device * | 
|---|
| 1213 | devm_thermal_of_cooling_device_register(struct device *dev, | 
|---|
| 1214 | struct device_node *np, | 
|---|
| 1215 | const char *type, void *devdata, | 
|---|
| 1216 | const struct thermal_cooling_device_ops *ops) | 
|---|
| 1217 | { | 
|---|
| 1218 | struct thermal_cooling_device **ptr, *tcd; | 
|---|
| 1219 |  | 
|---|
| 1220 | ptr = devres_alloc(thermal_cooling_device_release, sizeof(*ptr), | 
|---|
| 1221 | GFP_KERNEL); | 
|---|
| 1222 | if (!ptr) | 
|---|
| 1223 | return ERR_PTR(error: -ENOMEM); | 
|---|
| 1224 |  | 
|---|
| 1225 | tcd = __thermal_cooling_device_register(np, type, devdata, ops); | 
|---|
| 1226 | if (IS_ERR(ptr: tcd)) { | 
|---|
| 1227 | devres_free(res: ptr); | 
|---|
| 1228 | return tcd; | 
|---|
| 1229 | } | 
|---|
| 1230 |  | 
|---|
| 1231 | *ptr = tcd; | 
|---|
| 1232 | devres_add(dev, res: ptr); | 
|---|
| 1233 |  | 
|---|
| 1234 | return tcd; | 
|---|
| 1235 | } | 
|---|
| 1236 | EXPORT_SYMBOL_GPL(devm_thermal_of_cooling_device_register); | 
|---|
| 1237 |  | 
|---|
| 1238 | static bool thermal_cooling_device_present(struct thermal_cooling_device *cdev) | 
|---|
| 1239 | { | 
|---|
| 1240 | struct thermal_cooling_device *pos = NULL; | 
|---|
| 1241 |  | 
|---|
| 1242 | list_for_each_entry(pos, &thermal_cdev_list, node) { | 
|---|
| 1243 | if (pos == cdev) | 
|---|
| 1244 | return true; | 
|---|
| 1245 | } | 
|---|
| 1246 |  | 
|---|
| 1247 | return false; | 
|---|
| 1248 | } | 
|---|
| 1249 |  | 
|---|
| 1250 | /** | 
|---|
| 1251 | * thermal_cooling_device_update - Update a cooling device object | 
|---|
| 1252 | * @cdev: Target cooling device. | 
|---|
| 1253 | * | 
|---|
| 1254 | * Update @cdev to reflect a change of the underlying hardware or platform. | 
|---|
| 1255 | * | 
|---|
| 1256 | * Must be called when the maximum cooling state of @cdev becomes invalid and so | 
|---|
| 1257 | * its .get_max_state() callback needs to be run to produce the new maximum | 
|---|
| 1258 | * cooling state value. | 
|---|
| 1259 | */ | 
|---|
| 1260 | void thermal_cooling_device_update(struct thermal_cooling_device *cdev) | 
|---|
| 1261 | { | 
|---|
| 1262 | struct thermal_instance *ti; | 
|---|
| 1263 | unsigned long state; | 
|---|
| 1264 |  | 
|---|
| 1265 | if (IS_ERR_OR_NULL(ptr: cdev)) | 
|---|
| 1266 | return; | 
|---|
| 1267 |  | 
|---|
| 1268 | /* | 
|---|
| 1269 | * Hold thermal_list_lock throughout the update to prevent the device | 
|---|
| 1270 | * from going away while being updated. | 
|---|
| 1271 | */ | 
|---|
| 1272 | guard(mutex)(T: &thermal_list_lock); | 
|---|
| 1273 |  | 
|---|
| 1274 | if (!thermal_cooling_device_present(cdev)) | 
|---|
| 1275 | return; | 
|---|
| 1276 |  | 
|---|
| 1277 | /* | 
|---|
| 1278 | * Update under the cdev lock to prevent the state from being set beyond | 
|---|
| 1279 | * the new limit concurrently. | 
|---|
| 1280 | */ | 
|---|
| 1281 | guard(cooling_dev)(T: cdev); | 
|---|
| 1282 |  | 
|---|
| 1283 | if (cdev->ops->get_max_state(cdev, &cdev->max_state)) | 
|---|
| 1284 | return; | 
|---|
| 1285 |  | 
|---|
| 1286 | thermal_cooling_device_stats_reinit(cdev); | 
|---|
| 1287 |  | 
|---|
| 1288 | list_for_each_entry(ti, &cdev->thermal_instances, cdev_node) { | 
|---|
| 1289 | if (ti->upper == cdev->max_state) | 
|---|
| 1290 | continue; | 
|---|
| 1291 |  | 
|---|
| 1292 | if (ti->upper < cdev->max_state) { | 
|---|
| 1293 | if (ti->upper_no_limit) | 
|---|
| 1294 | ti->upper = cdev->max_state; | 
|---|
| 1295 |  | 
|---|
| 1296 | continue; | 
|---|
| 1297 | } | 
|---|
| 1298 |  | 
|---|
| 1299 | ti->upper = cdev->max_state; | 
|---|
| 1300 | if (ti->lower > ti->upper) | 
|---|
| 1301 | ti->lower = ti->upper; | 
|---|
| 1302 |  | 
|---|
| 1303 | if (ti->target == THERMAL_NO_TARGET) | 
|---|
| 1304 | continue; | 
|---|
| 1305 |  | 
|---|
| 1306 | if (ti->target > ti->upper) | 
|---|
| 1307 | ti->target = ti->upper; | 
|---|
| 1308 | } | 
|---|
| 1309 |  | 
|---|
| 1310 | if (cdev->ops->get_cur_state(cdev, &state) || state > cdev->max_state) | 
|---|
| 1311 | return; | 
|---|
| 1312 |  | 
|---|
| 1313 | thermal_cooling_device_stats_update(cdev, new_state: state); | 
|---|
| 1314 | } | 
|---|
| 1315 | EXPORT_SYMBOL_GPL(thermal_cooling_device_update); | 
|---|
| 1316 |  | 
|---|
| 1317 | static void __thermal_zone_cdev_unbind(struct thermal_zone_device *tz, | 
|---|
| 1318 | struct thermal_cooling_device *cdev) | 
|---|
| 1319 | { | 
|---|
| 1320 | struct thermal_trip_desc *td; | 
|---|
| 1321 |  | 
|---|
| 1322 | for_each_trip_desc(tz, td) | 
|---|
| 1323 | thermal_unbind_cdev_from_trip(tz, td, cdev); | 
|---|
| 1324 | } | 
|---|
| 1325 |  | 
|---|
| 1326 | static void thermal_zone_cdev_unbind(struct thermal_zone_device *tz, | 
|---|
| 1327 | struct thermal_cooling_device *cdev) | 
|---|
| 1328 | { | 
|---|
| 1329 | guard(thermal_zone)(T: tz); | 
|---|
| 1330 |  | 
|---|
| 1331 | __thermal_zone_cdev_unbind(tz, cdev); | 
|---|
| 1332 | } | 
|---|
| 1333 |  | 
|---|
| 1334 | static bool thermal_cooling_device_exit(struct thermal_cooling_device *cdev) | 
|---|
| 1335 | { | 
|---|
| 1336 | struct thermal_zone_device *tz; | 
|---|
| 1337 |  | 
|---|
| 1338 | guard(mutex)(T: &thermal_list_lock); | 
|---|
| 1339 |  | 
|---|
| 1340 | if (!thermal_cooling_device_present(cdev)) | 
|---|
| 1341 | return false; | 
|---|
| 1342 |  | 
|---|
| 1343 | list_del(entry: &cdev->node); | 
|---|
| 1344 |  | 
|---|
| 1345 | list_for_each_entry(tz, &thermal_tz_list, node) | 
|---|
| 1346 | thermal_zone_cdev_unbind(tz, cdev); | 
|---|
| 1347 |  | 
|---|
| 1348 | return true; | 
|---|
| 1349 | } | 
|---|
| 1350 |  | 
|---|
| 1351 | /** | 
|---|
| 1352 | * thermal_cooling_device_unregister() - removes a thermal cooling device | 
|---|
| 1353 | * @cdev: Thermal cooling device to remove. | 
|---|
| 1354 | */ | 
|---|
| 1355 | void thermal_cooling_device_unregister(struct thermal_cooling_device *cdev) | 
|---|
| 1356 | { | 
|---|
| 1357 | if (!cdev) | 
|---|
| 1358 | return; | 
|---|
| 1359 |  | 
|---|
| 1360 | thermal_debug_cdev_remove(cdev); | 
|---|
| 1361 |  | 
|---|
| 1362 | if (thermal_cooling_device_exit(cdev)) | 
|---|
| 1363 | device_unregister(dev: &cdev->device); | 
|---|
| 1364 | } | 
|---|
| 1365 | EXPORT_SYMBOL_GPL(thermal_cooling_device_unregister); | 
|---|
| 1366 |  | 
|---|
| 1367 | int thermal_zone_get_crit_temp(struct thermal_zone_device *tz, int *temp) | 
|---|
| 1368 | { | 
|---|
| 1369 | const struct thermal_trip_desc *td; | 
|---|
| 1370 | int ret = -EINVAL; | 
|---|
| 1371 |  | 
|---|
| 1372 | if (tz->ops.get_crit_temp) | 
|---|
| 1373 | return tz->ops.get_crit_temp(tz, temp); | 
|---|
| 1374 |  | 
|---|
| 1375 | guard(thermal_zone)(T: tz); | 
|---|
| 1376 |  | 
|---|
| 1377 | for_each_trip_desc(tz, td) { | 
|---|
| 1378 | const struct thermal_trip *trip = &td->trip; | 
|---|
| 1379 |  | 
|---|
| 1380 | if (trip->type == THERMAL_TRIP_CRITICAL) { | 
|---|
| 1381 | *temp = trip->temperature; | 
|---|
| 1382 | ret = 0; | 
|---|
| 1383 | break; | 
|---|
| 1384 | } | 
|---|
| 1385 | } | 
|---|
| 1386 |  | 
|---|
| 1387 | return ret; | 
|---|
| 1388 | } | 
|---|
| 1389 | EXPORT_SYMBOL_GPL(thermal_zone_get_crit_temp); | 
|---|
| 1390 |  | 
|---|
| 1391 | static void thermal_zone_device_check(struct work_struct *work) | 
|---|
| 1392 | { | 
|---|
| 1393 | struct thermal_zone_device *tz = container_of(work, struct | 
|---|
| 1394 | thermal_zone_device, | 
|---|
| 1395 | poll_queue.work); | 
|---|
| 1396 | thermal_zone_device_update(tz, THERMAL_EVENT_UNSPECIFIED); | 
|---|
| 1397 | } | 
|---|
| 1398 |  | 
|---|
| 1399 | static void thermal_zone_device_init(struct thermal_zone_device *tz) | 
|---|
| 1400 | { | 
|---|
| 1401 | struct thermal_trip_desc *td, *next; | 
|---|
| 1402 |  | 
|---|
| 1403 | INIT_DELAYED_WORK(&tz->poll_queue, thermal_zone_device_check); | 
|---|
| 1404 |  | 
|---|
| 1405 | tz->temperature = THERMAL_TEMP_INIT; | 
|---|
| 1406 | tz->passive = 0; | 
|---|
| 1407 | tz->prev_low_trip = -INT_MAX; | 
|---|
| 1408 | tz->prev_high_trip = INT_MAX; | 
|---|
| 1409 | for_each_trip_desc(tz, td) { | 
|---|
| 1410 | struct thermal_instance *instance; | 
|---|
| 1411 |  | 
|---|
| 1412 | list_for_each_entry(instance, &td->thermal_instances, trip_node) | 
|---|
| 1413 | instance->initialized = false; | 
|---|
| 1414 | } | 
|---|
| 1415 | /* | 
|---|
| 1416 | * At this point, all valid trips need to be moved to trips_high so that | 
|---|
| 1417 | * mitigation can be started if the zone temperature is above them. | 
|---|
| 1418 | */ | 
|---|
| 1419 | list_for_each_entry_safe(td, next, &tz->trips_invalid, list_node) { | 
|---|
| 1420 | if (td->trip.temperature != THERMAL_TEMP_INVALID) | 
|---|
| 1421 | move_to_trips_high(tz, td); | 
|---|
| 1422 | } | 
|---|
| 1423 | /* The trips_reached list may not be empty during system resume. */ | 
|---|
| 1424 | list_for_each_entry_safe(td, next, &tz->trips_reached, list_node) { | 
|---|
| 1425 | if (td->trip.temperature == THERMAL_TEMP_INVALID) | 
|---|
| 1426 | move_to_trips_invalid(tz, td); | 
|---|
| 1427 | else | 
|---|
| 1428 | move_to_trips_high(tz, td); | 
|---|
| 1429 | } | 
|---|
| 1430 | } | 
|---|
| 1431 |  | 
|---|
| 1432 | static int thermal_zone_init_governor(struct thermal_zone_device *tz) | 
|---|
| 1433 | { | 
|---|
| 1434 | struct thermal_governor *governor; | 
|---|
| 1435 |  | 
|---|
| 1436 | guard(mutex)(T: &thermal_governor_lock); | 
|---|
| 1437 |  | 
|---|
| 1438 | if (tz->tzp) | 
|---|
| 1439 | governor = __find_governor(name: tz->tzp->governor_name); | 
|---|
| 1440 | else | 
|---|
| 1441 | governor = def_governor; | 
|---|
| 1442 |  | 
|---|
| 1443 | return thermal_set_governor(tz, new_gov: governor); | 
|---|
| 1444 | } | 
|---|
| 1445 |  | 
|---|
| 1446 | static void thermal_zone_init_complete(struct thermal_zone_device *tz) | 
|---|
| 1447 | { | 
|---|
| 1448 | struct thermal_cooling_device *cdev; | 
|---|
| 1449 |  | 
|---|
| 1450 | guard(mutex)(T: &thermal_list_lock); | 
|---|
| 1451 |  | 
|---|
| 1452 | list_add_tail(new: &tz->node, head: &thermal_tz_list); | 
|---|
| 1453 |  | 
|---|
| 1454 | guard(thermal_zone)(T: tz); | 
|---|
| 1455 |  | 
|---|
| 1456 | /* Bind cooling devices for this zone. */ | 
|---|
| 1457 | list_for_each_entry(cdev, &thermal_cdev_list, node) | 
|---|
| 1458 | __thermal_zone_cdev_bind(tz, cdev); | 
|---|
| 1459 |  | 
|---|
| 1460 | tz->state &= ~TZ_STATE_FLAG_INIT; | 
|---|
| 1461 | /* | 
|---|
| 1462 | * If system suspend or resume is in progress at this point, the | 
|---|
| 1463 | * new thermal zone needs to be marked as suspended because | 
|---|
| 1464 | * thermal_pm_notify() has run already. | 
|---|
| 1465 | */ | 
|---|
| 1466 | if (thermal_pm_suspended) | 
|---|
| 1467 | tz->state |= TZ_STATE_FLAG_SUSPENDED; | 
|---|
| 1468 |  | 
|---|
| 1469 | __thermal_zone_device_update(tz, event: THERMAL_EVENT_UNSPECIFIED); | 
|---|
| 1470 | } | 
|---|
| 1471 |  | 
|---|
| 1472 | /** | 
|---|
| 1473 | * thermal_zone_device_register_with_trips() - register a new thermal zone device | 
|---|
| 1474 | * @type:	the thermal zone device type | 
|---|
| 1475 | * @trips:	a pointer to an array of thermal trips | 
|---|
| 1476 | * @num_trips:	the number of trip points the thermal zone support | 
|---|
| 1477 | * @devdata:	private device data | 
|---|
| 1478 | * @ops:	standard thermal zone device callbacks | 
|---|
| 1479 | * @tzp:	thermal zone platform parameters | 
|---|
| 1480 | * @passive_delay: number of milliseconds to wait between polls when | 
|---|
| 1481 | *		   performing passive cooling | 
|---|
| 1482 | * @polling_delay: number of milliseconds to wait between polls when checking | 
|---|
| 1483 | *		   whether trip points have been crossed (0 for interrupt | 
|---|
| 1484 | *		   driven systems) | 
|---|
| 1485 | * | 
|---|
| 1486 | * This interface function adds a new thermal zone device (sensor) to | 
|---|
| 1487 | * /sys/class/thermal folder as thermal_zone[0-*]. It tries to bind all the | 
|---|
| 1488 | * thermal cooling devices registered at the same time. | 
|---|
| 1489 | * thermal_zone_device_unregister() must be called when the device is no | 
|---|
| 1490 | * longer needed. The passive cooling depends on the .get_trend() return value. | 
|---|
| 1491 | * | 
|---|
| 1492 | * Return: a pointer to the created struct thermal_zone_device or an | 
|---|
| 1493 | * in case of error, an ERR_PTR. Caller must check return value with | 
|---|
| 1494 | * IS_ERR*() helpers. | 
|---|
| 1495 | */ | 
|---|
| 1496 | struct thermal_zone_device * | 
|---|
| 1497 | thermal_zone_device_register_with_trips(const char *type, | 
|---|
| 1498 | const struct thermal_trip *trips, | 
|---|
| 1499 | int num_trips, void *devdata, | 
|---|
| 1500 | const struct thermal_zone_device_ops *ops, | 
|---|
| 1501 | const struct thermal_zone_params *tzp, | 
|---|
| 1502 | unsigned int passive_delay, | 
|---|
| 1503 | unsigned int polling_delay) | 
|---|
| 1504 | { | 
|---|
| 1505 | const struct thermal_trip *trip = trips; | 
|---|
| 1506 | struct thermal_zone_device *tz; | 
|---|
| 1507 | struct thermal_trip_desc *td; | 
|---|
| 1508 | int id; | 
|---|
| 1509 | int result; | 
|---|
| 1510 |  | 
|---|
| 1511 | if (!type || strlen(type) == 0) { | 
|---|
| 1512 | pr_err( "No thermal zone type defined\n"); | 
|---|
| 1513 | return ERR_PTR(error: -EINVAL); | 
|---|
| 1514 | } | 
|---|
| 1515 |  | 
|---|
| 1516 | if (strlen(type) >= THERMAL_NAME_LENGTH) { | 
|---|
| 1517 | pr_err( "Thermal zone name (%s) too long, should be under %d chars\n", | 
|---|
| 1518 | type, THERMAL_NAME_LENGTH); | 
|---|
| 1519 | return ERR_PTR(error: -EINVAL); | 
|---|
| 1520 | } | 
|---|
| 1521 |  | 
|---|
| 1522 | if (num_trips < 0) { | 
|---|
| 1523 | pr_err( "Incorrect number of thermal trips\n"); | 
|---|
| 1524 | return ERR_PTR(error: -EINVAL); | 
|---|
| 1525 | } | 
|---|
| 1526 |  | 
|---|
| 1527 | if (!ops || !ops->get_temp) { | 
|---|
| 1528 | pr_err( "Thermal zone device ops not defined or invalid\n"); | 
|---|
| 1529 | return ERR_PTR(error: -EINVAL); | 
|---|
| 1530 | } | 
|---|
| 1531 |  | 
|---|
| 1532 | if (num_trips > 0 && !trips) | 
|---|
| 1533 | return ERR_PTR(error: -EINVAL); | 
|---|
| 1534 |  | 
|---|
| 1535 | if (polling_delay && passive_delay > polling_delay) | 
|---|
| 1536 | return ERR_PTR(error: -EINVAL); | 
|---|
| 1537 |  | 
|---|
| 1538 | if (!thermal_class) | 
|---|
| 1539 | return ERR_PTR(error: -ENODEV); | 
|---|
| 1540 |  | 
|---|
| 1541 | tz = kzalloc(struct_size(tz, trips, num_trips), GFP_KERNEL); | 
|---|
| 1542 | if (!tz) | 
|---|
| 1543 | return ERR_PTR(error: -ENOMEM); | 
|---|
| 1544 |  | 
|---|
| 1545 | if (tzp) { | 
|---|
| 1546 | tz->tzp = kmemdup(tzp, sizeof(*tzp), GFP_KERNEL); | 
|---|
| 1547 | if (!tz->tzp) { | 
|---|
| 1548 | result = -ENOMEM; | 
|---|
| 1549 | goto free_tz; | 
|---|
| 1550 | } | 
|---|
| 1551 | } | 
|---|
| 1552 |  | 
|---|
| 1553 | INIT_LIST_HEAD(list: &tz->node); | 
|---|
| 1554 | INIT_LIST_HEAD(list: &tz->trips_high); | 
|---|
| 1555 | INIT_LIST_HEAD(list: &tz->trips_reached); | 
|---|
| 1556 | INIT_LIST_HEAD(list: &tz->trips_invalid); | 
|---|
| 1557 | ida_init(ida: &tz->ida); | 
|---|
| 1558 | mutex_init(&tz->lock); | 
|---|
| 1559 | init_completion(x: &tz->removal); | 
|---|
| 1560 | init_completion(x: &tz->resume); | 
|---|
| 1561 | id = ida_alloc(ida: &thermal_tz_ida, GFP_KERNEL); | 
|---|
| 1562 | if (id < 0) { | 
|---|
| 1563 | result = id; | 
|---|
| 1564 | goto free_tzp; | 
|---|
| 1565 | } | 
|---|
| 1566 |  | 
|---|
| 1567 | tz->id = id; | 
|---|
| 1568 | strscpy(tz->type, type, sizeof(tz->type)); | 
|---|
| 1569 |  | 
|---|
| 1570 | tz->ops = *ops; | 
|---|
| 1571 | if (!tz->ops.critical) | 
|---|
| 1572 | tz->ops.critical = thermal_zone_device_critical; | 
|---|
| 1573 |  | 
|---|
| 1574 | tz->device.class = thermal_class; | 
|---|
| 1575 | tz->devdata = devdata; | 
|---|
| 1576 | tz->num_trips = num_trips; | 
|---|
| 1577 | for_each_trip_desc(tz, td) { | 
|---|
| 1578 | td->trip = *trip++; | 
|---|
| 1579 | INIT_LIST_HEAD(list: &td->thermal_instances); | 
|---|
| 1580 | INIT_LIST_HEAD(list: &td->list_node); | 
|---|
| 1581 | /* | 
|---|
| 1582 | * Mark all thresholds as invalid to start with even though | 
|---|
| 1583 | * this only matters for the trips that start as invalid and | 
|---|
| 1584 | * become valid later. | 
|---|
| 1585 | */ | 
|---|
| 1586 | move_to_trips_invalid(tz, td); | 
|---|
| 1587 | } | 
|---|
| 1588 |  | 
|---|
| 1589 | tz->polling_delay_jiffies = msecs_to_jiffies(m: polling_delay); | 
|---|
| 1590 | tz->passive_delay_jiffies = msecs_to_jiffies(m: passive_delay); | 
|---|
| 1591 | tz->recheck_delay_jiffies = THERMAL_RECHECK_DELAY; | 
|---|
| 1592 |  | 
|---|
| 1593 | tz->state = TZ_STATE_FLAG_INIT; | 
|---|
| 1594 |  | 
|---|
| 1595 | result = dev_set_name(dev: &tz->device, name: "thermal_zone%d", tz->id); | 
|---|
| 1596 | if (result) | 
|---|
| 1597 | goto remove_id; | 
|---|
| 1598 |  | 
|---|
| 1599 | thermal_zone_device_init(tz); | 
|---|
| 1600 |  | 
|---|
| 1601 | result = thermal_zone_init_governor(tz); | 
|---|
| 1602 | if (result) | 
|---|
| 1603 | goto remove_id; | 
|---|
| 1604 |  | 
|---|
| 1605 | /* sys I/F */ | 
|---|
| 1606 | /* Add nodes that are always present via .groups */ | 
|---|
| 1607 | result = thermal_zone_create_device_groups(tz); | 
|---|
| 1608 | if (result) | 
|---|
| 1609 | goto remove_id; | 
|---|
| 1610 |  | 
|---|
| 1611 | result = device_register(dev: &tz->device); | 
|---|
| 1612 | if (result) | 
|---|
| 1613 | goto release_device; | 
|---|
| 1614 |  | 
|---|
| 1615 | if (!tz->tzp || !tz->tzp->no_hwmon) { | 
|---|
| 1616 | result = thermal_add_hwmon_sysfs(tz); | 
|---|
| 1617 | if (result) | 
|---|
| 1618 | goto unregister; | 
|---|
| 1619 | } | 
|---|
| 1620 |  | 
|---|
| 1621 | result = thermal_thresholds_init(tz); | 
|---|
| 1622 | if (result) | 
|---|
| 1623 | goto remove_hwmon; | 
|---|
| 1624 |  | 
|---|
| 1625 | thermal_zone_init_complete(tz); | 
|---|
| 1626 |  | 
|---|
| 1627 | thermal_notify_tz_create(tz); | 
|---|
| 1628 |  | 
|---|
| 1629 | thermal_debug_tz_add(tz); | 
|---|
| 1630 |  | 
|---|
| 1631 | return tz; | 
|---|
| 1632 |  | 
|---|
| 1633 | remove_hwmon: | 
|---|
| 1634 | thermal_remove_hwmon_sysfs(tz); | 
|---|
| 1635 | unregister: | 
|---|
| 1636 | device_del(dev: &tz->device); | 
|---|
| 1637 | release_device: | 
|---|
| 1638 | put_device(dev: &tz->device); | 
|---|
| 1639 | remove_id: | 
|---|
| 1640 | ida_free(&thermal_tz_ida, id); | 
|---|
| 1641 | free_tzp: | 
|---|
| 1642 | kfree(objp: tz->tzp); | 
|---|
| 1643 | free_tz: | 
|---|
| 1644 | kfree(objp: tz); | 
|---|
| 1645 | return ERR_PTR(error: result); | 
|---|
| 1646 | } | 
|---|
| 1647 | EXPORT_SYMBOL_GPL(thermal_zone_device_register_with_trips); | 
|---|
| 1648 |  | 
|---|
| 1649 | struct thermal_zone_device *thermal_tripless_zone_device_register( | 
|---|
| 1650 | const char *type, | 
|---|
| 1651 | void *devdata, | 
|---|
| 1652 | const struct thermal_zone_device_ops *ops, | 
|---|
| 1653 | const struct thermal_zone_params *tzp) | 
|---|
| 1654 | { | 
|---|
| 1655 | return thermal_zone_device_register_with_trips(type, NULL, 0, devdata, | 
|---|
| 1656 | ops, tzp, 0, 0); | 
|---|
| 1657 | } | 
|---|
| 1658 | EXPORT_SYMBOL_GPL(thermal_tripless_zone_device_register); | 
|---|
| 1659 |  | 
|---|
| 1660 | void *thermal_zone_device_priv(struct thermal_zone_device *tzd) | 
|---|
| 1661 | { | 
|---|
| 1662 | return tzd->devdata; | 
|---|
| 1663 | } | 
|---|
| 1664 | EXPORT_SYMBOL_GPL(thermal_zone_device_priv); | 
|---|
| 1665 |  | 
|---|
| 1666 | const char *thermal_zone_device_type(struct thermal_zone_device *tzd) | 
|---|
| 1667 | { | 
|---|
| 1668 | return tzd->type; | 
|---|
| 1669 | } | 
|---|
| 1670 | EXPORT_SYMBOL_GPL(thermal_zone_device_type); | 
|---|
| 1671 |  | 
|---|
| 1672 | int thermal_zone_device_id(struct thermal_zone_device *tzd) | 
|---|
| 1673 | { | 
|---|
| 1674 | return tzd->id; | 
|---|
| 1675 | } | 
|---|
| 1676 | EXPORT_SYMBOL_GPL(thermal_zone_device_id); | 
|---|
| 1677 |  | 
|---|
| 1678 | struct device *thermal_zone_device(struct thermal_zone_device *tzd) | 
|---|
| 1679 | { | 
|---|
| 1680 | return &tzd->device; | 
|---|
| 1681 | } | 
|---|
| 1682 | EXPORT_SYMBOL_GPL(thermal_zone_device); | 
|---|
| 1683 |  | 
|---|
| 1684 | static bool thermal_zone_exit(struct thermal_zone_device *tz) | 
|---|
| 1685 | { | 
|---|
| 1686 | struct thermal_cooling_device *cdev; | 
|---|
| 1687 |  | 
|---|
| 1688 | guard(mutex)(T: &thermal_list_lock); | 
|---|
| 1689 |  | 
|---|
| 1690 | if (list_empty(head: &tz->node)) | 
|---|
| 1691 | return false; | 
|---|
| 1692 |  | 
|---|
| 1693 | guard(thermal_zone)(T: tz); | 
|---|
| 1694 |  | 
|---|
| 1695 | tz->state |= TZ_STATE_FLAG_EXIT; | 
|---|
| 1696 | list_del_init(entry: &tz->node); | 
|---|
| 1697 |  | 
|---|
| 1698 | /* Unbind all cdevs associated with this thermal zone. */ | 
|---|
| 1699 | list_for_each_entry(cdev, &thermal_cdev_list, node) | 
|---|
| 1700 | __thermal_zone_cdev_unbind(tz, cdev); | 
|---|
| 1701 |  | 
|---|
| 1702 | return true; | 
|---|
| 1703 | } | 
|---|
| 1704 |  | 
|---|
| 1705 | /** | 
|---|
| 1706 | * thermal_zone_device_unregister - removes the registered thermal zone device | 
|---|
| 1707 | * @tz: the thermal zone device to remove | 
|---|
| 1708 | */ | 
|---|
| 1709 | void thermal_zone_device_unregister(struct thermal_zone_device *tz) | 
|---|
| 1710 | { | 
|---|
| 1711 | if (!tz) | 
|---|
| 1712 | return; | 
|---|
| 1713 |  | 
|---|
| 1714 | thermal_debug_tz_remove(tz); | 
|---|
| 1715 |  | 
|---|
| 1716 | if (!thermal_zone_exit(tz)) | 
|---|
| 1717 | return; | 
|---|
| 1718 |  | 
|---|
| 1719 | cancel_delayed_work_sync(dwork: &tz->poll_queue); | 
|---|
| 1720 |  | 
|---|
| 1721 | thermal_set_governor(tz, NULL); | 
|---|
| 1722 |  | 
|---|
| 1723 | thermal_thresholds_exit(tz); | 
|---|
| 1724 | thermal_remove_hwmon_sysfs(tz); | 
|---|
| 1725 | ida_free(&thermal_tz_ida, id: tz->id); | 
|---|
| 1726 | ida_destroy(ida: &tz->ida); | 
|---|
| 1727 |  | 
|---|
| 1728 | device_del(dev: &tz->device); | 
|---|
| 1729 | put_device(dev: &tz->device); | 
|---|
| 1730 |  | 
|---|
| 1731 | thermal_notify_tz_delete(tz); | 
|---|
| 1732 |  | 
|---|
| 1733 | wait_for_completion(&tz->removal); | 
|---|
| 1734 | kfree(objp: tz->tzp); | 
|---|
| 1735 | kfree(objp: tz); | 
|---|
| 1736 | } | 
|---|
| 1737 | EXPORT_SYMBOL_GPL(thermal_zone_device_unregister); | 
|---|
| 1738 |  | 
|---|
| 1739 | /** | 
|---|
| 1740 | * thermal_zone_get_zone_by_name() - search for a zone and returns its ref | 
|---|
| 1741 | * @name: thermal zone name to fetch the temperature | 
|---|
| 1742 | * | 
|---|
| 1743 | * When only one zone is found with the passed name, returns a reference to it. | 
|---|
| 1744 | * | 
|---|
| 1745 | * Return: On success returns a reference to an unique thermal zone with | 
|---|
| 1746 | * matching name equals to @name, an ERR_PTR otherwise (-EINVAL for invalid | 
|---|
| 1747 | * paramenters, -ENODEV for not found and -EEXIST for multiple matches). | 
|---|
| 1748 | */ | 
|---|
| 1749 | struct thermal_zone_device *thermal_zone_get_zone_by_name(const char *name) | 
|---|
| 1750 | { | 
|---|
| 1751 | struct thermal_zone_device *pos = NULL, *ref = ERR_PTR(error: -EINVAL); | 
|---|
| 1752 | unsigned int found = 0; | 
|---|
| 1753 |  | 
|---|
| 1754 | if (!name) | 
|---|
| 1755 | return ERR_PTR(error: -EINVAL); | 
|---|
| 1756 |  | 
|---|
| 1757 | guard(mutex)(T: &thermal_list_lock); | 
|---|
| 1758 |  | 
|---|
| 1759 | list_for_each_entry(pos, &thermal_tz_list, node) | 
|---|
| 1760 | if (!strncasecmp(s1: name, s2: pos->type, THERMAL_NAME_LENGTH)) { | 
|---|
| 1761 | found++; | 
|---|
| 1762 | ref = pos; | 
|---|
| 1763 | } | 
|---|
| 1764 |  | 
|---|
| 1765 | if (!found) | 
|---|
| 1766 | return ERR_PTR(error: -ENODEV); | 
|---|
| 1767 |  | 
|---|
| 1768 | /* Success only when one zone is found. */ | 
|---|
| 1769 | if (found > 1) | 
|---|
| 1770 | return ERR_PTR(error: -EEXIST); | 
|---|
| 1771 |  | 
|---|
| 1772 | return ref; | 
|---|
| 1773 | } | 
|---|
| 1774 | EXPORT_SYMBOL_GPL(thermal_zone_get_zone_by_name); | 
|---|
| 1775 |  | 
|---|
| 1776 | static void thermal_zone_device_resume(struct work_struct *work) | 
|---|
| 1777 | { | 
|---|
| 1778 | struct thermal_zone_device *tz; | 
|---|
| 1779 |  | 
|---|
| 1780 | tz = container_of(work, struct thermal_zone_device, poll_queue.work); | 
|---|
| 1781 |  | 
|---|
| 1782 | guard(thermal_zone)(T: tz); | 
|---|
| 1783 |  | 
|---|
| 1784 | tz->state &= ~(TZ_STATE_FLAG_SUSPENDED | TZ_STATE_FLAG_RESUMING); | 
|---|
| 1785 |  | 
|---|
| 1786 | thermal_debug_tz_resume(tz); | 
|---|
| 1787 | thermal_zone_device_init(tz); | 
|---|
| 1788 | thermal_governor_update_tz(tz, reason: THERMAL_TZ_RESUME); | 
|---|
| 1789 | __thermal_zone_device_update(tz, event: THERMAL_TZ_RESUME); | 
|---|
| 1790 |  | 
|---|
| 1791 | complete(&tz->resume); | 
|---|
| 1792 | } | 
|---|
| 1793 |  | 
|---|
| 1794 | static void thermal_zone_pm_prepare(struct thermal_zone_device *tz) | 
|---|
| 1795 | { | 
|---|
| 1796 | guard(thermal_zone)(T: tz); | 
|---|
| 1797 |  | 
|---|
| 1798 | if (tz->state & TZ_STATE_FLAG_RESUMING) { | 
|---|
| 1799 | /* | 
|---|
| 1800 | * thermal_zone_device_resume() queued up for this zone has not | 
|---|
| 1801 | * acquired the lock yet, so release it to let the function run | 
|---|
| 1802 | * and wait util it has done the work. | 
|---|
| 1803 | */ | 
|---|
| 1804 | scoped_guard(thermal_zone_reverse, tz) { | 
|---|
| 1805 | wait_for_completion(&tz->resume); | 
|---|
| 1806 | } | 
|---|
| 1807 | } | 
|---|
| 1808 |  | 
|---|
| 1809 | tz->state |= TZ_STATE_FLAG_SUSPENDED; | 
|---|
| 1810 | } | 
|---|
| 1811 |  | 
|---|
| 1812 | static void thermal_pm_notify_prepare(void) | 
|---|
| 1813 | { | 
|---|
| 1814 | struct thermal_zone_device *tz; | 
|---|
| 1815 |  | 
|---|
| 1816 | guard(mutex)(T: &thermal_list_lock); | 
|---|
| 1817 |  | 
|---|
| 1818 | thermal_pm_suspended = true; | 
|---|
| 1819 |  | 
|---|
| 1820 | list_for_each_entry(tz, &thermal_tz_list, node) | 
|---|
| 1821 | thermal_zone_pm_prepare(tz); | 
|---|
| 1822 | } | 
|---|
| 1823 |  | 
|---|
| 1824 | static void thermal_zone_pm_complete(struct thermal_zone_device *tz) | 
|---|
| 1825 | { | 
|---|
| 1826 | guard(thermal_zone)(T: tz); | 
|---|
| 1827 |  | 
|---|
| 1828 | cancel_delayed_work(dwork: &tz->poll_queue); | 
|---|
| 1829 |  | 
|---|
| 1830 | reinit_completion(x: &tz->resume); | 
|---|
| 1831 | tz->state |= TZ_STATE_FLAG_RESUMING; | 
|---|
| 1832 |  | 
|---|
| 1833 | /* | 
|---|
| 1834 | * Replace the work function with the resume one, which will restore the | 
|---|
| 1835 | * original work function and schedule the polling work if needed. | 
|---|
| 1836 | */ | 
|---|
| 1837 | INIT_DELAYED_WORK(&tz->poll_queue, thermal_zone_device_resume); | 
|---|
| 1838 | /* Queue up the work without a delay. */ | 
|---|
| 1839 | mod_delayed_work(wq: system_freezable_power_efficient_wq, dwork: &tz->poll_queue, delay: 0); | 
|---|
| 1840 | } | 
|---|
| 1841 |  | 
|---|
| 1842 | static void thermal_pm_notify_complete(void) | 
|---|
| 1843 | { | 
|---|
| 1844 | struct thermal_zone_device *tz; | 
|---|
| 1845 |  | 
|---|
| 1846 | guard(mutex)(T: &thermal_list_lock); | 
|---|
| 1847 |  | 
|---|
| 1848 | thermal_pm_suspended = false; | 
|---|
| 1849 |  | 
|---|
| 1850 | list_for_each_entry(tz, &thermal_tz_list, node) | 
|---|
| 1851 | thermal_zone_pm_complete(tz); | 
|---|
| 1852 | } | 
|---|
| 1853 |  | 
|---|
| 1854 | static int thermal_pm_notify(struct notifier_block *nb, | 
|---|
| 1855 | unsigned long mode, void *_unused) | 
|---|
| 1856 | { | 
|---|
| 1857 | switch (mode) { | 
|---|
| 1858 | case PM_HIBERNATION_PREPARE: | 
|---|
| 1859 | case PM_RESTORE_PREPARE: | 
|---|
| 1860 | case PM_SUSPEND_PREPARE: | 
|---|
| 1861 | thermal_pm_notify_prepare(); | 
|---|
| 1862 | break; | 
|---|
| 1863 | case PM_POST_HIBERNATION: | 
|---|
| 1864 | case PM_POST_RESTORE: | 
|---|
| 1865 | case PM_POST_SUSPEND: | 
|---|
| 1866 | thermal_pm_notify_complete(); | 
|---|
| 1867 | break; | 
|---|
| 1868 | default: | 
|---|
| 1869 | break; | 
|---|
| 1870 | } | 
|---|
| 1871 | return 0; | 
|---|
| 1872 | } | 
|---|
| 1873 |  | 
|---|
| 1874 | static struct notifier_block thermal_pm_nb = { | 
|---|
| 1875 | .notifier_call = thermal_pm_notify, | 
|---|
| 1876 | /* | 
|---|
| 1877 | * Run at the lowest priority to avoid interference between the thermal | 
|---|
| 1878 | * zone resume work items spawned by thermal_pm_notify() and the other | 
|---|
| 1879 | * PM notifiers. | 
|---|
| 1880 | */ | 
|---|
| 1881 | .priority = INT_MIN, | 
|---|
| 1882 | }; | 
|---|
| 1883 |  | 
|---|
| 1884 | static int __init thermal_init(void) | 
|---|
| 1885 | { | 
|---|
| 1886 | int result; | 
|---|
| 1887 |  | 
|---|
| 1888 | thermal_debug_init(); | 
|---|
| 1889 |  | 
|---|
| 1890 | result = thermal_netlink_init(); | 
|---|
| 1891 | if (result) | 
|---|
| 1892 | goto error; | 
|---|
| 1893 |  | 
|---|
| 1894 | result = thermal_register_governors(); | 
|---|
| 1895 | if (result) | 
|---|
| 1896 | goto unregister_netlink; | 
|---|
| 1897 |  | 
|---|
| 1898 | thermal_class = kzalloc(sizeof(*thermal_class), GFP_KERNEL); | 
|---|
| 1899 | if (!thermal_class) { | 
|---|
| 1900 | result = -ENOMEM; | 
|---|
| 1901 | goto unregister_governors; | 
|---|
| 1902 | } | 
|---|
| 1903 |  | 
|---|
| 1904 | thermal_class->name = "thermal"; | 
|---|
| 1905 | thermal_class->dev_release = thermal_release; | 
|---|
| 1906 |  | 
|---|
| 1907 | result = class_register(class: thermal_class); | 
|---|
| 1908 | if (result) { | 
|---|
| 1909 | kfree(objp: thermal_class); | 
|---|
| 1910 | thermal_class = NULL; | 
|---|
| 1911 | goto unregister_governors; | 
|---|
| 1912 | } | 
|---|
| 1913 |  | 
|---|
| 1914 | result = register_pm_notifier(nb: &thermal_pm_nb); | 
|---|
| 1915 | if (result) | 
|---|
| 1916 | pr_warn( "Thermal: Can not register suspend notifier, return %d\n", | 
|---|
| 1917 | result); | 
|---|
| 1918 |  | 
|---|
| 1919 | return 0; | 
|---|
| 1920 |  | 
|---|
| 1921 | unregister_governors: | 
|---|
| 1922 | thermal_unregister_governors(); | 
|---|
| 1923 | unregister_netlink: | 
|---|
| 1924 | thermal_netlink_exit(); | 
|---|
| 1925 | error: | 
|---|
| 1926 | mutex_destroy(lock: &thermal_list_lock); | 
|---|
| 1927 | mutex_destroy(lock: &thermal_governor_lock); | 
|---|
| 1928 | return result; | 
|---|
| 1929 | } | 
|---|
| 1930 | postcore_initcall(thermal_init); | 
|---|
| 1931 |  | 
|---|