===================================================================
@@ -420,64 +420,6 @@ static void handle_critical_trips(struct
tz->ops.hot(tz);
}
-static void handle_thermal_trip(struct thermal_zone_device *tz,
- struct thermal_trip_desc *td,
- struct list_head *way_up_list,
- struct list_head *way_down_list)
-{
- const struct thermal_trip *trip = &td->trip;
- int old_threshold;
-
- if (trip->temperature == THERMAL_TEMP_INVALID)
- return;
-
- /*
- * If the trip temperature or hysteresis has been updated recently,
- * the threshold needs to be computed again using the new values.
- * However, its initial value still reflects the old ones and that
- * is what needs to be compared with the previous zone temperature
- * to decide which action to take.
- */
- old_threshold = td->threshold;
- td->threshold = trip->temperature;
-
- if (tz->last_temperature >= old_threshold &&
- tz->last_temperature != THERMAL_TEMP_INIT) {
- /*
- * Mitigation is under way, so it needs to stop if the zone
- * temperature falls below the low temperature of the trip.
- * In that case, the trip temperature becomes the new threshold.
- */
- if (tz->temperature < trip->temperature - trip->hysteresis) {
- td->notify_temp = trip->temperature - trip->hysteresis;
- thermal_trip_move_to_sorted_list(td, way_down_list);
-
- if (trip->type == THERMAL_TRIP_PASSIVE) {
- tz->passive--;
- WARN_ON(tz->passive < 0);
- }
- } else {
- td->threshold -= trip->hysteresis;
- }
- } else if (tz->temperature >= trip->temperature) {
- /*
- * There is no mitigation under way, so it needs to be started
- * if the zone temperature exceeds the trip one. The new
- * threshold is then set to the low temperature of the trip.
- */
- td->notify_temp = trip->temperature;
- thermal_trip_move_to_sorted_list(td, way_up_list);
-
- td->threshold -= trip->hysteresis;
-
- if (trip->type == THERMAL_TRIP_PASSIVE)
- tz->passive++;
- else if (trip->type == THERMAL_TRIP_CRITICAL ||
- trip->type == THERMAL_TRIP_HOT)
- handle_critical_trips(tz, trip);
- }
-}
-
static void thermal_zone_device_check(struct work_struct *work)
{
struct thermal_zone_device *tz = container_of(work, struct
@@ -486,9 +428,16 @@ static void thermal_zone_device_check(st
thermal_zone_device_update(tz, THERMAL_EVENT_UNSPECIFIED);
}
+static void move_trip_to_trips_above(struct thermal_trip_desc *td,
+ struct thermal_zone_device *tz)
+{
+ td->threshold = td->trip.temperature;
+ thermal_trip_move_to_sorted_list(td, &tz->trips_above);
+}
+
static void thermal_zone_device_init(struct thermal_zone_device *tz)
{
- struct thermal_trip_desc *td;
+ struct thermal_trip_desc *td, *next;
INIT_DELAYED_WORK(&tz->poll_queue, thermal_zone_device_check);
@@ -502,6 +451,16 @@ static void thermal_zone_device_init(str
list_for_each_entry(instance, &td->thermal_instances, trip_node)
instance->initialized = false;
}
+ list_for_each_entry_safe(td, next, &tz->trips_invalid, list_node) {
+ if (td->trip.temperature != THERMAL_TEMP_INVALID)
+ move_trip_to_trips_above(td, tz);
+ }
+ list_for_each_entry_safe(td, next, &tz->trips_below, list_node) {
+ if (td->trip.temperature == THERMAL_TEMP_INVALID)
+ list_move(&td->list_node, &tz->trips_invalid);
+ else
+ move_trip_to_trips_above(td, tz);
+ }
}
static void thermal_governor_trip_crossed(struct thermal_governor *governor,
@@ -569,27 +528,71 @@ void __thermal_zone_device_update(struct
tz->notify_event = event;
- for_each_trip_desc(tz, td) {
- handle_thermal_trip(tz, td, &way_up_list, &way_down_list);
+ /* Check trips that were previously above the zone temperature. */
+ list_for_each_entry_safe(td, next, &tz->trips_above, list_node) {
+ struct thermal_trip *trip = &td->trip;
- if (td->threshold <= tz->temperature && td->threshold > low)
- low = td->threshold;
+ if (td->threshold > tz->temperature)
+ break;
+
+ /*
+ * There is no mitigation under way, so it needs to be started.
+ * Move the trip to the "crossed on the way up" list (sorted by
+ * the old threshold) and set the new threshold to the low
+ * temperature of the trip.
+ */
+ thermal_trip_move_to_sorted_list(td, &way_up_list);
+ td->threshold = trip->temperature - trip->hysteresis;
- if (td->threshold >= tz->temperature && td->threshold < high)
- high = td->threshold;
+ if (trip->type == THERMAL_TRIP_PASSIVE)
+ tz->passive++;
+ else if (trip->type == THERMAL_TRIP_CRITICAL ||
+ trip->type == THERMAL_TRIP_HOT)
+ handle_critical_trips(tz, trip);
}
+ /* Check trips that were previously below or at the zone temperature. */
+ list_for_each_entry_safe_reverse(td, next, &tz->trips_below, list_node) {
+ struct thermal_trip *trip = &td->trip;
- thermal_zone_set_trips(tz, low, high);
+ if (td->threshold <= tz->temperature)
+ break;
+
+ /*
+ * Mitigation is under way, so it needs to stop. Move the trip
+ * to the "crossed on the way down" list (sorted by the old
+ * threshold) and set the new threshold to the trip temperature.
+ */
+ thermal_trip_move_to_sorted_list(td, &way_down_list);
+ td->threshold = trip->temperature;
+
+ if (trip->type == THERMAL_TRIP_PASSIVE) {
+ tz->passive--;
+ WARN_ON(tz->passive < 0);
+ }
+ }
list_for_each_entry_safe(td, next, &way_up_list, list_node) {
thermal_trip_crossed(tz, &td->trip, governor, true);
- list_del_init(&td->list_node);
+ thermal_trip_move_to_sorted_list(td, &tz->trips_below);
}
list_for_each_entry_safe_reverse(td, next, &way_down_list, list_node) {
thermal_trip_crossed(tz, &td->trip, governor, false);
- list_del_init(&td->list_node);
+ thermal_trip_move_to_sorted_list(td, &tz->trips_above);
+ }
+
+ if (!list_empty(&tz->trips_below)) {
+ td = list_last_entry(&tz->trips_below, struct thermal_trip_desc,
+ list_node);
+ /* Avoid checks that don't correspond to trip crossing. */
+ low = td->threshold - 1;
+ }
+ if (!list_empty(&tz->trips_above)) {
+ td = list_first_entry(&tz->trips_above, struct thermal_trip_desc,
+ list_node);
+ high = td->threshold;
}
+ thermal_zone_set_trips(tz, low, high);
if (governor->manage)
governor->manage(tz);
@@ -1409,6 +1412,9 @@ thermal_zone_device_register_with_trips(
}
INIT_LIST_HEAD(&tz->node);
+ INIT_LIST_HEAD(&tz->trips_above);
+ INIT_LIST_HEAD(&tz->trips_below);
+ INIT_LIST_HEAD(&tz->trips_invalid);
ida_init(&tz->ida);
mutex_init(&tz->lock);
init_completion(&tz->removal);
@@ -1432,13 +1438,7 @@ thermal_zone_device_register_with_trips(
for_each_trip_desc(tz, td) {
td->trip = *trip++;
INIT_LIST_HEAD(&td->thermal_instances);
- INIT_LIST_HEAD(&td->list_node);
- /*
- * Mark all thresholds as invalid to start with even though
- * this only matters for the trips that start as invalid and
- * become valid later.
- */
- td->threshold = INT_MAX;
+ list_add(&td->list_node, &tz->trips_invalid);
}
tz->polling_delay_jiffies = msecs_to_jiffies(polling_delay);
===================================================================
@@ -31,7 +31,6 @@ struct thermal_trip_desc {
struct thermal_trip_attrs trip_attrs;
struct list_head list_node;
struct list_head thermal_instances;
- int notify_temp;
int threshold;
};
@@ -69,6 +68,9 @@ struct thermal_governor {
* @device: &struct device for this thermal zone
* @removal: removal completion
* @resume: resume completion
+ * @trips_above: trips above the current zone temperature
+ * @trips_below: trips equal to or below the current zone temperature
+ * @trips_invalid: trips with invalid temperature
* @mode: current mode of this thermal zone
* @devdata: private pointer for device private data
* @num_trips: number of trip points the thermal zone supports
@@ -111,6 +113,9 @@ struct thermal_zone_device {
struct completion removal;
struct completion resume;
struct attribute_group trips_attribute_group;
+ struct list_head trips_above;
+ struct list_head trips_below;
+ struct list_head trips_invalid;
enum thermal_device_mode mode;
void *devdata;
int num_trips;
===================================================================
@@ -100,7 +100,7 @@ void thermal_trip_move_to_sorted_list(st
/* Assume that the new entry is likely to be the last one. */
list_for_each_entry_reverse(entry, list, list_node) {
- if (entry->notify_temp <= td->notify_temp) {
+ if (entry->threshold <= td->threshold) {
list_move(&td->list_node, &entry->list_node);
return;
}
@@ -111,39 +111,72 @@ void thermal_trip_move_to_sorted_list(st
void thermal_zone_set_trip_hyst(struct thermal_zone_device *tz,
struct thermal_trip *trip, int hyst)
{
+ struct thermal_trip_desc *td = trip_to_trip_desc(trip);
+
WRITE_ONCE(trip->hysteresis, hyst);
thermal_notify_tz_trip_change(tz, trip);
+ if (tz->temperature >= td->threshold) {
+ /*
+ * The zone temperature was above or at the trip, so the trip's
+ * new threshold should be equal to its low temperature.
+ */
+ td->threshold = trip->temperature - hyst;
+ thermal_trip_move_to_sorted_list(td, &tz->trips_below);
+ }
}
void thermal_zone_set_trip_temp(struct thermal_zone_device *tz,
struct thermal_trip *trip, int temp)
{
+ struct thermal_trip_desc *td = trip_to_trip_desc(trip);
+ struct list_head *list;
+
if (trip->temperature == temp)
return;
+ if (trip->temperature == THERMAL_TEMP_INVALID) {
+ td->threshold = temp;
+ /*
+ * Move the trip to the "trips above" list regardless of the new
+ * temperature value because there is no mitigation going on for
+ * it. If mitigation needs to be started, it will be moved to
+ * the "trips below" list later.
+ */
+ thermal_trip_move_to_sorted_list(td, &tz->trips_above);
+ list = NULL;
+ } else if (tz->temperature >= td->threshold) {
+ /* The trip's threshold was below the zone temperature */
+ td->threshold = temp - trip->hysteresis;
+ list = &tz->trips_below;
+ } else {
+ /* The trip's threshold was above the zone temperature */
+ td->threshold = temp;
+ list = &tz->trips_above;
+ }
+
WRITE_ONCE(trip->temperature, temp);
thermal_notify_tz_trip_change(tz, trip);
- if (temp == THERMAL_TEMP_INVALID) {
- struct thermal_trip_desc *td = trip_to_trip_desc(trip);
+ if (!list)
+ return;
- if (tz->temperature >= td->threshold) {
- /*
- * The trip has been crossed on the way up, so some
- * adjustments are needed to compensate for the lack
- * of it going forward.
- */
- if (trip->type == THERMAL_TRIP_PASSIVE) {
- tz->passive--;
- WARN_ON_ONCE(tz->passive < 0);
- }
- thermal_zone_trip_down(tz, trip);
- }
+ if (temp != THERMAL_TEMP_INVALID) {
+ thermal_trip_move_to_sorted_list(td, list);
+ return;
+ }
+
+ if (tz->temperature >= td->threshold) {
/*
- * Invalidate the threshold to avoid triggering a spurious
- * trip crossing notification when the trip becomes valid.
+ * The zone temperature was at or above the trip, so some
+ * adjustments are needed to compensate for the lack of it
+ * going forward.
*/
- td->threshold = INT_MAX;
+ if (trip->type == THERMAL_TRIP_PASSIVE) {
+ tz->passive--;
+ WARN_ON_ONCE(tz->passive < 0);
+ }
+ thermal_zone_trip_down(tz, trip);
}
+ list_move(&td->list_node, &tz->trips_invalid);
}
EXPORT_SYMBOL_GPL(thermal_zone_set_trip_temp);