Linux Audio

Check our new training course

Loading...
Note: File does not exist in v3.15.
  1// SPDX-License-Identifier: GPL-2.0
  2/*
  3 * Copyright 2024 Linaro Limited
  4 *
  5 * Author: Daniel Lezcano <daniel.lezcano@linaro.org>
  6 *
  7 * Thermal thresholds
  8 */
  9#include <linux/list.h>
 10#include <linux/list_sort.h>
 11#include <linux/slab.h>
 12
 13#include "thermal_core.h"
 14#include "thermal_thresholds.h"
 15
 16int thermal_thresholds_init(struct thermal_zone_device *tz)
 17{
 18	INIT_LIST_HEAD(&tz->user_thresholds);
 19
 20	return 0;
 21}
 22
 23static void __thermal_thresholds_flush(struct thermal_zone_device *tz)
 24{
 25	struct list_head *thresholds = &tz->user_thresholds;
 26	struct user_threshold *entry, *tmp;
 27
 28	list_for_each_entry_safe(entry, tmp, thresholds, list_node) {
 29		list_del(&entry->list_node);
 30		kfree(entry);
 31	}
 32}
 33
 34void thermal_thresholds_flush(struct thermal_zone_device *tz)
 35{
 36	lockdep_assert_held(&tz->lock);
 37
 38	__thermal_thresholds_flush(tz);
 39
 40	thermal_notify_threshold_flush(tz);
 41
 42	__thermal_zone_device_update(tz, THERMAL_TZ_FLUSH_THRESHOLDS);
 43}
 44
 45void thermal_thresholds_exit(struct thermal_zone_device *tz)
 46{
 47	__thermal_thresholds_flush(tz);
 48}
 49
 50static int __thermal_thresholds_cmp(void *data,
 51				    const struct list_head *l1,
 52				    const struct list_head *l2)
 53{
 54	struct user_threshold *t1 = container_of(l1, struct user_threshold, list_node);
 55	struct user_threshold *t2 = container_of(l2, struct user_threshold, list_node);
 56
 57	return t1->temperature - t2->temperature;
 58}
 59
 60static struct user_threshold *__thermal_thresholds_find(const struct list_head *thresholds,
 61							int temperature)
 62{
 63	struct user_threshold *t;
 64
 65	list_for_each_entry(t, thresholds, list_node)
 66		if (t->temperature == temperature)
 67			return t;
 68
 69	return NULL;
 70}
 71
 72static bool thermal_thresholds_handle_raising(struct list_head *thresholds, int temperature,
 73					      int last_temperature)
 74{
 75	struct user_threshold *t;
 76
 77	list_for_each_entry(t, thresholds, list_node) {
 78
 79		if (!(t->direction & THERMAL_THRESHOLD_WAY_UP))
 80		    continue;
 81
 82		if (temperature >= t->temperature &&
 83		    last_temperature < t->temperature)
 84			return true;
 85	}
 86
 87	return false;
 88}
 89
 90static bool thermal_thresholds_handle_dropping(struct list_head *thresholds, int temperature,
 91					       int last_temperature)
 92{
 93	struct user_threshold *t;
 94
 95	list_for_each_entry_reverse(t, thresholds, list_node) {
 96
 97		if (!(t->direction & THERMAL_THRESHOLD_WAY_DOWN))
 98		    continue;
 99
100		if (temperature <= t->temperature &&
101		    last_temperature > t->temperature)
102			return true;
103	}
104
105	return false;
106}
107
108static void thermal_threshold_find_boundaries(struct list_head *thresholds, int temperature,
109					      int *low, int *high)
110{
111	struct user_threshold *t;
112
113	list_for_each_entry(t, thresholds, list_node) {
114		if (temperature < t->temperature &&
115		    (t->direction & THERMAL_THRESHOLD_WAY_UP) &&
116		    *high > t->temperature)
117			*high = t->temperature;
118	}
119
120	list_for_each_entry_reverse(t, thresholds, list_node) {
121		if (temperature > t->temperature &&
122		    (t->direction & THERMAL_THRESHOLD_WAY_DOWN) &&
123		    *low < t->temperature)
124			*low = t->temperature;
125	}
126}
127
128void thermal_thresholds_handle(struct thermal_zone_device *tz, int *low, int *high)
129{
130	struct list_head *thresholds = &tz->user_thresholds;
131
132	int temperature = tz->temperature;
133	int last_temperature = tz->last_temperature;
134
135	lockdep_assert_held(&tz->lock);
136
137	thermal_threshold_find_boundaries(thresholds, temperature, low, high);
138
139	/*
140	 * We need a second update in order to detect a threshold being crossed
141	 */
142	if (last_temperature == THERMAL_TEMP_INVALID)
143		return;
144
145	/*
146	 * The temperature is stable, so obviously we can not have
147	 * crossed a threshold.
148	 */
149	if (last_temperature == temperature)
150		return;
151
152	/*
153	 * Since last update the temperature:
154	 * - increased : thresholds are crossed the way up
155	 * - decreased : thresholds are crossed the way down
156	 */
157	if (temperature > last_temperature) {
158		if (thermal_thresholds_handle_raising(thresholds,
159						      temperature, last_temperature))
160			thermal_notify_threshold_up(tz);
161	} else {
162		if (thermal_thresholds_handle_dropping(thresholds,
163						       temperature, last_temperature))
164			thermal_notify_threshold_down(tz);
165	}
166}
167
168int thermal_thresholds_add(struct thermal_zone_device *tz,
169			   int temperature, int direction)
170{
171	struct list_head *thresholds = &tz->user_thresholds;
172	struct user_threshold *t;
173
174	lockdep_assert_held(&tz->lock);
175
176	t = __thermal_thresholds_find(thresholds, temperature);
177	if (t) {
178		if (t->direction == direction)
179			return -EEXIST;
180
181		t->direction |= direction;
182	} else {
183
184		t = kmalloc(sizeof(*t), GFP_KERNEL);
185		if (!t)
186			return -ENOMEM;
187
188		INIT_LIST_HEAD(&t->list_node);
189		t->temperature = temperature;
190		t->direction = direction;
191		list_add(&t->list_node, thresholds);
192		list_sort(NULL, thresholds, __thermal_thresholds_cmp);
193	}
194
195	thermal_notify_threshold_add(tz, temperature, direction);
196
197	__thermal_zone_device_update(tz, THERMAL_TZ_ADD_THRESHOLD);
198
199	return 0;
200}
201
202int thermal_thresholds_delete(struct thermal_zone_device *tz,
203			      int temperature, int direction)
204{
205	struct list_head *thresholds = &tz->user_thresholds;
206	struct user_threshold *t;
207
208	lockdep_assert_held(&tz->lock);
209
210	t = __thermal_thresholds_find(thresholds, temperature);
211	if (!t)
212		return -ENOENT;
213
214	if (t->direction == direction) {
215		list_del(&t->list_node);
216		kfree(t);
217	} else {
218		t->direction &= ~direction;
219	}
220
221	thermal_notify_threshold_delete(tz, temperature, direction);
222
223	__thermal_zone_device_update(tz, THERMAL_TZ_DEL_THRESHOLD);
224
225	return 0;
226}
227
228int thermal_thresholds_for_each(struct thermal_zone_device *tz,
229				int (*cb)(struct user_threshold *, void *arg), void *arg)
230{
231	struct list_head *thresholds = &tz->user_thresholds;
232	struct user_threshold *entry;
233	int ret;
234
235	guard(thermal_zone)(tz);
236
237	list_for_each_entry(entry, thresholds, list_node) {
238		ret = cb(entry, arg);
239		if (ret)
240			return ret;
241	}
242
243	return 0;
244}