Blame view

drivers/thermal/thermal_core.c 43.3 KB
7e3c03817   Lina Iyer   drivers: thermal:...
1
  // SPDX-License-Identifier: GPL-2.0
203d3d4aa   Zhang Rui   the generic therm...
2
3
4
5
6
7
  /*
   *  thermal.c - Generic Thermal Management Sysfs support.
   *
   *  Copyright (C) 2008 Intel Corp
   *  Copyright (C) 2008 Zhang Rui <rui.zhang@intel.com>
   *  Copyright (C) 2008 Sujith Thomas <sujith.thomas@intel.com>
203d3d4aa   Zhang Rui   the generic therm...
8
   */
c5a01dd52   Joe Perches   thermal_sys: conv...
9
  #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
203d3d4aa   Zhang Rui   the generic therm...
10
11
12
  #include <linux/module.h>
  #include <linux/device.h>
  #include <linux/err.h>
5a0e3ad6a   Tejun Heo   include cleanup: ...
13
  #include <linux/slab.h>
203d3d4aa   Zhang Rui   the generic therm...
14
15
16
  #include <linux/kdev_t.h>
  #include <linux/idr.h>
  #include <linux/thermal.h>
b1569e99c   Matthew Garrett   ACPI: move therma...
17
  #include <linux/reboot.h>
42a5bf507   Andy Shevchenko   thermal: cut the ...
18
  #include <linux/string.h>
a116b5d44   Eduardo Valentin   thermal: core: in...
19
  #include <linux/of.h>
4cb187287   R.Durgadoss   thermal: Add even...
20
21
  #include <net/netlink.h>
  #include <net/genetlink.h>
ff140fea8   Zhang Rui   Thermal: handle t...
22
  #include <linux/suspend.h>
203d3d4aa   Zhang Rui   the generic therm...
23

100a8fdbf   Punit Agrawal   thermal: trace: T...
24
25
  #define CREATE_TRACE_POINTS
  #include <trace/events/thermal.h>
71350db43   Durgadoss R   Thermal: Move the...
26
  #include "thermal_core.h"
0dd88793a   Eduardo Valentin   thermal: hwmon: m...
27
  #include "thermal_hwmon.h"
71350db43   Durgadoss R   Thermal: Move the...
28

63c4ec905   Zhang Rui   thermal: add the ...
29
  MODULE_AUTHOR("Zhang Rui");
203d3d4aa   Zhang Rui   the generic therm...
30
  MODULE_DESCRIPTION("Generic thermal management sysfs support");
6d8d4974a   Eduardo Valentin   thermal: update d...
31
  MODULE_LICENSE("GPL v2");
203d3d4aa   Zhang Rui   the generic therm...
32

b31ef8285   Matthew Wilcox   thermal core: con...
33
34
  static DEFINE_IDA(thermal_tz_ida);
  static DEFINE_IDA(thermal_cdev_ida);
203d3d4aa   Zhang Rui   the generic therm...
35
36
37
  
  static LIST_HEAD(thermal_tz_list);
  static LIST_HEAD(thermal_cdev_list);
a4a15485f   Durgadoss R   Thermal: Add ther...
38
  static LIST_HEAD(thermal_governor_list);
203d3d4aa   Zhang Rui   the generic therm...
39
  static DEFINE_MUTEX(thermal_list_lock);
a4a15485f   Durgadoss R   Thermal: Add ther...
40
  static DEFINE_MUTEX(thermal_governor_lock);
e441fd686   Keerthy   thermal: core: Al...
41
  static DEFINE_MUTEX(poweroff_lock);
a4a15485f   Durgadoss R   Thermal: Add ther...
42

ff140fea8   Zhang Rui   Thermal: handle t...
43
  static atomic_t in_suspend;
e441fd686   Keerthy   thermal: core: Al...
44
  static bool power_off_triggered;
ff140fea8   Zhang Rui   Thermal: handle t...
45

f2234bcd0   Zhang Rui   Thermal: thermal ...
46
  static struct thermal_governor *def_governor;
1b4f48494   Eduardo Valentin   thermal: core: gr...
47
48
49
50
51
52
  /*
   * Governor section: set of functions to handle thermal governors
   *
   * Functions to help in the life cycle of thermal governors within
   * the thermal core and by the thermal governor code.
   */
a4a15485f   Durgadoss R   Thermal: Add ther...
53
54
55
  static struct thermal_governor *__find_governor(const char *name)
  {
  	struct thermal_governor *pos;
f2234bcd0   Zhang Rui   Thermal: thermal ...
56
57
  	if (!name || !name[0])
  		return def_governor;
a4a15485f   Durgadoss R   Thermal: Add ther...
58
  	list_for_each_entry(pos, &thermal_governor_list, governor_list)
484ac2f32   Rasmus Villemoes   thermal: replace ...
59
  		if (!strncasecmp(name, pos->name, THERMAL_NAME_LENGTH))
a4a15485f   Durgadoss R   Thermal: Add ther...
60
61
62
63
  			return pos;
  
  	return NULL;
  }
e33df1d2f   Javi Merino   thermal: let gove...
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
  /**
   * bind_previous_governor() - bind the previous governor of the thermal zone
   * @tz:		a valid pointer to a struct thermal_zone_device
   * @failed_gov_name:	the name of the governor that failed to register
   *
   * Register the previous governor of the thermal zone after a new
   * governor has failed to be bound.
   */
  static void bind_previous_governor(struct thermal_zone_device *tz,
  				   const char *failed_gov_name)
  {
  	if (tz->governor && tz->governor->bind_to_tz) {
  		if (tz->governor->bind_to_tz(tz)) {
  			dev_err(&tz->device,
  				"governor %s failed to bind and the previous one (%s) failed to bind again, thermal zone %s has no governor
  ",
  				failed_gov_name, tz->governor->name, tz->type);
  			tz->governor = NULL;
  		}
  	}
  }
  
  /**
   * thermal_set_governor() - Switch to another governor
   * @tz:		a valid pointer to a struct thermal_zone_device
   * @new_gov:	pointer to the new governor
   *
   * Change the governor of thermal zone @tz.
   *
   * Return: 0 on success, an error if the new governor's bind_to_tz() failed.
   */
  static int thermal_set_governor(struct thermal_zone_device *tz,
  				struct thermal_governor *new_gov)
  {
  	int ret = 0;
  
  	if (tz->governor && tz->governor->unbind_from_tz)
  		tz->governor->unbind_from_tz(tz);
  
  	if (new_gov && new_gov->bind_to_tz) {
  		ret = new_gov->bind_to_tz(tz);
  		if (ret) {
  			bind_previous_governor(tz, new_gov->name);
  
  			return ret;
  		}
  	}
  
  	tz->governor = new_gov;
  
  	return ret;
  }
a4a15485f   Durgadoss R   Thermal: Add ther...
116
117
118
119
120
121
122
123
124
125
126
127
  int thermal_register_governor(struct thermal_governor *governor)
  {
  	int err;
  	const char *name;
  	struct thermal_zone_device *pos;
  
  	if (!governor)
  		return -EINVAL;
  
  	mutex_lock(&thermal_governor_lock);
  
  	err = -EBUSY;
5027ba36c   Eduardo Valentin   thermal: core: sm...
128
  	if (!__find_governor(governor->name)) {
eb7be329b   Eduardo Valentin   thermal: core: st...
129
  		bool match_default;
a4a15485f   Durgadoss R   Thermal: Add ther...
130
131
  		err = 0;
  		list_add(&governor->governor_list, &thermal_governor_list);
eb7be329b   Eduardo Valentin   thermal: core: st...
132
133
134
135
136
  		match_default = !strncmp(governor->name,
  					 DEFAULT_THERMAL_GOVERNOR,
  					 THERMAL_NAME_LENGTH);
  
  		if (!def_governor && match_default)
f2234bcd0   Zhang Rui   Thermal: thermal ...
137
  			def_governor = governor;
a4a15485f   Durgadoss R   Thermal: Add ther...
138
139
140
141
142
  	}
  
  	mutex_lock(&thermal_list_lock);
  
  	list_for_each_entry(pos, &thermal_tz_list, node) {
f2234bcd0   Zhang Rui   Thermal: thermal ...
143
144
145
146
  		/*
  		 * only thermal zones with specified tz->tzp->governor_name
  		 * may run with tz->govenor unset
  		 */
a4a15485f   Durgadoss R   Thermal: Add ther...
147
148
  		if (pos->governor)
  			continue;
f2234bcd0   Zhang Rui   Thermal: thermal ...
149
150
  
  		name = pos->tzp->governor_name;
e33df1d2f   Javi Merino   thermal: let gove...
151
152
153
154
155
156
157
158
159
160
  		if (!strncasecmp(name, governor->name, THERMAL_NAME_LENGTH)) {
  			int ret;
  
  			ret = thermal_set_governor(pos, governor);
  			if (ret)
  				dev_err(&pos->device,
  					"Failed to set governor %s for thermal zone %s: %d
  ",
  					governor->name, pos->type, ret);
  		}
a4a15485f   Durgadoss R   Thermal: Add ther...
161
162
163
164
165
166
167
  	}
  
  	mutex_unlock(&thermal_list_lock);
  	mutex_unlock(&thermal_governor_lock);
  
  	return err;
  }
a4a15485f   Durgadoss R   Thermal: Add ther...
168
169
170
171
172
173
174
175
176
  
  void thermal_unregister_governor(struct thermal_governor *governor)
  {
  	struct thermal_zone_device *pos;
  
  	if (!governor)
  		return;
  
  	mutex_lock(&thermal_governor_lock);
5027ba36c   Eduardo Valentin   thermal: core: sm...
177
  	if (!__find_governor(governor->name))
a4a15485f   Durgadoss R   Thermal: Add ther...
178
179
180
181
182
  		goto exit;
  
  	mutex_lock(&thermal_list_lock);
  
  	list_for_each_entry(pos, &thermal_tz_list, node) {
484ac2f32   Rasmus Villemoes   thermal: replace ...
183
  		if (!strncasecmp(pos->governor->name, governor->name,
eb7be329b   Eduardo Valentin   thermal: core: st...
184
  				 THERMAL_NAME_LENGTH))
e33df1d2f   Javi Merino   thermal: let gove...
185
  			thermal_set_governor(pos, NULL);
a4a15485f   Durgadoss R   Thermal: Add ther...
186
187
188
189
190
191
  	}
  
  	mutex_unlock(&thermal_list_lock);
  	list_del(&governor->governor_list);
  exit:
  	mutex_unlock(&thermal_governor_lock);
9b4298a08   Durgadoss R   Thermal: Add get ...
192
  }
9b4298a08   Durgadoss R   Thermal: Add get ...
193

1b4f48494   Eduardo Valentin   thermal: core: gr...
194
195
  int thermal_zone_device_set_policy(struct thermal_zone_device *tz,
  				   char *policy)
9b4298a08   Durgadoss R   Thermal: Add get ...
196
  {
1b4f48494   Eduardo Valentin   thermal: core: gr...
197
198
  	struct thermal_governor *gov;
  	int ret = -EINVAL;
9b4298a08   Durgadoss R   Thermal: Add get ...
199

1b4f48494   Eduardo Valentin   thermal: core: gr...
200
  	mutex_lock(&thermal_governor_lock);
9b4298a08   Durgadoss R   Thermal: Add get ...
201
  	mutex_lock(&tz->lock);
9b4298a08   Durgadoss R   Thermal: Add get ...
202

1b4f48494   Eduardo Valentin   thermal: core: gr...
203
204
205
  	gov = __find_governor(strim(policy));
  	if (!gov)
  		goto exit;
9b4298a08   Durgadoss R   Thermal: Add get ...
206

1b4f48494   Eduardo Valentin   thermal: core: gr...
207
  	ret = thermal_set_governor(tz, gov);
9b4298a08   Durgadoss R   Thermal: Add get ...
208

1b4f48494   Eduardo Valentin   thermal: core: gr...
209
210
211
  exit:
  	mutex_unlock(&tz->lock);
  	mutex_unlock(&thermal_governor_lock);
9b4298a08   Durgadoss R   Thermal: Add get ...
212

1b4f48494   Eduardo Valentin   thermal: core: gr...
213
  	return ret;
7e8ee1e9d   Durgadoss R   Thermal: Update b...
214
  }
1b4f48494   Eduardo Valentin   thermal: core: gr...
215
  int thermal_build_list_of_policies(char *buf)
7e8ee1e9d   Durgadoss R   Thermal: Update b...
216
  {
1b4f48494   Eduardo Valentin   thermal: core: gr...
217
218
219
  	struct thermal_governor *pos;
  	ssize_t count = 0;
  	ssize_t size = PAGE_SIZE;
7e8ee1e9d   Durgadoss R   Thermal: Update b...
220

1b4f48494   Eduardo Valentin   thermal: core: gr...
221
  	mutex_lock(&thermal_governor_lock);
a8892d838   Eduardo Valentin   thermal: thermal_...
222

1b4f48494   Eduardo Valentin   thermal: core: gr...
223
224
225
  	list_for_each_entry(pos, &thermal_governor_list, governor_list) {
  		size = PAGE_SIZE - count;
  		count += scnprintf(buf + count, size, "%s ", pos->name);
7e8ee1e9d   Durgadoss R   Thermal: Update b...
226
  	}
1b4f48494   Eduardo Valentin   thermal: core: gr...
227
228
  	count += scnprintf(buf + count, size, "
  ");
7e8ee1e9d   Durgadoss R   Thermal: Update b...
229

1b4f48494   Eduardo Valentin   thermal: core: gr...
230
  	mutex_unlock(&thermal_governor_lock);
7e8ee1e9d   Durgadoss R   Thermal: Update b...
231

1b4f48494   Eduardo Valentin   thermal: core: gr...
232
  	return count;
7e8ee1e9d   Durgadoss R   Thermal: Update b...
233
  }
57c5b2ec9   Daniel Lezcano   thermal/drivers/c...
234
  static void __init thermal_unregister_governors(void)
7e8ee1e9d   Durgadoss R   Thermal: Update b...
235
  {
57c5b2ec9   Daniel Lezcano   thermal/drivers/c...
236
237
238
239
240
  	struct thermal_governor **governor;
  
  	for_each_governor_table(governor)
  		thermal_unregister_governor(*governor);
  }
7e8ee1e9d   Durgadoss R   Thermal: Update b...
241

57c5b2ec9   Daniel Lezcano   thermal/drivers/c...
242
243
244
245
  static int __init thermal_register_governors(void)
  {
  	int ret = 0;
  	struct thermal_governor **governor;
7e8ee1e9d   Durgadoss R   Thermal: Update b...
246

57c5b2ec9   Daniel Lezcano   thermal/drivers/c...
247
248
249
250
251
252
253
  	for_each_governor_table(governor) {
  		ret = thermal_register_governor(*governor);
  		if (ret) {
  			pr_err("Failed to register governor: '%s'",
  			       (*governor)->name);
  			break;
  		}
7e8ee1e9d   Durgadoss R   Thermal: Update b...
254

57c5b2ec9   Daniel Lezcano   thermal/drivers/c...
255
256
257
  		pr_info("Registered thermal governor '%s'",
  			(*governor)->name);
  	}
7e8ee1e9d   Durgadoss R   Thermal: Update b...
258

57c5b2ec9   Daniel Lezcano   thermal/drivers/c...
259
260
  	if (ret) {
  		struct thermal_governor **gov;
7e8ee1e9d   Durgadoss R   Thermal: Update b...
261

57c5b2ec9   Daniel Lezcano   thermal/drivers/c...
262
263
264
265
266
267
  		for_each_governor_table(gov) {
  			if (gov == governor)
  				break;
  			thermal_unregister_governor(*gov);
  		}
  	}
7e8ee1e9d   Durgadoss R   Thermal: Update b...
268

57c5b2ec9   Daniel Lezcano   thermal/drivers/c...
269
  	return ret;
7e8ee1e9d   Durgadoss R   Thermal: Update b...
270
  }
8772e185f   Eduardo Valentin   thermal: core: ad...
271
272
273
274
275
276
277
278
279
280
281
  /*
   * Zone update section: main control loop applied to each zone while monitoring
   *
   * in polling mode. The monitoring is done using a workqueue.
   * Same update may be done on a zone by calling thermal_zone_device_update().
   *
   * An update means:
   * - Non-critical trips will invoke the governor responsible for that zone;
   * - Hot trips will produce a notification to userspace;
   * - Critical trip point will cause a system shutdown.
   */
0c01ebbfd   Durgadoss R   Thermal: Remove t...
282
283
284
285
  static void thermal_zone_device_set_polling(struct thermal_zone_device *tz,
  					    int delay)
  {
  	if (delay > 1000)
c2b59d279   Jeson Gao   thermal: core: us...
286
287
  		mod_delayed_work(system_freezable_power_efficient_wq,
  				 &tz->poll_queue,
0c01ebbfd   Durgadoss R   Thermal: Remove t...
288
289
  				 round_jiffies(msecs_to_jiffies(delay)));
  	else if (delay)
c2b59d279   Jeson Gao   thermal: core: us...
290
291
  		mod_delayed_work(system_freezable_power_efficient_wq,
  				 &tz->poll_queue,
0c01ebbfd   Durgadoss R   Thermal: Remove t...
292
293
  				 msecs_to_jiffies(delay));
  	else
7d967912c   Wei Wang   thermal: Fix dead...
294
  		cancel_delayed_work(&tz->poll_queue);
0c01ebbfd   Durgadoss R   Thermal: Remove t...
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
  }
  
  static void monitor_thermal_zone(struct thermal_zone_device *tz)
  {
  	mutex_lock(&tz->lock);
  
  	if (tz->passive)
  		thermal_zone_device_set_polling(tz, tz->passive_delay);
  	else if (tz->polling_delay)
  		thermal_zone_device_set_polling(tz, tz->polling_delay);
  	else
  		thermal_zone_device_set_polling(tz, 0);
  
  	mutex_unlock(&tz->lock);
  }
5be52fcca   Lukasz Luba   thermal: remove u...
310
  static void handle_non_critical_trips(struct thermal_zone_device *tz, int trip)
0c01ebbfd   Durgadoss R   Thermal: Remove t...
311
  {
f2234bcd0   Zhang Rui   Thermal: thermal ...
312
313
  	tz->governor ? tz->governor->throttle(tz, trip) :
  		       def_governor->throttle(tz, trip);
0c01ebbfd   Durgadoss R   Thermal: Remove t...
314
  }
ef1d87e06   Keerthy   thermal: core: Ad...
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
  /**
   * thermal_emergency_poweroff_func - emergency poweroff work after a known delay
   * @work: work_struct associated with the emergency poweroff function
   *
   * This function is called in very critical situations to force
   * a kernel poweroff after a configurable timeout value.
   */
  static void thermal_emergency_poweroff_func(struct work_struct *work)
  {
  	/*
  	 * We have reached here after the emergency thermal shutdown
  	 * Waiting period has expired. This means orderly_poweroff has
  	 * not been able to shut off the system for some reason.
  	 * Try to shut down the system immediately using kernel_power_off
  	 * if populated
  	 */
  	WARN(1, "Attempting kernel_power_off: Temperature too high
  ");
  	kernel_power_off();
  
  	/*
  	 * Worst of the worst case trigger emergency restart
  	 */
  	WARN(1, "Attempting emergency_restart: Temperature too high
  ");
  	emergency_restart();
  }
  
  static DECLARE_DELAYED_WORK(thermal_emergency_poweroff_work,
  			    thermal_emergency_poweroff_func);
  
  /**
   * thermal_emergency_poweroff - Trigger an emergency system poweroff
   *
   * This may be called from any critical situation to trigger a system shutdown
   * after a known period of time. By default this is not scheduled.
   */
c4b379d06   Colin Ian King   thermal: core: ma...
352
  static void thermal_emergency_poweroff(void)
ef1d87e06   Keerthy   thermal: core: Ad...
353
354
355
356
357
358
359
360
361
362
363
  {
  	int poweroff_delay_ms = CONFIG_THERMAL_EMERGENCY_POWEROFF_DELAY_MS;
  	/*
  	 * poweroff_delay_ms must be a carefully profiled positive value.
  	 * Its a must for thermal_emergency_poweroff_work to be scheduled
  	 */
  	if (poweroff_delay_ms <= 0)
  		return;
  	schedule_delayed_work(&thermal_emergency_poweroff_work,
  			      msecs_to_jiffies(poweroff_delay_ms));
  }
0c01ebbfd   Durgadoss R   Thermal: Remove t...
364
  static void handle_critical_trips(struct thermal_zone_device *tz,
eb7be329b   Eduardo Valentin   thermal: core: st...
365
  				  int trip, enum thermal_trip_type trip_type)
0c01ebbfd   Durgadoss R   Thermal: Remove t...
366
  {
17e8351a7   Sascha Hauer   thermal: consiste...
367
  	int trip_temp;
0c01ebbfd   Durgadoss R   Thermal: Remove t...
368
369
370
371
  
  	tz->ops->get_trip_temp(tz, trip, &trip_temp);
  
  	/* If we have not crossed the trip_temp, we do not care. */
84ffe3ecc   Srinivas Pandruvada   thermal: core: ig...
372
  	if (trip_temp <= 0 || tz->temperature < trip_temp)
0c01ebbfd   Durgadoss R   Thermal: Remove t...
373
  		return;
208cd822a   Punit Agrawal   thermal: trace: T...
374
  	trace_thermal_zone_trip(tz, trip, trip_type);
0c01ebbfd   Durgadoss R   Thermal: Remove t...
375
376
377
378
  	if (tz->ops->notify)
  		tz->ops->notify(tz, trip, trip_type);
  
  	if (trip_type == THERMAL_TRIP_CRITICAL) {
923e0b1e8   Eduardo Valentin   thermal: cleanup:...
379
  		dev_emerg(&tz->device,
039f6cf5b   Icenowy Zheng   thermal: core: fi...
380
381
  			  "critical temperature reached (%d C), shutting down
  ",
923e0b1e8   Eduardo Valentin   thermal: cleanup:...
382
  			  tz->temperature / 1000);
e441fd686   Keerthy   thermal: core: Al...
383
384
  		mutex_lock(&poweroff_lock);
  		if (!power_off_triggered) {
ef1d87e06   Keerthy   thermal: core: Ad...
385
386
387
388
389
  			/*
  			 * Queue a backup emergency shutdown in the event of
  			 * orderly_poweroff failure
  			 */
  			thermal_emergency_poweroff();
e441fd686   Keerthy   thermal: core: Al...
390
391
392
393
  			orderly_poweroff(true);
  			power_off_triggered = true;
  		}
  		mutex_unlock(&poweroff_lock);
0c01ebbfd   Durgadoss R   Thermal: Remove t...
394
395
396
397
398
399
  	}
  }
  
  static void handle_thermal_trip(struct thermal_zone_device *tz, int trip)
  {
  	enum thermal_trip_type type;
81ad4276b   Zhang Rui   Thermal: Ignore i...
400
401
402
  	/* Ignore disabled trip points */
  	if (test_bit(trip, &tz->trips_disabled))
  		return;
0c01ebbfd   Durgadoss R   Thermal: Remove t...
403
404
405
406
407
  	tz->ops->get_trip_type(tz, trip, &type);
  
  	if (type == THERMAL_TRIP_CRITICAL || type == THERMAL_TRIP_HOT)
  		handle_critical_trips(tz, trip, type);
  	else
5be52fcca   Lukasz Luba   thermal: remove u...
408
  		handle_non_critical_trips(tz, trip);
0c01ebbfd   Durgadoss R   Thermal: Remove t...
409
410
411
412
413
414
415
416
417
  	/*
  	 * Alright, we handled this trip successfully.
  	 * So, start monitoring again.
  	 */
  	monitor_thermal_zone(tz);
  }
  
  static void update_temperature(struct thermal_zone_device *tz)
  {
17e8351a7   Sascha Hauer   thermal: consiste...
418
  	int temp, ret;
0c01ebbfd   Durgadoss R   Thermal: Remove t...
419

e6e238c38   Amit Daniel Kachhap   thermal: sysfs: A...
420
  	ret = thermal_zone_get_temp(tz, &temp);
0c01ebbfd   Durgadoss R   Thermal: Remove t...
421
  	if (ret) {
7e497a737   Hans de Goede   thermal: Do not l...
422
423
424
425
426
  		if (ret != -EAGAIN)
  			dev_warn(&tz->device,
  				 "failed to read out thermal zone (%d)
  ",
  				 ret);
e6e238c38   Amit Daniel Kachhap   thermal: sysfs: A...
427
  		return;
0c01ebbfd   Durgadoss R   Thermal: Remove t...
428
  	}
e6e238c38   Amit Daniel Kachhap   thermal: sysfs: A...
429
  	mutex_lock(&tz->lock);
0c01ebbfd   Durgadoss R   Thermal: Remove t...
430
431
  	tz->last_temperature = tz->temperature;
  	tz->temperature = temp;
0c01ebbfd   Durgadoss R   Thermal: Remove t...
432
  	mutex_unlock(&tz->lock);
06475b556   Aaron Lu   thermal: debug: a...
433

100a8fdbf   Punit Agrawal   thermal: trace: T...
434
  	trace_thermal_temperature(tz);
bb431ba26   Zhang Rui   Thermal: initiali...
435
436
437
438
439
440
441
442
443
  	if (tz->last_temperature == THERMAL_TEMP_INVALID)
  		dev_dbg(&tz->device, "last_temperature N/A, current_temperature=%d
  ",
  			tz->temperature);
  	else
  		dev_dbg(&tz->device, "last_temperature=%d, current_temperature=%d
  ",
  			tz->last_temperature, tz->temperature);
  }
964f4843a   Wei Wang   Thermal: do not c...
444
  static void thermal_zone_device_init(struct thermal_zone_device *tz)
bb431ba26   Zhang Rui   Thermal: initiali...
445
446
  {
  	struct thermal_instance *pos;
bb431ba26   Zhang Rui   Thermal: initiali...
447
  	tz->temperature = THERMAL_TEMP_INVALID;
bb431ba26   Zhang Rui   Thermal: initiali...
448
449
  	list_for_each_entry(pos, &tz->thermal_instances, tz_node)
  		pos->initialized = false;
0c01ebbfd   Durgadoss R   Thermal: Remove t...
450
  }
964f4843a   Wei Wang   Thermal: do not c...
451
452
453
454
455
  static void thermal_zone_device_reset(struct thermal_zone_device *tz)
  {
  	tz->passive = 0;
  	thermal_zone_device_init(tz);
  }
0e70f466f   Srinivas Pandruvada   thermal: Enhance ...
456
457
  void thermal_zone_device_update(struct thermal_zone_device *tz,
  				enum thermal_notify_event event)
0c01ebbfd   Durgadoss R   Thermal: Remove t...
458
459
  {
  	int count;
ff140fea8   Zhang Rui   Thermal: handle t...
460
461
  	if (atomic_read(&in_suspend))
  		return;
81bd4e1ce   Eduardo Valentin   thermal: allow re...
462
463
  	if (!tz->ops->get_temp)
  		return;
0c01ebbfd   Durgadoss R   Thermal: Remove t...
464
  	update_temperature(tz);
060c034a9   Sascha Hauer   thermal: Add supp...
465
  	thermal_zone_set_trips(tz);
0e70f466f   Srinivas Pandruvada   thermal: Enhance ...
466
  	tz->notify_event = event;
0c01ebbfd   Durgadoss R   Thermal: Remove t...
467
468
469
  	for (count = 0; count < tz->trips; count++)
  		handle_thermal_trip(tz, count);
  }
910cb1e34   Eduardo Valentin   thermal: use EXPO...
470
  EXPORT_SYMBOL_GPL(thermal_zone_device_update);
0c01ebbfd   Durgadoss R   Thermal: Remove t...
471

106339ab7   Eduardo Valentin   thermal: core: mo...
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
  /**
   * thermal_notify_framework - Sensor drivers use this API to notify framework
   * @tz:		thermal zone device
   * @trip:	indicates which trip point has been crossed
   *
   * This function handles the trip events from sensor drivers. It starts
   * throttling the cooling devices according to the policy configured.
   * For CRITICAL and HOT trip points, this notifies the respective drivers,
   * and does actual throttling for other trip points i.e ACTIVE and PASSIVE.
   * The throttling policy is based on the configured platform data; if no
   * platform data is provided, this uses the step_wise throttling policy.
   */
  void thermal_notify_framework(struct thermal_zone_device *tz, int trip)
  {
  	handle_thermal_trip(tz, trip);
  }
  EXPORT_SYMBOL_GPL(thermal_notify_framework);
0c01ebbfd   Durgadoss R   Thermal: Remove t...
489
490
491
492
493
  static void thermal_zone_device_check(struct work_struct *work)
  {
  	struct thermal_zone_device *tz = container_of(work, struct
  						      thermal_zone_device,
  						      poll_queue.work);
0e70f466f   Srinivas Pandruvada   thermal: Enhance ...
494
  	thermal_zone_device_update(tz, THERMAL_EVENT_UNSPECIFIED);
0c01ebbfd   Durgadoss R   Thermal: Remove t...
495
  }
712afbdfd   Eduardo Valentin   thermal: core: ad...
496
497
498
499
500
501
  /*
   * Power actor section: interface to power actors to estimate power
   *
   * Set of functions used to interact to cooling devices that know
   * how to estimate their devices power consumption.
   */
9f38271c6   Javi Merino   thermal: export t...
502

35b11d2e3   Javi Merino   thermal: extend t...
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
  /**
   * power_actor_get_max_power() - get the maximum power that a cdev can consume
   * @cdev:	pointer to &thermal_cooling_device
   * @tz:		a valid thermal zone device pointer
   * @max_power:	pointer in which to store the maximum power
   *
   * Calculate the maximum power consumption in milliwats that the
   * cooling device can currently consume and store it in @max_power.
   *
   * Return: 0 on success, -EINVAL if @cdev doesn't support the
   * power_actor API or -E* on other error.
   */
  int power_actor_get_max_power(struct thermal_cooling_device *cdev,
  			      struct thermal_zone_device *tz, u32 *max_power)
  {
  	if (!cdev_is_power_actor(cdev))
  		return -EINVAL;
  
  	return cdev->ops->state2power(cdev, tz, 0, max_power);
  }
  
  /**
c973c3bce   Javi Merino   thermal: Add a fu...
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
   * power_actor_get_min_power() - get the mainimum power that a cdev can consume
   * @cdev:	pointer to &thermal_cooling_device
   * @tz:		a valid thermal zone device pointer
   * @min_power:	pointer in which to store the minimum power
   *
   * Calculate the minimum power consumption in milliwatts that the
   * cooling device can currently consume and store it in @min_power.
   *
   * Return: 0 on success, -EINVAL if @cdev doesn't support the
   * power_actor API or -E* on other error.
   */
  int power_actor_get_min_power(struct thermal_cooling_device *cdev,
  			      struct thermal_zone_device *tz, u32 *min_power)
  {
  	unsigned long max_state;
  	int ret;
  
  	if (!cdev_is_power_actor(cdev))
  		return -EINVAL;
  
  	ret = cdev->ops->get_max_state(cdev, &max_state);
  	if (ret)
  		return ret;
  
  	return cdev->ops->state2power(cdev, tz, max_state, min_power);
  }
  
  /**
1a7e7cc03   Eduardo Valentin   thermal: core: mo...
553
   * power_actor_set_power() - limit the maximum power a cooling device consumes
35b11d2e3   Javi Merino   thermal: extend t...
554
555
556
557
   * @cdev:	pointer to &thermal_cooling_device
   * @instance:	thermal instance to update
   * @power:	the power in milliwatts
   *
1a7e7cc03   Eduardo Valentin   thermal: core: mo...
558
559
   * Set the cooling device to consume at most @power milliwatts. The limit is
   * expected to be a cap at the maximum power consumption.
35b11d2e3   Javi Merino   thermal: extend t...
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
   *
   * Return: 0 on success, -EINVAL if the cooling device does not
   * implement the power actor API or -E* for other failures.
   */
  int power_actor_set_power(struct thermal_cooling_device *cdev,
  			  struct thermal_instance *instance, u32 power)
  {
  	unsigned long state;
  	int ret;
  
  	if (!cdev_is_power_actor(cdev))
  		return -EINVAL;
  
  	ret = cdev->ops->power2state(cdev, instance->tz, power, &state);
  	if (ret)
  		return ret;
  
  	instance->target = state;
d0b7306d2   Michele Di Giorgio   thermal: fix race...
578
  	mutex_lock(&cdev->lock);
35b11d2e3   Javi Merino   thermal: extend t...
579
  	cdev->updated = false;
d0b7306d2   Michele Di Giorgio   thermal: fix race...
580
  	mutex_unlock(&cdev->lock);
35b11d2e3   Javi Merino   thermal: extend t...
581
582
583
584
  	thermal_cdev_update(cdev);
  
  	return 0;
  }
3d0055d2b   Eduardo Valentin   thermal: core: sp...
585
586
  void thermal_zone_device_rebind_exception(struct thermal_zone_device *tz,
  					  const char *cdev_type, size_t size)
203d3d4aa   Zhang Rui   the generic therm...
587
  {
3d0055d2b   Eduardo Valentin   thermal: core: sp...
588
  	struct thermal_cooling_device *cdev = NULL;
203d3d4aa   Zhang Rui   the generic therm...
589

3d0055d2b   Eduardo Valentin   thermal: core: sp...
590
591
592
593
594
595
596
597
598
599
600
601
602
  	mutex_lock(&thermal_list_lock);
  	list_for_each_entry(cdev, &thermal_cdev_list, node) {
  		/* skip non matching cdevs */
  		if (strncmp(cdev_type, cdev->type, size))
  			continue;
  
  		/* re binding the exception matching the type pattern */
  		thermal_zone_bind_cooling_device(tz, THERMAL_TRIPS_NONE, cdev,
  						 THERMAL_NO_LIMIT,
  						 THERMAL_NO_LIMIT,
  						 THERMAL_WEIGHT_DEFAULT);
  	}
  	mutex_unlock(&thermal_list_lock);
203d3d4aa   Zhang Rui   the generic therm...
603
  }
3d0055d2b   Eduardo Valentin   thermal: core: sp...
604
605
  void thermal_zone_device_unbind_exception(struct thermal_zone_device *tz,
  					  const char *cdev_type, size_t size)
203d3d4aa   Zhang Rui   the generic therm...
606
  {
3d0055d2b   Eduardo Valentin   thermal: core: sp...
607
  	struct thermal_cooling_device *cdev = NULL;
203d3d4aa   Zhang Rui   the generic therm...
608

3d0055d2b   Eduardo Valentin   thermal: core: sp...
609
610
611
612
613
614
615
616
617
618
  	mutex_lock(&thermal_list_lock);
  	list_for_each_entry(cdev, &thermal_cdev_list, node) {
  		/* skip non matching cdevs */
  		if (strncmp(cdev_type, cdev->type, size))
  			continue;
  		/* unbinding the exception matching the type pattern */
  		thermal_zone_unbind_cooling_device(tz, THERMAL_TRIPS_NONE,
  						   cdev);
  	}
  	mutex_unlock(&thermal_list_lock);
203d3d4aa   Zhang Rui   the generic therm...
619
  }
81193e2e6   Eduardo Valentin   thermal: core: ad...
620
621
622
623
624
625
626
627
628
  /*
   * Device management section: cooling devices, zones devices, and binding
   *
   * Set of functions provided by the thermal core for:
   * - cooling devices lifecycle: registration, unregistration,
   *				binding, and unbinding.
   * - thermal zone devices lifecycle: registration, unregistration,
   *				     binding, and unbinding.
   */
203d3d4aa   Zhang Rui   the generic therm...
629
630
  
  /**
d2e4eb83e   Eduardo Valentin   thermal: update k...
631
632
   * thermal_zone_bind_cooling_device() - bind a cooling device to a thermal zone
   * @tz:		pointer to struct thermal_zone_device
203d3d4aa   Zhang Rui   the generic therm...
633
634
   * @trip:	indicates which trip point the cooling devices is
   *		associated with in this thermal zone.
d2e4eb83e   Eduardo Valentin   thermal: update k...
635
636
637
638
639
640
641
   * @cdev:	pointer to struct thermal_cooling_device
   * @upper:	the Maximum cooling state for this trip point.
   *		THERMAL_NO_LIMIT means no upper limit,
   *		and the cooling device can be in max_state.
   * @lower:	the Minimum cooling state can be used for this trip point.
   *		THERMAL_NO_LIMIT means no lower limit,
   *		and the cooling device can be in cooling state 0.
6cd9e9f62   Kapileshwar Singh   thermal: of: fix ...
642
643
644
   * @weight:	The weight of the cooling device to be bound to the
   *		thermal zone. Use THERMAL_WEIGHT_DEFAULT for the
   *		default value
543a95614   Len Brown   ACPI: thermal: sy...
645
   *
d2e4eb83e   Eduardo Valentin   thermal: update k...
646
647
   * This interface function bind a thermal cooling device to the certain trip
   * point of a thermal zone device.
543a95614   Len Brown   ACPI: thermal: sy...
648
   * This function is usually called in the thermal zone device .bind callback.
d2e4eb83e   Eduardo Valentin   thermal: update k...
649
650
   *
   * Return: 0 on success, the proper error value otherwise.
203d3d4aa   Zhang Rui   the generic therm...
651
652
653
   */
  int thermal_zone_bind_cooling_device(struct thermal_zone_device *tz,
  				     int trip,
9d99842f9   Zhang Rui   Thermal: set uppe...
654
  				     struct thermal_cooling_device *cdev,
6cd9e9f62   Kapileshwar Singh   thermal: of: fix ...
655
656
  				     unsigned long upper, unsigned long lower,
  				     unsigned int weight)
203d3d4aa   Zhang Rui   the generic therm...
657
  {
b81b6ba3d   Zhang Rui   Thermal: rename s...
658
659
  	struct thermal_instance *dev;
  	struct thermal_instance *pos;
c75167090   Thomas Sujith   thermal: validate...
660
661
  	struct thermal_zone_device *pos1;
  	struct thermal_cooling_device *pos2;
74051ba50   Zhang Rui   Thermal: Introduc...
662
  	unsigned long max_state;
9a3031dc3   Lukasz Majewski   thermal:core:fix:...
663
  	int result, ret;
203d3d4aa   Zhang Rui   the generic therm...
664

543a95614   Len Brown   ACPI: thermal: sy...
665
  	if (trip >= tz->trips || (trip < 0 && trip != THERMAL_TRIPS_NONE))
203d3d4aa   Zhang Rui   the generic therm...
666
  		return -EINVAL;
c75167090   Thomas Sujith   thermal: validate...
667
668
669
670
671
672
673
674
675
676
  	list_for_each_entry(pos1, &thermal_tz_list, node) {
  		if (pos1 == tz)
  			break;
  	}
  	list_for_each_entry(pos2, &thermal_cdev_list, node) {
  		if (pos2 == cdev)
  			break;
  	}
  
  	if (tz != pos1 || cdev != pos2)
203d3d4aa   Zhang Rui   the generic therm...
677
  		return -EINVAL;
9a3031dc3   Lukasz Majewski   thermal:core:fix:...
678
679
680
  	ret = cdev->ops->get_max_state(cdev, &max_state);
  	if (ret)
  		return ret;
9d99842f9   Zhang Rui   Thermal: set uppe...
681
682
683
684
685
686
687
  
  	/* lower default 0, upper default max_state */
  	lower = lower == THERMAL_NO_LIMIT ? 0 : lower;
  	upper = upper == THERMAL_NO_LIMIT ? max_state : upper;
  
  	if (lower > upper || upper > max_state)
  		return -EINVAL;
95e3ed151   Eduardo Valentin   thermal: core: us...
688
  	dev = kzalloc(sizeof(*dev), GFP_KERNEL);
203d3d4aa   Zhang Rui   the generic therm...
689
690
691
692
693
  	if (!dev)
  		return -ENOMEM;
  	dev->tz = tz;
  	dev->cdev = cdev;
  	dev->trip = trip;
9d99842f9   Zhang Rui   Thermal: set uppe...
694
695
  	dev->upper = upper;
  	dev->lower = lower;
ce119f832   Zhang Rui   Thermal: Introduc...
696
  	dev->target = THERMAL_NO_TARGET;
6cd9e9f62   Kapileshwar Singh   thermal: of: fix ...
697
  	dev->weight = weight;
74051ba50   Zhang Rui   Thermal: Introduc...
698

b31ef8285   Matthew Wilcox   thermal core: con...
699
700
  	result = ida_simple_get(&tz->ida, 0, 0, GFP_KERNEL);
  	if (result < 0)
203d3d4aa   Zhang Rui   the generic therm...
701
  		goto free_mem;
b31ef8285   Matthew Wilcox   thermal core: con...
702
  	dev->id = result;
203d3d4aa   Zhang Rui   the generic therm...
703
704
705
706
  	sprintf(dev->name, "cdev%d", dev->id);
  	result =
  	    sysfs_create_link(&tz->device.kobj, &cdev->device.kobj, dev->name);
  	if (result)
b31ef8285   Matthew Wilcox   thermal core: con...
707
  		goto release_ida;
203d3d4aa   Zhang Rui   the generic therm...
708
709
  
  	sprintf(dev->attr_name, "cdev%d_trip_point", dev->id);
975f8c565   Sergey Senozhatsky   drivers/thermal/t...
710
  	sysfs_attr_init(&dev->attr.attr);
203d3d4aa   Zhang Rui   the generic therm...
711
712
  	dev->attr.attr.name = dev->attr_name;
  	dev->attr.attr.mode = 0444;
33e678d47   Viresh Kumar   thermal: Shorten ...
713
  	dev->attr.show = trip_point_show;
203d3d4aa   Zhang Rui   the generic therm...
714
715
716
  	result = device_create_file(&tz->device, &dev->attr);
  	if (result)
  		goto remove_symbol_link;
db9165131   Javi Merino   thermal: export w...
717
718
719
720
  	sprintf(dev->weight_attr_name, "cdev%d_weight", dev->id);
  	sysfs_attr_init(&dev->weight_attr.attr);
  	dev->weight_attr.attr.name = dev->weight_attr_name;
  	dev->weight_attr.attr.mode = S_IWUSR | S_IRUGO;
33e678d47   Viresh Kumar   thermal: Shorten ...
721
722
  	dev->weight_attr.show = weight_show;
  	dev->weight_attr.store = weight_store;
db9165131   Javi Merino   thermal: export w...
723
724
725
  	result = device_create_file(&tz->device, &dev->weight_attr);
  	if (result)
  		goto remove_trip_file;
203d3d4aa   Zhang Rui   the generic therm...
726
  	mutex_lock(&tz->lock);
f4a821ce6   Zhang Rui   Thermal: Introduc...
727
  	mutex_lock(&cdev->lock);
cddf31b3b   Zhang Rui   Thermal: Rename t...
728
  	list_for_each_entry(pos, &tz->thermal_instances, tz_node)
b659a30d7   Eduardo Valentin   thermal: core: re...
729
730
731
732
  		if (pos->tz == tz && pos->trip == trip && pos->cdev == cdev) {
  			result = -EEXIST;
  			break;
  		}
b5e4ae620   Zhang Rui   Thermal: List the...
733
  	if (!result) {
cddf31b3b   Zhang Rui   Thermal: Rename t...
734
  		list_add_tail(&dev->tz_node, &tz->thermal_instances);
b5e4ae620   Zhang Rui   Thermal: List the...
735
  		list_add_tail(&dev->cdev_node, &cdev->thermal_instances);
4511f7166   Chen Yu   Thermal: do therm...
736
  		atomic_set(&tz->need_update, 1);
b5e4ae620   Zhang Rui   Thermal: List the...
737
  	}
f4a821ce6   Zhang Rui   Thermal: Introduc...
738
  	mutex_unlock(&cdev->lock);
203d3d4aa   Zhang Rui   the generic therm...
739
740
741
742
  	mutex_unlock(&tz->lock);
  
  	if (!result)
  		return 0;
db9165131   Javi Merino   thermal: export w...
743
744
  	device_remove_file(&tz->device, &dev->weight_attr);
  remove_trip_file:
203d3d4aa   Zhang Rui   the generic therm...
745
  	device_remove_file(&tz->device, &dev->attr);
caca8b803   Joe Perches   thermal_sys: kern...
746
  remove_symbol_link:
203d3d4aa   Zhang Rui   the generic therm...
747
  	sysfs_remove_link(&tz->device.kobj, dev->name);
b31ef8285   Matthew Wilcox   thermal core: con...
748
749
  release_ida:
  	ida_simple_remove(&tz->ida, dev->id);
caca8b803   Joe Perches   thermal_sys: kern...
750
  free_mem:
203d3d4aa   Zhang Rui   the generic therm...
751
752
753
  	kfree(dev);
  	return result;
  }
910cb1e34   Eduardo Valentin   thermal: use EXPO...
754
  EXPORT_SYMBOL_GPL(thermal_zone_bind_cooling_device);
203d3d4aa   Zhang Rui   the generic therm...
755
756
  
  /**
9892e5dc5   Eduardo Valentin   thermal: update k...
757
758
759
   * thermal_zone_unbind_cooling_device() - unbind a cooling device from a
   *					  thermal zone.
   * @tz:		pointer to a struct thermal_zone_device.
203d3d4aa   Zhang Rui   the generic therm...
760
761
   * @trip:	indicates which trip point the cooling devices is
   *		associated with in this thermal zone.
9892e5dc5   Eduardo Valentin   thermal: update k...
762
   * @cdev:	pointer to a struct thermal_cooling_device.
543a95614   Len Brown   ACPI: thermal: sy...
763
   *
9892e5dc5   Eduardo Valentin   thermal: update k...
764
765
   * This interface function unbind a thermal cooling device from the certain
   * trip point of a thermal zone device.
543a95614   Len Brown   ACPI: thermal: sy...
766
   * This function is usually called in the thermal zone device .unbind callback.
9892e5dc5   Eduardo Valentin   thermal: update k...
767
768
   *
   * Return: 0 on success, the proper error value otherwise.
203d3d4aa   Zhang Rui   the generic therm...
769
770
771
772
773
   */
  int thermal_zone_unbind_cooling_device(struct thermal_zone_device *tz,
  				       int trip,
  				       struct thermal_cooling_device *cdev)
  {
b81b6ba3d   Zhang Rui   Thermal: rename s...
774
  	struct thermal_instance *pos, *next;
203d3d4aa   Zhang Rui   the generic therm...
775
776
  
  	mutex_lock(&tz->lock);
f4a821ce6   Zhang Rui   Thermal: Introduc...
777
  	mutex_lock(&cdev->lock);
cddf31b3b   Zhang Rui   Thermal: Rename t...
778
  	list_for_each_entry_safe(pos, next, &tz->thermal_instances, tz_node) {
543a95614   Len Brown   ACPI: thermal: sy...
779
  		if (pos->tz == tz && pos->trip == trip && pos->cdev == cdev) {
cddf31b3b   Zhang Rui   Thermal: Rename t...
780
  			list_del(&pos->tz_node);
b5e4ae620   Zhang Rui   Thermal: List the...
781
  			list_del(&pos->cdev_node);
f4a821ce6   Zhang Rui   Thermal: Introduc...
782
  			mutex_unlock(&cdev->lock);
203d3d4aa   Zhang Rui   the generic therm...
783
784
785
786
  			mutex_unlock(&tz->lock);
  			goto unbind;
  		}
  	}
f4a821ce6   Zhang Rui   Thermal: Introduc...
787
  	mutex_unlock(&cdev->lock);
203d3d4aa   Zhang Rui   the generic therm...
788
789
790
  	mutex_unlock(&tz->lock);
  
  	return -ENODEV;
caca8b803   Joe Perches   thermal_sys: kern...
791
  unbind:
528464eaa   Viresh Kumar   thermal: remove d...
792
  	device_remove_file(&tz->device, &pos->weight_attr);
203d3d4aa   Zhang Rui   the generic therm...
793
794
  	device_remove_file(&tz->device, &pos->attr);
  	sysfs_remove_link(&tz->device.kobj, pos->name);
b31ef8285   Matthew Wilcox   thermal core: con...
795
  	ida_simple_remove(&tz->ida, pos->id);
203d3d4aa   Zhang Rui   the generic therm...
796
797
798
  	kfree(pos);
  	return 0;
  }
910cb1e34   Eduardo Valentin   thermal: use EXPO...
799
  EXPORT_SYMBOL_GPL(thermal_zone_unbind_cooling_device);
203d3d4aa   Zhang Rui   the generic therm...
800
801
802
803
804
  
  static void thermal_release(struct device *dev)
  {
  	struct thermal_zone_device *tz;
  	struct thermal_cooling_device *cdev;
caca8b803   Joe Perches   thermal_sys: kern...
805
806
  	if (!strncmp(dev_name(dev), "thermal_zone",
  		     sizeof("thermal_zone") - 1)) {
203d3d4aa   Zhang Rui   the generic therm...
807
  		tz = to_thermal_zone(dev);
6a6cd25b5   Christophe Jaillet   thermal: core: Us...
808
  		thermal_zone_destroy_device_groups(tz);
203d3d4aa   Zhang Rui   the generic therm...
809
  		kfree(tz);
b659a30d7   Eduardo Valentin   thermal: core: re...
810
811
  	} else if (!strncmp(dev_name(dev), "cooling_device",
  			    sizeof("cooling_device") - 1)) {
203d3d4aa   Zhang Rui   the generic therm...
812
813
814
815
816
817
818
819
820
  		cdev = to_cooling_device(dev);
  		kfree(cdev);
  	}
  }
  
  static struct class thermal_class = {
  	.name = "thermal",
  	.dev_release = thermal_release,
  };
4b0d3c2d3   Eduardo Valentin   thermal: core: ad...
821
822
823
  static inline
  void print_bind_err_msg(struct thermal_zone_device *tz,
  			struct thermal_cooling_device *cdev, int ret)
f502ab844   Eduardo Valentin   thermal: core: mo...
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
  {
  	dev_err(&tz->device, "binding zone %s with cdev %s failed:%d
  ",
  		tz->type, cdev->type, ret);
  }
  
  static void __bind(struct thermal_zone_device *tz, int mask,
  		   struct thermal_cooling_device *cdev,
  		   unsigned long *limits,
  		   unsigned int weight)
  {
  	int i, ret;
  
  	for (i = 0; i < tz->trips; i++) {
  		if (mask & (1 << i)) {
  			unsigned long upper, lower;
  
  			upper = THERMAL_NO_LIMIT;
  			lower = THERMAL_NO_LIMIT;
  			if (limits) {
  				lower = limits[i * 2];
  				upper = limits[i * 2 + 1];
  			}
  			ret = thermal_zone_bind_cooling_device(tz, i, cdev,
  							       upper, lower,
  							       weight);
  			if (ret)
  				print_bind_err_msg(tz, cdev, ret);
  		}
  	}
  }
949aad839   Eduardo Valentin   thermal: core: mo...
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
  static void bind_cdev(struct thermal_cooling_device *cdev)
  {
  	int i, ret;
  	const struct thermal_zone_params *tzp;
  	struct thermal_zone_device *pos = NULL;
  
  	mutex_lock(&thermal_list_lock);
  
  	list_for_each_entry(pos, &thermal_tz_list, node) {
  		if (!pos->tzp && !pos->ops->bind)
  			continue;
  
  		if (pos->ops->bind) {
  			ret = pos->ops->bind(pos, cdev);
  			if (ret)
  				print_bind_err_msg(pos, cdev, ret);
  			continue;
  		}
  
  		tzp = pos->tzp;
  		if (!tzp || !tzp->tbp)
  			continue;
  
  		for (i = 0; i < tzp->num_tbps; i++) {
  			if (tzp->tbp[i].cdev || !tzp->tbp[i].match)
  				continue;
  			if (tzp->tbp[i].match(pos, cdev))
  				continue;
  			tzp->tbp[i].cdev = cdev;
  			__bind(pos, tzp->tbp[i].trip_mask, cdev,
  			       tzp->tbp[i].binding_limits,
  			       tzp->tbp[i].weight);
  		}
  	}
  
  	mutex_unlock(&thermal_list_lock);
  }
203d3d4aa   Zhang Rui   the generic therm...
892
  /**
a116b5d44   Eduardo Valentin   thermal: core: in...
893
894
   * __thermal_cooling_device_register() - register a new thermal cooling device
   * @np:		a pointer to a device tree node.
203d3d4aa   Zhang Rui   the generic therm...
895
896
897
   * @type:	the thermal cooling device type.
   * @devdata:	device private data.
   * @ops:		standard thermal cooling devices callbacks.
3a6eccb35   Eduardo Valentin   thermal: update k...
898
899
900
901
   *
   * This interface function adds a new thermal cooling device (fan/processor/...)
   * to /sys/class/thermal/ folder as cooling_device[0-*]. It tries to bind itself
   * to all the thermal zone devices registered at the same time.
a116b5d44   Eduardo Valentin   thermal: core: in...
902
903
   * It also gives the opportunity to link the cooling device to a device tree
   * node, so that it can be bound to a thermal zone created out of device tree.
3a6eccb35   Eduardo Valentin   thermal: update k...
904
905
906
   *
   * Return: a pointer to the created struct thermal_cooling_device or an
   * ERR_PTR. Caller must check return value with IS_ERR*() helpers.
203d3d4aa   Zhang Rui   the generic therm...
907
   */
a116b5d44   Eduardo Valentin   thermal: core: in...
908
909
  static struct thermal_cooling_device *
  __thermal_cooling_device_register(struct device_node *np,
f991de53a   Jean-Francois Dagenais   thermal: make dev...
910
  				  const char *type, void *devdata,
a116b5d44   Eduardo Valentin   thermal: core: in...
911
  				  const struct thermal_cooling_device_ops *ops)
203d3d4aa   Zhang Rui   the generic therm...
912
913
  {
  	struct thermal_cooling_device *cdev;
4511f7166   Chen Yu   Thermal: do therm...
914
  	struct thermal_zone_device *pos = NULL;
203d3d4aa   Zhang Rui   the generic therm...
915
  	int result;
204dd1d39   Guenter Roeck   thermal: Fix pote...
916
  	if (type && strlen(type) >= THERMAL_NAME_LENGTH)
3e6fda5c1   Thomas Sujith   thermal: use ERR_...
917
  		return ERR_PTR(-EINVAL);
203d3d4aa   Zhang Rui   the generic therm...
918
919
  
  	if (!ops || !ops->get_max_state || !ops->get_cur_state ||
543a95614   Len Brown   ACPI: thermal: sy...
920
  	    !ops->set_cur_state)
3e6fda5c1   Thomas Sujith   thermal: use ERR_...
921
  		return ERR_PTR(-EINVAL);
203d3d4aa   Zhang Rui   the generic therm...
922

95e3ed151   Eduardo Valentin   thermal: core: us...
923
  	cdev = kzalloc(sizeof(*cdev), GFP_KERNEL);
203d3d4aa   Zhang Rui   the generic therm...
924
  	if (!cdev)
3e6fda5c1   Thomas Sujith   thermal: use ERR_...
925
  		return ERR_PTR(-ENOMEM);
203d3d4aa   Zhang Rui   the generic therm...
926

b31ef8285   Matthew Wilcox   thermal core: con...
927
928
  	result = ida_simple_get(&thermal_cdev_ida, 0, 0, GFP_KERNEL);
  	if (result < 0) {
203d3d4aa   Zhang Rui   the generic therm...
929
  		kfree(cdev);
3e6fda5c1   Thomas Sujith   thermal: use ERR_...
930
  		return ERR_PTR(result);
203d3d4aa   Zhang Rui   the generic therm...
931
  	}
b31ef8285   Matthew Wilcox   thermal core: con...
932
  	cdev->id = result;
c7a8b9d91   Eduardo Valentin   thermal: use strl...
933
  	strlcpy(cdev->type, type ? : "", sizeof(cdev->type));
f4a821ce6   Zhang Rui   Thermal: Introduc...
934
  	mutex_init(&cdev->lock);
b5e4ae620   Zhang Rui   Thermal: List the...
935
  	INIT_LIST_HEAD(&cdev->thermal_instances);
a116b5d44   Eduardo Valentin   thermal: core: in...
936
  	cdev->np = np;
203d3d4aa   Zhang Rui   the generic therm...
937
  	cdev->ops = ops;
5ca0cce56   Ni Wade   Thermal: Allow fi...
938
  	cdev->updated = false;
203d3d4aa   Zhang Rui   the generic therm...
939
940
  	cdev->device.class = &thermal_class;
  	cdev->devdata = devdata;
8ea229511   Viresh Kumar   thermal: Add cool...
941
  	thermal_cooling_device_setup_sysfs(cdev);
354655ea9   Kay Sievers   thermal: struct d...
942
  	dev_set_name(&cdev->device, "cooling_device%d", cdev->id);
203d3d4aa   Zhang Rui   the generic therm...
943
944
  	result = device_register(&cdev->device);
  	if (result) {
b31ef8285   Matthew Wilcox   thermal core: con...
945
  		ida_simple_remove(&thermal_cdev_ida, cdev->id);
adc8749b1   Yue Hu   thermal/drivers/c...
946
  		put_device(&cdev->device);
3e6fda5c1   Thomas Sujith   thermal: use ERR_...
947
  		return ERR_PTR(result);
203d3d4aa   Zhang Rui   the generic therm...
948
  	}
7e8ee1e9d   Durgadoss R   Thermal: Update b...
949
  	/* Add 'this' new cdev to the global cdev list */
203d3d4aa   Zhang Rui   the generic therm...
950
951
  	mutex_lock(&thermal_list_lock);
  	list_add(&cdev->node, &thermal_cdev_list);
203d3d4aa   Zhang Rui   the generic therm...
952
  	mutex_unlock(&thermal_list_lock);
7e8ee1e9d   Durgadoss R   Thermal: Update b...
953
954
  	/* Update binding information for 'this' new cdev */
  	bind_cdev(cdev);
4511f7166   Chen Yu   Thermal: do therm...
955
956
957
  	mutex_lock(&thermal_list_lock);
  	list_for_each_entry(pos, &thermal_tz_list, node)
  		if (atomic_cmpxchg(&pos->need_update, 1, 0))
0e70f466f   Srinivas Pandruvada   thermal: Enhance ...
958
959
  			thermal_zone_device_update(pos,
  						   THERMAL_EVENT_UNSPECIFIED);
4511f7166   Chen Yu   Thermal: do therm...
960
  	mutex_unlock(&thermal_list_lock);
7e8ee1e9d   Durgadoss R   Thermal: Update b...
961
  	return cdev;
203d3d4aa   Zhang Rui   the generic therm...
962
  }
a116b5d44   Eduardo Valentin   thermal: core: in...
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
  
  /**
   * thermal_cooling_device_register() - register a new thermal cooling device
   * @type:	the thermal cooling device type.
   * @devdata:	device private data.
   * @ops:		standard thermal cooling devices callbacks.
   *
   * This interface function adds a new thermal cooling device (fan/processor/...)
   * to /sys/class/thermal/ folder as cooling_device[0-*]. It tries to bind itself
   * to all the thermal zone devices registered at the same time.
   *
   * Return: a pointer to the created struct thermal_cooling_device or an
   * ERR_PTR. Caller must check return value with IS_ERR*() helpers.
   */
  struct thermal_cooling_device *
f991de53a   Jean-Francois Dagenais   thermal: make dev...
978
  thermal_cooling_device_register(const char *type, void *devdata,
a116b5d44   Eduardo Valentin   thermal: core: in...
979
980
981
982
  				const struct thermal_cooling_device_ops *ops)
  {
  	return __thermal_cooling_device_register(NULL, type, devdata, ops);
  }
910cb1e34   Eduardo Valentin   thermal: use EXPO...
983
  EXPORT_SYMBOL_GPL(thermal_cooling_device_register);
203d3d4aa   Zhang Rui   the generic therm...
984
985
  
  /**
a116b5d44   Eduardo Valentin   thermal: core: in...
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
   * thermal_of_cooling_device_register() - register an OF thermal cooling device
   * @np:		a pointer to a device tree node.
   * @type:	the thermal cooling device type.
   * @devdata:	device private data.
   * @ops:		standard thermal cooling devices callbacks.
   *
   * This function will register a cooling device with device tree node reference.
   * This interface function adds a new thermal cooling device (fan/processor/...)
   * to /sys/class/thermal/ folder as cooling_device[0-*]. It tries to bind itself
   * to all the thermal zone devices registered at the same time.
   *
   * Return: a pointer to the created struct thermal_cooling_device or an
   * ERR_PTR. Caller must check return value with IS_ERR*() helpers.
   */
  struct thermal_cooling_device *
  thermal_of_cooling_device_register(struct device_node *np,
f991de53a   Jean-Francois Dagenais   thermal: make dev...
1002
  				   const char *type, void *devdata,
a116b5d44   Eduardo Valentin   thermal: core: in...
1003
1004
1005
1006
1007
  				   const struct thermal_cooling_device_ops *ops)
  {
  	return __thermal_cooling_device_register(np, type, devdata, ops);
  }
  EXPORT_SYMBOL_GPL(thermal_of_cooling_device_register);
b4ab114cc   Guenter Roeck   thermal: Introduc...
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
  static void thermal_cooling_device_release(struct device *dev, void *res)
  {
  	thermal_cooling_device_unregister(
  				*(struct thermal_cooling_device **)res);
  }
  
  /**
   * devm_thermal_of_cooling_device_register() - register an OF thermal cooling
   *					       device
   * @dev:	a valid struct device pointer of a sensor device.
   * @np:		a pointer to a device tree node.
   * @type:	the thermal cooling device type.
   * @devdata:	device private data.
   * @ops:	standard thermal cooling devices callbacks.
   *
   * This function will register a cooling device with device tree node reference.
   * This interface function adds a new thermal cooling device (fan/processor/...)
   * to /sys/class/thermal/ folder as cooling_device[0-*]. It tries to bind itself
   * to all the thermal zone devices registered at the same time.
   *
   * Return: a pointer to the created struct thermal_cooling_device or an
   * ERR_PTR. Caller must check return value with IS_ERR*() helpers.
   */
  struct thermal_cooling_device *
  devm_thermal_of_cooling_device_register(struct device *dev,
  				struct device_node *np,
  				char *type, void *devdata,
  				const struct thermal_cooling_device_ops *ops)
  {
  	struct thermal_cooling_device **ptr, *tcd;
  
  	ptr = devres_alloc(thermal_cooling_device_release, sizeof(*ptr),
  			   GFP_KERNEL);
  	if (!ptr)
  		return ERR_PTR(-ENOMEM);
  
  	tcd = __thermal_cooling_device_register(np, type, devdata, ops);
  	if (IS_ERR(tcd)) {
  		devres_free(ptr);
  		return tcd;
  	}
  
  	*ptr = tcd;
  	devres_add(dev, ptr);
  
  	return tcd;
  }
  EXPORT_SYMBOL_GPL(devm_thermal_of_cooling_device_register);
f11997fa2   Eduardo Valentin   thermal: core: mo...
1056
1057
1058
1059
1060
1061
1062
1063
1064
  static void __unbind(struct thermal_zone_device *tz, int mask,
  		     struct thermal_cooling_device *cdev)
  {
  	int i;
  
  	for (i = 0; i < tz->trips; i++)
  		if (mask & (1 << i))
  			thermal_zone_unbind_cooling_device(tz, i, cdev);
  }
a116b5d44   Eduardo Valentin   thermal: core: in...
1065
  /**
38e7b549a   Eduardo Valentin   thermal: core: im...
1066
   * thermal_cooling_device_unregister - removes a thermal cooling device
203d3d4aa   Zhang Rui   the generic therm...
1067
1068
   * @cdev:	the thermal cooling device to remove.
   *
38e7b549a   Eduardo Valentin   thermal: core: im...
1069
1070
   * thermal_cooling_device_unregister() must be called when a registered
   * thermal cooling device is no longer needed.
203d3d4aa   Zhang Rui   the generic therm...
1071
   */
7e8ee1e9d   Durgadoss R   Thermal: Update b...
1072
  void thermal_cooling_device_unregister(struct thermal_cooling_device *cdev)
203d3d4aa   Zhang Rui   the generic therm...
1073
  {
7e8ee1e9d   Durgadoss R   Thermal: Update b...
1074
1075
  	int i;
  	const struct thermal_zone_params *tzp;
203d3d4aa   Zhang Rui   the generic therm...
1076
1077
1078
1079
1080
1081
1082
1083
  	struct thermal_zone_device *tz;
  	struct thermal_cooling_device *pos = NULL;
  
  	if (!cdev)
  		return;
  
  	mutex_lock(&thermal_list_lock);
  	list_for_each_entry(pos, &thermal_cdev_list, node)
b659a30d7   Eduardo Valentin   thermal: core: re...
1084
1085
  		if (pos == cdev)
  			break;
203d3d4aa   Zhang Rui   the generic therm...
1086
1087
1088
1089
1090
1091
  	if (pos != cdev) {
  		/* thermal cooling device not found */
  		mutex_unlock(&thermal_list_lock);
  		return;
  	}
  	list_del(&cdev->node);
7e8ee1e9d   Durgadoss R   Thermal: Update b...
1092
1093
  
  	/* Unbind all thermal zones associated with 'this' cdev */
203d3d4aa   Zhang Rui   the generic therm...
1094
  	list_for_each_entry(tz, &thermal_tz_list, node) {
7e8ee1e9d   Durgadoss R   Thermal: Update b...
1095
1096
1097
1098
1099
1100
  		if (tz->ops->unbind) {
  			tz->ops->unbind(tz, cdev);
  			continue;
  		}
  
  		if (!tz->tzp || !tz->tzp->tbp)
203d3d4aa   Zhang Rui   the generic therm...
1101
  			continue;
7e8ee1e9d   Durgadoss R   Thermal: Update b...
1102
1103
1104
1105
1106
1107
1108
1109
  
  		tzp = tz->tzp;
  		for (i = 0; i < tzp->num_tbps; i++) {
  			if (tzp->tbp[i].cdev == cdev) {
  				__unbind(tz, tzp->tbp[i].trip_mask, cdev);
  				tzp->tbp[i].cdev = NULL;
  			}
  		}
203d3d4aa   Zhang Rui   the generic therm...
1110
  	}
7e8ee1e9d   Durgadoss R   Thermal: Update b...
1111

203d3d4aa   Zhang Rui   the generic therm...
1112
  	mutex_unlock(&thermal_list_lock);
7e8ee1e9d   Durgadoss R   Thermal: Update b...
1113

b31ef8285   Matthew Wilcox   thermal core: con...
1114
  	ida_simple_remove(&thermal_cdev_ida, cdev->id);
3c5877682   Dmitry Osipenko   thermal: core: Fi...
1115
  	device_del(&cdev->device);
8ea229511   Viresh Kumar   thermal: Add cool...
1116
  	thermal_cooling_device_destroy_sysfs(cdev);
3c5877682   Dmitry Osipenko   thermal: core: Fi...
1117
  	put_device(&cdev->device);
203d3d4aa   Zhang Rui   the generic therm...
1118
  }
910cb1e34   Eduardo Valentin   thermal: use EXPO...
1119
  EXPORT_SYMBOL_GPL(thermal_cooling_device_unregister);
203d3d4aa   Zhang Rui   the generic therm...
1120

90f5b5bb7   Eduardo Valentin   thermal: core: mo...
1121
  static void bind_tz(struct thermal_zone_device *tz)
ce119f832   Zhang Rui   Thermal: Introduc...
1122
  {
90f5b5bb7   Eduardo Valentin   thermal: core: mo...
1123
1124
1125
  	int i, ret;
  	struct thermal_cooling_device *pos = NULL;
  	const struct thermal_zone_params *tzp = tz->tzp;
ce119f832   Zhang Rui   Thermal: Introduc...
1126

90f5b5bb7   Eduardo Valentin   thermal: core: mo...
1127
  	if (!tzp && !tz->ops->bind)
ce119f832   Zhang Rui   Thermal: Introduc...
1128
  		return;
c56f5c034   Durgadoss R   Thermal: Make The...
1129

90f5b5bb7   Eduardo Valentin   thermal: core: mo...
1130
  	mutex_lock(&thermal_list_lock);
c56f5c034   Durgadoss R   Thermal: Make The...
1131

90f5b5bb7   Eduardo Valentin   thermal: core: mo...
1132
1133
1134
1135
1136
1137
  	/* If there is ops->bind, try to use ops->bind */
  	if (tz->ops->bind) {
  		list_for_each_entry(pos, &thermal_cdev_list, node) {
  			ret = tz->ops->bind(tz, pos);
  			if (ret)
  				print_bind_err_msg(tz, pos, ret);
27365a6c7   Durgadoss R   Thermal: Add Hyst...
1138
  		}
90f5b5bb7   Eduardo Valentin   thermal: core: mo...
1139
  		goto exit;
27365a6c7   Durgadoss R   Thermal: Add Hyst...
1140
  	}
c56f5c034   Durgadoss R   Thermal: Make The...
1141

90f5b5bb7   Eduardo Valentin   thermal: core: mo...
1142
1143
  	if (!tzp || !tzp->tbp)
  		goto exit;
27365a6c7   Durgadoss R   Thermal: Add Hyst...
1144

90f5b5bb7   Eduardo Valentin   thermal: core: mo...
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
  	list_for_each_entry(pos, &thermal_cdev_list, node) {
  		for (i = 0; i < tzp->num_tbps; i++) {
  			if (tzp->tbp[i].cdev || !tzp->tbp[i].match)
  				continue;
  			if (tzp->tbp[i].match(tz, pos))
  				continue;
  			tzp->tbp[i].cdev = pos;
  			__bind(tz, tzp->tbp[i].trip_mask, pos,
  			       tzp->tbp[i].binding_limits,
  			       tzp->tbp[i].weight);
27365a6c7   Durgadoss R   Thermal: Add Hyst...
1155
  		}
c56f5c034   Durgadoss R   Thermal: Make The...
1156
  	}
90f5b5bb7   Eduardo Valentin   thermal: core: mo...
1157
1158
  exit:
  	mutex_unlock(&thermal_list_lock);
c56f5c034   Durgadoss R   Thermal: Make The...
1159
1160
1161
  }
  
  /**
a00e55f9c   Eduardo Valentin   thermal: update k...
1162
   * thermal_zone_device_register() - register a new thermal zone device
203d3d4aa   Zhang Rui   the generic therm...
1163
1164
   * @type:	the thermal zone device type
   * @trips:	the number of trip points the thermal zone support
c56f5c034   Durgadoss R   Thermal: Make The...
1165
   * @mask:	a bit string indicating the writeablility of trip points
203d3d4aa   Zhang Rui   the generic therm...
1166
1167
   * @devdata:	private device data
   * @ops:	standard thermal zone device callbacks
50125a9b2   Durgadoss R   Thermal: Pass zon...
1168
   * @tzp:	thermal zone platform parameters
b1569e99c   Matthew Garrett   ACPI: move therma...
1169
1170
1171
1172
1173
   * @passive_delay: number of milliseconds to wait between polls when
   *		   performing passive cooling
   * @polling_delay: number of milliseconds to wait between polls when checking
   *		   whether trip points have been crossed (0 for interrupt
   *		   driven systems)
203d3d4aa   Zhang Rui   the generic therm...
1174
   *
a00e55f9c   Eduardo Valentin   thermal: update k...
1175
1176
1177
   * This interface function adds a new thermal zone device (sensor) to
   * /sys/class/thermal folder as thermal_zone[0-*]. It tries to bind all the
   * thermal cooling devices registered at the same time.
203d3d4aa   Zhang Rui   the generic therm...
1178
   * thermal_zone_device_unregister() must be called when the device is no
1b7ddb840   Zhang Rui   Thermal: Remove t...
1179
   * longer needed. The passive cooling depends on the .get_trend() return value.
a00e55f9c   Eduardo Valentin   thermal: update k...
1180
1181
1182
1183
   *
   * Return: a pointer to the created struct thermal_zone_device or an
   * in case of error, an ERR_PTR. Caller must check return value with
   * IS_ERR*() helpers.
203d3d4aa   Zhang Rui   the generic therm...
1184
   */
eb7be329b   Eduardo Valentin   thermal: core: st...
1185
1186
1187
1188
1189
  struct thermal_zone_device *
  thermal_zone_device_register(const char *type, int trips, int mask,
  			     void *devdata, struct thermal_zone_device_ops *ops,
  			     struct thermal_zone_params *tzp, int passive_delay,
  			     int polling_delay)
203d3d4aa   Zhang Rui   the generic therm...
1190
1191
  {
  	struct thermal_zone_device *tz;
03a971a28   Matthew Garrett   thermal: support ...
1192
  	enum thermal_trip_type trip_type;
81ad4276b   Zhang Rui   Thermal: Ignore i...
1193
  	int trip_temp;
adc8749b1   Yue Hu   thermal/drivers/c...
1194
  	int id;
203d3d4aa   Zhang Rui   the generic therm...
1195
1196
  	int result;
  	int count;
e33df1d2f   Javi Merino   thermal: let gove...
1197
  	struct thermal_governor *governor;
203d3d4aa   Zhang Rui   the generic therm...
1198

67eed44b8   Amit Kucheria   thermal: Add some...
1199
1200
1201
  	if (!type || strlen(type) == 0) {
  		pr_err("Error: No thermal zone type defined
  ");
54fa38cc2   Eduardo Valentin   thermal: core: pr...
1202
  		return ERR_PTR(-EINVAL);
67eed44b8   Amit Kucheria   thermal: Add some...
1203
  	}
54fa38cc2   Eduardo Valentin   thermal: core: pr...
1204

67eed44b8   Amit Kucheria   thermal: Add some...
1205
1206
1207
1208
  	if (type && strlen(type) >= THERMAL_NAME_LENGTH) {
  		pr_err("Error: Thermal zone name (%s) too long, should be under %d chars
  ",
  		       type, THERMAL_NAME_LENGTH);
3e6fda5c1   Thomas Sujith   thermal: use ERR_...
1209
  		return ERR_PTR(-EINVAL);
67eed44b8   Amit Kucheria   thermal: Add some...
1210
  	}
203d3d4aa   Zhang Rui   the generic therm...
1211

67eed44b8   Amit Kucheria   thermal: Add some...
1212
1213
1214
  	if (trips > THERMAL_MAX_TRIPS || trips < 0 || mask >> trips) {
  		pr_err("Error: Incorrect number of thermal trips
  ");
3e6fda5c1   Thomas Sujith   thermal: use ERR_...
1215
  		return ERR_PTR(-EINVAL);
67eed44b8   Amit Kucheria   thermal: Add some...
1216
  	}
203d3d4aa   Zhang Rui   the generic therm...
1217

67eed44b8   Amit Kucheria   thermal: Add some...
1218
1219
1220
  	if (!ops) {
  		pr_err("Error: Thermal zone device ops not defined
  ");
3e6fda5c1   Thomas Sujith   thermal: use ERR_...
1221
  		return ERR_PTR(-EINVAL);
67eed44b8   Amit Kucheria   thermal: Add some...
1222
  	}
203d3d4aa   Zhang Rui   the generic therm...
1223

83720d0b7   Jonghwa Lee   Thermal: core: As...
1224
  	if (trips > 0 && (!ops->get_trip_type || !ops->get_trip_temp))
6b2aa51d6   Eduardo Valentin   thermal: check fo...
1225
  		return ERR_PTR(-EINVAL);
95e3ed151   Eduardo Valentin   thermal: core: us...
1226
  	tz = kzalloc(sizeof(*tz), GFP_KERNEL);
203d3d4aa   Zhang Rui   the generic therm...
1227
  	if (!tz)
3e6fda5c1   Thomas Sujith   thermal: use ERR_...
1228
  		return ERR_PTR(-ENOMEM);
203d3d4aa   Zhang Rui   the generic therm...
1229

2d374139d   Zhang Rui   Thermal: Rename t...
1230
  	INIT_LIST_HEAD(&tz->thermal_instances);
b31ef8285   Matthew Wilcox   thermal core: con...
1231
  	ida_init(&tz->ida);
203d3d4aa   Zhang Rui   the generic therm...
1232
  	mutex_init(&tz->lock);
adc8749b1   Yue Hu   thermal/drivers/c...
1233
1234
1235
  	id = ida_simple_get(&thermal_tz_ida, 0, 0, GFP_KERNEL);
  	if (id < 0) {
  		result = id;
9d9ca1f9f   Christophe Jaillet   thermal: core: Fi...
1236
  		goto free_tz;
adc8749b1   Yue Hu   thermal/drivers/c...
1237
  	}
203d3d4aa   Zhang Rui   the generic therm...
1238

adc8749b1   Yue Hu   thermal/drivers/c...
1239
  	tz->id = id;
54fa38cc2   Eduardo Valentin   thermal: core: pr...
1240
  	strlcpy(tz->type, type, sizeof(tz->type));
203d3d4aa   Zhang Rui   the generic therm...
1241
  	tz->ops = ops;
50125a9b2   Durgadoss R   Thermal: Pass zon...
1242
  	tz->tzp = tzp;
203d3d4aa   Zhang Rui   the generic therm...
1243
1244
1245
  	tz->device.class = &thermal_class;
  	tz->devdata = devdata;
  	tz->trips = trips;
b1569e99c   Matthew Garrett   ACPI: move therma...
1246
1247
  	tz->passive_delay = passive_delay;
  	tz->polling_delay = polling_delay;
1c600861f   Eduardo Valentin   thermal: core: us...
1248

4d0fe7490   Eduardo Valentin   thermal: core: mo...
1249
  	/* sys I/F */
1c600861f   Eduardo Valentin   thermal: core: us...
1250
  	/* Add nodes that are always present via .groups */
4d0fe7490   Eduardo Valentin   thermal: core: mo...
1251
1252
  	result = thermal_zone_create_device_groups(tz, mask);
  	if (result)
9d9ca1f9f   Christophe Jaillet   thermal: core: Fi...
1253
  		goto remove_id;
4d0fe7490   Eduardo Valentin   thermal: core: mo...
1254

4511f7166   Chen Yu   Thermal: do therm...
1255
1256
  	/* A new thermal zone needs to be updated anyway. */
  	atomic_set(&tz->need_update, 1);
b1569e99c   Matthew Garrett   ACPI: move therma...
1257

354655ea9   Kay Sievers   thermal: struct d...
1258
  	dev_set_name(&tz->device, "thermal_zone%d", tz->id);
203d3d4aa   Zhang Rui   the generic therm...
1259
  	result = device_register(&tz->device);
9d9ca1f9f   Christophe Jaillet   thermal: core: Fi...
1260
  	if (result)
adc8749b1   Yue Hu   thermal/drivers/c...
1261
  		goto release_device;
203d3d4aa   Zhang Rui   the generic therm...
1262

203d3d4aa   Zhang Rui   the generic therm...
1263
  	for (count = 0; count < trips; count++) {
81ad4276b   Zhang Rui   Thermal: Ignore i...
1264
1265
  		if (tz->ops->get_trip_type(tz, count, &trip_type))
  			set_bit(count, &tz->trips_disabled);
81ad4276b   Zhang Rui   Thermal: Ignore i...
1266
1267
1268
1269
1270
  		if (tz->ops->get_trip_temp(tz, count, &trip_temp))
  			set_bit(count, &tz->trips_disabled);
  		/* Check for bogus trip points */
  		if (trip_temp == 0)
  			set_bit(count, &tz->trips_disabled);
203d3d4aa   Zhang Rui   the generic therm...
1271
  	}
a4a15485f   Durgadoss R   Thermal: Add ther...
1272
1273
1274
1275
  	/* Update 'this' zone's governor information */
  	mutex_lock(&thermal_governor_lock);
  
  	if (tz->tzp)
e33df1d2f   Javi Merino   thermal: let gove...
1276
  		governor = __find_governor(tz->tzp->governor_name);
a4a15485f   Durgadoss R   Thermal: Add ther...
1277
  	else
e33df1d2f   Javi Merino   thermal: let gove...
1278
1279
1280
1281
1282
1283
1284
  		governor = def_governor;
  
  	result = thermal_set_governor(tz, governor);
  	if (result) {
  		mutex_unlock(&thermal_governor_lock);
  		goto unregister;
  	}
a4a15485f   Durgadoss R   Thermal: Add ther...
1285
1286
  
  	mutex_unlock(&thermal_governor_lock);
ccba4ffd9   Eduardo Valentin   drivers: thermal:...
1287
1288
1289
1290
1291
  	if (!tz->tzp || !tz->tzp->no_hwmon) {
  		result = thermal_add_hwmon_sysfs(tz);
  		if (result)
  			goto unregister;
  	}
e68b16abd   Zhang Rui   thermal: add hwmo...
1292

203d3d4aa   Zhang Rui   the generic therm...
1293
1294
  	mutex_lock(&thermal_list_lock);
  	list_add_tail(&tz->node, &thermal_tz_list);
203d3d4aa   Zhang Rui   the generic therm...
1295
  	mutex_unlock(&thermal_list_lock);
7e8ee1e9d   Durgadoss R   Thermal: Update b...
1296
1297
  	/* Bind cooling devices for this zone */
  	bind_tz(tz);
b659a30d7   Eduardo Valentin   thermal: core: re...
1298
  	INIT_DELAYED_WORK(&tz->poll_queue, thermal_zone_device_check);
b1569e99c   Matthew Garrett   ACPI: move therma...
1299

bb431ba26   Zhang Rui   Thermal: initiali...
1300
  	thermal_zone_device_reset(tz);
4511f7166   Chen Yu   Thermal: do therm...
1301
1302
  	/* Update the new thermal zone and mark it as already updated. */
  	if (atomic_cmpxchg(&tz->need_update, 1, 0))
0e70f466f   Srinivas Pandruvada   thermal: Enhance ...
1303
  		thermal_zone_device_update(tz, THERMAL_EVENT_UNSPECIFIED);
b1569e99c   Matthew Garrett   ACPI: move therma...
1304

140158605   Yao Dongdong   Thermal:Remove us...
1305
  	return tz;
203d3d4aa   Zhang Rui   the generic therm...
1306

caca8b803   Joe Perches   thermal_sys: kern...
1307
  unregister:
adc8749b1   Yue Hu   thermal/drivers/c...
1308
1309
1310
1311
  	device_del(&tz->device);
  release_device:
  	put_device(&tz->device);
  	tz = NULL;
9d9ca1f9f   Christophe Jaillet   thermal: core: Fi...
1312
  remove_id:
adc8749b1   Yue Hu   thermal/drivers/c...
1313
  	ida_simple_remove(&thermal_tz_ida, id);
9d9ca1f9f   Christophe Jaillet   thermal: core: Fi...
1314
1315
1316
  free_tz:
  	kfree(tz);
  	return ERR_PTR(result);
203d3d4aa   Zhang Rui   the generic therm...
1317
  }
910cb1e34   Eduardo Valentin   thermal: use EXPO...
1318
  EXPORT_SYMBOL_GPL(thermal_zone_device_register);
203d3d4aa   Zhang Rui   the generic therm...
1319
1320
1321
  
  /**
   * thermal_device_unregister - removes the registered thermal zone device
203d3d4aa   Zhang Rui   the generic therm...
1322
1323
1324
1325
   * @tz: the thermal zone device to remove
   */
  void thermal_zone_device_unregister(struct thermal_zone_device *tz)
  {
7e8ee1e9d   Durgadoss R   Thermal: Update b...
1326
1327
  	int i;
  	const struct thermal_zone_params *tzp;
203d3d4aa   Zhang Rui   the generic therm...
1328
1329
  	struct thermal_cooling_device *cdev;
  	struct thermal_zone_device *pos = NULL;
203d3d4aa   Zhang Rui   the generic therm...
1330
1331
1332
  
  	if (!tz)
  		return;
7e8ee1e9d   Durgadoss R   Thermal: Update b...
1333
  	tzp = tz->tzp;
203d3d4aa   Zhang Rui   the generic therm...
1334
1335
  	mutex_lock(&thermal_list_lock);
  	list_for_each_entry(pos, &thermal_tz_list, node)
b659a30d7   Eduardo Valentin   thermal: core: re...
1336
1337
  		if (pos == tz)
  			break;
203d3d4aa   Zhang Rui   the generic therm...
1338
1339
1340
1341
1342
1343
  	if (pos != tz) {
  		/* thermal zone device not found */
  		mutex_unlock(&thermal_list_lock);
  		return;
  	}
  	list_del(&tz->node);
7e8ee1e9d   Durgadoss R   Thermal: Update b...
1344
1345
1346
1347
1348
1349
1350
1351
1352
1353
1354
1355
1356
1357
1358
1359
1360
1361
  
  	/* Unbind all cdevs associated with 'this' thermal zone */
  	list_for_each_entry(cdev, &thermal_cdev_list, node) {
  		if (tz->ops->unbind) {
  			tz->ops->unbind(tz, cdev);
  			continue;
  		}
  
  		if (!tzp || !tzp->tbp)
  			break;
  
  		for (i = 0; i < tzp->num_tbps; i++) {
  			if (tzp->tbp[i].cdev == cdev) {
  				__unbind(tz, tzp->tbp[i].trip_mask, cdev);
  				tzp->tbp[i].cdev = NULL;
  			}
  		}
  	}
203d3d4aa   Zhang Rui   the generic therm...
1362
  	mutex_unlock(&thermal_list_lock);
7d967912c   Wei Wang   thermal: Fix dead...
1363
  	cancel_delayed_work_sync(&tz->poll_queue);
b1569e99c   Matthew Garrett   ACPI: move therma...
1364

e33df1d2f   Javi Merino   thermal: let gove...
1365
  	thermal_set_governor(tz, NULL);
203d3d4aa   Zhang Rui   the generic therm...
1366

e68b16abd   Zhang Rui   thermal: add hwmo...
1367
  	thermal_remove_hwmon_sysfs(tz);
b31ef8285   Matthew Wilcox   thermal core: con...
1368
1369
  	ida_simple_remove(&thermal_tz_ida, tz->id);
  	ida_destroy(&tz->ida);
203d3d4aa   Zhang Rui   the generic therm...
1370
1371
  	mutex_destroy(&tz->lock);
  	device_unregister(&tz->device);
203d3d4aa   Zhang Rui   the generic therm...
1372
  }
910cb1e34   Eduardo Valentin   thermal: use EXPO...
1373
  EXPORT_SYMBOL_GPL(thermal_zone_device_unregister);
203d3d4aa   Zhang Rui   the generic therm...
1374

63c4d919c   Eduardo Valentin   thermal: introduc...
1375
1376
1377
1378
1379
1380
1381
1382
1383
1384
1385
1386
1387
1388
1389
1390
1391
1392
1393
1394
  /**
   * thermal_zone_get_zone_by_name() - search for a zone and returns its ref
   * @name: thermal zone name to fetch the temperature
   *
   * When only one zone is found with the passed name, returns a reference to it.
   *
   * Return: On success returns a reference to an unique thermal zone with
   * matching name equals to @name, an ERR_PTR otherwise (-EINVAL for invalid
   * paramenters, -ENODEV for not found and -EEXIST for multiple matches).
   */
  struct thermal_zone_device *thermal_zone_get_zone_by_name(const char *name)
  {
  	struct thermal_zone_device *pos = NULL, *ref = ERR_PTR(-EINVAL);
  	unsigned int found = 0;
  
  	if (!name)
  		goto exit;
  
  	mutex_lock(&thermal_list_lock);
  	list_for_each_entry(pos, &thermal_tz_list, node)
484ac2f32   Rasmus Villemoes   thermal: replace ...
1395
  		if (!strncasecmp(name, pos->type, THERMAL_NAME_LENGTH)) {
63c4d919c   Eduardo Valentin   thermal: introduc...
1396
1397
1398
1399
1400
1401
1402
1403
1404
1405
1406
1407
1408
1409
1410
1411
  			found++;
  			ref = pos;
  		}
  	mutex_unlock(&thermal_list_lock);
  
  	/* nothing has been found, thus an error code for it */
  	if (found == 0)
  		ref = ERR_PTR(-ENODEV);
  	else if (found > 1)
  	/* Success only when an unique zone is found */
  		ref = ERR_PTR(-EEXIST);
  
  exit:
  	return ref;
  }
  EXPORT_SYMBOL_GPL(thermal_zone_get_zone_by_name);
af06216a8   Rafael J. Wysocki   ACPI: Fix build f...
1412
  #ifdef CONFIG_NET
2a94fe48f   Johannes Berg   genetlink: make m...
1413
1414
1415
  static const struct genl_multicast_group thermal_event_mcgrps[] = {
  	{ .name = THERMAL_GENL_MCAST_GROUP_NAME, },
  };
56989f6d8   Johannes Berg   genetlink: mark f...
1416
  static struct genl_family thermal_event_genl_family __ro_after_init = {
489111e5c   Johannes Berg   genetlink: static...
1417
  	.module = THIS_MODULE,
af06216a8   Rafael J. Wysocki   ACPI: Fix build f...
1418
1419
1420
  	.name = THERMAL_GENL_FAMILY_NAME,
  	.version = THERMAL_GENL_VERSION,
  	.maxattr = THERMAL_GENL_ATTR_MAX,
2a94fe48f   Johannes Berg   genetlink: make m...
1421
1422
  	.mcgrps = thermal_event_mcgrps,
  	.n_mcgrps = ARRAY_SIZE(thermal_event_mcgrps),
af06216a8   Rafael J. Wysocki   ACPI: Fix build f...
1423
  };
8ab3e6a08   Eduardo Valentin   thermal: Use ther...
1424
  int thermal_generate_netlink_event(struct thermal_zone_device *tz,
eb7be329b   Eduardo Valentin   thermal: core: st...
1425
  				   enum events event)
4cb187287   R.Durgadoss   thermal: Add even...
1426
1427
1428
1429
1430
1431
1432
  {
  	struct sk_buff *skb;
  	struct nlattr *attr;
  	struct thermal_genl_event *thermal_event;
  	void *msg_header;
  	int size;
  	int result;
b11de07ce   Fabio Estevam   drivers/thermal/t...
1433
  	static unsigned int thermal_event_seqnum;
4cb187287   R.Durgadoss   thermal: Add even...
1434

8ab3e6a08   Eduardo Valentin   thermal: Use ther...
1435
1436
  	if (!tz)
  		return -EINVAL;
4cb187287   R.Durgadoss   thermal: Add even...
1437
  	/* allocate memory */
886ee5463   Joe Perches   thermal_sys: remo...
1438
1439
  	size = nla_total_size(sizeof(struct thermal_genl_event)) +
  	       nla_total_size(0);
4cb187287   R.Durgadoss   thermal: Add even...
1440
1441
1442
1443
1444
1445
1446
1447
1448
1449
1450
1451
1452
1453
1454
  
  	skb = genlmsg_new(size, GFP_ATOMIC);
  	if (!skb)
  		return -ENOMEM;
  
  	/* add the genetlink message header */
  	msg_header = genlmsg_put(skb, 0, thermal_event_seqnum++,
  				 &thermal_event_genl_family, 0,
  				 THERMAL_GENL_CMD_EVENT);
  	if (!msg_header) {
  		nlmsg_free(skb);
  		return -ENOMEM;
  	}
  
  	/* fill the data */
886ee5463   Joe Perches   thermal_sys: remo...
1455
1456
  	attr = nla_reserve(skb, THERMAL_GENL_ATTR_EVENT,
  			   sizeof(struct thermal_genl_event));
4cb187287   R.Durgadoss   thermal: Add even...
1457
1458
1459
1460
1461
1462
1463
1464
1465
1466
1467
1468
1469
  
  	if (!attr) {
  		nlmsg_free(skb);
  		return -EINVAL;
  	}
  
  	thermal_event = nla_data(attr);
  	if (!thermal_event) {
  		nlmsg_free(skb);
  		return -EINVAL;
  	}
  
  	memset(thermal_event, 0, sizeof(struct thermal_genl_event));
8ab3e6a08   Eduardo Valentin   thermal: Use ther...
1470
  	thermal_event->orig = tz->id;
4cb187287   R.Durgadoss   thermal: Add even...
1471
1472
1473
  	thermal_event->event = event;
  
  	/* send multicast genetlink message */
053c095a8   Johannes Berg   netlink: make nlm...
1474
  	genlmsg_end(skb, msg_header);
4cb187287   R.Durgadoss   thermal: Add even...
1475

68eb55031   Johannes Berg   genetlink: pass f...
1476
  	result = genlmsg_multicast(&thermal_event_genl_family, skb, 0,
2a94fe48f   Johannes Berg   genetlink: make m...
1477
  				   0, GFP_ATOMIC);
4cb187287   R.Durgadoss   thermal: Add even...
1478
  	if (result)
923e0b1e8   Eduardo Valentin   thermal: cleanup:...
1479
  		dev_err(&tz->device, "Failed to send netlink event:%d", result);
4cb187287   R.Durgadoss   thermal: Add even...
1480
1481
1482
  
  	return result;
  }
910cb1e34   Eduardo Valentin   thermal: use EXPO...
1483
  EXPORT_SYMBOL_GPL(thermal_generate_netlink_event);
4cb187287   R.Durgadoss   thermal: Add even...
1484

56989f6d8   Johannes Berg   genetlink: mark f...
1485
  static int __init genetlink_init(void)
4cb187287   R.Durgadoss   thermal: Add even...
1486
  {
2a94fe48f   Johannes Berg   genetlink: make m...
1487
  	return genl_register_family(&thermal_event_genl_family);
4cb187287   R.Durgadoss   thermal: Add even...
1488
  }
af06216a8   Rafael J. Wysocki   ACPI: Fix build f...
1489
1490
1491
1492
1493
1494
1495
1496
  static void genetlink_exit(void)
  {
  	genl_unregister_family(&thermal_event_genl_family);
  }
  #else /* !CONFIG_NET */
  static inline int genetlink_init(void) { return 0; }
  static inline void genetlink_exit(void) {}
  #endif /* !CONFIG_NET */
ff140fea8   Zhang Rui   Thermal: handle t...
1497
  static int thermal_pm_notify(struct notifier_block *nb,
eb7be329b   Eduardo Valentin   thermal: core: st...
1498
  			     unsigned long mode, void *_unused)
ff140fea8   Zhang Rui   Thermal: handle t...
1499
1500
  {
  	struct thermal_zone_device *tz;
ff54bbd1b   Wei Wang   thermal: core: sk...
1501
  	enum thermal_device_mode tz_mode;
ff140fea8   Zhang Rui   Thermal: handle t...
1502
1503
1504
1505
1506
1507
1508
1509
1510
1511
1512
1513
  
  	switch (mode) {
  	case PM_HIBERNATION_PREPARE:
  	case PM_RESTORE_PREPARE:
  	case PM_SUSPEND_PREPARE:
  		atomic_set(&in_suspend, 1);
  		break;
  	case PM_POST_HIBERNATION:
  	case PM_POST_RESTORE:
  	case PM_POST_SUSPEND:
  		atomic_set(&in_suspend, 0);
  		list_for_each_entry(tz, &thermal_tz_list, node) {
ff54bbd1b   Wei Wang   thermal: core: sk...
1514
1515
1516
1517
1518
1519
  			tz_mode = THERMAL_DEVICE_ENABLED;
  			if (tz->ops->get_mode)
  				tz->ops->get_mode(tz, &tz_mode);
  
  			if (tz_mode == THERMAL_DEVICE_DISABLED)
  				continue;
964f4843a   Wei Wang   Thermal: do not c...
1520
  			thermal_zone_device_init(tz);
0e70f466f   Srinivas Pandruvada   thermal: Enhance ...
1521
1522
  			thermal_zone_device_update(tz,
  						   THERMAL_EVENT_UNSPECIFIED);
ff140fea8   Zhang Rui   Thermal: handle t...
1523
1524
1525
1526
1527
1528
1529
1530
1531
1532
1533
  		}
  		break;
  	default:
  		break;
  	}
  	return 0;
  }
  
  static struct notifier_block thermal_pm_nb = {
  	.notifier_call = thermal_pm_notify,
  };
203d3d4aa   Zhang Rui   the generic therm...
1534
1535
  static int __init thermal_init(void)
  {
80a26a5c2   Zhang Rui   Thermal: build th...
1536
  	int result;
e441fd686   Keerthy   thermal: core: Al...
1537
  	mutex_init(&poweroff_lock);
80a26a5c2   Zhang Rui   Thermal: build th...
1538
1539
1540
  	result = thermal_register_governors();
  	if (result)
  		goto error;
203d3d4aa   Zhang Rui   the generic therm...
1541
1542
  
  	result = class_register(&thermal_class);
80a26a5c2   Zhang Rui   Thermal: build th...
1543
1544
  	if (result)
  		goto unregister_governors;
4cb187287   R.Durgadoss   thermal: Add even...
1545
  	result = genetlink_init();
80a26a5c2   Zhang Rui   Thermal: build th...
1546
1547
  	if (result)
  		goto unregister_class;
4e5e4705b   Eduardo Valentin   thermal: introduc...
1548
1549
1550
  	result = of_parse_thermal_zones();
  	if (result)
  		goto exit_netlink;
ff140fea8   Zhang Rui   Thermal: handle t...
1551
1552
1553
1554
1555
  	result = register_pm_notifier(&thermal_pm_nb);
  	if (result)
  		pr_warn("Thermal: Can not register suspend notifier, return %d
  ",
  			result);
80a26a5c2   Zhang Rui   Thermal: build th...
1556
  	return 0;
4e5e4705b   Eduardo Valentin   thermal: introduc...
1557
1558
  exit_netlink:
  	genetlink_exit();
80a26a5c2   Zhang Rui   Thermal: build th...
1559
1560
  unregister_class:
  	class_unregister(&thermal_class);
9d367e5e7   Luis Henriques   thermal: Fix erro...
1561
1562
  unregister_governors:
  	thermal_unregister_governors();
80a26a5c2   Zhang Rui   Thermal: build th...
1563
  error:
b31ef8285   Matthew Wilcox   thermal core: con...
1564
1565
  	ida_destroy(&thermal_tz_ida);
  	ida_destroy(&thermal_cdev_ida);
80a26a5c2   Zhang Rui   Thermal: build th...
1566
1567
  	mutex_destroy(&thermal_list_lock);
  	mutex_destroy(&thermal_governor_lock);
e441fd686   Keerthy   thermal: core: Al...
1568
  	mutex_destroy(&poweroff_lock);
203d3d4aa   Zhang Rui   the generic therm...
1569
1570
  	return result;
  }
4cb187287   R.Durgadoss   thermal: Add even...
1571
  fs_initcall(thermal_init);