1 // SPDX-License-Identifier: GPL-2.0 2 /* 3 * Copyright (C) 2008 Intel Corp 4 * Copyright (C) 2008 Zhang Rui <rui.zhang@intel.com> 5 * Copyright (C) 2008 Sujith Thomas <sujith.thomas@intel.com> 6 * Copyright 2022 Linaro Limited 7 * 8 * Thermal trips handling 9 */ 10 #include "thermal_core.h" 11 12 int for_each_thermal_trip(struct thermal_zone_device *tz, 13 int (*cb)(struct thermal_trip *, void *), 14 void *data) 15 { 16 struct thermal_trip_desc *td; 17 int ret; 18 19 for_each_trip_desc(tz, td) { 20 ret = cb(&td->trip, data); 21 if (ret) 22 return ret; 23 } 24 25 return 0; 26 } 27 EXPORT_SYMBOL_GPL(for_each_thermal_trip); 28 29 int thermal_zone_for_each_trip(struct thermal_zone_device *tz, 30 int (*cb)(struct thermal_trip *, void *), 31 void *data) 32 { 33 int ret; 34 35 mutex_lock(&tz->lock); 36 ret = for_each_thermal_trip(tz, cb, data); 37 mutex_unlock(&tz->lock); 38 39 return ret; 40 } 41 EXPORT_SYMBOL_GPL(thermal_zone_for_each_trip); 42 43 int thermal_zone_get_num_trips(struct thermal_zone_device *tz) 44 { 45 return tz->num_trips; 46 } 47 EXPORT_SYMBOL_GPL(thermal_zone_get_num_trips); 48 49 /** 50 * __thermal_zone_set_trips - Computes the next trip points for the driver 51 * @tz: a pointer to a thermal zone device structure 52 * 53 * The function computes the next temperature boundaries by browsing 54 * the trip points. The result is the closer low and high trip points 55 * to the current temperature. These values are passed to the backend 56 * driver to let it set its own notification mechanism (usually an 57 * interrupt). 58 * 59 * This function must be called with tz->lock held. Both tz and tz->ops 60 * must be valid pointers. 61 * 62 * It does not return a value 63 */ 64 void __thermal_zone_set_trips(struct thermal_zone_device *tz) 65 { 66 const struct thermal_trip_desc *td; 67 int low = -INT_MAX, high = INT_MAX; 68 int ret; 69 70 lockdep_assert_held(&tz->lock); 71 72 if (!tz->ops.set_trips) 73 return; 74 75 for_each_trip_desc(tz, td) { 76 const struct thermal_trip *trip = &td->trip; 77 int trip_low; 78 79 trip_low = trip->temperature - trip->hysteresis; 80 81 if (trip_low < tz->temperature && trip_low > low) 82 low = trip_low; 83 84 if (trip->temperature > tz->temperature && 85 trip->temperature < high) 86 high = trip->temperature; 87 } 88 89 /* No need to change trip points */ 90 if (tz->prev_low_trip == low && tz->prev_high_trip == high) 91 return; 92 93 tz->prev_low_trip = low; 94 tz->prev_high_trip = high; 95 96 dev_dbg(&tz->device, 97 "new temperature boundaries: %d < x < %d\n", low, high); 98 99 /* 100 * Set a temperature window. When this window is left the driver 101 * must inform the thermal core via thermal_zone_device_update. 102 */ 103 ret = tz->ops.set_trips(tz, low, high); 104 if (ret) 105 dev_err(&tz->device, "Failed to set trips: %d\n", ret); 106 } 107 108 int __thermal_zone_get_trip(struct thermal_zone_device *tz, int trip_id, 109 struct thermal_trip *trip) 110 { 111 if (!tz || trip_id < 0 || trip_id >= tz->num_trips || !trip) 112 return -EINVAL; 113 114 *trip = tz->trips[trip_id].trip; 115 return 0; 116 } 117 EXPORT_SYMBOL_GPL(__thermal_zone_get_trip); 118 119 int thermal_zone_get_trip(struct thermal_zone_device *tz, int trip_id, 120 struct thermal_trip *trip) 121 { 122 int ret; 123 124 mutex_lock(&tz->lock); 125 ret = __thermal_zone_get_trip(tz, trip_id, trip); 126 mutex_unlock(&tz->lock); 127 128 return ret; 129 } 130 EXPORT_SYMBOL_GPL(thermal_zone_get_trip); 131 132 int thermal_zone_trip_id(const struct thermal_zone_device *tz, 133 const struct thermal_trip *trip) 134 { 135 /* 136 * Assume the trip to be located within the bounds of the thermal 137 * zone's trips[] table. 138 */ 139 return trip_to_trip_desc(trip) - tz->trips; 140 } 141 142 void thermal_zone_trip_updated(struct thermal_zone_device *tz, 143 const struct thermal_trip *trip) 144 { 145 thermal_notify_tz_trip_change(tz, trip); 146 __thermal_zone_device_update(tz, THERMAL_TRIP_CHANGED); 147 } 148 149 void thermal_zone_set_trip_temp(struct thermal_zone_device *tz, 150 struct thermal_trip *trip, int temp) 151 { 152 if (trip->temperature == temp) 153 return; 154 155 if (temp == THERMAL_TEMP_INVALID) { 156 struct thermal_trip_desc *td = trip_to_trip_desc(trip); 157 158 if (trip->type == THERMAL_TRIP_PASSIVE && 159 tz->temperature >= td->threshold) { 160 /* 161 * The trip has been crossed, so the thermal zone's 162 * passive count needs to be adjusted. 163 */ 164 tz->passive--; 165 WARN_ON_ONCE(tz->passive < 0); 166 } 167 /* 168 * Invalidate the threshold to avoid triggering a spurious 169 * trip crossing notification when the trip becomes valid. 170 */ 171 td->threshold = INT_MAX; 172 } 173 trip->temperature = temp; 174 thermal_notify_tz_trip_change(tz, trip); 175 } 176 EXPORT_SYMBOL_GPL(thermal_zone_set_trip_temp); 177