Commit | Line | Data |
---|---|---|
7e3c0381 | 1 | // SPDX-License-Identifier: GPL-2.0 |
203d3d4a ZR |
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> | |
203d3d4a ZR |
8 | */ |
9 | ||
c5a01dd5 JP |
10 | #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt |
11 | ||
203d3d4a ZR |
12 | #include <linux/device.h> |
13 | #include <linux/err.h> | |
3f0cfea3 | 14 | #include <linux/export.h> |
5a0e3ad6 | 15 | #include <linux/slab.h> |
203d3d4a ZR |
16 | #include <linux/kdev_t.h> |
17 | #include <linux/idr.h> | |
7454f2c4 | 18 | #include <linux/list_sort.h> |
203d3d4a | 19 | #include <linux/thermal.h> |
b1569e99 | 20 | #include <linux/reboot.h> |
42a5bf50 | 21 | #include <linux/string.h> |
a116b5d4 | 22 | #include <linux/of.h> |
ff140fea | 23 | #include <linux/suspend.h> |
203d3d4a | 24 | |
100a8fdb | 25 | #define CREATE_TRACE_POINTS |
32a7a021 | 26 | #include "thermal_trace.h" |
100a8fdb | 27 | |
71350db4 | 28 | #include "thermal_core.h" |
0dd88793 | 29 | #include "thermal_hwmon.h" |
71350db4 | 30 | |
b31ef828 MW |
31 | static DEFINE_IDA(thermal_tz_ida); |
32 | static DEFINE_IDA(thermal_cdev_ida); | |
203d3d4a ZR |
33 | |
34 | static LIST_HEAD(thermal_tz_list); | |
35 | static LIST_HEAD(thermal_cdev_list); | |
a4a15485 D |
36 | static LIST_HEAD(thermal_governor_list); |
37 | ||
203d3d4a | 38 | static DEFINE_MUTEX(thermal_list_lock); |
a4a15485 D |
39 | static DEFINE_MUTEX(thermal_governor_lock); |
40 | ||
f2234bcd ZR |
41 | static struct thermal_governor *def_governor; |
42 | ||
1b4f4849 EV |
43 | /* |
44 | * Governor section: set of functions to handle thermal governors | |
45 | * | |
46 | * Functions to help in the life cycle of thermal governors within | |
47 | * the thermal core and by the thermal governor code. | |
48 | */ | |
49 | ||
a4a15485 D |
50 | static struct thermal_governor *__find_governor(const char *name) |
51 | { | |
52 | struct thermal_governor *pos; | |
53 | ||
f2234bcd ZR |
54 | if (!name || !name[0]) |
55 | return def_governor; | |
56 | ||
a4a15485 | 57 | list_for_each_entry(pos, &thermal_governor_list, governor_list) |
484ac2f3 | 58 | if (!strncasecmp(name, pos->name, THERMAL_NAME_LENGTH)) |
a4a15485 D |
59 | return pos; |
60 | ||
61 | return NULL; | |
62 | } | |
63 | ||
e33df1d2 JM |
64 | /** |
65 | * bind_previous_governor() - bind the previous governor of the thermal zone | |
66 | * @tz: a valid pointer to a struct thermal_zone_device | |
67 | * @failed_gov_name: the name of the governor that failed to register | |
68 | * | |
69 | * Register the previous governor of the thermal zone after a new | |
70 | * governor has failed to be bound. | |
71 | */ | |
72 | static void bind_previous_governor(struct thermal_zone_device *tz, | |
73 | const char *failed_gov_name) | |
74 | { | |
75 | if (tz->governor && tz->governor->bind_to_tz) { | |
76 | if (tz->governor->bind_to_tz(tz)) { | |
77 | dev_err(&tz->device, | |
78 | "governor %s failed to bind and the previous one (%s) failed to bind again, thermal zone %s has no governor\n", | |
79 | failed_gov_name, tz->governor->name, tz->type); | |
80 | tz->governor = NULL; | |
81 | } | |
82 | } | |
83 | } | |
84 | ||
85 | /** | |
86 | * thermal_set_governor() - Switch to another governor | |
87 | * @tz: a valid pointer to a struct thermal_zone_device | |
88 | * @new_gov: pointer to the new governor | |
89 | * | |
90 | * Change the governor of thermal zone @tz. | |
91 | * | |
92 | * Return: 0 on success, an error if the new governor's bind_to_tz() failed. | |
93 | */ | |
94 | static int thermal_set_governor(struct thermal_zone_device *tz, | |
95 | struct thermal_governor *new_gov) | |
96 | { | |
97 | int ret = 0; | |
98 | ||
99 | if (tz->governor && tz->governor->unbind_from_tz) | |
100 | tz->governor->unbind_from_tz(tz); | |
101 | ||
102 | if (new_gov && new_gov->bind_to_tz) { | |
103 | ret = new_gov->bind_to_tz(tz); | |
104 | if (ret) { | |
105 | bind_previous_governor(tz, new_gov->name); | |
106 | ||
107 | return ret; | |
108 | } | |
109 | } | |
110 | ||
111 | tz->governor = new_gov; | |
112 | ||
113 | return ret; | |
114 | } | |
115 | ||
a4a15485 D |
116 | int thermal_register_governor(struct thermal_governor *governor) |
117 | { | |
118 | int err; | |
119 | const char *name; | |
120 | struct thermal_zone_device *pos; | |
121 | ||
122 | if (!governor) | |
123 | return -EINVAL; | |
124 | ||
125 | mutex_lock(&thermal_governor_lock); | |
126 | ||
127 | err = -EBUSY; | |
5027ba36 | 128 | if (!__find_governor(governor->name)) { |
eb7be329 EV |
129 | bool match_default; |
130 | ||
a4a15485 D |
131 | err = 0; |
132 | list_add(&governor->governor_list, &thermal_governor_list); | |
eb7be329 EV |
133 | match_default = !strncmp(governor->name, |
134 | DEFAULT_THERMAL_GOVERNOR, | |
135 | THERMAL_NAME_LENGTH); | |
136 | ||
137 | if (!def_governor && match_default) | |
f2234bcd | 138 | def_governor = governor; |
a4a15485 D |
139 | } |
140 | ||
141 | mutex_lock(&thermal_list_lock); | |
142 | ||
143 | list_for_each_entry(pos, &thermal_tz_list, node) { | |
f2234bcd ZR |
144 | /* |
145 | * only thermal zones with specified tz->tzp->governor_name | |
146 | * may run with tz->govenor unset | |
147 | */ | |
a4a15485 D |
148 | if (pos->governor) |
149 | continue; | |
f2234bcd ZR |
150 | |
151 | name = pos->tzp->governor_name; | |
152 | ||
e33df1d2 JM |
153 | if (!strncasecmp(name, governor->name, THERMAL_NAME_LENGTH)) { |
154 | int ret; | |
155 | ||
156 | ret = thermal_set_governor(pos, governor); | |
157 | if (ret) | |
158 | dev_err(&pos->device, | |
159 | "Failed to set governor %s for thermal zone %s: %d\n", | |
160 | governor->name, pos->type, ret); | |
161 | } | |
a4a15485 D |
162 | } |
163 | ||
164 | mutex_unlock(&thermal_list_lock); | |
165 | mutex_unlock(&thermal_governor_lock); | |
166 | ||
167 | return err; | |
168 | } | |
a4a15485 D |
169 | |
170 | void thermal_unregister_governor(struct thermal_governor *governor) | |
171 | { | |
172 | struct thermal_zone_device *pos; | |
173 | ||
174 | if (!governor) | |
175 | return; | |
176 | ||
177 | mutex_lock(&thermal_governor_lock); | |
178 | ||
5027ba36 | 179 | if (!__find_governor(governor->name)) |
a4a15485 D |
180 | goto exit; |
181 | ||
182 | mutex_lock(&thermal_list_lock); | |
183 | ||
184 | list_for_each_entry(pos, &thermal_tz_list, node) { | |
484ac2f3 | 185 | if (!strncasecmp(pos->governor->name, governor->name, |
eb7be329 | 186 | THERMAL_NAME_LENGTH)) |
e33df1d2 | 187 | thermal_set_governor(pos, NULL); |
a4a15485 D |
188 | } |
189 | ||
190 | mutex_unlock(&thermal_list_lock); | |
191 | list_del(&governor->governor_list); | |
192 | exit: | |
193 | mutex_unlock(&thermal_governor_lock); | |
9b4298a0 | 194 | } |
9b4298a0 | 195 | |
1b4f4849 EV |
196 | int thermal_zone_device_set_policy(struct thermal_zone_device *tz, |
197 | char *policy) | |
9b4298a0 | 198 | { |
1b4f4849 EV |
199 | struct thermal_governor *gov; |
200 | int ret = -EINVAL; | |
9b4298a0 | 201 | |
1b4f4849 | 202 | mutex_lock(&thermal_governor_lock); |
9b4298a0 | 203 | mutex_lock(&tz->lock); |
9b4298a0 | 204 | |
1b4f4849 EV |
205 | gov = __find_governor(strim(policy)); |
206 | if (!gov) | |
207 | goto exit; | |
9b4298a0 | 208 | |
1b4f4849 | 209 | ret = thermal_set_governor(tz, gov); |
9b4298a0 | 210 | |
1b4f4849 EV |
211 | exit: |
212 | mutex_unlock(&tz->lock); | |
213 | mutex_unlock(&thermal_governor_lock); | |
9b4298a0 | 214 | |
2f521890 | 215 | thermal_notify_tz_gov_change(tz, policy); |
55cdf0a2 | 216 | |
1b4f4849 | 217 | return ret; |
7e8ee1e9 D |
218 | } |
219 | ||
1b4f4849 | 220 | int thermal_build_list_of_policies(char *buf) |
7e8ee1e9 | 221 | { |
1b4f4849 EV |
222 | struct thermal_governor *pos; |
223 | ssize_t count = 0; | |
7e8ee1e9 | 224 | |
1b4f4849 | 225 | mutex_lock(&thermal_governor_lock); |
a8892d83 | 226 | |
1b4f4849 | 227 | list_for_each_entry(pos, &thermal_governor_list, governor_list) { |
5bbafd43 | 228 | count += sysfs_emit_at(buf, count, "%s ", pos->name); |
7e8ee1e9 | 229 | } |
5bbafd43 | 230 | count += sysfs_emit_at(buf, count, "\n"); |
7e8ee1e9 | 231 | |
1b4f4849 | 232 | mutex_unlock(&thermal_governor_lock); |
7e8ee1e9 | 233 | |
1b4f4849 | 234 | return count; |
7e8ee1e9 D |
235 | } |
236 | ||
57c5b2ec | 237 | static void __init thermal_unregister_governors(void) |
7e8ee1e9 | 238 | { |
57c5b2ec DL |
239 | struct thermal_governor **governor; |
240 | ||
241 | for_each_governor_table(governor) | |
242 | thermal_unregister_governor(*governor); | |
243 | } | |
7e8ee1e9 | 244 | |
57c5b2ec DL |
245 | static int __init thermal_register_governors(void) |
246 | { | |
247 | int ret = 0; | |
248 | struct thermal_governor **governor; | |
7e8ee1e9 | 249 | |
57c5b2ec DL |
250 | for_each_governor_table(governor) { |
251 | ret = thermal_register_governor(*governor); | |
252 | if (ret) { | |
253 | pr_err("Failed to register governor: '%s'", | |
254 | (*governor)->name); | |
255 | break; | |
256 | } | |
7e8ee1e9 | 257 | |
57c5b2ec DL |
258 | pr_info("Registered thermal governor '%s'", |
259 | (*governor)->name); | |
260 | } | |
7e8ee1e9 | 261 | |
57c5b2ec DL |
262 | if (ret) { |
263 | struct thermal_governor **gov; | |
7e8ee1e9 | 264 | |
57c5b2ec DL |
265 | for_each_governor_table(gov) { |
266 | if (gov == governor) | |
267 | break; | |
268 | thermal_unregister_governor(*gov); | |
269 | } | |
270 | } | |
7e8ee1e9 | 271 | |
57c5b2ec | 272 | return ret; |
7e8ee1e9 D |
273 | } |
274 | ||
8772e185 EV |
275 | /* |
276 | * Zone update section: main control loop applied to each zone while monitoring | |
8772e185 EV |
277 | * in polling mode. The monitoring is done using a workqueue. |
278 | * Same update may be done on a zone by calling thermal_zone_device_update(). | |
279 | * | |
280 | * An update means: | |
281 | * - Non-critical trips will invoke the governor responsible for that zone; | |
282 | * - Hot trips will produce a notification to userspace; | |
283 | * - Critical trip point will cause a system shutdown. | |
284 | */ | |
0c01ebbf | 285 | static void thermal_zone_device_set_polling(struct thermal_zone_device *tz, |
39a38808 | 286 | unsigned long delay) |
0c01ebbf | 287 | { |
39a38808 | 288 | if (delay) |
c2b59d27 | 289 | mod_delayed_work(system_freezable_power_efficient_wq, |
39a38808 | 290 | &tz->poll_queue, delay); |
0c01ebbf | 291 | else |
163b00cd | 292 | cancel_delayed_work(&tz->poll_queue); |
0c01ebbf D |
293 | } |
294 | ||
295 | static void monitor_thermal_zone(struct thermal_zone_device *tz) | |
296 | { | |
15a73839 DL |
297 | if (tz->mode != THERMAL_DEVICE_ENABLED) |
298 | thermal_zone_device_set_polling(tz, 0); | |
042a3d80 | 299 | else if (tz->passive > 0) |
39a38808 | 300 | thermal_zone_device_set_polling(tz, tz->passive_delay_jiffies); |
15a73839 | 301 | else if (tz->polling_delay_jiffies) |
39a38808 | 302 | thermal_zone_device_set_polling(tz, tz->polling_delay_jiffies); |
0c01ebbf D |
303 | } |
304 | ||
80f5fd45 RW |
305 | static struct thermal_governor *thermal_get_tz_governor(struct thermal_zone_device *tz) |
306 | { | |
307 | if (tz->governor) | |
308 | return tz->governor; | |
309 | ||
310 | return def_governor; | |
311 | } | |
312 | ||
a8c95940 LL |
313 | void thermal_governor_update_tz(struct thermal_zone_device *tz, |
314 | enum thermal_notify_event reason) | |
315 | { | |
316 | if (!tz->governor || !tz->governor->update_tz) | |
317 | return; | |
318 | ||
319 | tz->governor->update_tz(tz, reason); | |
320 | } | |
321 | ||
5a0e2410 | 322 | static void thermal_zone_device_halt(struct thermal_zone_device *tz, bool shutdown) |
ef1d87e0 | 323 | { |
ef1d87e0 K |
324 | /* |
325 | * poweroff_delay_ms must be a carefully profiled positive value. | |
db0aeb4f | 326 | * Its a must for forced_emergency_poweroff_work to be scheduled. |
ef1d87e0 | 327 | */ |
db0aeb4f | 328 | int poweroff_delay_ms = CONFIG_THERMAL_EMERGENCY_POWEROFF_DELAY_MS; |
5a0e2410 | 329 | const char *msg = "Temperature too high"; |
ef1d87e0 | 330 | |
5a0e2410 | 331 | dev_emerg(&tz->device, "%s: critical temperature reached\n", tz->type); |
d7203eed | 332 | |
5a0e2410 FE |
333 | if (shutdown) |
334 | hw_protection_shutdown(msg, poweroff_delay_ms); | |
79fa723b FE |
335 | else |
336 | hw_protection_reboot(msg, poweroff_delay_ms); | |
5a0e2410 | 337 | } |
d7203eed | 338 | |
5a0e2410 FE |
339 | void thermal_zone_device_critical(struct thermal_zone_device *tz) |
340 | { | |
341 | thermal_zone_device_halt(tz, true); | |
d7203eed DL |
342 | } |
343 | EXPORT_SYMBOL(thermal_zone_device_critical); | |
344 | ||
79fa723b FE |
345 | void thermal_zone_device_critical_reboot(struct thermal_zone_device *tz) |
346 | { | |
347 | thermal_zone_device_halt(tz, false); | |
348 | } | |
349 | ||
0c01ebbf | 350 | static void handle_critical_trips(struct thermal_zone_device *tz, |
8c35b1f4 | 351 | const struct thermal_trip *trip) |
0c01ebbf | 352 | { |
8c35b1f4 | 353 | trace_thermal_zone_trip(tz, thermal_zone_trip_id(tz, trip), trip->type); |
208cd822 | 354 | |
8c35b1f4 | 355 | if (trip->type == THERMAL_TRIP_CRITICAL) |
698a1eb1 RW |
356 | tz->ops.critical(tz); |
357 | else if (tz->ops.hot) | |
358 | tz->ops.hot(tz); | |
0c01ebbf D |
359 | } |
360 | ||
8c35b1f4 | 361 | static void handle_thermal_trip(struct thermal_zone_device *tz, |
7454f2c4 RW |
362 | struct thermal_trip_desc *td, |
363 | struct list_head *way_up_list, | |
364 | struct list_head *way_down_list) | |
0c01ebbf | 365 | { |
daeeb032 | 366 | const struct thermal_trip *trip = &td->trip; |
9ad18043 | 367 | int old_threshold; |
daeeb032 | 368 | |
8c35b1f4 | 369 | if (trip->temperature == THERMAL_TEMP_INVALID) |
fb2c1024 RW |
370 | return; |
371 | ||
f99c1b87 RW |
372 | /* |
373 | * If the trip temperature or hysteresis has been updated recently, | |
374 | * the threshold needs to be computed again using the new values. | |
375 | * However, its initial value still reflects the old ones and that | |
376 | * is what needs to be compared with the previous zone temperature | |
377 | * to decide which action to take. | |
378 | */ | |
9ad18043 RW |
379 | old_threshold = td->threshold; |
380 | td->threshold = trip->temperature; | |
381 | ||
382 | if (tz->last_temperature >= old_threshold && | |
383 | tz->last_temperature != THERMAL_TEMP_INVALID) { | |
44844db9 | 384 | /* |
f99c1b87 RW |
385 | * Mitigation is under way, so it needs to stop if the zone |
386 | * temperature falls below the low temperature of the trip. | |
387 | * In that case, the trip temperature becomes the new threshold. | |
44844db9 RW |
388 | */ |
389 | if (tz->temperature < trip->temperature - trip->hysteresis) { | |
7454f2c4 RW |
390 | list_add(&td->notify_list_node, way_down_list); |
391 | td->notify_temp = trip->temperature - trip->hysteresis; | |
042a3d80 RW |
392 | |
393 | if (trip->type == THERMAL_TRIP_PASSIVE) { | |
394 | tz->passive--; | |
395 | WARN_ON(tz->passive < 0); | |
396 | } | |
44844db9 | 397 | } else { |
9ad18043 | 398 | td->threshold -= trip->hysteresis; |
44844db9 | 399 | } |
9ad18043 RW |
400 | } else if (tz->temperature >= trip->temperature) { |
401 | /* | |
402 | * There is no mitigation under way, so it needs to be started | |
403 | * if the zone temperature exceeds the trip one. The new | |
404 | * threshold is then set to the low temperature of the trip. | |
405 | */ | |
7454f2c4 RW |
406 | list_add_tail(&td->notify_list_node, way_up_list); |
407 | td->notify_temp = trip->temperature; | |
9ad18043 | 408 | td->threshold -= trip->hysteresis; |
0c01ebbf | 409 | |
042a3d80 RW |
410 | if (trip->type == THERMAL_TRIP_PASSIVE) |
411 | tz->passive++; | |
412 | else if (trip->type == THERMAL_TRIP_CRITICAL || | |
413 | trip->type == THERMAL_TRIP_HOT) | |
2ae0998c RW |
414 | handle_critical_trips(tz, trip); |
415 | } | |
0c01ebbf D |
416 | } |
417 | ||
418 | static void update_temperature(struct thermal_zone_device *tz) | |
419 | { | |
17e8351a | 420 | int temp, ret; |
0c01ebbf | 421 | |
a930da9b | 422 | ret = __thermal_zone_get_temp(tz, &temp); |
0c01ebbf | 423 | if (ret) { |
7e497a73 HG |
424 | if (ret != -EAGAIN) |
425 | dev_warn(&tz->device, | |
426 | "failed to read out thermal zone (%d)\n", | |
427 | ret); | |
e6e238c3 | 428 | return; |
0c01ebbf D |
429 | } |
430 | ||
431 | tz->last_temperature = tz->temperature; | |
432 | tz->temperature = temp; | |
06475b55 | 433 | |
100a8fdb | 434 | trace_thermal_temperature(tz); |
55cdf0a2 DL |
435 | |
436 | thermal_genl_sampling_temp(tz->id, temp); | |
bb431ba2 ZR |
437 | } |
438 | ||
33fcb595 RW |
439 | static void thermal_zone_device_check(struct work_struct *work) |
440 | { | |
441 | struct thermal_zone_device *tz = container_of(work, struct | |
442 | thermal_zone_device, | |
443 | poll_queue.work); | |
444 | thermal_zone_device_update(tz, THERMAL_EVENT_UNSPECIFIED); | |
445 | } | |
446 | ||
964f4843 | 447 | static void thermal_zone_device_init(struct thermal_zone_device *tz) |
bb431ba2 ZR |
448 | { |
449 | struct thermal_instance *pos; | |
33fcb595 RW |
450 | |
451 | INIT_DELAYED_WORK(&tz->poll_queue, thermal_zone_device_check); | |
452 | ||
bb431ba2 | 453 | tz->temperature = THERMAL_TEMP_INVALID; |
042a3d80 | 454 | tz->passive = 0; |
99b63316 MMP |
455 | tz->prev_low_trip = -INT_MAX; |
456 | tz->prev_high_trip = INT_MAX; | |
bb431ba2 ZR |
457 | list_for_each_entry(pos, &tz->thermal_instances, tz_node) |
458 | pos->initialized = false; | |
0c01ebbf D |
459 | } |
460 | ||
f831892e RW |
461 | static void thermal_governor_trip_crossed(struct thermal_governor *governor, |
462 | struct thermal_zone_device *tz, | |
463 | const struct thermal_trip *trip, | |
464 | bool crossed_up) | |
465 | { | |
466 | if (governor->trip_crossed) | |
467 | governor->trip_crossed(tz, trip, crossed_up); | |
468 | } | |
469 | ||
cb573eec RW |
470 | static void thermal_trip_crossed(struct thermal_zone_device *tz, |
471 | const struct thermal_trip *trip, | |
472 | struct thermal_governor *governor, | |
473 | bool crossed_up) | |
474 | { | |
475 | if (crossed_up) { | |
476 | thermal_notify_tz_trip_up(tz, trip); | |
477 | thermal_debug_tz_trip_up(tz, trip); | |
478 | } else { | |
479 | thermal_notify_tz_trip_down(tz, trip); | |
480 | thermal_debug_tz_trip_down(tz, trip); | |
481 | } | |
482 | thermal_governor_trip_crossed(governor, tz, trip, crossed_up); | |
483 | } | |
484 | ||
7454f2c4 RW |
485 | static int thermal_trip_notify_cmp(void *ascending, const struct list_head *a, |
486 | const struct list_head *b) | |
487 | { | |
488 | struct thermal_trip_desc *tda = container_of(a, struct thermal_trip_desc, | |
489 | notify_list_node); | |
490 | struct thermal_trip_desc *tdb = container_of(b, struct thermal_trip_desc, | |
491 | notify_list_node); | |
492 | int ret = tdb->notify_temp - tda->notify_temp; | |
493 | ||
494 | return ascending ? ret : -ret; | |
495 | } | |
496 | ||
05eeee2b GR |
497 | void __thermal_zone_device_update(struct thermal_zone_device *tz, |
498 | enum thermal_notify_event event) | |
1c439dec | 499 | { |
80f5fd45 | 500 | struct thermal_governor *governor = thermal_get_tz_governor(tz); |
daeeb032 | 501 | struct thermal_trip_desc *td; |
7454f2c4 RW |
502 | LIST_HEAD(way_down_list); |
503 | LIST_HEAD(way_up_list); | |
1c439dec | 504 | |
4e814173 | 505 | if (tz->suspended) |
1c439dec GR |
506 | return; |
507 | ||
1c439dec GR |
508 | if (!thermal_zone_device_is_enabled(tz)) |
509 | return; | |
510 | ||
511 | update_temperature(tz); | |
512 | ||
202aa0d4 RW |
513 | if (tz->temperature == THERMAL_TEMP_INVALID) |
514 | return; | |
515 | ||
1c439dec GR |
516 | __thermal_zone_set_trips(tz); |
517 | ||
518 | tz->notify_event = event; | |
519 | ||
daeeb032 | 520 | for_each_trip_desc(tz, td) |
7454f2c4 RW |
521 | handle_thermal_trip(tz, td, &way_up_list, &way_down_list); |
522 | ||
523 | list_sort(&way_up_list, &way_up_list, thermal_trip_notify_cmp); | |
cb573eec RW |
524 | list_for_each_entry(td, &way_up_list, notify_list_node) |
525 | thermal_trip_crossed(tz, &td->trip, governor, true); | |
7454f2c4 RW |
526 | |
527 | list_sort(NULL, &way_down_list, thermal_trip_notify_cmp); | |
cb573eec RW |
528 | list_for_each_entry(td, &way_down_list, notify_list_node) |
529 | thermal_trip_crossed(tz, &td->trip, governor, false); | |
1c439dec | 530 | |
976f4413 RW |
531 | if (governor->manage) |
532 | governor->manage(tz); | |
533 | ||
8dff6e84 | 534 | thermal_debug_update_trip_stats(tz); |
0a293c77 | 535 | |
1c439dec GR |
536 | monitor_thermal_zone(tz); |
537 | } | |
538 | ||
ac5d9ecc AP |
539 | static int thermal_zone_device_set_mode(struct thermal_zone_device *tz, |
540 | enum thermal_device_mode mode) | |
541 | { | |
542 | int ret = 0; | |
543 | ||
544 | mutex_lock(&tz->lock); | |
545 | ||
546 | /* do nothing if mode isn't changing */ | |
547 | if (mode == tz->mode) { | |
548 | mutex_unlock(&tz->lock); | |
549 | ||
550 | return ret; | |
551 | } | |
552 | ||
698a1eb1 RW |
553 | if (tz->ops.change_mode) |
554 | ret = tz->ops.change_mode(tz, mode); | |
ac5d9ecc AP |
555 | |
556 | if (!ret) | |
557 | tz->mode = mode; | |
558 | ||
1c439dec | 559 | __thermal_zone_device_update(tz, THERMAL_EVENT_UNSPECIFIED); |
ac5d9ecc | 560 | |
1c439dec | 561 | mutex_unlock(&tz->lock); |
ac5d9ecc | 562 | |
25be77e5 | 563 | if (mode == THERMAL_DEVICE_ENABLED) |
2f521890 | 564 | thermal_notify_tz_enable(tz); |
25be77e5 | 565 | else |
2f521890 | 566 | thermal_notify_tz_disable(tz); |
25be77e5 | 567 | |
ac5d9ecc AP |
568 | return ret; |
569 | } | |
570 | ||
571 | int thermal_zone_device_enable(struct thermal_zone_device *tz) | |
572 | { | |
573 | return thermal_zone_device_set_mode(tz, THERMAL_DEVICE_ENABLED); | |
574 | } | |
575 | EXPORT_SYMBOL_GPL(thermal_zone_device_enable); | |
576 | ||
577 | int thermal_zone_device_disable(struct thermal_zone_device *tz) | |
578 | { | |
579 | return thermal_zone_device_set_mode(tz, THERMAL_DEVICE_DISABLED); | |
580 | } | |
581 | EXPORT_SYMBOL_GPL(thermal_zone_device_disable); | |
582 | ||
583 | int thermal_zone_device_is_enabled(struct thermal_zone_device *tz) | |
584 | { | |
a930da9b | 585 | lockdep_assert_held(&tz->lock); |
ac5d9ecc | 586 | |
a930da9b | 587 | return tz->mode == THERMAL_DEVICE_ENABLED; |
ac5d9ecc | 588 | } |
ac5d9ecc | 589 | |
b38aa87f RW |
590 | static bool thermal_zone_is_present(struct thermal_zone_device *tz) |
591 | { | |
592 | return !list_empty(&tz->node); | |
593 | } | |
594 | ||
0e70f466 SP |
595 | void thermal_zone_device_update(struct thermal_zone_device *tz, |
596 | enum thermal_notify_event event) | |
0c01ebbf | 597 | { |
a930da9b | 598 | mutex_lock(&tz->lock); |
b38aa87f | 599 | if (thermal_zone_is_present(tz)) |
b778b4d7 | 600 | __thermal_zone_device_update(tz, event); |
a930da9b | 601 | mutex_unlock(&tz->lock); |
0c01ebbf | 602 | } |
910cb1e3 | 603 | EXPORT_SYMBOL_GPL(thermal_zone_device_update); |
0c01ebbf | 604 | |
ae2170d6 RW |
605 | void thermal_zone_trip_down(struct thermal_zone_device *tz, |
606 | const struct thermal_trip *trip) | |
607 | { | |
608 | thermal_trip_crossed(tz, trip, thermal_get_tz_governor(tz), false); | |
609 | } | |
610 | ||
3d44a509 DL |
611 | int for_each_thermal_governor(int (*cb)(struct thermal_governor *, void *), |
612 | void *data) | |
613 | { | |
614 | struct thermal_governor *gov; | |
615 | int ret = 0; | |
616 | ||
617 | mutex_lock(&thermal_governor_lock); | |
618 | list_for_each_entry(gov, &thermal_governor_list, governor_list) { | |
619 | ret = cb(gov, data); | |
620 | if (ret) | |
621 | break; | |
622 | } | |
623 | mutex_unlock(&thermal_governor_lock); | |
624 | ||
625 | return ret; | |
626 | } | |
627 | ||
628 | int for_each_thermal_cooling_device(int (*cb)(struct thermal_cooling_device *, | |
629 | void *), void *data) | |
630 | { | |
631 | struct thermal_cooling_device *cdev; | |
632 | int ret = 0; | |
633 | ||
634 | mutex_lock(&thermal_list_lock); | |
635 | list_for_each_entry(cdev, &thermal_cdev_list, node) { | |
636 | ret = cb(cdev, data); | |
637 | if (ret) | |
638 | break; | |
639 | } | |
640 | mutex_unlock(&thermal_list_lock); | |
641 | ||
642 | return ret; | |
643 | } | |
644 | ||
645 | int for_each_thermal_zone(int (*cb)(struct thermal_zone_device *, void *), | |
646 | void *data) | |
647 | { | |
648 | struct thermal_zone_device *tz; | |
649 | int ret = 0; | |
650 | ||
651 | mutex_lock(&thermal_list_lock); | |
652 | list_for_each_entry(tz, &thermal_tz_list, node) { | |
653 | ret = cb(tz, data); | |
654 | if (ret) | |
655 | break; | |
656 | } | |
657 | mutex_unlock(&thermal_list_lock); | |
658 | ||
659 | return ret; | |
660 | } | |
661 | ||
329b064f DL |
662 | struct thermal_zone_device *thermal_zone_get_by_id(int id) |
663 | { | |
82aa68af | 664 | struct thermal_zone_device *tz, *match = NULL; |
329b064f DL |
665 | |
666 | mutex_lock(&thermal_list_lock); | |
667 | list_for_each_entry(tz, &thermal_tz_list, node) { | |
82aa68af TR |
668 | if (tz->id == id) { |
669 | match = tz; | |
329b064f | 670 | break; |
82aa68af | 671 | } |
329b064f DL |
672 | } |
673 | mutex_unlock(&thermal_list_lock); | |
674 | ||
82aa68af | 675 | return match; |
329b064f DL |
676 | } |
677 | ||
81193e2e EV |
678 | /* |
679 | * Device management section: cooling devices, zones devices, and binding | |
680 | * | |
681 | * Set of functions provided by the thermal core for: | |
682 | * - cooling devices lifecycle: registration, unregistration, | |
683 | * binding, and unbinding. | |
684 | * - thermal zone devices lifecycle: registration, unregistration, | |
685 | * binding, and unbinding. | |
686 | */ | |
203d3d4a ZR |
687 | |
688 | /** | |
d069ed6b | 689 | * thermal_bind_cdev_to_trip - bind a cooling device to a thermal zone |
d2e4eb83 | 690 | * @tz: pointer to struct thermal_zone_device |
d069ed6b | 691 | * @trip: trip point the cooling devices is associated with in this zone. |
d2e4eb83 EV |
692 | * @cdev: pointer to struct thermal_cooling_device |
693 | * @upper: the Maximum cooling state for this trip point. | |
694 | * THERMAL_NO_LIMIT means no upper limit, | |
695 | * and the cooling device can be in max_state. | |
696 | * @lower: the Minimum cooling state can be used for this trip point. | |
697 | * THERMAL_NO_LIMIT means no lower limit, | |
698 | * and the cooling device can be in cooling state 0. | |
6cd9e9f6 KS |
699 | * @weight: The weight of the cooling device to be bound to the |
700 | * thermal zone. Use THERMAL_WEIGHT_DEFAULT for the | |
701 | * default value | |
543a9561 | 702 | * |
d2e4eb83 EV |
703 | * This interface function bind a thermal cooling device to the certain trip |
704 | * point of a thermal zone device. | |
543a9561 | 705 | * This function is usually called in the thermal zone device .bind callback. |
d2e4eb83 EV |
706 | * |
707 | * Return: 0 on success, the proper error value otherwise. | |
203d3d4a | 708 | */ |
d069ed6b RW |
709 | int thermal_bind_cdev_to_trip(struct thermal_zone_device *tz, |
710 | const struct thermal_trip *trip, | |
9d99842f | 711 | struct thermal_cooling_device *cdev, |
6cd9e9f6 KS |
712 | unsigned long upper, unsigned long lower, |
713 | unsigned int weight) | |
203d3d4a | 714 | { |
b81b6ba3 ZR |
715 | struct thermal_instance *dev; |
716 | struct thermal_instance *pos; | |
c7516709 TS |
717 | struct thermal_zone_device *pos1; |
718 | struct thermal_cooling_device *pos2; | |
790930f4 | 719 | bool upper_no_limit; |
c408b3d1 | 720 | int result; |
203d3d4a | 721 | |
c7516709 TS |
722 | list_for_each_entry(pos1, &thermal_tz_list, node) { |
723 | if (pos1 == tz) | |
724 | break; | |
725 | } | |
726 | list_for_each_entry(pos2, &thermal_cdev_list, node) { | |
727 | if (pos2 == cdev) | |
728 | break; | |
729 | } | |
730 | ||
731 | if (tz != pos1 || cdev != pos2) | |
203d3d4a ZR |
732 | return -EINVAL; |
733 | ||
9d99842f ZR |
734 | /* lower default 0, upper default max_state */ |
735 | lower = lower == THERMAL_NO_LIMIT ? 0 : lower; | |
790930f4 RW |
736 | |
737 | if (upper == THERMAL_NO_LIMIT) { | |
738 | upper = cdev->max_state; | |
739 | upper_no_limit = true; | |
740 | } else { | |
741 | upper_no_limit = false; | |
742 | } | |
9d99842f | 743 | |
c408b3d1 | 744 | if (lower > upper || upper > cdev->max_state) |
9d99842f ZR |
745 | return -EINVAL; |
746 | ||
95e3ed15 | 747 | dev = kzalloc(sizeof(*dev), GFP_KERNEL); |
203d3d4a ZR |
748 | if (!dev) |
749 | return -ENOMEM; | |
750 | dev->tz = tz; | |
751 | dev->cdev = cdev; | |
752 | dev->trip = trip; | |
9d99842f | 753 | dev->upper = upper; |
790930f4 | 754 | dev->upper_no_limit = upper_no_limit; |
9d99842f | 755 | dev->lower = lower; |
ce119f83 | 756 | dev->target = THERMAL_NO_TARGET; |
6cd9e9f6 | 757 | dev->weight = weight; |
74051ba5 | 758 | |
5a5b7d8d | 759 | result = ida_alloc(&tz->ida, GFP_KERNEL); |
b31ef828 | 760 | if (result < 0) |
203d3d4a ZR |
761 | goto free_mem; |
762 | ||
b31ef828 | 763 | dev->id = result; |
203d3d4a ZR |
764 | sprintf(dev->name, "cdev%d", dev->id); |
765 | result = | |
766 | sysfs_create_link(&tz->device.kobj, &cdev->device.kobj, dev->name); | |
767 | if (result) | |
b31ef828 | 768 | goto release_ida; |
203d3d4a | 769 | |
c9962609 DC |
770 | snprintf(dev->attr_name, sizeof(dev->attr_name), "cdev%d_trip_point", |
771 | dev->id); | |
975f8c56 | 772 | sysfs_attr_init(&dev->attr.attr); |
203d3d4a ZR |
773 | dev->attr.attr.name = dev->attr_name; |
774 | dev->attr.attr.mode = 0444; | |
33e678d4 | 775 | dev->attr.show = trip_point_show; |
203d3d4a ZR |
776 | result = device_create_file(&tz->device, &dev->attr); |
777 | if (result) | |
778 | goto remove_symbol_link; | |
779 | ||
c9962609 DC |
780 | snprintf(dev->weight_attr_name, sizeof(dev->weight_attr_name), |
781 | "cdev%d_weight", dev->id); | |
db916513 JM |
782 | sysfs_attr_init(&dev->weight_attr.attr); |
783 | dev->weight_attr.attr.name = dev->weight_attr_name; | |
784 | dev->weight_attr.attr.mode = S_IWUSR | S_IRUGO; | |
33e678d4 VK |
785 | dev->weight_attr.show = weight_show; |
786 | dev->weight_attr.store = weight_store; | |
db916513 JM |
787 | result = device_create_file(&tz->device, &dev->weight_attr); |
788 | if (result) | |
789 | goto remove_trip_file; | |
790 | ||
203d3d4a | 791 | mutex_lock(&tz->lock); |
f4a821ce | 792 | mutex_lock(&cdev->lock); |
cddf31b3 | 793 | list_for_each_entry(pos, &tz->thermal_instances, tz_node) |
b659a30d EV |
794 | if (pos->tz == tz && pos->trip == trip && pos->cdev == cdev) { |
795 | result = -EEXIST; | |
796 | break; | |
797 | } | |
b5e4ae62 | 798 | if (!result) { |
cddf31b3 | 799 | list_add_tail(&dev->tz_node, &tz->thermal_instances); |
b5e4ae62 | 800 | list_add_tail(&dev->cdev_node, &cdev->thermal_instances); |
4511f716 | 801 | atomic_set(&tz->need_update, 1); |
a8c95940 LL |
802 | |
803 | thermal_governor_update_tz(tz, THERMAL_TZ_BIND_CDEV); | |
b5e4ae62 | 804 | } |
f4a821ce | 805 | mutex_unlock(&cdev->lock); |
203d3d4a ZR |
806 | mutex_unlock(&tz->lock); |
807 | ||
808 | if (!result) | |
809 | return 0; | |
810 | ||
db916513 JM |
811 | device_remove_file(&tz->device, &dev->weight_attr); |
812 | remove_trip_file: | |
203d3d4a | 813 | device_remove_file(&tz->device, &dev->attr); |
caca8b80 | 814 | remove_symbol_link: |
203d3d4a | 815 | sysfs_remove_link(&tz->device.kobj, dev->name); |
b31ef828 | 816 | release_ida: |
5a5b7d8d | 817 | ida_free(&tz->ida, dev->id); |
caca8b80 | 818 | free_mem: |
203d3d4a ZR |
819 | kfree(dev); |
820 | return result; | |
821 | } | |
d069ed6b RW |
822 | EXPORT_SYMBOL_GPL(thermal_bind_cdev_to_trip); |
823 | ||
824 | int thermal_zone_bind_cooling_device(struct thermal_zone_device *tz, | |
825 | int trip_index, | |
826 | struct thermal_cooling_device *cdev, | |
827 | unsigned long upper, unsigned long lower, | |
828 | unsigned int weight) | |
829 | { | |
830 | if (trip_index < 0 || trip_index >= tz->num_trips) | |
831 | return -EINVAL; | |
832 | ||
daeeb032 | 833 | return thermal_bind_cdev_to_trip(tz, &tz->trips[trip_index].trip, cdev, |
d069ed6b RW |
834 | upper, lower, weight); |
835 | } | |
910cb1e3 | 836 | EXPORT_SYMBOL_GPL(thermal_zone_bind_cooling_device); |
203d3d4a ZR |
837 | |
838 | /** | |
d069ed6b | 839 | * thermal_unbind_cdev_from_trip - unbind a cooling device from a thermal zone. |
9892e5dc | 840 | * @tz: pointer to a struct thermal_zone_device. |
d069ed6b | 841 | * @trip: trip point the cooling devices is associated with in this zone. |
9892e5dc | 842 | * @cdev: pointer to a struct thermal_cooling_device. |
543a9561 | 843 | * |
9892e5dc EV |
844 | * This interface function unbind a thermal cooling device from the certain |
845 | * trip point of a thermal zone device. | |
543a9561 | 846 | * This function is usually called in the thermal zone device .unbind callback. |
9892e5dc EV |
847 | * |
848 | * Return: 0 on success, the proper error value otherwise. | |
203d3d4a | 849 | */ |
d069ed6b RW |
850 | int thermal_unbind_cdev_from_trip(struct thermal_zone_device *tz, |
851 | const struct thermal_trip *trip, | |
852 | struct thermal_cooling_device *cdev) | |
203d3d4a | 853 | { |
b81b6ba3 | 854 | struct thermal_instance *pos, *next; |
203d3d4a ZR |
855 | |
856 | mutex_lock(&tz->lock); | |
f4a821ce | 857 | mutex_lock(&cdev->lock); |
cddf31b3 | 858 | list_for_each_entry_safe(pos, next, &tz->thermal_instances, tz_node) { |
543a9561 | 859 | if (pos->tz == tz && pos->trip == trip && pos->cdev == cdev) { |
cddf31b3 | 860 | list_del(&pos->tz_node); |
b5e4ae62 | 861 | list_del(&pos->cdev_node); |
a8c95940 LL |
862 | |
863 | thermal_governor_update_tz(tz, THERMAL_TZ_UNBIND_CDEV); | |
864 | ||
f4a821ce | 865 | mutex_unlock(&cdev->lock); |
203d3d4a ZR |
866 | mutex_unlock(&tz->lock); |
867 | goto unbind; | |
868 | } | |
869 | } | |
f4a821ce | 870 | mutex_unlock(&cdev->lock); |
203d3d4a ZR |
871 | mutex_unlock(&tz->lock); |
872 | ||
873 | return -ENODEV; | |
874 | ||
caca8b80 | 875 | unbind: |
528464ea | 876 | device_remove_file(&tz->device, &pos->weight_attr); |
203d3d4a ZR |
877 | device_remove_file(&tz->device, &pos->attr); |
878 | sysfs_remove_link(&tz->device.kobj, pos->name); | |
5a5b7d8d | 879 | ida_free(&tz->ida, pos->id); |
203d3d4a ZR |
880 | kfree(pos); |
881 | return 0; | |
882 | } | |
d069ed6b RW |
883 | EXPORT_SYMBOL_GPL(thermal_unbind_cdev_from_trip); |
884 | ||
885 | int thermal_zone_unbind_cooling_device(struct thermal_zone_device *tz, | |
886 | int trip_index, | |
887 | struct thermal_cooling_device *cdev) | |
888 | { | |
889 | if (trip_index < 0 || trip_index >= tz->num_trips) | |
890 | return -EINVAL; | |
891 | ||
daeeb032 | 892 | return thermal_unbind_cdev_from_trip(tz, &tz->trips[trip_index].trip, cdev); |
d069ed6b | 893 | } |
910cb1e3 | 894 | EXPORT_SYMBOL_GPL(thermal_zone_unbind_cooling_device); |
203d3d4a ZR |
895 | |
896 | static void thermal_release(struct device *dev) | |
897 | { | |
898 | struct thermal_zone_device *tz; | |
899 | struct thermal_cooling_device *cdev; | |
900 | ||
caca8b80 JP |
901 | if (!strncmp(dev_name(dev), "thermal_zone", |
902 | sizeof("thermal_zone") - 1)) { | |
203d3d4a | 903 | tz = to_thermal_zone(dev); |
6a6cd25b | 904 | thermal_zone_destroy_device_groups(tz); |
d35f29ed | 905 | mutex_destroy(&tz->lock); |
4649620d | 906 | complete(&tz->removal); |
b659a30d EV |
907 | } else if (!strncmp(dev_name(dev), "cooling_device", |
908 | sizeof("cooling_device") - 1)) { | |
203d3d4a | 909 | cdev = to_cooling_device(dev); |
e398421f | 910 | thermal_cooling_device_destroy_sysfs(cdev); |
57a427c8 | 911 | kfree_const(cdev->type); |
e398421f | 912 | ida_free(&thermal_cdev_ida, cdev->id); |
203d3d4a ZR |
913 | kfree(cdev); |
914 | } | |
915 | } | |
916 | ||
9e0a9be2 | 917 | static struct class *thermal_class; |
203d3d4a | 918 | |
4b0d3c2d EV |
919 | static inline |
920 | void print_bind_err_msg(struct thermal_zone_device *tz, | |
921 | struct thermal_cooling_device *cdev, int ret) | |
f502ab84 EV |
922 | { |
923 | dev_err(&tz->device, "binding zone %s with cdev %s failed:%d\n", | |
924 | tz->type, cdev->type, ret); | |
925 | } | |
926 | ||
949aad83 EV |
927 | static void bind_cdev(struct thermal_cooling_device *cdev) |
928 | { | |
ded2d383 | 929 | int ret; |
949aad83 EV |
930 | struct thermal_zone_device *pos = NULL; |
931 | ||
949aad83 | 932 | list_for_each_entry(pos, &thermal_tz_list, node) { |
698a1eb1 RW |
933 | if (pos->ops.bind) { |
934 | ret = pos->ops.bind(pos, cdev); | |
949aad83 EV |
935 | if (ret) |
936 | print_bind_err_msg(pos, cdev, ret); | |
949aad83 EV |
937 | } |
938 | } | |
949aad83 EV |
939 | } |
940 | ||
203d3d4a | 941 | /** |
a116b5d4 EV |
942 | * __thermal_cooling_device_register() - register a new thermal cooling device |
943 | * @np: a pointer to a device tree node. | |
203d3d4a ZR |
944 | * @type: the thermal cooling device type. |
945 | * @devdata: device private data. | |
946 | * @ops: standard thermal cooling devices callbacks. | |
3a6eccb3 EV |
947 | * |
948 | * This interface function adds a new thermal cooling device (fan/processor/...) | |
949 | * to /sys/class/thermal/ folder as cooling_device[0-*]. It tries to bind itself | |
950 | * to all the thermal zone devices registered at the same time. | |
a116b5d4 EV |
951 | * It also gives the opportunity to link the cooling device to a device tree |
952 | * node, so that it can be bound to a thermal zone created out of device tree. | |
3a6eccb3 EV |
953 | * |
954 | * Return: a pointer to the created struct thermal_cooling_device or an | |
955 | * ERR_PTR. Caller must check return value with IS_ERR*() helpers. | |
203d3d4a | 956 | */ |
a116b5d4 EV |
957 | static struct thermal_cooling_device * |
958 | __thermal_cooling_device_register(struct device_node *np, | |
f991de53 | 959 | const char *type, void *devdata, |
a116b5d4 | 960 | const struct thermal_cooling_device_ops *ops) |
203d3d4a ZR |
961 | { |
962 | struct thermal_cooling_device *cdev; | |
4511f716 | 963 | struct thermal_zone_device *pos = NULL; |
31a0fa00 | 964 | unsigned long current_state; |
0a5c2671 | 965 | int id, ret; |
203d3d4a ZR |
966 | |
967 | if (!ops || !ops->get_max_state || !ops->get_cur_state || | |
543a9561 | 968 | !ops->set_cur_state) |
3e6fda5c | 969 | return ERR_PTR(-EINVAL); |
203d3d4a | 970 | |
9e0a9be2 RW |
971 | if (!thermal_class) |
972 | return ERR_PTR(-ENODEV); | |
973 | ||
95e3ed15 | 974 | cdev = kzalloc(sizeof(*cdev), GFP_KERNEL); |
203d3d4a | 975 | if (!cdev) |
3e6fda5c | 976 | return ERR_PTR(-ENOMEM); |
203d3d4a | 977 | |
5a5b7d8d | 978 | ret = ida_alloc(&thermal_cdev_ida, GFP_KERNEL); |
58483761 DL |
979 | if (ret < 0) |
980 | goto out_kfree_cdev; | |
981 | cdev->id = ret; | |
0a5c2671 | 982 | id = ret; |
58483761 | 983 | |
57a427c8 | 984 | cdev->type = kstrdup_const(type ? type : "", GFP_KERNEL); |
58483761 DL |
985 | if (!cdev->type) { |
986 | ret = -ENOMEM; | |
987 | goto out_ida_remove; | |
203d3d4a ZR |
988 | } |
989 | ||
f4a821ce | 990 | mutex_init(&cdev->lock); |
b5e4ae62 | 991 | INIT_LIST_HEAD(&cdev->thermal_instances); |
a116b5d4 | 992 | cdev->np = np; |
203d3d4a | 993 | cdev->ops = ops; |
5ca0cce5 | 994 | cdev->updated = false; |
9e0a9be2 | 995 | cdev->device.class = thermal_class; |
203d3d4a | 996 | cdev->devdata = devdata; |
c408b3d1 | 997 | |
e49a1e1e | 998 | ret = cdev->ops->get_max_state(cdev, &cdev->max_state); |
e398421f VK |
999 | if (ret) |
1000 | goto out_cdev_type; | |
c408b3d1 | 1001 | |
1af89ded RW |
1002 | /* |
1003 | * The cooling device's current state is only needed for debug | |
1004 | * initialization below, so a failure to get it does not cause | |
1005 | * the entire cooling device initialization to fail. However, | |
1006 | * the debug will not work for the device if its initial state | |
1007 | * cannot be determined and drivers are responsible for ensuring | |
1008 | * that this will not happen. | |
1009 | */ | |
31a0fa00 RW |
1010 | ret = cdev->ops->get_cur_state(cdev, ¤t_state); |
1011 | if (ret) | |
1af89ded | 1012 | current_state = ULONG_MAX; |
31a0fa00 | 1013 | |
8ea22951 | 1014 | thermal_cooling_device_setup_sysfs(cdev); |
6c54b7bc | 1015 | |
4748f968 | 1016 | ret = dev_set_name(&cdev->device, "cooling_device%d", cdev->id); |
e398421f VK |
1017 | if (ret) |
1018 | goto out_cooling_dev; | |
6c54b7bc | 1019 | |
58483761 | 1020 | ret = device_register(&cdev->device); |
e398421f VK |
1021 | if (ret) { |
1022 | /* thermal_release() handles rest of the cleanup */ | |
1023 | put_device(&cdev->device); | |
1024 | return ERR_PTR(ret); | |
1025 | } | |
203d3d4a | 1026 | |
1af89ded RW |
1027 | if (current_state <= cdev->max_state) |
1028 | thermal_debug_cdev_add(cdev, current_state); | |
31a0fa00 | 1029 | |
7e8ee1e9 | 1030 | /* Add 'this' new cdev to the global cdev list */ |
203d3d4a | 1031 | mutex_lock(&thermal_list_lock); |
cd246fa9 | 1032 | |
203d3d4a | 1033 | list_add(&cdev->node, &thermal_cdev_list); |
203d3d4a | 1034 | |
7e8ee1e9 D |
1035 | /* Update binding information for 'this' new cdev */ |
1036 | bind_cdev(cdev); | |
1037 | ||
4511f716 CY |
1038 | list_for_each_entry(pos, &thermal_tz_list, node) |
1039 | if (atomic_cmpxchg(&pos->need_update, 1, 0)) | |
0e70f466 SP |
1040 | thermal_zone_device_update(pos, |
1041 | THERMAL_EVENT_UNSPECIFIED); | |
cd246fa9 | 1042 | |
4511f716 CY |
1043 | mutex_unlock(&thermal_list_lock); |
1044 | ||
7e8ee1e9 | 1045 | return cdev; |
58483761 | 1046 | |
e398421f | 1047 | out_cooling_dev: |
98a160e8 | 1048 | thermal_cooling_device_destroy_sysfs(cdev); |
e398421f | 1049 | out_cdev_type: |
57a427c8 | 1050 | kfree_const(cdev->type); |
58483761 | 1051 | out_ida_remove: |
5a5b7d8d | 1052 | ida_free(&thermal_cdev_ida, id); |
58483761 | 1053 | out_kfree_cdev: |
d44616c6 | 1054 | kfree(cdev); |
58483761 | 1055 | return ERR_PTR(ret); |
203d3d4a | 1056 | } |
a116b5d4 EV |
1057 | |
1058 | /** | |
1059 | * thermal_cooling_device_register() - register a new thermal cooling device | |
1060 | * @type: the thermal cooling device type. | |
1061 | * @devdata: device private data. | |
1062 | * @ops: standard thermal cooling devices callbacks. | |
1063 | * | |
1064 | * This interface function adds a new thermal cooling device (fan/processor/...) | |
1065 | * to /sys/class/thermal/ folder as cooling_device[0-*]. It tries to bind itself | |
1066 | * to all the thermal zone devices registered at the same time. | |
1067 | * | |
1068 | * Return: a pointer to the created struct thermal_cooling_device or an | |
1069 | * ERR_PTR. Caller must check return value with IS_ERR*() helpers. | |
1070 | */ | |
1071 | struct thermal_cooling_device * | |
f991de53 | 1072 | thermal_cooling_device_register(const char *type, void *devdata, |
a116b5d4 EV |
1073 | const struct thermal_cooling_device_ops *ops) |
1074 | { | |
1075 | return __thermal_cooling_device_register(NULL, type, devdata, ops); | |
1076 | } | |
910cb1e3 | 1077 | EXPORT_SYMBOL_GPL(thermal_cooling_device_register); |
203d3d4a | 1078 | |
a116b5d4 EV |
1079 | /** |
1080 | * thermal_of_cooling_device_register() - register an OF thermal cooling device | |
1081 | * @np: a pointer to a device tree node. | |
1082 | * @type: the thermal cooling device type. | |
1083 | * @devdata: device private data. | |
1084 | * @ops: standard thermal cooling devices callbacks. | |
1085 | * | |
1086 | * This function will register a cooling device with device tree node reference. | |
1087 | * This interface function adds a new thermal cooling device (fan/processor/...) | |
1088 | * to /sys/class/thermal/ folder as cooling_device[0-*]. It tries to bind itself | |
1089 | * to all the thermal zone devices registered at the same time. | |
1090 | * | |
1091 | * Return: a pointer to the created struct thermal_cooling_device or an | |
1092 | * ERR_PTR. Caller must check return value with IS_ERR*() helpers. | |
1093 | */ | |
1094 | struct thermal_cooling_device * | |
1095 | thermal_of_cooling_device_register(struct device_node *np, | |
f991de53 | 1096 | const char *type, void *devdata, |
a116b5d4 EV |
1097 | const struct thermal_cooling_device_ops *ops) |
1098 | { | |
1099 | return __thermal_cooling_device_register(np, type, devdata, ops); | |
1100 | } | |
1101 | EXPORT_SYMBOL_GPL(thermal_of_cooling_device_register); | |
1102 | ||
b4ab114c GR |
1103 | static void thermal_cooling_device_release(struct device *dev, void *res) |
1104 | { | |
1105 | thermal_cooling_device_unregister( | |
1106 | *(struct thermal_cooling_device **)res); | |
1107 | } | |
1108 | ||
1109 | /** | |
1110 | * devm_thermal_of_cooling_device_register() - register an OF thermal cooling | |
1111 | * device | |
1112 | * @dev: a valid struct device pointer of a sensor device. | |
1113 | * @np: a pointer to a device tree node. | |
1114 | * @type: the thermal cooling device type. | |
1115 | * @devdata: device private data. | |
1116 | * @ops: standard thermal cooling devices callbacks. | |
1117 | * | |
1118 | * This function will register a cooling device with device tree node reference. | |
1119 | * This interface function adds a new thermal cooling device (fan/processor/...) | |
1120 | * to /sys/class/thermal/ folder as cooling_device[0-*]. It tries to bind itself | |
1121 | * to all the thermal zone devices registered at the same time. | |
1122 | * | |
1123 | * Return: a pointer to the created struct thermal_cooling_device or an | |
1124 | * ERR_PTR. Caller must check return value with IS_ERR*() helpers. | |
1125 | */ | |
1126 | struct thermal_cooling_device * | |
1127 | devm_thermal_of_cooling_device_register(struct device *dev, | |
1128 | struct device_node *np, | |
1129 | char *type, void *devdata, | |
1130 | const struct thermal_cooling_device_ops *ops) | |
1131 | { | |
1132 | struct thermal_cooling_device **ptr, *tcd; | |
1133 | ||
1134 | ptr = devres_alloc(thermal_cooling_device_release, sizeof(*ptr), | |
1135 | GFP_KERNEL); | |
1136 | if (!ptr) | |
1137 | return ERR_PTR(-ENOMEM); | |
1138 | ||
1139 | tcd = __thermal_cooling_device_register(np, type, devdata, ops); | |
1140 | if (IS_ERR(tcd)) { | |
1141 | devres_free(ptr); | |
1142 | return tcd; | |
1143 | } | |
1144 | ||
1145 | *ptr = tcd; | |
1146 | devres_add(dev, ptr); | |
1147 | ||
1148 | return tcd; | |
1149 | } | |
1150 | EXPORT_SYMBOL_GPL(devm_thermal_of_cooling_device_register); | |
1151 | ||
c43198af RW |
1152 | static bool thermal_cooling_device_present(struct thermal_cooling_device *cdev) |
1153 | { | |
1154 | struct thermal_cooling_device *pos = NULL; | |
1155 | ||
1156 | list_for_each_entry(pos, &thermal_cdev_list, node) { | |
1157 | if (pos == cdev) | |
1158 | return true; | |
1159 | } | |
1160 | ||
1161 | return false; | |
1162 | } | |
1163 | ||
790930f4 RW |
1164 | /** |
1165 | * thermal_cooling_device_update - Update a cooling device object | |
1166 | * @cdev: Target cooling device. | |
1167 | * | |
1168 | * Update @cdev to reflect a change of the underlying hardware or platform. | |
1169 | * | |
1170 | * Must be called when the maximum cooling state of @cdev becomes invalid and so | |
1171 | * its .get_max_state() callback needs to be run to produce the new maximum | |
1172 | * cooling state value. | |
1173 | */ | |
1174 | void thermal_cooling_device_update(struct thermal_cooling_device *cdev) | |
1175 | { | |
1176 | struct thermal_instance *ti; | |
1177 | unsigned long state; | |
1178 | ||
1179 | if (IS_ERR_OR_NULL(cdev)) | |
1180 | return; | |
1181 | ||
1182 | /* | |
1183 | * Hold thermal_list_lock throughout the update to prevent the device | |
1184 | * from going away while being updated. | |
1185 | */ | |
1186 | mutex_lock(&thermal_list_lock); | |
1187 | ||
1188 | if (!thermal_cooling_device_present(cdev)) | |
1189 | goto unlock_list; | |
1190 | ||
1191 | /* | |
1192 | * Update under the cdev lock to prevent the state from being set beyond | |
1193 | * the new limit concurrently. | |
1194 | */ | |
1195 | mutex_lock(&cdev->lock); | |
1196 | ||
1197 | if (cdev->ops->get_max_state(cdev, &cdev->max_state)) | |
1198 | goto unlock; | |
1199 | ||
1200 | thermal_cooling_device_stats_reinit(cdev); | |
1201 | ||
1202 | list_for_each_entry(ti, &cdev->thermal_instances, cdev_node) { | |
1203 | if (ti->upper == cdev->max_state) | |
1204 | continue; | |
1205 | ||
1206 | if (ti->upper < cdev->max_state) { | |
1207 | if (ti->upper_no_limit) | |
1208 | ti->upper = cdev->max_state; | |
1209 | ||
1210 | continue; | |
1211 | } | |
1212 | ||
1213 | ti->upper = cdev->max_state; | |
1214 | if (ti->lower > ti->upper) | |
1215 | ti->lower = ti->upper; | |
1216 | ||
1217 | if (ti->target == THERMAL_NO_TARGET) | |
1218 | continue; | |
1219 | ||
1220 | if (ti->target > ti->upper) | |
1221 | ti->target = ti->upper; | |
1222 | } | |
1223 | ||
1224 | if (cdev->ops->get_cur_state(cdev, &state) || state > cdev->max_state) | |
1225 | goto unlock; | |
1226 | ||
1227 | thermal_cooling_device_stats_update(cdev, state); | |
1228 | ||
1229 | unlock: | |
1230 | mutex_unlock(&cdev->lock); | |
1231 | ||
1232 | unlock_list: | |
1233 | mutex_unlock(&thermal_list_lock); | |
1234 | } | |
1235 | EXPORT_SYMBOL_GPL(thermal_cooling_device_update); | |
1236 | ||
203d3d4a | 1237 | /** |
38e7b549 | 1238 | * thermal_cooling_device_unregister - removes a thermal cooling device |
203d3d4a ZR |
1239 | * @cdev: the thermal cooling device to remove. |
1240 | * | |
38e7b549 EV |
1241 | * thermal_cooling_device_unregister() must be called when a registered |
1242 | * thermal cooling device is no longer needed. | |
203d3d4a | 1243 | */ |
7e8ee1e9 | 1244 | void thermal_cooling_device_unregister(struct thermal_cooling_device *cdev) |
203d3d4a ZR |
1245 | { |
1246 | struct thermal_zone_device *tz; | |
203d3d4a ZR |
1247 | |
1248 | if (!cdev) | |
1249 | return; | |
1250 | ||
755113d7 DL |
1251 | thermal_debug_cdev_remove(cdev); |
1252 | ||
203d3d4a | 1253 | mutex_lock(&thermal_list_lock); |
c43198af RW |
1254 | |
1255 | if (!thermal_cooling_device_present(cdev)) { | |
203d3d4a ZR |
1256 | mutex_unlock(&thermal_list_lock); |
1257 | return; | |
1258 | } | |
c43198af | 1259 | |
203d3d4a | 1260 | list_del(&cdev->node); |
7e8ee1e9 D |
1261 | |
1262 | /* Unbind all thermal zones associated with 'this' cdev */ | |
203d3d4a | 1263 | list_for_each_entry(tz, &thermal_tz_list, node) { |
698a1eb1 RW |
1264 | if (tz->ops.unbind) |
1265 | tz->ops.unbind(tz, cdev); | |
203d3d4a | 1266 | } |
7e8ee1e9 | 1267 | |
203d3d4a | 1268 | mutex_unlock(&thermal_list_lock); |
7e8ee1e9 | 1269 | |
47e3f000 | 1270 | device_unregister(&cdev->device); |
203d3d4a | 1271 | } |
910cb1e3 | 1272 | EXPORT_SYMBOL_GPL(thermal_cooling_device_unregister); |
203d3d4a | 1273 | |
90f5b5bb | 1274 | static void bind_tz(struct thermal_zone_device *tz) |
ce119f83 | 1275 | { |
ded2d383 | 1276 | int ret; |
90f5b5bb | 1277 | struct thermal_cooling_device *pos = NULL; |
ce119f83 | 1278 | |
698a1eb1 | 1279 | if (!tz->ops.bind) |
ce119f83 | 1280 | return; |
c56f5c03 | 1281 | |
90f5b5bb | 1282 | mutex_lock(&thermal_list_lock); |
c56f5c03 | 1283 | |
90f5b5bb | 1284 | list_for_each_entry(pos, &thermal_cdev_list, node) { |
698a1eb1 | 1285 | ret = tz->ops.bind(tz, pos); |
ded2d383 ZR |
1286 | if (ret) |
1287 | print_bind_err_msg(tz, pos, ret); | |
c56f5c03 | 1288 | } |
ded2d383 | 1289 | |
90f5b5bb | 1290 | mutex_unlock(&thermal_list_lock); |
c56f5c03 D |
1291 | } |
1292 | ||
e5f2cda6 DL |
1293 | static void thermal_set_delay_jiffies(unsigned long *delay_jiffies, int delay_ms) |
1294 | { | |
1295 | *delay_jiffies = msecs_to_jiffies(delay_ms); | |
1296 | if (delay_ms > 1000) | |
1297 | *delay_jiffies = round_jiffies(*delay_jiffies); | |
1298 | } | |
1299 | ||
7c3d5c20 DL |
1300 | int thermal_zone_get_crit_temp(struct thermal_zone_device *tz, int *temp) |
1301 | { | |
daeeb032 RW |
1302 | const struct thermal_trip_desc *td; |
1303 | int ret = -EINVAL; | |
7c3d5c20 | 1304 | |
698a1eb1 RW |
1305 | if (tz->ops.get_crit_temp) |
1306 | return tz->ops.get_crit_temp(tz, temp); | |
7c3d5c20 | 1307 | |
7c3d5c20 DL |
1308 | mutex_lock(&tz->lock); |
1309 | ||
daeeb032 RW |
1310 | for_each_trip_desc(tz, td) { |
1311 | const struct thermal_trip *trip = &td->trip; | |
1312 | ||
1313 | if (trip->type == THERMAL_TRIP_CRITICAL) { | |
1314 | *temp = trip->temperature; | |
7c3d5c20 DL |
1315 | ret = 0; |
1316 | break; | |
1317 | } | |
1318 | } | |
1319 | ||
1320 | mutex_unlock(&tz->lock); | |
1321 | ||
1322 | return ret; | |
1323 | } | |
1324 | EXPORT_SYMBOL_GPL(thermal_zone_get_crit_temp); | |
1325 | ||
203d3d4a | 1326 | /** |
fae11de5 | 1327 | * thermal_zone_device_register_with_trips() - register a new thermal zone device |
203d3d4a | 1328 | * @type: the thermal zone device type |
fae11de5 | 1329 | * @trips: a pointer to an array of thermal trips |
e5bfcd30 | 1330 | * @num_trips: the number of trip points the thermal zone support |
203d3d4a ZR |
1331 | * @devdata: private device data |
1332 | * @ops: standard thermal zone device callbacks | |
50125a9b | 1333 | * @tzp: thermal zone platform parameters |
b1569e99 MG |
1334 | * @passive_delay: number of milliseconds to wait between polls when |
1335 | * performing passive cooling | |
1336 | * @polling_delay: number of milliseconds to wait between polls when checking | |
1337 | * whether trip points have been crossed (0 for interrupt | |
1338 | * driven systems) | |
203d3d4a | 1339 | * |
a00e55f9 EV |
1340 | * This interface function adds a new thermal zone device (sensor) to |
1341 | * /sys/class/thermal folder as thermal_zone[0-*]. It tries to bind all the | |
1342 | * thermal cooling devices registered at the same time. | |
203d3d4a | 1343 | * thermal_zone_device_unregister() must be called when the device is no |
1b7ddb84 | 1344 | * longer needed. The passive cooling depends on the .get_trend() return value. |
a00e55f9 EV |
1345 | * |
1346 | * Return: a pointer to the created struct thermal_zone_device or an | |
1347 | * in case of error, an ERR_PTR. Caller must check return value with | |
1348 | * IS_ERR*() helpers. | |
203d3d4a | 1349 | */ |
eb7be329 | 1350 | struct thermal_zone_device * |
9b0a6275 RW |
1351 | thermal_zone_device_register_with_trips(const char *type, |
1352 | const struct thermal_trip *trips, | |
4a62d588 | 1353 | int num_trips, void *devdata, |
698a1eb1 | 1354 | const struct thermal_zone_device_ops *ops, |
9b0a6275 RW |
1355 | const struct thermal_zone_params *tzp, |
1356 | int passive_delay, int polling_delay) | |
203d3d4a | 1357 | { |
daeeb032 | 1358 | const struct thermal_trip *trip = trips; |
203d3d4a | 1359 | struct thermal_zone_device *tz; |
daeeb032 | 1360 | struct thermal_trip_desc *td; |
adc8749b | 1361 | int id; |
203d3d4a | 1362 | int result; |
e33df1d2 | 1363 | struct thermal_governor *governor; |
203d3d4a | 1364 | |
67eed44b | 1365 | if (!type || strlen(type) == 0) { |
3f95ac32 | 1366 | pr_err("No thermal zone type defined\n"); |
54fa38cc | 1367 | return ERR_PTR(-EINVAL); |
67eed44b | 1368 | } |
54fa38cc | 1369 | |
c71d8035 | 1370 | if (strlen(type) >= THERMAL_NAME_LENGTH) { |
3f95ac32 | 1371 | pr_err("Thermal zone name (%s) too long, should be under %d chars\n", |
67eed44b | 1372 | type, THERMAL_NAME_LENGTH); |
3e6fda5c | 1373 | return ERR_PTR(-EINVAL); |
67eed44b | 1374 | } |
203d3d4a | 1375 | |
4a62d588 | 1376 | if (num_trips < 0) { |
3f95ac32 | 1377 | pr_err("Incorrect number of thermal trips\n"); |
3e6fda5c | 1378 | return ERR_PTR(-EINVAL); |
67eed44b | 1379 | } |
203d3d4a | 1380 | |
404f62cd | 1381 | if (!ops || !ops->get_temp) { |
3f95ac32 | 1382 | pr_err("Thermal zone device ops not defined\n"); |
3e6fda5c | 1383 | return ERR_PTR(-EINVAL); |
67eed44b | 1384 | } |
203d3d4a | 1385 | |
35d8dbbb | 1386 | if (num_trips > 0 && !trips) |
6b2aa51d EV |
1387 | return ERR_PTR(-EINVAL); |
1388 | ||
9e0a9be2 RW |
1389 | if (!thermal_class) |
1390 | return ERR_PTR(-ENODEV); | |
1391 | ||
9b0a6275 | 1392 | tz = kzalloc(struct_size(tz, trips, num_trips), GFP_KERNEL); |
203d3d4a | 1393 | if (!tz) |
3e6fda5c | 1394 | return ERR_PTR(-ENOMEM); |
203d3d4a | 1395 | |
3d439b1a DL |
1396 | if (tzp) { |
1397 | tz->tzp = kmemdup(tzp, sizeof(*tzp), GFP_KERNEL); | |
1398 | if (!tz->tzp) { | |
1399 | result = -ENOMEM; | |
1400 | goto free_tz; | |
1401 | } | |
1402 | } | |
1403 | ||
2d374139 | 1404 | INIT_LIST_HEAD(&tz->thermal_instances); |
b38aa87f | 1405 | INIT_LIST_HEAD(&tz->node); |
b31ef828 | 1406 | ida_init(&tz->ida); |
203d3d4a | 1407 | mutex_init(&tz->lock); |
4649620d | 1408 | init_completion(&tz->removal); |
5a5b7d8d | 1409 | id = ida_alloc(&thermal_tz_ida, GFP_KERNEL); |
adc8749b YH |
1410 | if (id < 0) { |
1411 | result = id; | |
3d439b1a | 1412 | goto free_tzp; |
adc8749b | 1413 | } |
203d3d4a | 1414 | |
adc8749b | 1415 | tz->id = id; |
1e6c8fb8 | 1416 | strscpy(tz->type, type, sizeof(tz->type)); |
d7203eed | 1417 | |
698a1eb1 RW |
1418 | tz->ops = *ops; |
1419 | if (!tz->ops.critical) | |
1420 | tz->ops.critical = thermal_zone_device_critical; | |
d7203eed | 1421 | |
9e0a9be2 | 1422 | tz->device.class = thermal_class; |
203d3d4a | 1423 | tz->devdata = devdata; |
e5bfcd30 | 1424 | tz->num_trips = num_trips; |
8c69a777 | 1425 | for_each_trip_desc(tz, td) { |
daeeb032 | 1426 | td->trip = *trip++; |
8c69a777 RW |
1427 | /* |
1428 | * Mark all thresholds as invalid to start with even though | |
1429 | * this only matters for the trips that start as invalid and | |
1430 | * become valid later. | |
1431 | */ | |
1432 | td->threshold = INT_MAX; | |
1433 | } | |
1c600861 | 1434 | |
17d399cd DL |
1435 | thermal_set_delay_jiffies(&tz->passive_delay_jiffies, passive_delay); |
1436 | thermal_set_delay_jiffies(&tz->polling_delay_jiffies, polling_delay); | |
1437 | ||
4d0fe749 | 1438 | /* sys I/F */ |
1c600861 | 1439 | /* Add nodes that are always present via .groups */ |
5340f764 | 1440 | result = thermal_zone_create_device_groups(tz); |
4d0fe749 | 1441 | if (result) |
9d9ca1f9 | 1442 | goto remove_id; |
4d0fe749 | 1443 | |
4511f716 CY |
1444 | /* A new thermal zone needs to be updated anyway. */ |
1445 | atomic_set(&tz->need_update, 1); | |
b1569e99 | 1446 | |
4748f968 YY |
1447 | result = dev_set_name(&tz->device, "thermal_zone%d", tz->id); |
1448 | if (result) { | |
1449 | thermal_zone_destroy_device_groups(tz); | |
1450 | goto remove_id; | |
1451 | } | |
203d3d4a | 1452 | result = device_register(&tz->device); |
9d9ca1f9 | 1453 | if (result) |
adc8749b | 1454 | goto release_device; |
203d3d4a | 1455 | |
a4a15485 D |
1456 | /* Update 'this' zone's governor information */ |
1457 | mutex_lock(&thermal_governor_lock); | |
1458 | ||
1459 | if (tz->tzp) | |
e33df1d2 | 1460 | governor = __find_governor(tz->tzp->governor_name); |
a4a15485 | 1461 | else |
e33df1d2 JM |
1462 | governor = def_governor; |
1463 | ||
1464 | result = thermal_set_governor(tz, governor); | |
1465 | if (result) { | |
1466 | mutex_unlock(&thermal_governor_lock); | |
1467 | goto unregister; | |
1468 | } | |
a4a15485 D |
1469 | |
1470 | mutex_unlock(&thermal_governor_lock); | |
1471 | ||
ccba4ffd EV |
1472 | if (!tz->tzp || !tz->tzp->no_hwmon) { |
1473 | result = thermal_add_hwmon_sysfs(tz); | |
1474 | if (result) | |
1475 | goto unregister; | |
1476 | } | |
e68b16ab | 1477 | |
203d3d4a | 1478 | mutex_lock(&thermal_list_lock); |
b38aa87f | 1479 | mutex_lock(&tz->lock); |
203d3d4a | 1480 | list_add_tail(&tz->node, &thermal_tz_list); |
b38aa87f | 1481 | mutex_unlock(&tz->lock); |
203d3d4a ZR |
1482 | mutex_unlock(&thermal_list_lock); |
1483 | ||
7e8ee1e9 D |
1484 | /* Bind cooling devices for this zone */ |
1485 | bind_tz(tz); | |
1486 | ||
d0df264f | 1487 | thermal_zone_device_init(tz); |
4511f716 CY |
1488 | /* Update the new thermal zone and mark it as already updated. */ |
1489 | if (atomic_cmpxchg(&tz->need_update, 1, 0)) | |
0e70f466 | 1490 | thermal_zone_device_update(tz, THERMAL_EVENT_UNSPECIFIED); |
b1569e99 | 1491 | |
2f521890 | 1492 | thermal_notify_tz_create(tz); |
55cdf0a2 | 1493 | |
7ef01f22 DL |
1494 | thermal_debug_tz_add(tz); |
1495 | ||
14015860 | 1496 | return tz; |
203d3d4a | 1497 | |
caca8b80 | 1498 | unregister: |
adc8749b YH |
1499 | device_del(&tz->device); |
1500 | release_device: | |
1501 | put_device(&tz->device); | |
9d9ca1f9 | 1502 | remove_id: |
5a5b7d8d | 1503 | ida_free(&thermal_tz_ida, id); |
3d439b1a DL |
1504 | free_tzp: |
1505 | kfree(tz->tzp); | |
9d9ca1f9 CJ |
1506 | free_tz: |
1507 | kfree(tz); | |
1508 | return ERR_PTR(result); | |
203d3d4a | 1509 | } |
a921be53 | 1510 | EXPORT_SYMBOL_GPL(thermal_zone_device_register_with_trips); |
fae11de5 | 1511 | |
d332db8f RW |
1512 | struct thermal_zone_device *thermal_tripless_zone_device_register( |
1513 | const char *type, | |
1514 | void *devdata, | |
698a1eb1 | 1515 | const struct thermal_zone_device_ops *ops, |
d332db8f RW |
1516 | const struct thermal_zone_params *tzp) |
1517 | { | |
4a62d588 | 1518 | return thermal_zone_device_register_with_trips(type, NULL, 0, devdata, |
d332db8f RW |
1519 | ops, tzp, 0, 0); |
1520 | } | |
1521 | EXPORT_SYMBOL_GPL(thermal_tripless_zone_device_register); | |
1522 | ||
a6ff3c00 DL |
1523 | void *thermal_zone_device_priv(struct thermal_zone_device *tzd) |
1524 | { | |
1525 | return tzd->devdata; | |
1526 | } | |
1527 | EXPORT_SYMBOL_GPL(thermal_zone_device_priv); | |
1528 | ||
072e35c9 DL |
1529 | const char *thermal_zone_device_type(struct thermal_zone_device *tzd) |
1530 | { | |
1531 | return tzd->type; | |
1532 | } | |
1533 | EXPORT_SYMBOL_GPL(thermal_zone_device_type); | |
1534 | ||
3034f859 DL |
1535 | int thermal_zone_device_id(struct thermal_zone_device *tzd) |
1536 | { | |
1537 | return tzd->id; | |
1538 | } | |
1539 | EXPORT_SYMBOL_GPL(thermal_zone_device_id); | |
1540 | ||
7cefbaf0 DL |
1541 | struct device *thermal_zone_device(struct thermal_zone_device *tzd) |
1542 | { | |
1543 | return &tzd->device; | |
1544 | } | |
1545 | EXPORT_SYMBOL_GPL(thermal_zone_device); | |
1546 | ||
203d3d4a | 1547 | /** |
a052b511 | 1548 | * thermal_zone_device_unregister - removes the registered thermal zone device |
203d3d4a ZR |
1549 | * @tz: the thermal zone device to remove |
1550 | */ | |
1551 | void thermal_zone_device_unregister(struct thermal_zone_device *tz) | |
1552 | { | |
1553 | struct thermal_cooling_device *cdev; | |
1554 | struct thermal_zone_device *pos = NULL; | |
203d3d4a ZR |
1555 | |
1556 | if (!tz) | |
1557 | return; | |
1558 | ||
7ef01f22 DL |
1559 | thermal_debug_tz_remove(tz); |
1560 | ||
203d3d4a ZR |
1561 | mutex_lock(&thermal_list_lock); |
1562 | list_for_each_entry(pos, &thermal_tz_list, node) | |
b659a30d EV |
1563 | if (pos == tz) |
1564 | break; | |
203d3d4a ZR |
1565 | if (pos != tz) { |
1566 | /* thermal zone device not found */ | |
1567 | mutex_unlock(&thermal_list_lock); | |
1568 | return; | |
1569 | } | |
b38aa87f RW |
1570 | |
1571 | mutex_lock(&tz->lock); | |
203d3d4a | 1572 | list_del(&tz->node); |
b38aa87f | 1573 | mutex_unlock(&tz->lock); |
7e8ee1e9 D |
1574 | |
1575 | /* Unbind all cdevs associated with 'this' thermal zone */ | |
ded2d383 | 1576 | list_for_each_entry(cdev, &thermal_cdev_list, node) |
698a1eb1 RW |
1577 | if (tz->ops.unbind) |
1578 | tz->ops.unbind(tz, cdev); | |
7e8ee1e9 | 1579 | |
203d3d4a ZR |
1580 | mutex_unlock(&thermal_list_lock); |
1581 | ||
163b00cd | 1582 | cancel_delayed_work_sync(&tz->poll_queue); |
b1569e99 | 1583 | |
e33df1d2 | 1584 | thermal_set_governor(tz, NULL); |
203d3d4a | 1585 | |
e68b16ab | 1586 | thermal_remove_hwmon_sysfs(tz); |
5a5b7d8d | 1587 | ida_free(&thermal_tz_ida, tz->id); |
b31ef828 | 1588 | ida_destroy(&tz->ida); |
30b2ae07 | 1589 | |
30b2ae07 | 1590 | device_del(&tz->device); |
30b2ae07 | 1591 | |
3d439b1a DL |
1592 | kfree(tz->tzp); |
1593 | ||
30b2ae07 | 1594 | put_device(&tz->device); |
55cdf0a2 | 1595 | |
2f521890 | 1596 | thermal_notify_tz_delete(tz); |
4649620d RW |
1597 | |
1598 | wait_for_completion(&tz->removal); | |
1599 | kfree(tz); | |
203d3d4a | 1600 | } |
910cb1e3 | 1601 | EXPORT_SYMBOL_GPL(thermal_zone_device_unregister); |
203d3d4a | 1602 | |
63c4d919 EV |
1603 | /** |
1604 | * thermal_zone_get_zone_by_name() - search for a zone and returns its ref | |
1605 | * @name: thermal zone name to fetch the temperature | |
1606 | * | |
1607 | * When only one zone is found with the passed name, returns a reference to it. | |
1608 | * | |
1609 | * Return: On success returns a reference to an unique thermal zone with | |
1610 | * matching name equals to @name, an ERR_PTR otherwise (-EINVAL for invalid | |
1611 | * paramenters, -ENODEV for not found and -EEXIST for multiple matches). | |
1612 | */ | |
1613 | struct thermal_zone_device *thermal_zone_get_zone_by_name(const char *name) | |
1614 | { | |
1615 | struct thermal_zone_device *pos = NULL, *ref = ERR_PTR(-EINVAL); | |
1616 | unsigned int found = 0; | |
1617 | ||
1618 | if (!name) | |
1619 | goto exit; | |
1620 | ||
1621 | mutex_lock(&thermal_list_lock); | |
1622 | list_for_each_entry(pos, &thermal_tz_list, node) | |
484ac2f3 | 1623 | if (!strncasecmp(name, pos->type, THERMAL_NAME_LENGTH)) { |
63c4d919 EV |
1624 | found++; |
1625 | ref = pos; | |
1626 | } | |
1627 | mutex_unlock(&thermal_list_lock); | |
1628 | ||
1629 | /* nothing has been found, thus an error code for it */ | |
1630 | if (found == 0) | |
1631 | ref = ERR_PTR(-ENODEV); | |
1632 | else if (found > 1) | |
1633 | /* Success only when an unique zone is found */ | |
1634 | ref = ERR_PTR(-EEXIST); | |
1635 | ||
1636 | exit: | |
1637 | return ref; | |
1638 | } | |
1639 | EXPORT_SYMBOL_GPL(thermal_zone_get_zone_by_name); | |
1640 | ||
5a5efdaf RW |
1641 | static void thermal_zone_device_resume(struct work_struct *work) |
1642 | { | |
1643 | struct thermal_zone_device *tz; | |
1644 | ||
1645 | tz = container_of(work, struct thermal_zone_device, poll_queue.work); | |
1646 | ||
1647 | mutex_lock(&tz->lock); | |
1648 | ||
1649 | tz->suspended = false; | |
1650 | ||
1651 | thermal_zone_device_init(tz); | |
1652 | __thermal_zone_device_update(tz, THERMAL_EVENT_UNSPECIFIED); | |
1653 | ||
1654 | mutex_unlock(&tz->lock); | |
1655 | } | |
1656 | ||
ff140fea | 1657 | static int thermal_pm_notify(struct notifier_block *nb, |
eb7be329 | 1658 | unsigned long mode, void *_unused) |
ff140fea ZR |
1659 | { |
1660 | struct thermal_zone_device *tz; | |
1661 | ||
1662 | switch (mode) { | |
1663 | case PM_HIBERNATION_PREPARE: | |
1664 | case PM_RESTORE_PREPARE: | |
1665 | case PM_SUSPEND_PREPARE: | |
4e814173 RW |
1666 | mutex_lock(&thermal_list_lock); |
1667 | ||
1668 | list_for_each_entry(tz, &thermal_tz_list, node) { | |
1669 | mutex_lock(&tz->lock); | |
1670 | ||
1671 | tz->suspended = true; | |
1672 | ||
1673 | mutex_unlock(&tz->lock); | |
1674 | } | |
1675 | ||
1676 | mutex_unlock(&thermal_list_lock); | |
ff140fea ZR |
1677 | break; |
1678 | case PM_POST_HIBERNATION: | |
1679 | case PM_POST_RESTORE: | |
1680 | case PM_POST_SUSPEND: | |
4e814173 RW |
1681 | mutex_lock(&thermal_list_lock); |
1682 | ||
ff140fea | 1683 | list_for_each_entry(tz, &thermal_tz_list, node) { |
4e814173 RW |
1684 | mutex_lock(&tz->lock); |
1685 | ||
33fcb595 RW |
1686 | cancel_delayed_work(&tz->poll_queue); |
1687 | ||
5a5efdaf RW |
1688 | /* |
1689 | * Replace the work function with the resume one, which | |
1690 | * will restore the original work function and schedule | |
1691 | * the polling work if needed. | |
1692 | */ | |
1693 | INIT_DELAYED_WORK(&tz->poll_queue, | |
1694 | thermal_zone_device_resume); | |
1695 | /* Queue up the work without a delay. */ | |
1696 | mod_delayed_work(system_freezable_power_efficient_wq, | |
1697 | &tz->poll_queue, 0); | |
4e814173 RW |
1698 | |
1699 | mutex_unlock(&tz->lock); | |
ff140fea | 1700 | } |
4e814173 RW |
1701 | |
1702 | mutex_unlock(&thermal_list_lock); | |
ff140fea ZR |
1703 | break; |
1704 | default: | |
1705 | break; | |
1706 | } | |
1707 | return 0; | |
1708 | } | |
1709 | ||
1710 | static struct notifier_block thermal_pm_nb = { | |
1711 | .notifier_call = thermal_pm_notify, | |
1712 | }; | |
1713 | ||
203d3d4a ZR |
1714 | static int __init thermal_init(void) |
1715 | { | |
80a26a5c ZR |
1716 | int result; |
1717 | ||
755113d7 DL |
1718 | thermal_debug_init(); |
1719 | ||
d2a89b52 DL |
1720 | result = thermal_netlink_init(); |
1721 | if (result) | |
1722 | goto error; | |
1723 | ||
80a26a5c ZR |
1724 | result = thermal_register_governors(); |
1725 | if (result) | |
58d1c9fd | 1726 | goto unregister_netlink; |
203d3d4a | 1727 | |
9e0a9be2 RW |
1728 | thermal_class = kzalloc(sizeof(*thermal_class), GFP_KERNEL); |
1729 | if (!thermal_class) { | |
1730 | result = -ENOMEM; | |
1731 | goto unregister_governors; | |
1732 | } | |
1733 | ||
1734 | thermal_class->name = "thermal"; | |
1735 | thermal_class->dev_release = thermal_release; | |
1736 | ||
1737 | result = class_register(thermal_class); | |
1738 | if (result) { | |
1739 | kfree(thermal_class); | |
1740 | thermal_class = NULL; | |
80a26a5c | 1741 | goto unregister_governors; |
9e0a9be2 | 1742 | } |
80a26a5c | 1743 | |
ff140fea ZR |
1744 | result = register_pm_notifier(&thermal_pm_nb); |
1745 | if (result) | |
1746 | pr_warn("Thermal: Can not register suspend notifier, return %d\n", | |
1747 | result); | |
1748 | ||
80a26a5c ZR |
1749 | return 0; |
1750 | ||
9d367e5e LH |
1751 | unregister_governors: |
1752 | thermal_unregister_governors(); | |
58d1c9fd DL |
1753 | unregister_netlink: |
1754 | thermal_netlink_exit(); | |
80a26a5c | 1755 | error: |
80a26a5c ZR |
1756 | mutex_destroy(&thermal_list_lock); |
1757 | mutex_destroy(&thermal_governor_lock); | |
203d3d4a ZR |
1758 | return result; |
1759 | } | |
3f5a2cbe | 1760 | postcore_initcall(thermal_init); |