162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci *  thermal.c - Generic Thermal Management Sysfs support.
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci *  Copyright (C) 2008 Intel Corp
662306a36Sopenharmony_ci *  Copyright (C) 2008 Zhang Rui <rui.zhang@intel.com>
762306a36Sopenharmony_ci *  Copyright (C) 2008 Sujith Thomas <sujith.thomas@intel.com>
862306a36Sopenharmony_ci */
962306a36Sopenharmony_ci
1062306a36Sopenharmony_ci#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
1162306a36Sopenharmony_ci
1262306a36Sopenharmony_ci#include <linux/device.h>
1362306a36Sopenharmony_ci#include <linux/err.h>
1462306a36Sopenharmony_ci#include <linux/export.h>
1562306a36Sopenharmony_ci#include <linux/slab.h>
1662306a36Sopenharmony_ci#include <linux/kdev_t.h>
1762306a36Sopenharmony_ci#include <linux/idr.h>
1862306a36Sopenharmony_ci#include <linux/thermal.h>
1962306a36Sopenharmony_ci#include <linux/reboot.h>
2062306a36Sopenharmony_ci#include <linux/string.h>
2162306a36Sopenharmony_ci#include <linux/of.h>
2262306a36Sopenharmony_ci#include <linux/suspend.h>
2362306a36Sopenharmony_ci
2462306a36Sopenharmony_ci#define CREATE_TRACE_POINTS
2562306a36Sopenharmony_ci#include "thermal_trace.h"
2662306a36Sopenharmony_ci
2762306a36Sopenharmony_ci#include "thermal_core.h"
2862306a36Sopenharmony_ci#include "thermal_hwmon.h"
2962306a36Sopenharmony_ci
3062306a36Sopenharmony_cistatic DEFINE_IDA(thermal_tz_ida);
3162306a36Sopenharmony_cistatic DEFINE_IDA(thermal_cdev_ida);
3262306a36Sopenharmony_ci
3362306a36Sopenharmony_cistatic LIST_HEAD(thermal_tz_list);
3462306a36Sopenharmony_cistatic LIST_HEAD(thermal_cdev_list);
3562306a36Sopenharmony_cistatic LIST_HEAD(thermal_governor_list);
3662306a36Sopenharmony_ci
3762306a36Sopenharmony_cistatic DEFINE_MUTEX(thermal_list_lock);
3862306a36Sopenharmony_cistatic DEFINE_MUTEX(thermal_governor_lock);
3962306a36Sopenharmony_ci
4062306a36Sopenharmony_cistatic struct thermal_governor *def_governor;
4162306a36Sopenharmony_ci
4262306a36Sopenharmony_ci/*
4362306a36Sopenharmony_ci * Governor section: set of functions to handle thermal governors
4462306a36Sopenharmony_ci *
4562306a36Sopenharmony_ci * Functions to help in the life cycle of thermal governors within
4662306a36Sopenharmony_ci * the thermal core and by the thermal governor code.
4762306a36Sopenharmony_ci */
4862306a36Sopenharmony_ci
4962306a36Sopenharmony_cistatic struct thermal_governor *__find_governor(const char *name)
5062306a36Sopenharmony_ci{
5162306a36Sopenharmony_ci	struct thermal_governor *pos;
5262306a36Sopenharmony_ci
5362306a36Sopenharmony_ci	if (!name || !name[0])
5462306a36Sopenharmony_ci		return def_governor;
5562306a36Sopenharmony_ci
5662306a36Sopenharmony_ci	list_for_each_entry(pos, &thermal_governor_list, governor_list)
5762306a36Sopenharmony_ci		if (!strncasecmp(name, pos->name, THERMAL_NAME_LENGTH))
5862306a36Sopenharmony_ci			return pos;
5962306a36Sopenharmony_ci
6062306a36Sopenharmony_ci	return NULL;
6162306a36Sopenharmony_ci}
6262306a36Sopenharmony_ci
6362306a36Sopenharmony_ci/**
6462306a36Sopenharmony_ci * bind_previous_governor() - bind the previous governor of the thermal zone
6562306a36Sopenharmony_ci * @tz:		a valid pointer to a struct thermal_zone_device
6662306a36Sopenharmony_ci * @failed_gov_name:	the name of the governor that failed to register
6762306a36Sopenharmony_ci *
6862306a36Sopenharmony_ci * Register the previous governor of the thermal zone after a new
6962306a36Sopenharmony_ci * governor has failed to be bound.
7062306a36Sopenharmony_ci */
7162306a36Sopenharmony_cistatic void bind_previous_governor(struct thermal_zone_device *tz,
7262306a36Sopenharmony_ci				   const char *failed_gov_name)
7362306a36Sopenharmony_ci{
7462306a36Sopenharmony_ci	if (tz->governor && tz->governor->bind_to_tz) {
7562306a36Sopenharmony_ci		if (tz->governor->bind_to_tz(tz)) {
7662306a36Sopenharmony_ci			dev_err(&tz->device,
7762306a36Sopenharmony_ci				"governor %s failed to bind and the previous one (%s) failed to bind again, thermal zone %s has no governor\n",
7862306a36Sopenharmony_ci				failed_gov_name, tz->governor->name, tz->type);
7962306a36Sopenharmony_ci			tz->governor = NULL;
8062306a36Sopenharmony_ci		}
8162306a36Sopenharmony_ci	}
8262306a36Sopenharmony_ci}
8362306a36Sopenharmony_ci
8462306a36Sopenharmony_ci/**
8562306a36Sopenharmony_ci * thermal_set_governor() - Switch to another governor
8662306a36Sopenharmony_ci * @tz:		a valid pointer to a struct thermal_zone_device
8762306a36Sopenharmony_ci * @new_gov:	pointer to the new governor
8862306a36Sopenharmony_ci *
8962306a36Sopenharmony_ci * Change the governor of thermal zone @tz.
9062306a36Sopenharmony_ci *
9162306a36Sopenharmony_ci * Return: 0 on success, an error if the new governor's bind_to_tz() failed.
9262306a36Sopenharmony_ci */
9362306a36Sopenharmony_cistatic int thermal_set_governor(struct thermal_zone_device *tz,
9462306a36Sopenharmony_ci				struct thermal_governor *new_gov)
9562306a36Sopenharmony_ci{
9662306a36Sopenharmony_ci	int ret = 0;
9762306a36Sopenharmony_ci
9862306a36Sopenharmony_ci	if (tz->governor && tz->governor->unbind_from_tz)
9962306a36Sopenharmony_ci		tz->governor->unbind_from_tz(tz);
10062306a36Sopenharmony_ci
10162306a36Sopenharmony_ci	if (new_gov && new_gov->bind_to_tz) {
10262306a36Sopenharmony_ci		ret = new_gov->bind_to_tz(tz);
10362306a36Sopenharmony_ci		if (ret) {
10462306a36Sopenharmony_ci			bind_previous_governor(tz, new_gov->name);
10562306a36Sopenharmony_ci
10662306a36Sopenharmony_ci			return ret;
10762306a36Sopenharmony_ci		}
10862306a36Sopenharmony_ci	}
10962306a36Sopenharmony_ci
11062306a36Sopenharmony_ci	tz->governor = new_gov;
11162306a36Sopenharmony_ci
11262306a36Sopenharmony_ci	return ret;
11362306a36Sopenharmony_ci}
11462306a36Sopenharmony_ci
11562306a36Sopenharmony_ciint thermal_register_governor(struct thermal_governor *governor)
11662306a36Sopenharmony_ci{
11762306a36Sopenharmony_ci	int err;
11862306a36Sopenharmony_ci	const char *name;
11962306a36Sopenharmony_ci	struct thermal_zone_device *pos;
12062306a36Sopenharmony_ci
12162306a36Sopenharmony_ci	if (!governor)
12262306a36Sopenharmony_ci		return -EINVAL;
12362306a36Sopenharmony_ci
12462306a36Sopenharmony_ci	mutex_lock(&thermal_governor_lock);
12562306a36Sopenharmony_ci
12662306a36Sopenharmony_ci	err = -EBUSY;
12762306a36Sopenharmony_ci	if (!__find_governor(governor->name)) {
12862306a36Sopenharmony_ci		bool match_default;
12962306a36Sopenharmony_ci
13062306a36Sopenharmony_ci		err = 0;
13162306a36Sopenharmony_ci		list_add(&governor->governor_list, &thermal_governor_list);
13262306a36Sopenharmony_ci		match_default = !strncmp(governor->name,
13362306a36Sopenharmony_ci					 DEFAULT_THERMAL_GOVERNOR,
13462306a36Sopenharmony_ci					 THERMAL_NAME_LENGTH);
13562306a36Sopenharmony_ci
13662306a36Sopenharmony_ci		if (!def_governor && match_default)
13762306a36Sopenharmony_ci			def_governor = governor;
13862306a36Sopenharmony_ci	}
13962306a36Sopenharmony_ci
14062306a36Sopenharmony_ci	mutex_lock(&thermal_list_lock);
14162306a36Sopenharmony_ci
14262306a36Sopenharmony_ci	list_for_each_entry(pos, &thermal_tz_list, node) {
14362306a36Sopenharmony_ci		/*
14462306a36Sopenharmony_ci		 * only thermal zones with specified tz->tzp->governor_name
14562306a36Sopenharmony_ci		 * may run with tz->govenor unset
14662306a36Sopenharmony_ci		 */
14762306a36Sopenharmony_ci		if (pos->governor)
14862306a36Sopenharmony_ci			continue;
14962306a36Sopenharmony_ci
15062306a36Sopenharmony_ci		name = pos->tzp->governor_name;
15162306a36Sopenharmony_ci
15262306a36Sopenharmony_ci		if (!strncasecmp(name, governor->name, THERMAL_NAME_LENGTH)) {
15362306a36Sopenharmony_ci			int ret;
15462306a36Sopenharmony_ci
15562306a36Sopenharmony_ci			ret = thermal_set_governor(pos, governor);
15662306a36Sopenharmony_ci			if (ret)
15762306a36Sopenharmony_ci				dev_err(&pos->device,
15862306a36Sopenharmony_ci					"Failed to set governor %s for thermal zone %s: %d\n",
15962306a36Sopenharmony_ci					governor->name, pos->type, ret);
16062306a36Sopenharmony_ci		}
16162306a36Sopenharmony_ci	}
16262306a36Sopenharmony_ci
16362306a36Sopenharmony_ci	mutex_unlock(&thermal_list_lock);
16462306a36Sopenharmony_ci	mutex_unlock(&thermal_governor_lock);
16562306a36Sopenharmony_ci
16662306a36Sopenharmony_ci	return err;
16762306a36Sopenharmony_ci}
16862306a36Sopenharmony_ci
16962306a36Sopenharmony_civoid thermal_unregister_governor(struct thermal_governor *governor)
17062306a36Sopenharmony_ci{
17162306a36Sopenharmony_ci	struct thermal_zone_device *pos;
17262306a36Sopenharmony_ci
17362306a36Sopenharmony_ci	if (!governor)
17462306a36Sopenharmony_ci		return;
17562306a36Sopenharmony_ci
17662306a36Sopenharmony_ci	mutex_lock(&thermal_governor_lock);
17762306a36Sopenharmony_ci
17862306a36Sopenharmony_ci	if (!__find_governor(governor->name))
17962306a36Sopenharmony_ci		goto exit;
18062306a36Sopenharmony_ci
18162306a36Sopenharmony_ci	mutex_lock(&thermal_list_lock);
18262306a36Sopenharmony_ci
18362306a36Sopenharmony_ci	list_for_each_entry(pos, &thermal_tz_list, node) {
18462306a36Sopenharmony_ci		if (!strncasecmp(pos->governor->name, governor->name,
18562306a36Sopenharmony_ci				 THERMAL_NAME_LENGTH))
18662306a36Sopenharmony_ci			thermal_set_governor(pos, NULL);
18762306a36Sopenharmony_ci	}
18862306a36Sopenharmony_ci
18962306a36Sopenharmony_ci	mutex_unlock(&thermal_list_lock);
19062306a36Sopenharmony_ci	list_del(&governor->governor_list);
19162306a36Sopenharmony_ciexit:
19262306a36Sopenharmony_ci	mutex_unlock(&thermal_governor_lock);
19362306a36Sopenharmony_ci}
19462306a36Sopenharmony_ci
19562306a36Sopenharmony_ciint thermal_zone_device_set_policy(struct thermal_zone_device *tz,
19662306a36Sopenharmony_ci				   char *policy)
19762306a36Sopenharmony_ci{
19862306a36Sopenharmony_ci	struct thermal_governor *gov;
19962306a36Sopenharmony_ci	int ret = -EINVAL;
20062306a36Sopenharmony_ci
20162306a36Sopenharmony_ci	mutex_lock(&thermal_governor_lock);
20262306a36Sopenharmony_ci	mutex_lock(&tz->lock);
20362306a36Sopenharmony_ci
20462306a36Sopenharmony_ci	if (!device_is_registered(&tz->device))
20562306a36Sopenharmony_ci		goto exit;
20662306a36Sopenharmony_ci
20762306a36Sopenharmony_ci	gov = __find_governor(strim(policy));
20862306a36Sopenharmony_ci	if (!gov)
20962306a36Sopenharmony_ci		goto exit;
21062306a36Sopenharmony_ci
21162306a36Sopenharmony_ci	ret = thermal_set_governor(tz, gov);
21262306a36Sopenharmony_ci
21362306a36Sopenharmony_ciexit:
21462306a36Sopenharmony_ci	mutex_unlock(&tz->lock);
21562306a36Sopenharmony_ci	mutex_unlock(&thermal_governor_lock);
21662306a36Sopenharmony_ci
21762306a36Sopenharmony_ci	thermal_notify_tz_gov_change(tz->id, policy);
21862306a36Sopenharmony_ci
21962306a36Sopenharmony_ci	return ret;
22062306a36Sopenharmony_ci}
22162306a36Sopenharmony_ci
22262306a36Sopenharmony_ciint thermal_build_list_of_policies(char *buf)
22362306a36Sopenharmony_ci{
22462306a36Sopenharmony_ci	struct thermal_governor *pos;
22562306a36Sopenharmony_ci	ssize_t count = 0;
22662306a36Sopenharmony_ci
22762306a36Sopenharmony_ci	mutex_lock(&thermal_governor_lock);
22862306a36Sopenharmony_ci
22962306a36Sopenharmony_ci	list_for_each_entry(pos, &thermal_governor_list, governor_list) {
23062306a36Sopenharmony_ci		count += sysfs_emit_at(buf, count, "%s ", pos->name);
23162306a36Sopenharmony_ci	}
23262306a36Sopenharmony_ci	count += sysfs_emit_at(buf, count, "\n");
23362306a36Sopenharmony_ci
23462306a36Sopenharmony_ci	mutex_unlock(&thermal_governor_lock);
23562306a36Sopenharmony_ci
23662306a36Sopenharmony_ci	return count;
23762306a36Sopenharmony_ci}
23862306a36Sopenharmony_ci
23962306a36Sopenharmony_cistatic void __init thermal_unregister_governors(void)
24062306a36Sopenharmony_ci{
24162306a36Sopenharmony_ci	struct thermal_governor **governor;
24262306a36Sopenharmony_ci
24362306a36Sopenharmony_ci	for_each_governor_table(governor)
24462306a36Sopenharmony_ci		thermal_unregister_governor(*governor);
24562306a36Sopenharmony_ci}
24662306a36Sopenharmony_ci
24762306a36Sopenharmony_cistatic int __init thermal_register_governors(void)
24862306a36Sopenharmony_ci{
24962306a36Sopenharmony_ci	int ret = 0;
25062306a36Sopenharmony_ci	struct thermal_governor **governor;
25162306a36Sopenharmony_ci
25262306a36Sopenharmony_ci	for_each_governor_table(governor) {
25362306a36Sopenharmony_ci		ret = thermal_register_governor(*governor);
25462306a36Sopenharmony_ci		if (ret) {
25562306a36Sopenharmony_ci			pr_err("Failed to register governor: '%s'",
25662306a36Sopenharmony_ci			       (*governor)->name);
25762306a36Sopenharmony_ci			break;
25862306a36Sopenharmony_ci		}
25962306a36Sopenharmony_ci
26062306a36Sopenharmony_ci		pr_info("Registered thermal governor '%s'",
26162306a36Sopenharmony_ci			(*governor)->name);
26262306a36Sopenharmony_ci	}
26362306a36Sopenharmony_ci
26462306a36Sopenharmony_ci	if (ret) {
26562306a36Sopenharmony_ci		struct thermal_governor **gov;
26662306a36Sopenharmony_ci
26762306a36Sopenharmony_ci		for_each_governor_table(gov) {
26862306a36Sopenharmony_ci			if (gov == governor)
26962306a36Sopenharmony_ci				break;
27062306a36Sopenharmony_ci			thermal_unregister_governor(*gov);
27162306a36Sopenharmony_ci		}
27262306a36Sopenharmony_ci	}
27362306a36Sopenharmony_ci
27462306a36Sopenharmony_ci	return ret;
27562306a36Sopenharmony_ci}
27662306a36Sopenharmony_ci
27762306a36Sopenharmony_ci/*
27862306a36Sopenharmony_ci * Zone update section: main control loop applied to each zone while monitoring
27962306a36Sopenharmony_ci *
28062306a36Sopenharmony_ci * in polling mode. The monitoring is done using a workqueue.
28162306a36Sopenharmony_ci * Same update may be done on a zone by calling thermal_zone_device_update().
28262306a36Sopenharmony_ci *
28362306a36Sopenharmony_ci * An update means:
28462306a36Sopenharmony_ci * - Non-critical trips will invoke the governor responsible for that zone;
28562306a36Sopenharmony_ci * - Hot trips will produce a notification to userspace;
28662306a36Sopenharmony_ci * - Critical trip point will cause a system shutdown.
28762306a36Sopenharmony_ci */
28862306a36Sopenharmony_cistatic void thermal_zone_device_set_polling(struct thermal_zone_device *tz,
28962306a36Sopenharmony_ci					    unsigned long delay)
29062306a36Sopenharmony_ci{
29162306a36Sopenharmony_ci	if (delay)
29262306a36Sopenharmony_ci		mod_delayed_work(system_freezable_power_efficient_wq,
29362306a36Sopenharmony_ci				 &tz->poll_queue, delay);
29462306a36Sopenharmony_ci	else
29562306a36Sopenharmony_ci		cancel_delayed_work(&tz->poll_queue);
29662306a36Sopenharmony_ci}
29762306a36Sopenharmony_ci
29862306a36Sopenharmony_cistatic void monitor_thermal_zone(struct thermal_zone_device *tz)
29962306a36Sopenharmony_ci{
30062306a36Sopenharmony_ci	if (tz->mode != THERMAL_DEVICE_ENABLED)
30162306a36Sopenharmony_ci		thermal_zone_device_set_polling(tz, 0);
30262306a36Sopenharmony_ci	else if (tz->passive)
30362306a36Sopenharmony_ci		thermal_zone_device_set_polling(tz, tz->passive_delay_jiffies);
30462306a36Sopenharmony_ci	else if (tz->polling_delay_jiffies)
30562306a36Sopenharmony_ci		thermal_zone_device_set_polling(tz, tz->polling_delay_jiffies);
30662306a36Sopenharmony_ci}
30762306a36Sopenharmony_ci
30862306a36Sopenharmony_cistatic void handle_non_critical_trips(struct thermal_zone_device *tz, int trip)
30962306a36Sopenharmony_ci{
31062306a36Sopenharmony_ci	tz->governor ? tz->governor->throttle(tz, trip) :
31162306a36Sopenharmony_ci		       def_governor->throttle(tz, trip);
31262306a36Sopenharmony_ci}
31362306a36Sopenharmony_ci
31462306a36Sopenharmony_civoid thermal_zone_device_critical(struct thermal_zone_device *tz)
31562306a36Sopenharmony_ci{
31662306a36Sopenharmony_ci	/*
31762306a36Sopenharmony_ci	 * poweroff_delay_ms must be a carefully profiled positive value.
31862306a36Sopenharmony_ci	 * Its a must for forced_emergency_poweroff_work to be scheduled.
31962306a36Sopenharmony_ci	 */
32062306a36Sopenharmony_ci	int poweroff_delay_ms = CONFIG_THERMAL_EMERGENCY_POWEROFF_DELAY_MS;
32162306a36Sopenharmony_ci
32262306a36Sopenharmony_ci	dev_emerg(&tz->device, "%s: critical temperature reached, "
32362306a36Sopenharmony_ci		  "shutting down\n", tz->type);
32462306a36Sopenharmony_ci
32562306a36Sopenharmony_ci	hw_protection_shutdown("Temperature too high", poweroff_delay_ms);
32662306a36Sopenharmony_ci}
32762306a36Sopenharmony_ciEXPORT_SYMBOL(thermal_zone_device_critical);
32862306a36Sopenharmony_ci
32962306a36Sopenharmony_cistatic void handle_critical_trips(struct thermal_zone_device *tz,
33062306a36Sopenharmony_ci				  int trip, int trip_temp, enum thermal_trip_type trip_type)
33162306a36Sopenharmony_ci{
33262306a36Sopenharmony_ci	/* If we have not crossed the trip_temp, we do not care. */
33362306a36Sopenharmony_ci	if (trip_temp <= 0 || tz->temperature < trip_temp)
33462306a36Sopenharmony_ci		return;
33562306a36Sopenharmony_ci
33662306a36Sopenharmony_ci	trace_thermal_zone_trip(tz, trip, trip_type);
33762306a36Sopenharmony_ci
33862306a36Sopenharmony_ci	if (trip_type == THERMAL_TRIP_HOT && tz->ops->hot)
33962306a36Sopenharmony_ci		tz->ops->hot(tz);
34062306a36Sopenharmony_ci	else if (trip_type == THERMAL_TRIP_CRITICAL)
34162306a36Sopenharmony_ci		tz->ops->critical(tz);
34262306a36Sopenharmony_ci}
34362306a36Sopenharmony_ci
34462306a36Sopenharmony_cistatic void handle_thermal_trip(struct thermal_zone_device *tz, int trip_id)
34562306a36Sopenharmony_ci{
34662306a36Sopenharmony_ci	struct thermal_trip trip;
34762306a36Sopenharmony_ci
34862306a36Sopenharmony_ci	/* Ignore disabled trip points */
34962306a36Sopenharmony_ci	if (test_bit(trip_id, &tz->trips_disabled))
35062306a36Sopenharmony_ci		return;
35162306a36Sopenharmony_ci
35262306a36Sopenharmony_ci	__thermal_zone_get_trip(tz, trip_id, &trip);
35362306a36Sopenharmony_ci
35462306a36Sopenharmony_ci	if (trip.temperature == THERMAL_TEMP_INVALID)
35562306a36Sopenharmony_ci		return;
35662306a36Sopenharmony_ci
35762306a36Sopenharmony_ci	if (tz->last_temperature != THERMAL_TEMP_INVALID) {
35862306a36Sopenharmony_ci		if (tz->last_temperature < trip.temperature &&
35962306a36Sopenharmony_ci		    tz->temperature >= trip.temperature)
36062306a36Sopenharmony_ci			thermal_notify_tz_trip_up(tz->id, trip_id,
36162306a36Sopenharmony_ci						  tz->temperature);
36262306a36Sopenharmony_ci		if (tz->last_temperature >= trip.temperature &&
36362306a36Sopenharmony_ci		    tz->temperature < (trip.temperature - trip.hysteresis))
36462306a36Sopenharmony_ci			thermal_notify_tz_trip_down(tz->id, trip_id,
36562306a36Sopenharmony_ci						    tz->temperature);
36662306a36Sopenharmony_ci	}
36762306a36Sopenharmony_ci
36862306a36Sopenharmony_ci	if (trip.type == THERMAL_TRIP_CRITICAL || trip.type == THERMAL_TRIP_HOT)
36962306a36Sopenharmony_ci		handle_critical_trips(tz, trip_id, trip.temperature, trip.type);
37062306a36Sopenharmony_ci	else
37162306a36Sopenharmony_ci		handle_non_critical_trips(tz, trip_id);
37262306a36Sopenharmony_ci}
37362306a36Sopenharmony_ci
37462306a36Sopenharmony_cistatic void update_temperature(struct thermal_zone_device *tz)
37562306a36Sopenharmony_ci{
37662306a36Sopenharmony_ci	int temp, ret;
37762306a36Sopenharmony_ci
37862306a36Sopenharmony_ci	ret = __thermal_zone_get_temp(tz, &temp);
37962306a36Sopenharmony_ci	if (ret) {
38062306a36Sopenharmony_ci		if (ret != -EAGAIN)
38162306a36Sopenharmony_ci			dev_warn(&tz->device,
38262306a36Sopenharmony_ci				 "failed to read out thermal zone (%d)\n",
38362306a36Sopenharmony_ci				 ret);
38462306a36Sopenharmony_ci		return;
38562306a36Sopenharmony_ci	}
38662306a36Sopenharmony_ci
38762306a36Sopenharmony_ci	tz->last_temperature = tz->temperature;
38862306a36Sopenharmony_ci	tz->temperature = temp;
38962306a36Sopenharmony_ci
39062306a36Sopenharmony_ci	trace_thermal_temperature(tz);
39162306a36Sopenharmony_ci
39262306a36Sopenharmony_ci	thermal_genl_sampling_temp(tz->id, temp);
39362306a36Sopenharmony_ci}
39462306a36Sopenharmony_ci
39562306a36Sopenharmony_cistatic void thermal_zone_device_init(struct thermal_zone_device *tz)
39662306a36Sopenharmony_ci{
39762306a36Sopenharmony_ci	struct thermal_instance *pos;
39862306a36Sopenharmony_ci	tz->temperature = THERMAL_TEMP_INVALID;
39962306a36Sopenharmony_ci	tz->prev_low_trip = -INT_MAX;
40062306a36Sopenharmony_ci	tz->prev_high_trip = INT_MAX;
40162306a36Sopenharmony_ci	list_for_each_entry(pos, &tz->thermal_instances, tz_node)
40262306a36Sopenharmony_ci		pos->initialized = false;
40362306a36Sopenharmony_ci}
40462306a36Sopenharmony_ci
40562306a36Sopenharmony_civoid __thermal_zone_device_update(struct thermal_zone_device *tz,
40662306a36Sopenharmony_ci				  enum thermal_notify_event event)
40762306a36Sopenharmony_ci{
40862306a36Sopenharmony_ci	int count;
40962306a36Sopenharmony_ci
41062306a36Sopenharmony_ci	if (tz->suspended)
41162306a36Sopenharmony_ci		return;
41262306a36Sopenharmony_ci
41362306a36Sopenharmony_ci	if (WARN_ONCE(!tz->ops->get_temp,
41462306a36Sopenharmony_ci		      "'%s' must not be called without 'get_temp' ops set\n",
41562306a36Sopenharmony_ci		      __func__))
41662306a36Sopenharmony_ci		return;
41762306a36Sopenharmony_ci
41862306a36Sopenharmony_ci	if (!thermal_zone_device_is_enabled(tz))
41962306a36Sopenharmony_ci		return;
42062306a36Sopenharmony_ci
42162306a36Sopenharmony_ci	update_temperature(tz);
42262306a36Sopenharmony_ci
42362306a36Sopenharmony_ci	__thermal_zone_set_trips(tz);
42462306a36Sopenharmony_ci
42562306a36Sopenharmony_ci	tz->notify_event = event;
42662306a36Sopenharmony_ci
42762306a36Sopenharmony_ci	for (count = 0; count < tz->num_trips; count++)
42862306a36Sopenharmony_ci		handle_thermal_trip(tz, count);
42962306a36Sopenharmony_ci
43062306a36Sopenharmony_ci	monitor_thermal_zone(tz);
43162306a36Sopenharmony_ci}
43262306a36Sopenharmony_ci
43362306a36Sopenharmony_cistatic int thermal_zone_device_set_mode(struct thermal_zone_device *tz,
43462306a36Sopenharmony_ci					enum thermal_device_mode mode)
43562306a36Sopenharmony_ci{
43662306a36Sopenharmony_ci	int ret = 0;
43762306a36Sopenharmony_ci
43862306a36Sopenharmony_ci	mutex_lock(&tz->lock);
43962306a36Sopenharmony_ci
44062306a36Sopenharmony_ci	/* do nothing if mode isn't changing */
44162306a36Sopenharmony_ci	if (mode == tz->mode) {
44262306a36Sopenharmony_ci		mutex_unlock(&tz->lock);
44362306a36Sopenharmony_ci
44462306a36Sopenharmony_ci		return ret;
44562306a36Sopenharmony_ci	}
44662306a36Sopenharmony_ci
44762306a36Sopenharmony_ci	if (!device_is_registered(&tz->device)) {
44862306a36Sopenharmony_ci		mutex_unlock(&tz->lock);
44962306a36Sopenharmony_ci
45062306a36Sopenharmony_ci		return -ENODEV;
45162306a36Sopenharmony_ci	}
45262306a36Sopenharmony_ci
45362306a36Sopenharmony_ci	if (tz->ops->change_mode)
45462306a36Sopenharmony_ci		ret = tz->ops->change_mode(tz, mode);
45562306a36Sopenharmony_ci
45662306a36Sopenharmony_ci	if (!ret)
45762306a36Sopenharmony_ci		tz->mode = mode;
45862306a36Sopenharmony_ci
45962306a36Sopenharmony_ci	__thermal_zone_device_update(tz, THERMAL_EVENT_UNSPECIFIED);
46062306a36Sopenharmony_ci
46162306a36Sopenharmony_ci	mutex_unlock(&tz->lock);
46262306a36Sopenharmony_ci
46362306a36Sopenharmony_ci	if (mode == THERMAL_DEVICE_ENABLED)
46462306a36Sopenharmony_ci		thermal_notify_tz_enable(tz->id);
46562306a36Sopenharmony_ci	else
46662306a36Sopenharmony_ci		thermal_notify_tz_disable(tz->id);
46762306a36Sopenharmony_ci
46862306a36Sopenharmony_ci	return ret;
46962306a36Sopenharmony_ci}
47062306a36Sopenharmony_ci
47162306a36Sopenharmony_ciint thermal_zone_device_enable(struct thermal_zone_device *tz)
47262306a36Sopenharmony_ci{
47362306a36Sopenharmony_ci	return thermal_zone_device_set_mode(tz, THERMAL_DEVICE_ENABLED);
47462306a36Sopenharmony_ci}
47562306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(thermal_zone_device_enable);
47662306a36Sopenharmony_ci
47762306a36Sopenharmony_ciint thermal_zone_device_disable(struct thermal_zone_device *tz)
47862306a36Sopenharmony_ci{
47962306a36Sopenharmony_ci	return thermal_zone_device_set_mode(tz, THERMAL_DEVICE_DISABLED);
48062306a36Sopenharmony_ci}
48162306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(thermal_zone_device_disable);
48262306a36Sopenharmony_ci
48362306a36Sopenharmony_ciint thermal_zone_device_is_enabled(struct thermal_zone_device *tz)
48462306a36Sopenharmony_ci{
48562306a36Sopenharmony_ci	lockdep_assert_held(&tz->lock);
48662306a36Sopenharmony_ci
48762306a36Sopenharmony_ci	return tz->mode == THERMAL_DEVICE_ENABLED;
48862306a36Sopenharmony_ci}
48962306a36Sopenharmony_ci
49062306a36Sopenharmony_civoid thermal_zone_device_update(struct thermal_zone_device *tz,
49162306a36Sopenharmony_ci				enum thermal_notify_event event)
49262306a36Sopenharmony_ci{
49362306a36Sopenharmony_ci	mutex_lock(&tz->lock);
49462306a36Sopenharmony_ci	if (device_is_registered(&tz->device))
49562306a36Sopenharmony_ci		__thermal_zone_device_update(tz, event);
49662306a36Sopenharmony_ci	mutex_unlock(&tz->lock);
49762306a36Sopenharmony_ci}
49862306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(thermal_zone_device_update);
49962306a36Sopenharmony_ci
50062306a36Sopenharmony_ci/**
50162306a36Sopenharmony_ci * thermal_zone_device_exec - Run a callback under the zone lock.
50262306a36Sopenharmony_ci * @tz: Thermal zone.
50362306a36Sopenharmony_ci * @cb: Callback to run.
50462306a36Sopenharmony_ci * @data: Data to pass to the callback.
50562306a36Sopenharmony_ci */
50662306a36Sopenharmony_civoid thermal_zone_device_exec(struct thermal_zone_device *tz,
50762306a36Sopenharmony_ci			      void (*cb)(struct thermal_zone_device *,
50862306a36Sopenharmony_ci					 unsigned long),
50962306a36Sopenharmony_ci			      unsigned long data)
51062306a36Sopenharmony_ci{
51162306a36Sopenharmony_ci	mutex_lock(&tz->lock);
51262306a36Sopenharmony_ci
51362306a36Sopenharmony_ci	cb(tz, data);
51462306a36Sopenharmony_ci
51562306a36Sopenharmony_ci	mutex_unlock(&tz->lock);
51662306a36Sopenharmony_ci}
51762306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(thermal_zone_device_exec);
51862306a36Sopenharmony_ci
51962306a36Sopenharmony_cistatic void thermal_zone_device_check(struct work_struct *work)
52062306a36Sopenharmony_ci{
52162306a36Sopenharmony_ci	struct thermal_zone_device *tz = container_of(work, struct
52262306a36Sopenharmony_ci						      thermal_zone_device,
52362306a36Sopenharmony_ci						      poll_queue.work);
52462306a36Sopenharmony_ci	thermal_zone_device_update(tz, THERMAL_EVENT_UNSPECIFIED);
52562306a36Sopenharmony_ci}
52662306a36Sopenharmony_ci
52762306a36Sopenharmony_ciint for_each_thermal_governor(int (*cb)(struct thermal_governor *, void *),
52862306a36Sopenharmony_ci			      void *data)
52962306a36Sopenharmony_ci{
53062306a36Sopenharmony_ci	struct thermal_governor *gov;
53162306a36Sopenharmony_ci	int ret = 0;
53262306a36Sopenharmony_ci
53362306a36Sopenharmony_ci	mutex_lock(&thermal_governor_lock);
53462306a36Sopenharmony_ci	list_for_each_entry(gov, &thermal_governor_list, governor_list) {
53562306a36Sopenharmony_ci		ret = cb(gov, data);
53662306a36Sopenharmony_ci		if (ret)
53762306a36Sopenharmony_ci			break;
53862306a36Sopenharmony_ci	}
53962306a36Sopenharmony_ci	mutex_unlock(&thermal_governor_lock);
54062306a36Sopenharmony_ci
54162306a36Sopenharmony_ci	return ret;
54262306a36Sopenharmony_ci}
54362306a36Sopenharmony_ci
54462306a36Sopenharmony_ciint for_each_thermal_cooling_device(int (*cb)(struct thermal_cooling_device *,
54562306a36Sopenharmony_ci					      void *), void *data)
54662306a36Sopenharmony_ci{
54762306a36Sopenharmony_ci	struct thermal_cooling_device *cdev;
54862306a36Sopenharmony_ci	int ret = 0;
54962306a36Sopenharmony_ci
55062306a36Sopenharmony_ci	mutex_lock(&thermal_list_lock);
55162306a36Sopenharmony_ci	list_for_each_entry(cdev, &thermal_cdev_list, node) {
55262306a36Sopenharmony_ci		ret = cb(cdev, data);
55362306a36Sopenharmony_ci		if (ret)
55462306a36Sopenharmony_ci			break;
55562306a36Sopenharmony_ci	}
55662306a36Sopenharmony_ci	mutex_unlock(&thermal_list_lock);
55762306a36Sopenharmony_ci
55862306a36Sopenharmony_ci	return ret;
55962306a36Sopenharmony_ci}
56062306a36Sopenharmony_ci
56162306a36Sopenharmony_ciint for_each_thermal_zone(int (*cb)(struct thermal_zone_device *, void *),
56262306a36Sopenharmony_ci			  void *data)
56362306a36Sopenharmony_ci{
56462306a36Sopenharmony_ci	struct thermal_zone_device *tz;
56562306a36Sopenharmony_ci	int ret = 0;
56662306a36Sopenharmony_ci
56762306a36Sopenharmony_ci	mutex_lock(&thermal_list_lock);
56862306a36Sopenharmony_ci	list_for_each_entry(tz, &thermal_tz_list, node) {
56962306a36Sopenharmony_ci		ret = cb(tz, data);
57062306a36Sopenharmony_ci		if (ret)
57162306a36Sopenharmony_ci			break;
57262306a36Sopenharmony_ci	}
57362306a36Sopenharmony_ci	mutex_unlock(&thermal_list_lock);
57462306a36Sopenharmony_ci
57562306a36Sopenharmony_ci	return ret;
57662306a36Sopenharmony_ci}
57762306a36Sopenharmony_ci
57862306a36Sopenharmony_cistruct thermal_zone_device *thermal_zone_get_by_id(int id)
57962306a36Sopenharmony_ci{
58062306a36Sopenharmony_ci	struct thermal_zone_device *tz, *match = NULL;
58162306a36Sopenharmony_ci
58262306a36Sopenharmony_ci	mutex_lock(&thermal_list_lock);
58362306a36Sopenharmony_ci	list_for_each_entry(tz, &thermal_tz_list, node) {
58462306a36Sopenharmony_ci		if (tz->id == id) {
58562306a36Sopenharmony_ci			match = tz;
58662306a36Sopenharmony_ci			break;
58762306a36Sopenharmony_ci		}
58862306a36Sopenharmony_ci	}
58962306a36Sopenharmony_ci	mutex_unlock(&thermal_list_lock);
59062306a36Sopenharmony_ci
59162306a36Sopenharmony_ci	return match;
59262306a36Sopenharmony_ci}
59362306a36Sopenharmony_ci
59462306a36Sopenharmony_ci/*
59562306a36Sopenharmony_ci * Device management section: cooling devices, zones devices, and binding
59662306a36Sopenharmony_ci *
59762306a36Sopenharmony_ci * Set of functions provided by the thermal core for:
59862306a36Sopenharmony_ci * - cooling devices lifecycle: registration, unregistration,
59962306a36Sopenharmony_ci *				binding, and unbinding.
60062306a36Sopenharmony_ci * - thermal zone devices lifecycle: registration, unregistration,
60162306a36Sopenharmony_ci *				     binding, and unbinding.
60262306a36Sopenharmony_ci */
60362306a36Sopenharmony_ci
60462306a36Sopenharmony_ci/**
60562306a36Sopenharmony_ci * thermal_zone_bind_cooling_device() - bind a cooling device to a thermal zone
60662306a36Sopenharmony_ci * @tz:		pointer to struct thermal_zone_device
60762306a36Sopenharmony_ci * @trip_index:	indicates which trip point the cooling devices is
60862306a36Sopenharmony_ci *		associated with in this thermal zone.
60962306a36Sopenharmony_ci * @cdev:	pointer to struct thermal_cooling_device
61062306a36Sopenharmony_ci * @upper:	the Maximum cooling state for this trip point.
61162306a36Sopenharmony_ci *		THERMAL_NO_LIMIT means no upper limit,
61262306a36Sopenharmony_ci *		and the cooling device can be in max_state.
61362306a36Sopenharmony_ci * @lower:	the Minimum cooling state can be used for this trip point.
61462306a36Sopenharmony_ci *		THERMAL_NO_LIMIT means no lower limit,
61562306a36Sopenharmony_ci *		and the cooling device can be in cooling state 0.
61662306a36Sopenharmony_ci * @weight:	The weight of the cooling device to be bound to the
61762306a36Sopenharmony_ci *		thermal zone. Use THERMAL_WEIGHT_DEFAULT for the
61862306a36Sopenharmony_ci *		default value
61962306a36Sopenharmony_ci *
62062306a36Sopenharmony_ci * This interface function bind a thermal cooling device to the certain trip
62162306a36Sopenharmony_ci * point of a thermal zone device.
62262306a36Sopenharmony_ci * This function is usually called in the thermal zone device .bind callback.
62362306a36Sopenharmony_ci *
62462306a36Sopenharmony_ci * Return: 0 on success, the proper error value otherwise.
62562306a36Sopenharmony_ci */
62662306a36Sopenharmony_ciint thermal_zone_bind_cooling_device(struct thermal_zone_device *tz,
62762306a36Sopenharmony_ci				     int trip_index,
62862306a36Sopenharmony_ci				     struct thermal_cooling_device *cdev,
62962306a36Sopenharmony_ci				     unsigned long upper, unsigned long lower,
63062306a36Sopenharmony_ci				     unsigned int weight)
63162306a36Sopenharmony_ci{
63262306a36Sopenharmony_ci	struct thermal_instance *dev;
63362306a36Sopenharmony_ci	struct thermal_instance *pos;
63462306a36Sopenharmony_ci	struct thermal_zone_device *pos1;
63562306a36Sopenharmony_ci	struct thermal_cooling_device *pos2;
63662306a36Sopenharmony_ci	const struct thermal_trip *trip;
63762306a36Sopenharmony_ci	bool upper_no_limit;
63862306a36Sopenharmony_ci	int result;
63962306a36Sopenharmony_ci
64062306a36Sopenharmony_ci	if (trip_index >= tz->num_trips || trip_index < 0)
64162306a36Sopenharmony_ci		return -EINVAL;
64262306a36Sopenharmony_ci
64362306a36Sopenharmony_ci	trip = &tz->trips[trip_index];
64462306a36Sopenharmony_ci
64562306a36Sopenharmony_ci	list_for_each_entry(pos1, &thermal_tz_list, node) {
64662306a36Sopenharmony_ci		if (pos1 == tz)
64762306a36Sopenharmony_ci			break;
64862306a36Sopenharmony_ci	}
64962306a36Sopenharmony_ci	list_for_each_entry(pos2, &thermal_cdev_list, node) {
65062306a36Sopenharmony_ci		if (pos2 == cdev)
65162306a36Sopenharmony_ci			break;
65262306a36Sopenharmony_ci	}
65362306a36Sopenharmony_ci
65462306a36Sopenharmony_ci	if (tz != pos1 || cdev != pos2)
65562306a36Sopenharmony_ci		return -EINVAL;
65662306a36Sopenharmony_ci
65762306a36Sopenharmony_ci	/* lower default 0, upper default max_state */
65862306a36Sopenharmony_ci	lower = lower == THERMAL_NO_LIMIT ? 0 : lower;
65962306a36Sopenharmony_ci
66062306a36Sopenharmony_ci	if (upper == THERMAL_NO_LIMIT) {
66162306a36Sopenharmony_ci		upper = cdev->max_state;
66262306a36Sopenharmony_ci		upper_no_limit = true;
66362306a36Sopenharmony_ci	} else {
66462306a36Sopenharmony_ci		upper_no_limit = false;
66562306a36Sopenharmony_ci	}
66662306a36Sopenharmony_ci
66762306a36Sopenharmony_ci	if (lower > upper || upper > cdev->max_state)
66862306a36Sopenharmony_ci		return -EINVAL;
66962306a36Sopenharmony_ci
67062306a36Sopenharmony_ci	dev = kzalloc(sizeof(*dev), GFP_KERNEL);
67162306a36Sopenharmony_ci	if (!dev)
67262306a36Sopenharmony_ci		return -ENOMEM;
67362306a36Sopenharmony_ci	dev->tz = tz;
67462306a36Sopenharmony_ci	dev->cdev = cdev;
67562306a36Sopenharmony_ci	dev->trip = trip;
67662306a36Sopenharmony_ci	dev->upper = upper;
67762306a36Sopenharmony_ci	dev->upper_no_limit = upper_no_limit;
67862306a36Sopenharmony_ci	dev->lower = lower;
67962306a36Sopenharmony_ci	dev->target = THERMAL_NO_TARGET;
68062306a36Sopenharmony_ci	dev->weight = weight;
68162306a36Sopenharmony_ci
68262306a36Sopenharmony_ci	result = ida_alloc(&tz->ida, GFP_KERNEL);
68362306a36Sopenharmony_ci	if (result < 0)
68462306a36Sopenharmony_ci		goto free_mem;
68562306a36Sopenharmony_ci
68662306a36Sopenharmony_ci	dev->id = result;
68762306a36Sopenharmony_ci	sprintf(dev->name, "cdev%d", dev->id);
68862306a36Sopenharmony_ci	result =
68962306a36Sopenharmony_ci	    sysfs_create_link(&tz->device.kobj, &cdev->device.kobj, dev->name);
69062306a36Sopenharmony_ci	if (result)
69162306a36Sopenharmony_ci		goto release_ida;
69262306a36Sopenharmony_ci
69362306a36Sopenharmony_ci	snprintf(dev->attr_name, sizeof(dev->attr_name), "cdev%d_trip_point",
69462306a36Sopenharmony_ci		 dev->id);
69562306a36Sopenharmony_ci	sysfs_attr_init(&dev->attr.attr);
69662306a36Sopenharmony_ci	dev->attr.attr.name = dev->attr_name;
69762306a36Sopenharmony_ci	dev->attr.attr.mode = 0444;
69862306a36Sopenharmony_ci	dev->attr.show = trip_point_show;
69962306a36Sopenharmony_ci	result = device_create_file(&tz->device, &dev->attr);
70062306a36Sopenharmony_ci	if (result)
70162306a36Sopenharmony_ci		goto remove_symbol_link;
70262306a36Sopenharmony_ci
70362306a36Sopenharmony_ci	snprintf(dev->weight_attr_name, sizeof(dev->weight_attr_name),
70462306a36Sopenharmony_ci		 "cdev%d_weight", dev->id);
70562306a36Sopenharmony_ci	sysfs_attr_init(&dev->weight_attr.attr);
70662306a36Sopenharmony_ci	dev->weight_attr.attr.name = dev->weight_attr_name;
70762306a36Sopenharmony_ci	dev->weight_attr.attr.mode = S_IWUSR | S_IRUGO;
70862306a36Sopenharmony_ci	dev->weight_attr.show = weight_show;
70962306a36Sopenharmony_ci	dev->weight_attr.store = weight_store;
71062306a36Sopenharmony_ci	result = device_create_file(&tz->device, &dev->weight_attr);
71162306a36Sopenharmony_ci	if (result)
71262306a36Sopenharmony_ci		goto remove_trip_file;
71362306a36Sopenharmony_ci
71462306a36Sopenharmony_ci	mutex_lock(&tz->lock);
71562306a36Sopenharmony_ci	mutex_lock(&cdev->lock);
71662306a36Sopenharmony_ci	list_for_each_entry(pos, &tz->thermal_instances, tz_node)
71762306a36Sopenharmony_ci		if (pos->tz == tz && pos->trip == trip && pos->cdev == cdev) {
71862306a36Sopenharmony_ci			result = -EEXIST;
71962306a36Sopenharmony_ci			break;
72062306a36Sopenharmony_ci		}
72162306a36Sopenharmony_ci	if (!result) {
72262306a36Sopenharmony_ci		list_add_tail(&dev->tz_node, &tz->thermal_instances);
72362306a36Sopenharmony_ci		list_add_tail(&dev->cdev_node, &cdev->thermal_instances);
72462306a36Sopenharmony_ci		atomic_set(&tz->need_update, 1);
72562306a36Sopenharmony_ci	}
72662306a36Sopenharmony_ci	mutex_unlock(&cdev->lock);
72762306a36Sopenharmony_ci	mutex_unlock(&tz->lock);
72862306a36Sopenharmony_ci
72962306a36Sopenharmony_ci	if (!result)
73062306a36Sopenharmony_ci		return 0;
73162306a36Sopenharmony_ci
73262306a36Sopenharmony_ci	device_remove_file(&tz->device, &dev->weight_attr);
73362306a36Sopenharmony_ciremove_trip_file:
73462306a36Sopenharmony_ci	device_remove_file(&tz->device, &dev->attr);
73562306a36Sopenharmony_ciremove_symbol_link:
73662306a36Sopenharmony_ci	sysfs_remove_link(&tz->device.kobj, dev->name);
73762306a36Sopenharmony_cirelease_ida:
73862306a36Sopenharmony_ci	ida_free(&tz->ida, dev->id);
73962306a36Sopenharmony_cifree_mem:
74062306a36Sopenharmony_ci	kfree(dev);
74162306a36Sopenharmony_ci	return result;
74262306a36Sopenharmony_ci}
74362306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(thermal_zone_bind_cooling_device);
74462306a36Sopenharmony_ci
74562306a36Sopenharmony_ci/**
74662306a36Sopenharmony_ci * thermal_zone_unbind_cooling_device() - unbind a cooling device from a
74762306a36Sopenharmony_ci *					  thermal zone.
74862306a36Sopenharmony_ci * @tz:		pointer to a struct thermal_zone_device.
74962306a36Sopenharmony_ci * @trip_index:	indicates which trip point the cooling devices is
75062306a36Sopenharmony_ci *		associated with in this thermal zone.
75162306a36Sopenharmony_ci * @cdev:	pointer to a struct thermal_cooling_device.
75262306a36Sopenharmony_ci *
75362306a36Sopenharmony_ci * This interface function unbind a thermal cooling device from the certain
75462306a36Sopenharmony_ci * trip point of a thermal zone device.
75562306a36Sopenharmony_ci * This function is usually called in the thermal zone device .unbind callback.
75662306a36Sopenharmony_ci *
75762306a36Sopenharmony_ci * Return: 0 on success, the proper error value otherwise.
75862306a36Sopenharmony_ci */
75962306a36Sopenharmony_ciint thermal_zone_unbind_cooling_device(struct thermal_zone_device *tz,
76062306a36Sopenharmony_ci				       int trip_index,
76162306a36Sopenharmony_ci				       struct thermal_cooling_device *cdev)
76262306a36Sopenharmony_ci{
76362306a36Sopenharmony_ci	struct thermal_instance *pos, *next;
76462306a36Sopenharmony_ci	const struct thermal_trip *trip;
76562306a36Sopenharmony_ci
76662306a36Sopenharmony_ci	mutex_lock(&tz->lock);
76762306a36Sopenharmony_ci	mutex_lock(&cdev->lock);
76862306a36Sopenharmony_ci	trip = &tz->trips[trip_index];
76962306a36Sopenharmony_ci	list_for_each_entry_safe(pos, next, &tz->thermal_instances, tz_node) {
77062306a36Sopenharmony_ci		if (pos->tz == tz && pos->trip == trip && pos->cdev == cdev) {
77162306a36Sopenharmony_ci			list_del(&pos->tz_node);
77262306a36Sopenharmony_ci			list_del(&pos->cdev_node);
77362306a36Sopenharmony_ci			mutex_unlock(&cdev->lock);
77462306a36Sopenharmony_ci			mutex_unlock(&tz->lock);
77562306a36Sopenharmony_ci			goto unbind;
77662306a36Sopenharmony_ci		}
77762306a36Sopenharmony_ci	}
77862306a36Sopenharmony_ci	mutex_unlock(&cdev->lock);
77962306a36Sopenharmony_ci	mutex_unlock(&tz->lock);
78062306a36Sopenharmony_ci
78162306a36Sopenharmony_ci	return -ENODEV;
78262306a36Sopenharmony_ci
78362306a36Sopenharmony_ciunbind:
78462306a36Sopenharmony_ci	device_remove_file(&tz->device, &pos->weight_attr);
78562306a36Sopenharmony_ci	device_remove_file(&tz->device, &pos->attr);
78662306a36Sopenharmony_ci	sysfs_remove_link(&tz->device.kobj, pos->name);
78762306a36Sopenharmony_ci	ida_free(&tz->ida, pos->id);
78862306a36Sopenharmony_ci	kfree(pos);
78962306a36Sopenharmony_ci	return 0;
79062306a36Sopenharmony_ci}
79162306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(thermal_zone_unbind_cooling_device);
79262306a36Sopenharmony_ci
79362306a36Sopenharmony_cistatic void thermal_release(struct device *dev)
79462306a36Sopenharmony_ci{
79562306a36Sopenharmony_ci	struct thermal_zone_device *tz;
79662306a36Sopenharmony_ci	struct thermal_cooling_device *cdev;
79762306a36Sopenharmony_ci
79862306a36Sopenharmony_ci	if (!strncmp(dev_name(dev), "thermal_zone",
79962306a36Sopenharmony_ci		     sizeof("thermal_zone") - 1)) {
80062306a36Sopenharmony_ci		tz = to_thermal_zone(dev);
80162306a36Sopenharmony_ci		thermal_zone_destroy_device_groups(tz);
80262306a36Sopenharmony_ci		mutex_destroy(&tz->lock);
80362306a36Sopenharmony_ci		kfree(tz);
80462306a36Sopenharmony_ci	} else if (!strncmp(dev_name(dev), "cooling_device",
80562306a36Sopenharmony_ci			    sizeof("cooling_device") - 1)) {
80662306a36Sopenharmony_ci		cdev = to_cooling_device(dev);
80762306a36Sopenharmony_ci		thermal_cooling_device_destroy_sysfs(cdev);
80862306a36Sopenharmony_ci		kfree(cdev->type);
80962306a36Sopenharmony_ci		ida_free(&thermal_cdev_ida, cdev->id);
81062306a36Sopenharmony_ci		kfree(cdev);
81162306a36Sopenharmony_ci	}
81262306a36Sopenharmony_ci}
81362306a36Sopenharmony_ci
81462306a36Sopenharmony_cistatic struct class *thermal_class;
81562306a36Sopenharmony_ci
81662306a36Sopenharmony_cistatic inline
81762306a36Sopenharmony_civoid print_bind_err_msg(struct thermal_zone_device *tz,
81862306a36Sopenharmony_ci			struct thermal_cooling_device *cdev, int ret)
81962306a36Sopenharmony_ci{
82062306a36Sopenharmony_ci	dev_err(&tz->device, "binding zone %s with cdev %s failed:%d\n",
82162306a36Sopenharmony_ci		tz->type, cdev->type, ret);
82262306a36Sopenharmony_ci}
82362306a36Sopenharmony_ci
82462306a36Sopenharmony_cistatic void bind_cdev(struct thermal_cooling_device *cdev)
82562306a36Sopenharmony_ci{
82662306a36Sopenharmony_ci	int ret;
82762306a36Sopenharmony_ci	struct thermal_zone_device *pos = NULL;
82862306a36Sopenharmony_ci
82962306a36Sopenharmony_ci	list_for_each_entry(pos, &thermal_tz_list, node) {
83062306a36Sopenharmony_ci		if (pos->ops->bind) {
83162306a36Sopenharmony_ci			ret = pos->ops->bind(pos, cdev);
83262306a36Sopenharmony_ci			if (ret)
83362306a36Sopenharmony_ci				print_bind_err_msg(pos, cdev, ret);
83462306a36Sopenharmony_ci		}
83562306a36Sopenharmony_ci	}
83662306a36Sopenharmony_ci}
83762306a36Sopenharmony_ci
83862306a36Sopenharmony_ci/**
83962306a36Sopenharmony_ci * __thermal_cooling_device_register() - register a new thermal cooling device
84062306a36Sopenharmony_ci * @np:		a pointer to a device tree node.
84162306a36Sopenharmony_ci * @type:	the thermal cooling device type.
84262306a36Sopenharmony_ci * @devdata:	device private data.
84362306a36Sopenharmony_ci * @ops:		standard thermal cooling devices callbacks.
84462306a36Sopenharmony_ci *
84562306a36Sopenharmony_ci * This interface function adds a new thermal cooling device (fan/processor/...)
84662306a36Sopenharmony_ci * to /sys/class/thermal/ folder as cooling_device[0-*]. It tries to bind itself
84762306a36Sopenharmony_ci * to all the thermal zone devices registered at the same time.
84862306a36Sopenharmony_ci * It also gives the opportunity to link the cooling device to a device tree
84962306a36Sopenharmony_ci * node, so that it can be bound to a thermal zone created out of device tree.
85062306a36Sopenharmony_ci *
85162306a36Sopenharmony_ci * Return: a pointer to the created struct thermal_cooling_device or an
85262306a36Sopenharmony_ci * ERR_PTR. Caller must check return value with IS_ERR*() helpers.
85362306a36Sopenharmony_ci */
85462306a36Sopenharmony_cistatic struct thermal_cooling_device *
85562306a36Sopenharmony_ci__thermal_cooling_device_register(struct device_node *np,
85662306a36Sopenharmony_ci				  const char *type, void *devdata,
85762306a36Sopenharmony_ci				  const struct thermal_cooling_device_ops *ops)
85862306a36Sopenharmony_ci{
85962306a36Sopenharmony_ci	struct thermal_cooling_device *cdev;
86062306a36Sopenharmony_ci	struct thermal_zone_device *pos = NULL;
86162306a36Sopenharmony_ci	int id, ret;
86262306a36Sopenharmony_ci
86362306a36Sopenharmony_ci	if (!ops || !ops->get_max_state || !ops->get_cur_state ||
86462306a36Sopenharmony_ci	    !ops->set_cur_state)
86562306a36Sopenharmony_ci		return ERR_PTR(-EINVAL);
86662306a36Sopenharmony_ci
86762306a36Sopenharmony_ci	if (!thermal_class)
86862306a36Sopenharmony_ci		return ERR_PTR(-ENODEV);
86962306a36Sopenharmony_ci
87062306a36Sopenharmony_ci	cdev = kzalloc(sizeof(*cdev), GFP_KERNEL);
87162306a36Sopenharmony_ci	if (!cdev)
87262306a36Sopenharmony_ci		return ERR_PTR(-ENOMEM);
87362306a36Sopenharmony_ci
87462306a36Sopenharmony_ci	ret = ida_alloc(&thermal_cdev_ida, GFP_KERNEL);
87562306a36Sopenharmony_ci	if (ret < 0)
87662306a36Sopenharmony_ci		goto out_kfree_cdev;
87762306a36Sopenharmony_ci	cdev->id = ret;
87862306a36Sopenharmony_ci	id = ret;
87962306a36Sopenharmony_ci
88062306a36Sopenharmony_ci	cdev->type = kstrdup(type ? type : "", GFP_KERNEL);
88162306a36Sopenharmony_ci	if (!cdev->type) {
88262306a36Sopenharmony_ci		ret = -ENOMEM;
88362306a36Sopenharmony_ci		goto out_ida_remove;
88462306a36Sopenharmony_ci	}
88562306a36Sopenharmony_ci
88662306a36Sopenharmony_ci	mutex_init(&cdev->lock);
88762306a36Sopenharmony_ci	INIT_LIST_HEAD(&cdev->thermal_instances);
88862306a36Sopenharmony_ci	cdev->np = np;
88962306a36Sopenharmony_ci	cdev->ops = ops;
89062306a36Sopenharmony_ci	cdev->updated = false;
89162306a36Sopenharmony_ci	cdev->device.class = thermal_class;
89262306a36Sopenharmony_ci	cdev->devdata = devdata;
89362306a36Sopenharmony_ci
89462306a36Sopenharmony_ci	ret = cdev->ops->get_max_state(cdev, &cdev->max_state);
89562306a36Sopenharmony_ci	if (ret)
89662306a36Sopenharmony_ci		goto out_cdev_type;
89762306a36Sopenharmony_ci
89862306a36Sopenharmony_ci	thermal_cooling_device_setup_sysfs(cdev);
89962306a36Sopenharmony_ci
90062306a36Sopenharmony_ci	ret = dev_set_name(&cdev->device, "cooling_device%d", cdev->id);
90162306a36Sopenharmony_ci	if (ret)
90262306a36Sopenharmony_ci		goto out_cooling_dev;
90362306a36Sopenharmony_ci
90462306a36Sopenharmony_ci	ret = device_register(&cdev->device);
90562306a36Sopenharmony_ci	if (ret) {
90662306a36Sopenharmony_ci		/* thermal_release() handles rest of the cleanup */
90762306a36Sopenharmony_ci		put_device(&cdev->device);
90862306a36Sopenharmony_ci		return ERR_PTR(ret);
90962306a36Sopenharmony_ci	}
91062306a36Sopenharmony_ci
91162306a36Sopenharmony_ci	/* Add 'this' new cdev to the global cdev list */
91262306a36Sopenharmony_ci	mutex_lock(&thermal_list_lock);
91362306a36Sopenharmony_ci
91462306a36Sopenharmony_ci	list_add(&cdev->node, &thermal_cdev_list);
91562306a36Sopenharmony_ci
91662306a36Sopenharmony_ci	/* Update binding information for 'this' new cdev */
91762306a36Sopenharmony_ci	bind_cdev(cdev);
91862306a36Sopenharmony_ci
91962306a36Sopenharmony_ci	list_for_each_entry(pos, &thermal_tz_list, node)
92062306a36Sopenharmony_ci		if (atomic_cmpxchg(&pos->need_update, 1, 0))
92162306a36Sopenharmony_ci			thermal_zone_device_update(pos,
92262306a36Sopenharmony_ci						   THERMAL_EVENT_UNSPECIFIED);
92362306a36Sopenharmony_ci
92462306a36Sopenharmony_ci	mutex_unlock(&thermal_list_lock);
92562306a36Sopenharmony_ci
92662306a36Sopenharmony_ci	return cdev;
92762306a36Sopenharmony_ci
92862306a36Sopenharmony_ciout_cooling_dev:
92962306a36Sopenharmony_ci	thermal_cooling_device_destroy_sysfs(cdev);
93062306a36Sopenharmony_ciout_cdev_type:
93162306a36Sopenharmony_ci	kfree(cdev->type);
93262306a36Sopenharmony_ciout_ida_remove:
93362306a36Sopenharmony_ci	ida_free(&thermal_cdev_ida, id);
93462306a36Sopenharmony_ciout_kfree_cdev:
93562306a36Sopenharmony_ci	kfree(cdev);
93662306a36Sopenharmony_ci	return ERR_PTR(ret);
93762306a36Sopenharmony_ci}
93862306a36Sopenharmony_ci
93962306a36Sopenharmony_ci/**
94062306a36Sopenharmony_ci * thermal_cooling_device_register() - register a new thermal cooling device
94162306a36Sopenharmony_ci * @type:	the thermal cooling device type.
94262306a36Sopenharmony_ci * @devdata:	device private data.
94362306a36Sopenharmony_ci * @ops:		standard thermal cooling devices callbacks.
94462306a36Sopenharmony_ci *
94562306a36Sopenharmony_ci * This interface function adds a new thermal cooling device (fan/processor/...)
94662306a36Sopenharmony_ci * to /sys/class/thermal/ folder as cooling_device[0-*]. It tries to bind itself
94762306a36Sopenharmony_ci * to all the thermal zone devices registered at the same time.
94862306a36Sopenharmony_ci *
94962306a36Sopenharmony_ci * Return: a pointer to the created struct thermal_cooling_device or an
95062306a36Sopenharmony_ci * ERR_PTR. Caller must check return value with IS_ERR*() helpers.
95162306a36Sopenharmony_ci */
95262306a36Sopenharmony_cistruct thermal_cooling_device *
95362306a36Sopenharmony_cithermal_cooling_device_register(const char *type, void *devdata,
95462306a36Sopenharmony_ci				const struct thermal_cooling_device_ops *ops)
95562306a36Sopenharmony_ci{
95662306a36Sopenharmony_ci	return __thermal_cooling_device_register(NULL, type, devdata, ops);
95762306a36Sopenharmony_ci}
95862306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(thermal_cooling_device_register);
95962306a36Sopenharmony_ci
96062306a36Sopenharmony_ci/**
96162306a36Sopenharmony_ci * thermal_of_cooling_device_register() - register an OF thermal cooling device
96262306a36Sopenharmony_ci * @np:		a pointer to a device tree node.
96362306a36Sopenharmony_ci * @type:	the thermal cooling device type.
96462306a36Sopenharmony_ci * @devdata:	device private data.
96562306a36Sopenharmony_ci * @ops:		standard thermal cooling devices callbacks.
96662306a36Sopenharmony_ci *
96762306a36Sopenharmony_ci * This function will register a cooling device with device tree node reference.
96862306a36Sopenharmony_ci * This interface function adds a new thermal cooling device (fan/processor/...)
96962306a36Sopenharmony_ci * to /sys/class/thermal/ folder as cooling_device[0-*]. It tries to bind itself
97062306a36Sopenharmony_ci * to all the thermal zone devices registered at the same time.
97162306a36Sopenharmony_ci *
97262306a36Sopenharmony_ci * Return: a pointer to the created struct thermal_cooling_device or an
97362306a36Sopenharmony_ci * ERR_PTR. Caller must check return value with IS_ERR*() helpers.
97462306a36Sopenharmony_ci */
97562306a36Sopenharmony_cistruct thermal_cooling_device *
97662306a36Sopenharmony_cithermal_of_cooling_device_register(struct device_node *np,
97762306a36Sopenharmony_ci				   const char *type, void *devdata,
97862306a36Sopenharmony_ci				   const struct thermal_cooling_device_ops *ops)
97962306a36Sopenharmony_ci{
98062306a36Sopenharmony_ci	return __thermal_cooling_device_register(np, type, devdata, ops);
98162306a36Sopenharmony_ci}
98262306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(thermal_of_cooling_device_register);
98362306a36Sopenharmony_ci
98462306a36Sopenharmony_cistatic void thermal_cooling_device_release(struct device *dev, void *res)
98562306a36Sopenharmony_ci{
98662306a36Sopenharmony_ci	thermal_cooling_device_unregister(
98762306a36Sopenharmony_ci				*(struct thermal_cooling_device **)res);
98862306a36Sopenharmony_ci}
98962306a36Sopenharmony_ci
99062306a36Sopenharmony_ci/**
99162306a36Sopenharmony_ci * devm_thermal_of_cooling_device_register() - register an OF thermal cooling
99262306a36Sopenharmony_ci *					       device
99362306a36Sopenharmony_ci * @dev:	a valid struct device pointer of a sensor device.
99462306a36Sopenharmony_ci * @np:		a pointer to a device tree node.
99562306a36Sopenharmony_ci * @type:	the thermal cooling device type.
99662306a36Sopenharmony_ci * @devdata:	device private data.
99762306a36Sopenharmony_ci * @ops:	standard thermal cooling devices callbacks.
99862306a36Sopenharmony_ci *
99962306a36Sopenharmony_ci * This function will register a cooling device with device tree node reference.
100062306a36Sopenharmony_ci * This interface function adds a new thermal cooling device (fan/processor/...)
100162306a36Sopenharmony_ci * to /sys/class/thermal/ folder as cooling_device[0-*]. It tries to bind itself
100262306a36Sopenharmony_ci * to all the thermal zone devices registered at the same time.
100362306a36Sopenharmony_ci *
100462306a36Sopenharmony_ci * Return: a pointer to the created struct thermal_cooling_device or an
100562306a36Sopenharmony_ci * ERR_PTR. Caller must check return value with IS_ERR*() helpers.
100662306a36Sopenharmony_ci */
100762306a36Sopenharmony_cistruct thermal_cooling_device *
100862306a36Sopenharmony_cidevm_thermal_of_cooling_device_register(struct device *dev,
100962306a36Sopenharmony_ci				struct device_node *np,
101062306a36Sopenharmony_ci				char *type, void *devdata,
101162306a36Sopenharmony_ci				const struct thermal_cooling_device_ops *ops)
101262306a36Sopenharmony_ci{
101362306a36Sopenharmony_ci	struct thermal_cooling_device **ptr, *tcd;
101462306a36Sopenharmony_ci
101562306a36Sopenharmony_ci	ptr = devres_alloc(thermal_cooling_device_release, sizeof(*ptr),
101662306a36Sopenharmony_ci			   GFP_KERNEL);
101762306a36Sopenharmony_ci	if (!ptr)
101862306a36Sopenharmony_ci		return ERR_PTR(-ENOMEM);
101962306a36Sopenharmony_ci
102062306a36Sopenharmony_ci	tcd = __thermal_cooling_device_register(np, type, devdata, ops);
102162306a36Sopenharmony_ci	if (IS_ERR(tcd)) {
102262306a36Sopenharmony_ci		devres_free(ptr);
102362306a36Sopenharmony_ci		return tcd;
102462306a36Sopenharmony_ci	}
102562306a36Sopenharmony_ci
102662306a36Sopenharmony_ci	*ptr = tcd;
102762306a36Sopenharmony_ci	devres_add(dev, ptr);
102862306a36Sopenharmony_ci
102962306a36Sopenharmony_ci	return tcd;
103062306a36Sopenharmony_ci}
103162306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(devm_thermal_of_cooling_device_register);
103262306a36Sopenharmony_ci
103362306a36Sopenharmony_cistatic bool thermal_cooling_device_present(struct thermal_cooling_device *cdev)
103462306a36Sopenharmony_ci{
103562306a36Sopenharmony_ci	struct thermal_cooling_device *pos = NULL;
103662306a36Sopenharmony_ci
103762306a36Sopenharmony_ci	list_for_each_entry(pos, &thermal_cdev_list, node) {
103862306a36Sopenharmony_ci		if (pos == cdev)
103962306a36Sopenharmony_ci			return true;
104062306a36Sopenharmony_ci	}
104162306a36Sopenharmony_ci
104262306a36Sopenharmony_ci	return false;
104362306a36Sopenharmony_ci}
104462306a36Sopenharmony_ci
104562306a36Sopenharmony_ci/**
104662306a36Sopenharmony_ci * thermal_cooling_device_update - Update a cooling device object
104762306a36Sopenharmony_ci * @cdev: Target cooling device.
104862306a36Sopenharmony_ci *
104962306a36Sopenharmony_ci * Update @cdev to reflect a change of the underlying hardware or platform.
105062306a36Sopenharmony_ci *
105162306a36Sopenharmony_ci * Must be called when the maximum cooling state of @cdev becomes invalid and so
105262306a36Sopenharmony_ci * its .get_max_state() callback needs to be run to produce the new maximum
105362306a36Sopenharmony_ci * cooling state value.
105462306a36Sopenharmony_ci */
105562306a36Sopenharmony_civoid thermal_cooling_device_update(struct thermal_cooling_device *cdev)
105662306a36Sopenharmony_ci{
105762306a36Sopenharmony_ci	struct thermal_instance *ti;
105862306a36Sopenharmony_ci	unsigned long state;
105962306a36Sopenharmony_ci
106062306a36Sopenharmony_ci	if (IS_ERR_OR_NULL(cdev))
106162306a36Sopenharmony_ci		return;
106262306a36Sopenharmony_ci
106362306a36Sopenharmony_ci	/*
106462306a36Sopenharmony_ci	 * Hold thermal_list_lock throughout the update to prevent the device
106562306a36Sopenharmony_ci	 * from going away while being updated.
106662306a36Sopenharmony_ci	 */
106762306a36Sopenharmony_ci	mutex_lock(&thermal_list_lock);
106862306a36Sopenharmony_ci
106962306a36Sopenharmony_ci	if (!thermal_cooling_device_present(cdev))
107062306a36Sopenharmony_ci		goto unlock_list;
107162306a36Sopenharmony_ci
107262306a36Sopenharmony_ci	/*
107362306a36Sopenharmony_ci	 * Update under the cdev lock to prevent the state from being set beyond
107462306a36Sopenharmony_ci	 * the new limit concurrently.
107562306a36Sopenharmony_ci	 */
107662306a36Sopenharmony_ci	mutex_lock(&cdev->lock);
107762306a36Sopenharmony_ci
107862306a36Sopenharmony_ci	if (cdev->ops->get_max_state(cdev, &cdev->max_state))
107962306a36Sopenharmony_ci		goto unlock;
108062306a36Sopenharmony_ci
108162306a36Sopenharmony_ci	thermal_cooling_device_stats_reinit(cdev);
108262306a36Sopenharmony_ci
108362306a36Sopenharmony_ci	list_for_each_entry(ti, &cdev->thermal_instances, cdev_node) {
108462306a36Sopenharmony_ci		if (ti->upper == cdev->max_state)
108562306a36Sopenharmony_ci			continue;
108662306a36Sopenharmony_ci
108762306a36Sopenharmony_ci		if (ti->upper < cdev->max_state) {
108862306a36Sopenharmony_ci			if (ti->upper_no_limit)
108962306a36Sopenharmony_ci				ti->upper = cdev->max_state;
109062306a36Sopenharmony_ci
109162306a36Sopenharmony_ci			continue;
109262306a36Sopenharmony_ci		}
109362306a36Sopenharmony_ci
109462306a36Sopenharmony_ci		ti->upper = cdev->max_state;
109562306a36Sopenharmony_ci		if (ti->lower > ti->upper)
109662306a36Sopenharmony_ci			ti->lower = ti->upper;
109762306a36Sopenharmony_ci
109862306a36Sopenharmony_ci		if (ti->target == THERMAL_NO_TARGET)
109962306a36Sopenharmony_ci			continue;
110062306a36Sopenharmony_ci
110162306a36Sopenharmony_ci		if (ti->target > ti->upper)
110262306a36Sopenharmony_ci			ti->target = ti->upper;
110362306a36Sopenharmony_ci	}
110462306a36Sopenharmony_ci
110562306a36Sopenharmony_ci	if (cdev->ops->get_cur_state(cdev, &state) || state > cdev->max_state)
110662306a36Sopenharmony_ci		goto unlock;
110762306a36Sopenharmony_ci
110862306a36Sopenharmony_ci	thermal_cooling_device_stats_update(cdev, state);
110962306a36Sopenharmony_ci
111062306a36Sopenharmony_ciunlock:
111162306a36Sopenharmony_ci	mutex_unlock(&cdev->lock);
111262306a36Sopenharmony_ci
111362306a36Sopenharmony_ciunlock_list:
111462306a36Sopenharmony_ci	mutex_unlock(&thermal_list_lock);
111562306a36Sopenharmony_ci}
111662306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(thermal_cooling_device_update);
111762306a36Sopenharmony_ci
111862306a36Sopenharmony_ci/**
111962306a36Sopenharmony_ci * thermal_cooling_device_unregister - removes a thermal cooling device
112062306a36Sopenharmony_ci * @cdev:	the thermal cooling device to remove.
112162306a36Sopenharmony_ci *
112262306a36Sopenharmony_ci * thermal_cooling_device_unregister() must be called when a registered
112362306a36Sopenharmony_ci * thermal cooling device is no longer needed.
112462306a36Sopenharmony_ci */
112562306a36Sopenharmony_civoid thermal_cooling_device_unregister(struct thermal_cooling_device *cdev)
112662306a36Sopenharmony_ci{
112762306a36Sopenharmony_ci	struct thermal_zone_device *tz;
112862306a36Sopenharmony_ci
112962306a36Sopenharmony_ci	if (!cdev)
113062306a36Sopenharmony_ci		return;
113162306a36Sopenharmony_ci
113262306a36Sopenharmony_ci	mutex_lock(&thermal_list_lock);
113362306a36Sopenharmony_ci
113462306a36Sopenharmony_ci	if (!thermal_cooling_device_present(cdev)) {
113562306a36Sopenharmony_ci		mutex_unlock(&thermal_list_lock);
113662306a36Sopenharmony_ci		return;
113762306a36Sopenharmony_ci	}
113862306a36Sopenharmony_ci
113962306a36Sopenharmony_ci	list_del(&cdev->node);
114062306a36Sopenharmony_ci
114162306a36Sopenharmony_ci	/* Unbind all thermal zones associated with 'this' cdev */
114262306a36Sopenharmony_ci	list_for_each_entry(tz, &thermal_tz_list, node) {
114362306a36Sopenharmony_ci		if (tz->ops->unbind)
114462306a36Sopenharmony_ci			tz->ops->unbind(tz, cdev);
114562306a36Sopenharmony_ci	}
114662306a36Sopenharmony_ci
114762306a36Sopenharmony_ci	mutex_unlock(&thermal_list_lock);
114862306a36Sopenharmony_ci
114962306a36Sopenharmony_ci	device_unregister(&cdev->device);
115062306a36Sopenharmony_ci}
115162306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(thermal_cooling_device_unregister);
115262306a36Sopenharmony_ci
115362306a36Sopenharmony_cistatic void bind_tz(struct thermal_zone_device *tz)
115462306a36Sopenharmony_ci{
115562306a36Sopenharmony_ci	int ret;
115662306a36Sopenharmony_ci	struct thermal_cooling_device *pos = NULL;
115762306a36Sopenharmony_ci
115862306a36Sopenharmony_ci	if (!tz->ops->bind)
115962306a36Sopenharmony_ci		return;
116062306a36Sopenharmony_ci
116162306a36Sopenharmony_ci	mutex_lock(&thermal_list_lock);
116262306a36Sopenharmony_ci
116362306a36Sopenharmony_ci	list_for_each_entry(pos, &thermal_cdev_list, node) {
116462306a36Sopenharmony_ci		ret = tz->ops->bind(tz, pos);
116562306a36Sopenharmony_ci		if (ret)
116662306a36Sopenharmony_ci			print_bind_err_msg(tz, pos, ret);
116762306a36Sopenharmony_ci	}
116862306a36Sopenharmony_ci
116962306a36Sopenharmony_ci	mutex_unlock(&thermal_list_lock);
117062306a36Sopenharmony_ci}
117162306a36Sopenharmony_ci
117262306a36Sopenharmony_cistatic void thermal_set_delay_jiffies(unsigned long *delay_jiffies, int delay_ms)
117362306a36Sopenharmony_ci{
117462306a36Sopenharmony_ci	*delay_jiffies = msecs_to_jiffies(delay_ms);
117562306a36Sopenharmony_ci	if (delay_ms > 1000)
117662306a36Sopenharmony_ci		*delay_jiffies = round_jiffies(*delay_jiffies);
117762306a36Sopenharmony_ci}
117862306a36Sopenharmony_ci
117962306a36Sopenharmony_ciint thermal_zone_get_crit_temp(struct thermal_zone_device *tz, int *temp)
118062306a36Sopenharmony_ci{
118162306a36Sopenharmony_ci	int i, ret = -EINVAL;
118262306a36Sopenharmony_ci
118362306a36Sopenharmony_ci	if (tz->ops->get_crit_temp)
118462306a36Sopenharmony_ci		return tz->ops->get_crit_temp(tz, temp);
118562306a36Sopenharmony_ci
118662306a36Sopenharmony_ci	if (!tz->trips)
118762306a36Sopenharmony_ci		return -EINVAL;
118862306a36Sopenharmony_ci
118962306a36Sopenharmony_ci	mutex_lock(&tz->lock);
119062306a36Sopenharmony_ci
119162306a36Sopenharmony_ci	for (i = 0; i < tz->num_trips; i++) {
119262306a36Sopenharmony_ci		if (tz->trips[i].type == THERMAL_TRIP_CRITICAL) {
119362306a36Sopenharmony_ci			*temp = tz->trips[i].temperature;
119462306a36Sopenharmony_ci			ret = 0;
119562306a36Sopenharmony_ci			break;
119662306a36Sopenharmony_ci		}
119762306a36Sopenharmony_ci	}
119862306a36Sopenharmony_ci
119962306a36Sopenharmony_ci	mutex_unlock(&tz->lock);
120062306a36Sopenharmony_ci
120162306a36Sopenharmony_ci	return ret;
120262306a36Sopenharmony_ci}
120362306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(thermal_zone_get_crit_temp);
120462306a36Sopenharmony_ci
120562306a36Sopenharmony_ci/**
120662306a36Sopenharmony_ci * thermal_zone_device_register_with_trips() - register a new thermal zone device
120762306a36Sopenharmony_ci * @type:	the thermal zone device type
120862306a36Sopenharmony_ci * @trips:	a pointer to an array of thermal trips
120962306a36Sopenharmony_ci * @num_trips:	the number of trip points the thermal zone support
121062306a36Sopenharmony_ci * @mask:	a bit string indicating the writeablility of trip points
121162306a36Sopenharmony_ci * @devdata:	private device data
121262306a36Sopenharmony_ci * @ops:	standard thermal zone device callbacks
121362306a36Sopenharmony_ci * @tzp:	thermal zone platform parameters
121462306a36Sopenharmony_ci * @passive_delay: number of milliseconds to wait between polls when
121562306a36Sopenharmony_ci *		   performing passive cooling
121662306a36Sopenharmony_ci * @polling_delay: number of milliseconds to wait between polls when checking
121762306a36Sopenharmony_ci *		   whether trip points have been crossed (0 for interrupt
121862306a36Sopenharmony_ci *		   driven systems)
121962306a36Sopenharmony_ci *
122062306a36Sopenharmony_ci * This interface function adds a new thermal zone device (sensor) to
122162306a36Sopenharmony_ci * /sys/class/thermal folder as thermal_zone[0-*]. It tries to bind all the
122262306a36Sopenharmony_ci * thermal cooling devices registered at the same time.
122362306a36Sopenharmony_ci * thermal_zone_device_unregister() must be called when the device is no
122462306a36Sopenharmony_ci * longer needed. The passive cooling depends on the .get_trend() return value.
122562306a36Sopenharmony_ci *
122662306a36Sopenharmony_ci * Return: a pointer to the created struct thermal_zone_device or an
122762306a36Sopenharmony_ci * in case of error, an ERR_PTR. Caller must check return value with
122862306a36Sopenharmony_ci * IS_ERR*() helpers.
122962306a36Sopenharmony_ci */
123062306a36Sopenharmony_cistruct thermal_zone_device *
123162306a36Sopenharmony_cithermal_zone_device_register_with_trips(const char *type, struct thermal_trip *trips, int num_trips, int mask,
123262306a36Sopenharmony_ci					void *devdata, struct thermal_zone_device_ops *ops,
123362306a36Sopenharmony_ci					const struct thermal_zone_params *tzp, int passive_delay,
123462306a36Sopenharmony_ci					int polling_delay)
123562306a36Sopenharmony_ci{
123662306a36Sopenharmony_ci	struct thermal_zone_device *tz;
123762306a36Sopenharmony_ci	int id;
123862306a36Sopenharmony_ci	int result;
123962306a36Sopenharmony_ci	int count;
124062306a36Sopenharmony_ci	struct thermal_governor *governor;
124162306a36Sopenharmony_ci
124262306a36Sopenharmony_ci	if (!type || strlen(type) == 0) {
124362306a36Sopenharmony_ci		pr_err("No thermal zone type defined\n");
124462306a36Sopenharmony_ci		return ERR_PTR(-EINVAL);
124562306a36Sopenharmony_ci	}
124662306a36Sopenharmony_ci
124762306a36Sopenharmony_ci	if (strlen(type) >= THERMAL_NAME_LENGTH) {
124862306a36Sopenharmony_ci		pr_err("Thermal zone name (%s) too long, should be under %d chars\n",
124962306a36Sopenharmony_ci		       type, THERMAL_NAME_LENGTH);
125062306a36Sopenharmony_ci		return ERR_PTR(-EINVAL);
125162306a36Sopenharmony_ci	}
125262306a36Sopenharmony_ci
125362306a36Sopenharmony_ci	/*
125462306a36Sopenharmony_ci	 * Max trip count can't exceed 31 as the "mask >> num_trips" condition.
125562306a36Sopenharmony_ci	 * For example, shifting by 32 will result in compiler warning:
125662306a36Sopenharmony_ci	 * warning: right shift count >= width of type [-Wshift-count- overflow]
125762306a36Sopenharmony_ci	 *
125862306a36Sopenharmony_ci	 * Also "mask >> num_trips" will always be true with 32 bit shift.
125962306a36Sopenharmony_ci	 * E.g. mask = 0x80000000 for trip id 31 to be RW. Then
126062306a36Sopenharmony_ci	 * mask >> 32 = 0x80000000
126162306a36Sopenharmony_ci	 * This will result in failure for the below condition.
126262306a36Sopenharmony_ci	 *
126362306a36Sopenharmony_ci	 * Check will be true when the bit 31 of the mask is set.
126462306a36Sopenharmony_ci	 * 32 bit shift will cause overflow of 4 byte integer.
126562306a36Sopenharmony_ci	 */
126662306a36Sopenharmony_ci	if (num_trips > (BITS_PER_TYPE(int) - 1) || num_trips < 0 || mask >> num_trips) {
126762306a36Sopenharmony_ci		pr_err("Incorrect number of thermal trips\n");
126862306a36Sopenharmony_ci		return ERR_PTR(-EINVAL);
126962306a36Sopenharmony_ci	}
127062306a36Sopenharmony_ci
127162306a36Sopenharmony_ci	if (!ops) {
127262306a36Sopenharmony_ci		pr_err("Thermal zone device ops not defined\n");
127362306a36Sopenharmony_ci		return ERR_PTR(-EINVAL);
127462306a36Sopenharmony_ci	}
127562306a36Sopenharmony_ci
127662306a36Sopenharmony_ci	if (num_trips > 0 && !trips)
127762306a36Sopenharmony_ci		return ERR_PTR(-EINVAL);
127862306a36Sopenharmony_ci
127962306a36Sopenharmony_ci	if (!thermal_class)
128062306a36Sopenharmony_ci		return ERR_PTR(-ENODEV);
128162306a36Sopenharmony_ci
128262306a36Sopenharmony_ci	tz = kzalloc(sizeof(*tz), GFP_KERNEL);
128362306a36Sopenharmony_ci	if (!tz)
128462306a36Sopenharmony_ci		return ERR_PTR(-ENOMEM);
128562306a36Sopenharmony_ci
128662306a36Sopenharmony_ci	if (tzp) {
128762306a36Sopenharmony_ci		tz->tzp = kmemdup(tzp, sizeof(*tzp), GFP_KERNEL);
128862306a36Sopenharmony_ci		if (!tz->tzp) {
128962306a36Sopenharmony_ci			result = -ENOMEM;
129062306a36Sopenharmony_ci			goto free_tz;
129162306a36Sopenharmony_ci		}
129262306a36Sopenharmony_ci	}
129362306a36Sopenharmony_ci
129462306a36Sopenharmony_ci	INIT_LIST_HEAD(&tz->thermal_instances);
129562306a36Sopenharmony_ci	ida_init(&tz->ida);
129662306a36Sopenharmony_ci	mutex_init(&tz->lock);
129762306a36Sopenharmony_ci	id = ida_alloc(&thermal_tz_ida, GFP_KERNEL);
129862306a36Sopenharmony_ci	if (id < 0) {
129962306a36Sopenharmony_ci		result = id;
130062306a36Sopenharmony_ci		goto free_tzp;
130162306a36Sopenharmony_ci	}
130262306a36Sopenharmony_ci
130362306a36Sopenharmony_ci	tz->id = id;
130462306a36Sopenharmony_ci	strscpy(tz->type, type, sizeof(tz->type));
130562306a36Sopenharmony_ci
130662306a36Sopenharmony_ci	if (!ops->critical)
130762306a36Sopenharmony_ci		ops->critical = thermal_zone_device_critical;
130862306a36Sopenharmony_ci
130962306a36Sopenharmony_ci	tz->ops = ops;
131062306a36Sopenharmony_ci	tz->device.class = thermal_class;
131162306a36Sopenharmony_ci	tz->devdata = devdata;
131262306a36Sopenharmony_ci	tz->trips = trips;
131362306a36Sopenharmony_ci	tz->num_trips = num_trips;
131462306a36Sopenharmony_ci
131562306a36Sopenharmony_ci	thermal_set_delay_jiffies(&tz->passive_delay_jiffies, passive_delay);
131662306a36Sopenharmony_ci	thermal_set_delay_jiffies(&tz->polling_delay_jiffies, polling_delay);
131762306a36Sopenharmony_ci
131862306a36Sopenharmony_ci	/* sys I/F */
131962306a36Sopenharmony_ci	/* Add nodes that are always present via .groups */
132062306a36Sopenharmony_ci	result = thermal_zone_create_device_groups(tz, mask);
132162306a36Sopenharmony_ci	if (result)
132262306a36Sopenharmony_ci		goto remove_id;
132362306a36Sopenharmony_ci
132462306a36Sopenharmony_ci	/* A new thermal zone needs to be updated anyway. */
132562306a36Sopenharmony_ci	atomic_set(&tz->need_update, 1);
132662306a36Sopenharmony_ci
132762306a36Sopenharmony_ci	result = dev_set_name(&tz->device, "thermal_zone%d", tz->id);
132862306a36Sopenharmony_ci	if (result) {
132962306a36Sopenharmony_ci		thermal_zone_destroy_device_groups(tz);
133062306a36Sopenharmony_ci		goto remove_id;
133162306a36Sopenharmony_ci	}
133262306a36Sopenharmony_ci	result = device_register(&tz->device);
133362306a36Sopenharmony_ci	if (result)
133462306a36Sopenharmony_ci		goto release_device;
133562306a36Sopenharmony_ci
133662306a36Sopenharmony_ci	for (count = 0; count < num_trips; count++) {
133762306a36Sopenharmony_ci		struct thermal_trip trip;
133862306a36Sopenharmony_ci
133962306a36Sopenharmony_ci		result = thermal_zone_get_trip(tz, count, &trip);
134062306a36Sopenharmony_ci		if (result || !trip.temperature)
134162306a36Sopenharmony_ci			set_bit(count, &tz->trips_disabled);
134262306a36Sopenharmony_ci	}
134362306a36Sopenharmony_ci
134462306a36Sopenharmony_ci	/* Update 'this' zone's governor information */
134562306a36Sopenharmony_ci	mutex_lock(&thermal_governor_lock);
134662306a36Sopenharmony_ci
134762306a36Sopenharmony_ci	if (tz->tzp)
134862306a36Sopenharmony_ci		governor = __find_governor(tz->tzp->governor_name);
134962306a36Sopenharmony_ci	else
135062306a36Sopenharmony_ci		governor = def_governor;
135162306a36Sopenharmony_ci
135262306a36Sopenharmony_ci	result = thermal_set_governor(tz, governor);
135362306a36Sopenharmony_ci	if (result) {
135462306a36Sopenharmony_ci		mutex_unlock(&thermal_governor_lock);
135562306a36Sopenharmony_ci		goto unregister;
135662306a36Sopenharmony_ci	}
135762306a36Sopenharmony_ci
135862306a36Sopenharmony_ci	mutex_unlock(&thermal_governor_lock);
135962306a36Sopenharmony_ci
136062306a36Sopenharmony_ci	if (!tz->tzp || !tz->tzp->no_hwmon) {
136162306a36Sopenharmony_ci		result = thermal_add_hwmon_sysfs(tz);
136262306a36Sopenharmony_ci		if (result)
136362306a36Sopenharmony_ci			goto unregister;
136462306a36Sopenharmony_ci	}
136562306a36Sopenharmony_ci
136662306a36Sopenharmony_ci	mutex_lock(&thermal_list_lock);
136762306a36Sopenharmony_ci	list_add_tail(&tz->node, &thermal_tz_list);
136862306a36Sopenharmony_ci	mutex_unlock(&thermal_list_lock);
136962306a36Sopenharmony_ci
137062306a36Sopenharmony_ci	/* Bind cooling devices for this zone */
137162306a36Sopenharmony_ci	bind_tz(tz);
137262306a36Sopenharmony_ci
137362306a36Sopenharmony_ci	INIT_DELAYED_WORK(&tz->poll_queue, thermal_zone_device_check);
137462306a36Sopenharmony_ci
137562306a36Sopenharmony_ci	thermal_zone_device_init(tz);
137662306a36Sopenharmony_ci	/* Update the new thermal zone and mark it as already updated. */
137762306a36Sopenharmony_ci	if (atomic_cmpxchg(&tz->need_update, 1, 0))
137862306a36Sopenharmony_ci		thermal_zone_device_update(tz, THERMAL_EVENT_UNSPECIFIED);
137962306a36Sopenharmony_ci
138062306a36Sopenharmony_ci	thermal_notify_tz_create(tz->id, tz->type);
138162306a36Sopenharmony_ci
138262306a36Sopenharmony_ci	return tz;
138362306a36Sopenharmony_ci
138462306a36Sopenharmony_ciunregister:
138562306a36Sopenharmony_ci	device_del(&tz->device);
138662306a36Sopenharmony_cirelease_device:
138762306a36Sopenharmony_ci	put_device(&tz->device);
138862306a36Sopenharmony_ciremove_id:
138962306a36Sopenharmony_ci	ida_free(&thermal_tz_ida, id);
139062306a36Sopenharmony_cifree_tzp:
139162306a36Sopenharmony_ci	kfree(tz->tzp);
139262306a36Sopenharmony_cifree_tz:
139362306a36Sopenharmony_ci	kfree(tz);
139462306a36Sopenharmony_ci	return ERR_PTR(result);
139562306a36Sopenharmony_ci}
139662306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(thermal_zone_device_register_with_trips);
139762306a36Sopenharmony_ci
139862306a36Sopenharmony_cistruct thermal_zone_device *thermal_tripless_zone_device_register(
139962306a36Sopenharmony_ci					const char *type,
140062306a36Sopenharmony_ci					void *devdata,
140162306a36Sopenharmony_ci					struct thermal_zone_device_ops *ops,
140262306a36Sopenharmony_ci					const struct thermal_zone_params *tzp)
140362306a36Sopenharmony_ci{
140462306a36Sopenharmony_ci	return thermal_zone_device_register_with_trips(type, NULL, 0, 0, devdata,
140562306a36Sopenharmony_ci						       ops, tzp, 0, 0);
140662306a36Sopenharmony_ci}
140762306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(thermal_tripless_zone_device_register);
140862306a36Sopenharmony_ci
140962306a36Sopenharmony_civoid *thermal_zone_device_priv(struct thermal_zone_device *tzd)
141062306a36Sopenharmony_ci{
141162306a36Sopenharmony_ci	return tzd->devdata;
141262306a36Sopenharmony_ci}
141362306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(thermal_zone_device_priv);
141462306a36Sopenharmony_ci
141562306a36Sopenharmony_ciconst char *thermal_zone_device_type(struct thermal_zone_device *tzd)
141662306a36Sopenharmony_ci{
141762306a36Sopenharmony_ci	return tzd->type;
141862306a36Sopenharmony_ci}
141962306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(thermal_zone_device_type);
142062306a36Sopenharmony_ci
142162306a36Sopenharmony_ciint thermal_zone_device_id(struct thermal_zone_device *tzd)
142262306a36Sopenharmony_ci{
142362306a36Sopenharmony_ci	return tzd->id;
142462306a36Sopenharmony_ci}
142562306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(thermal_zone_device_id);
142662306a36Sopenharmony_ci
142762306a36Sopenharmony_cistruct device *thermal_zone_device(struct thermal_zone_device *tzd)
142862306a36Sopenharmony_ci{
142962306a36Sopenharmony_ci	return &tzd->device;
143062306a36Sopenharmony_ci}
143162306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(thermal_zone_device);
143262306a36Sopenharmony_ci
143362306a36Sopenharmony_ci/**
143462306a36Sopenharmony_ci * thermal_zone_device_unregister - removes the registered thermal zone device
143562306a36Sopenharmony_ci * @tz: the thermal zone device to remove
143662306a36Sopenharmony_ci */
143762306a36Sopenharmony_civoid thermal_zone_device_unregister(struct thermal_zone_device *tz)
143862306a36Sopenharmony_ci{
143962306a36Sopenharmony_ci	int tz_id;
144062306a36Sopenharmony_ci	struct thermal_cooling_device *cdev;
144162306a36Sopenharmony_ci	struct thermal_zone_device *pos = NULL;
144262306a36Sopenharmony_ci
144362306a36Sopenharmony_ci	if (!tz)
144462306a36Sopenharmony_ci		return;
144562306a36Sopenharmony_ci
144662306a36Sopenharmony_ci	tz_id = tz->id;
144762306a36Sopenharmony_ci
144862306a36Sopenharmony_ci	mutex_lock(&thermal_list_lock);
144962306a36Sopenharmony_ci	list_for_each_entry(pos, &thermal_tz_list, node)
145062306a36Sopenharmony_ci		if (pos == tz)
145162306a36Sopenharmony_ci			break;
145262306a36Sopenharmony_ci	if (pos != tz) {
145362306a36Sopenharmony_ci		/* thermal zone device not found */
145462306a36Sopenharmony_ci		mutex_unlock(&thermal_list_lock);
145562306a36Sopenharmony_ci		return;
145662306a36Sopenharmony_ci	}
145762306a36Sopenharmony_ci	list_del(&tz->node);
145862306a36Sopenharmony_ci
145962306a36Sopenharmony_ci	/* Unbind all cdevs associated with 'this' thermal zone */
146062306a36Sopenharmony_ci	list_for_each_entry(cdev, &thermal_cdev_list, node)
146162306a36Sopenharmony_ci		if (tz->ops->unbind)
146262306a36Sopenharmony_ci			tz->ops->unbind(tz, cdev);
146362306a36Sopenharmony_ci
146462306a36Sopenharmony_ci	mutex_unlock(&thermal_list_lock);
146562306a36Sopenharmony_ci
146662306a36Sopenharmony_ci	cancel_delayed_work_sync(&tz->poll_queue);
146762306a36Sopenharmony_ci
146862306a36Sopenharmony_ci	thermal_set_governor(tz, NULL);
146962306a36Sopenharmony_ci
147062306a36Sopenharmony_ci	thermal_remove_hwmon_sysfs(tz);
147162306a36Sopenharmony_ci	ida_free(&thermal_tz_ida, tz->id);
147262306a36Sopenharmony_ci	ida_destroy(&tz->ida);
147362306a36Sopenharmony_ci
147462306a36Sopenharmony_ci	mutex_lock(&tz->lock);
147562306a36Sopenharmony_ci	device_del(&tz->device);
147662306a36Sopenharmony_ci	mutex_unlock(&tz->lock);
147762306a36Sopenharmony_ci
147862306a36Sopenharmony_ci	kfree(tz->tzp);
147962306a36Sopenharmony_ci
148062306a36Sopenharmony_ci	put_device(&tz->device);
148162306a36Sopenharmony_ci
148262306a36Sopenharmony_ci	thermal_notify_tz_delete(tz_id);
148362306a36Sopenharmony_ci}
148462306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(thermal_zone_device_unregister);
148562306a36Sopenharmony_ci
148662306a36Sopenharmony_ci/**
148762306a36Sopenharmony_ci * thermal_zone_get_zone_by_name() - search for a zone and returns its ref
148862306a36Sopenharmony_ci * @name: thermal zone name to fetch the temperature
148962306a36Sopenharmony_ci *
149062306a36Sopenharmony_ci * When only one zone is found with the passed name, returns a reference to it.
149162306a36Sopenharmony_ci *
149262306a36Sopenharmony_ci * Return: On success returns a reference to an unique thermal zone with
149362306a36Sopenharmony_ci * matching name equals to @name, an ERR_PTR otherwise (-EINVAL for invalid
149462306a36Sopenharmony_ci * paramenters, -ENODEV for not found and -EEXIST for multiple matches).
149562306a36Sopenharmony_ci */
149662306a36Sopenharmony_cistruct thermal_zone_device *thermal_zone_get_zone_by_name(const char *name)
149762306a36Sopenharmony_ci{
149862306a36Sopenharmony_ci	struct thermal_zone_device *pos = NULL, *ref = ERR_PTR(-EINVAL);
149962306a36Sopenharmony_ci	unsigned int found = 0;
150062306a36Sopenharmony_ci
150162306a36Sopenharmony_ci	if (!name)
150262306a36Sopenharmony_ci		goto exit;
150362306a36Sopenharmony_ci
150462306a36Sopenharmony_ci	mutex_lock(&thermal_list_lock);
150562306a36Sopenharmony_ci	list_for_each_entry(pos, &thermal_tz_list, node)
150662306a36Sopenharmony_ci		if (!strncasecmp(name, pos->type, THERMAL_NAME_LENGTH)) {
150762306a36Sopenharmony_ci			found++;
150862306a36Sopenharmony_ci			ref = pos;
150962306a36Sopenharmony_ci		}
151062306a36Sopenharmony_ci	mutex_unlock(&thermal_list_lock);
151162306a36Sopenharmony_ci
151262306a36Sopenharmony_ci	/* nothing has been found, thus an error code for it */
151362306a36Sopenharmony_ci	if (found == 0)
151462306a36Sopenharmony_ci		ref = ERR_PTR(-ENODEV);
151562306a36Sopenharmony_ci	else if (found > 1)
151662306a36Sopenharmony_ci	/* Success only when an unique zone is found */
151762306a36Sopenharmony_ci		ref = ERR_PTR(-EEXIST);
151862306a36Sopenharmony_ci
151962306a36Sopenharmony_ciexit:
152062306a36Sopenharmony_ci	return ref;
152162306a36Sopenharmony_ci}
152262306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(thermal_zone_get_zone_by_name);
152362306a36Sopenharmony_ci
152462306a36Sopenharmony_cistatic int thermal_pm_notify(struct notifier_block *nb,
152562306a36Sopenharmony_ci			     unsigned long mode, void *_unused)
152662306a36Sopenharmony_ci{
152762306a36Sopenharmony_ci	struct thermal_zone_device *tz;
152862306a36Sopenharmony_ci
152962306a36Sopenharmony_ci	switch (mode) {
153062306a36Sopenharmony_ci	case PM_HIBERNATION_PREPARE:
153162306a36Sopenharmony_ci	case PM_RESTORE_PREPARE:
153262306a36Sopenharmony_ci	case PM_SUSPEND_PREPARE:
153362306a36Sopenharmony_ci		mutex_lock(&thermal_list_lock);
153462306a36Sopenharmony_ci
153562306a36Sopenharmony_ci		list_for_each_entry(tz, &thermal_tz_list, node) {
153662306a36Sopenharmony_ci			mutex_lock(&tz->lock);
153762306a36Sopenharmony_ci
153862306a36Sopenharmony_ci			tz->suspended = true;
153962306a36Sopenharmony_ci
154062306a36Sopenharmony_ci			mutex_unlock(&tz->lock);
154162306a36Sopenharmony_ci		}
154262306a36Sopenharmony_ci
154362306a36Sopenharmony_ci		mutex_unlock(&thermal_list_lock);
154462306a36Sopenharmony_ci		break;
154562306a36Sopenharmony_ci	case PM_POST_HIBERNATION:
154662306a36Sopenharmony_ci	case PM_POST_RESTORE:
154762306a36Sopenharmony_ci	case PM_POST_SUSPEND:
154862306a36Sopenharmony_ci		mutex_lock(&thermal_list_lock);
154962306a36Sopenharmony_ci
155062306a36Sopenharmony_ci		list_for_each_entry(tz, &thermal_tz_list, node) {
155162306a36Sopenharmony_ci			mutex_lock(&tz->lock);
155262306a36Sopenharmony_ci
155362306a36Sopenharmony_ci			tz->suspended = false;
155462306a36Sopenharmony_ci
155562306a36Sopenharmony_ci			thermal_zone_device_init(tz);
155662306a36Sopenharmony_ci			__thermal_zone_device_update(tz, THERMAL_EVENT_UNSPECIFIED);
155762306a36Sopenharmony_ci
155862306a36Sopenharmony_ci			mutex_unlock(&tz->lock);
155962306a36Sopenharmony_ci		}
156062306a36Sopenharmony_ci
156162306a36Sopenharmony_ci		mutex_unlock(&thermal_list_lock);
156262306a36Sopenharmony_ci		break;
156362306a36Sopenharmony_ci	default:
156462306a36Sopenharmony_ci		break;
156562306a36Sopenharmony_ci	}
156662306a36Sopenharmony_ci	return 0;
156762306a36Sopenharmony_ci}
156862306a36Sopenharmony_ci
156962306a36Sopenharmony_cistatic struct notifier_block thermal_pm_nb = {
157062306a36Sopenharmony_ci	.notifier_call = thermal_pm_notify,
157162306a36Sopenharmony_ci};
157262306a36Sopenharmony_ci
157362306a36Sopenharmony_cistatic int __init thermal_init(void)
157462306a36Sopenharmony_ci{
157562306a36Sopenharmony_ci	int result;
157662306a36Sopenharmony_ci
157762306a36Sopenharmony_ci	result = thermal_netlink_init();
157862306a36Sopenharmony_ci	if (result)
157962306a36Sopenharmony_ci		goto error;
158062306a36Sopenharmony_ci
158162306a36Sopenharmony_ci	result = thermal_register_governors();
158262306a36Sopenharmony_ci	if (result)
158362306a36Sopenharmony_ci		goto unregister_netlink;
158462306a36Sopenharmony_ci
158562306a36Sopenharmony_ci	thermal_class = kzalloc(sizeof(*thermal_class), GFP_KERNEL);
158662306a36Sopenharmony_ci	if (!thermal_class) {
158762306a36Sopenharmony_ci		result = -ENOMEM;
158862306a36Sopenharmony_ci		goto unregister_governors;
158962306a36Sopenharmony_ci	}
159062306a36Sopenharmony_ci
159162306a36Sopenharmony_ci	thermal_class->name = "thermal";
159262306a36Sopenharmony_ci	thermal_class->dev_release = thermal_release;
159362306a36Sopenharmony_ci
159462306a36Sopenharmony_ci	result = class_register(thermal_class);
159562306a36Sopenharmony_ci	if (result) {
159662306a36Sopenharmony_ci		kfree(thermal_class);
159762306a36Sopenharmony_ci		thermal_class = NULL;
159862306a36Sopenharmony_ci		goto unregister_governors;
159962306a36Sopenharmony_ci	}
160062306a36Sopenharmony_ci
160162306a36Sopenharmony_ci	result = register_pm_notifier(&thermal_pm_nb);
160262306a36Sopenharmony_ci	if (result)
160362306a36Sopenharmony_ci		pr_warn("Thermal: Can not register suspend notifier, return %d\n",
160462306a36Sopenharmony_ci			result);
160562306a36Sopenharmony_ci
160662306a36Sopenharmony_ci	return 0;
160762306a36Sopenharmony_ci
160862306a36Sopenharmony_ciunregister_governors:
160962306a36Sopenharmony_ci	thermal_unregister_governors();
161062306a36Sopenharmony_ciunregister_netlink:
161162306a36Sopenharmony_ci	thermal_netlink_exit();
161262306a36Sopenharmony_cierror:
161362306a36Sopenharmony_ci	mutex_destroy(&thermal_list_lock);
161462306a36Sopenharmony_ci	mutex_destroy(&thermal_governor_lock);
161562306a36Sopenharmony_ci	return result;
161662306a36Sopenharmony_ci}
161762306a36Sopenharmony_cipostcore_initcall(thermal_init);
1618