• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // SPDX-License-Identifier: GPL-2.0
2 /*
3  *  thermal_helpers.c - helper functions to handle thermal devices
4  *
5  *  Copyright (C) 2016 Eduardo Valentin <edubezval@gmail.com>
6  *
7  *  Highly based on original thermal_core.c
8  *  Copyright (C) 2008 Intel Corp
9  *  Copyright (C) 2008 Zhang Rui <rui.zhang@intel.com>
10  *  Copyright (C) 2008 Sujith Thomas <sujith.thomas@intel.com>
11  */
12 
13 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
14 
15 #include <linux/device.h>
16 #include <linux/err.h>
17 #include <linux/export.h>
18 #include <linux/slab.h>
19 #include <linux/string.h>
20 #include <linux/sysfs.h>
21 
22 #include "thermal_core.h"
23 #include "thermal_trace.h"
24 
get_tz_trend(struct thermal_zone_device * tz,const struct thermal_trip * trip)25 int get_tz_trend(struct thermal_zone_device *tz, const struct thermal_trip *trip)
26 {
27 	enum thermal_trend trend;
28 
29 	if (tz->emul_temperature || !tz->ops.get_trend ||
30 	    tz->ops.get_trend(tz, trip, &trend)) {
31 		if (tz->temperature > tz->last_temperature)
32 			trend = THERMAL_TREND_RAISING;
33 		else if (tz->temperature < tz->last_temperature)
34 			trend = THERMAL_TREND_DROPPING;
35 		else
36 			trend = THERMAL_TREND_STABLE;
37 	}
38 
39 	return trend;
40 }
41 
thermal_instance_present(struct thermal_zone_device * tz,struct thermal_cooling_device * cdev,const struct thermal_trip * trip)42 static bool thermal_instance_present(struct thermal_zone_device *tz,
43 				     struct thermal_cooling_device *cdev,
44 				     const struct thermal_trip *trip)
45 {
46 	const struct thermal_trip_desc *td = trip_to_trip_desc(trip);
47 	struct thermal_instance *ti;
48 
49 	list_for_each_entry(ti, &td->thermal_instances, trip_node) {
50 		if (ti->cdev == cdev)
51 			return true;
52 	}
53 
54 	return false;
55 }
56 
thermal_trip_is_bound_to_cdev(struct thermal_zone_device * tz,const struct thermal_trip * trip,struct thermal_cooling_device * cdev)57 bool thermal_trip_is_bound_to_cdev(struct thermal_zone_device *tz,
58 				   const struct thermal_trip *trip,
59 				   struct thermal_cooling_device *cdev)
60 {
61 	bool ret;
62 
63 	mutex_lock(&tz->lock);
64 	mutex_lock(&cdev->lock);
65 
66 	ret = thermal_instance_present(tz, cdev, trip);
67 
68 	mutex_unlock(&cdev->lock);
69 	mutex_unlock(&tz->lock);
70 
71 	return ret;
72 }
73 EXPORT_SYMBOL_GPL(thermal_trip_is_bound_to_cdev);
74 
75 /**
76  * __thermal_zone_get_temp() - returns the temperature of a thermal zone
77  * @tz: a valid pointer to a struct thermal_zone_device
78  * @temp: a valid pointer to where to store the resulting temperature.
79  *
80  * When a valid thermal zone reference is passed, it will fetch its
81  * temperature and fill @temp.
82  *
83  * Both tz and tz->ops must be valid pointers when calling this function,
84  * and the tz->ops.get_temp callback must be provided.
85  * The function must be called under tz->lock.
86  *
87  * Return: On success returns 0, an error code otherwise
88  */
__thermal_zone_get_temp(struct thermal_zone_device * tz,int * temp)89 int __thermal_zone_get_temp(struct thermal_zone_device *tz, int *temp)
90 {
91 	const struct thermal_trip_desc *td;
92 	int crit_temp = INT_MAX;
93 	int ret = -EINVAL;
94 
95 	lockdep_assert_held(&tz->lock);
96 
97 	ret = tz->ops.get_temp(tz, temp);
98 
99 	if (IS_ENABLED(CONFIG_THERMAL_EMULATION) && tz->emul_temperature) {
100 		for_each_trip_desc(tz, td) {
101 			const struct thermal_trip *trip = &td->trip;
102 
103 			if (trip->type == THERMAL_TRIP_CRITICAL) {
104 				crit_temp = trip->temperature;
105 				break;
106 			}
107 		}
108 
109 		/*
110 		 * Only allow emulating a temperature when the real temperature
111 		 * is below the critical temperature so that the emulation code
112 		 * cannot hide critical conditions.
113 		 */
114 		if (!ret && *temp < crit_temp)
115 			*temp = tz->emul_temperature;
116 	}
117 
118 	if (ret)
119 		dev_dbg(&tz->device, "Failed to get temperature: %d\n", ret);
120 
121 	return ret;
122 }
123 
124 /**
125  * thermal_zone_get_temp() - returns the temperature of a thermal zone
126  * @tz: a valid pointer to a struct thermal_zone_device
127  * @temp: a valid pointer to where to store the resulting temperature.
128  *
129  * When a valid thermal zone reference is passed, it will fetch its
130  * temperature and fill @temp.
131  *
132  * Return: On success returns 0, an error code otherwise
133  */
thermal_zone_get_temp(struct thermal_zone_device * tz,int * temp)134 int thermal_zone_get_temp(struct thermal_zone_device *tz, int *temp)
135 {
136 	int ret;
137 
138 	if (IS_ERR_OR_NULL(tz))
139 		return -EINVAL;
140 
141 	mutex_lock(&tz->lock);
142 
143 	if (!tz->ops.get_temp) {
144 		ret = -EINVAL;
145 		goto unlock;
146 	}
147 
148 	ret = __thermal_zone_get_temp(tz, temp);
149 	if (!ret && *temp <= THERMAL_TEMP_INVALID)
150 		ret = -ENODATA;
151 
152 unlock:
153 	mutex_unlock(&tz->lock);
154 
155 	return ret;
156 }
157 EXPORT_SYMBOL_GPL(thermal_zone_get_temp);
158 
thermal_cdev_set_cur_state(struct thermal_cooling_device * cdev,int state)159 static int thermal_cdev_set_cur_state(struct thermal_cooling_device *cdev, int state)
160 {
161 	int ret;
162 
163 	/*
164 	 * No check is needed for the ops->set_cur_state as the
165 	 * registering function checked the ops are correctly set
166 	 */
167 	ret = cdev->ops->set_cur_state(cdev, state);
168 	if (ret)
169 		return ret;
170 
171 	thermal_notify_cdev_state_update(cdev, state);
172 	thermal_cooling_device_stats_update(cdev, state);
173 	thermal_debug_cdev_state_update(cdev, state);
174 
175 	return 0;
176 }
177 
__thermal_cdev_update(struct thermal_cooling_device * cdev)178 void __thermal_cdev_update(struct thermal_cooling_device *cdev)
179 {
180 	struct thermal_instance *instance;
181 	unsigned long target = 0;
182 
183 	/* Make sure cdev enters the deepest cooling state */
184 	list_for_each_entry(instance, &cdev->thermal_instances, cdev_node) {
185 		if (instance->target == THERMAL_NO_TARGET)
186 			continue;
187 		if (instance->target > target)
188 			target = instance->target;
189 	}
190 
191 	thermal_cdev_set_cur_state(cdev, target);
192 
193 	trace_cdev_update(cdev, target);
194 	dev_dbg(&cdev->device, "set to state %lu\n", target);
195 }
196 
197 /**
198  * thermal_cdev_update - update cooling device state if needed
199  * @cdev:	pointer to struct thermal_cooling_device
200  *
201  * Update the cooling device state if there is a need.
202  */
thermal_cdev_update(struct thermal_cooling_device * cdev)203 void thermal_cdev_update(struct thermal_cooling_device *cdev)
204 {
205 	mutex_lock(&cdev->lock);
206 	if (!cdev->updated) {
207 		__thermal_cdev_update(cdev);
208 		cdev->updated = true;
209 	}
210 	mutex_unlock(&cdev->lock);
211 }
212 EXPORT_SYMBOL_GPL(thermal_cdev_update);
213 
214 /**
215  * thermal_zone_get_slope - return the slope attribute of the thermal zone
216  * @tz: thermal zone device with the slope attribute
217  *
218  * Return: If the thermal zone device has a slope attribute, return it, else
219  * return 1.
220  */
thermal_zone_get_slope(struct thermal_zone_device * tz)221 int thermal_zone_get_slope(struct thermal_zone_device *tz)
222 {
223 	if (tz && tz->tzp)
224 		return tz->tzp->slope;
225 	return 1;
226 }
227 EXPORT_SYMBOL_GPL(thermal_zone_get_slope);
228 
229 /**
230  * thermal_zone_get_offset - return the offset attribute of the thermal zone
231  * @tz: thermal zone device with the offset attribute
232  *
233  * Return: If the thermal zone device has a offset attribute, return it, else
234  * return 0.
235  */
thermal_zone_get_offset(struct thermal_zone_device * tz)236 int thermal_zone_get_offset(struct thermal_zone_device *tz)
237 {
238 	if (tz && tz->tzp)
239 		return tz->tzp->offset;
240 	return 0;
241 }
242 EXPORT_SYMBOL_GPL(thermal_zone_get_offset);
243