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 *trip; 17 int ret; 18 19 for_each_trip(tz, trip) { 20 ret = cb(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 struct thermal_trip trip; 67 int low = -INT_MAX, high = INT_MAX; 68 bool same_trip = false; 69 int i, ret; 70 71 lockdep_assert_held(&tz->lock); 72 73 if (!tz->ops->set_trips) 74 return; 75 76 for (i = 0; i < tz->num_trips; i++) { 77 bool low_set = false; 78 int trip_low; 79 80 ret = __thermal_zone_get_trip(tz, i , &trip); 81 if (ret) 82 return; 83 84 trip_low = trip.temperature - trip.hysteresis; 85 86 if (trip_low < tz->temperature && trip_low > low) { 87 low = trip_low; 88 low_set = true; 89 same_trip = false; 90 } 91 92 if (trip.temperature > tz->temperature && 93 trip.temperature < high) { 94 high = trip.temperature; 95 same_trip = low_set; 96 } 97 } 98 99 /* No need to change trip points */ 100 if (tz->prev_low_trip == low && tz->prev_high_trip == high) 101 return; 102 103 /* 104 * If "high" and "low" are the same, skip the change unless this is the 105 * first time. 106 */ 107 if (same_trip && (tz->prev_low_trip != -INT_MAX || 108 tz->prev_high_trip != INT_MAX)) 109 return; 110 111 tz->prev_low_trip = low; 112 tz->prev_high_trip = high; 113 114 dev_dbg(&tz->device, 115 "new temperature boundaries: %d < x < %d\n", low, high); 116 117 /* 118 * Set a temperature window. When this window is left the driver 119 * must inform the thermal core via thermal_zone_device_update. 120 */ 121 ret = tz->ops->set_trips(tz, low, high); 122 if (ret) 123 dev_err(&tz->device, "Failed to set trips: %d\n", ret); 124 } 125 126 int __thermal_zone_get_trip(struct thermal_zone_device *tz, int trip_id, 127 struct thermal_trip *trip) 128 { 129 if (!tz || !tz->trips || trip_id < 0 || trip_id >= tz->num_trips || !trip) 130 return -EINVAL; 131 132 *trip = tz->trips[trip_id]; 133 return 0; 134 } 135 EXPORT_SYMBOL_GPL(__thermal_zone_get_trip); 136 137 int thermal_zone_get_trip(struct thermal_zone_device *tz, int trip_id, 138 struct thermal_trip *trip) 139 { 140 int ret; 141 142 mutex_lock(&tz->lock); 143 ret = __thermal_zone_get_trip(tz, trip_id, trip); 144 mutex_unlock(&tz->lock); 145 146 return ret; 147 } 148 EXPORT_SYMBOL_GPL(thermal_zone_get_trip); 149 150 int thermal_zone_set_trip(struct thermal_zone_device *tz, int trip_id, 151 const struct thermal_trip *trip) 152 { 153 struct thermal_trip t; 154 int ret; 155 156 if (!tz->ops->set_trip_temp && !tz->ops->set_trip_hyst && !tz->trips) 157 return -EINVAL; 158 159 ret = __thermal_zone_get_trip(tz, trip_id, &t); 160 if (ret) 161 return ret; 162 163 if (t.type != trip->type) 164 return -EINVAL; 165 166 if (t.temperature != trip->temperature && tz->ops->set_trip_temp) { 167 ret = tz->ops->set_trip_temp(tz, trip_id, trip->temperature); 168 if (ret) 169 return ret; 170 } 171 172 if (t.hysteresis != trip->hysteresis && tz->ops->set_trip_hyst) { 173 ret = tz->ops->set_trip_hyst(tz, trip_id, trip->hysteresis); 174 if (ret) 175 return ret; 176 } 177 178 if (tz->trips && (t.temperature != trip->temperature || t.hysteresis != trip->hysteresis)) 179 tz->trips[trip_id] = *trip; 180 181 thermal_notify_tz_trip_change(tz->id, trip_id, trip->type, 182 trip->temperature, trip->hysteresis); 183 184 __thermal_zone_device_update(tz, THERMAL_TRIP_CHANGED); 185 186 return 0; 187 } 188 189 int thermal_zone_trip_id(struct thermal_zone_device *tz, 190 const struct thermal_trip *trip) 191 { 192 /* 193 * Assume the trip to be located within the bounds of the thermal 194 * zone's trips[] table. 195 */ 196 return trip - tz->trips; 197 } 198