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 int i, ret; 17 18 lockdep_assert_held(&tz->lock); 19 20 for (i = 0; i < tz->num_trips; i++) { 21 ret = cb(&tz->trips[i], data); 22 if (ret) 23 return ret; 24 } 25 26 return 0; 27 } 28 EXPORT_SYMBOL_GPL(for_each_thermal_trip); 29 30 int thermal_zone_get_num_trips(struct thermal_zone_device *tz) 31 { 32 return tz->num_trips; 33 } 34 EXPORT_SYMBOL_GPL(thermal_zone_get_num_trips); 35 36 /** 37 * __thermal_zone_set_trips - Computes the next trip points for the driver 38 * @tz: a pointer to a thermal zone device structure 39 * 40 * The function computes the next temperature boundaries by browsing 41 * the trip points. The result is the closer low and high trip points 42 * to the current temperature. These values are passed to the backend 43 * driver to let it set its own notification mechanism (usually an 44 * interrupt). 45 * 46 * This function must be called with tz->lock held. Both tz and tz->ops 47 * must be valid pointers. 48 * 49 * It does not return a value 50 */ 51 void __thermal_zone_set_trips(struct thermal_zone_device *tz) 52 { 53 struct thermal_trip trip; 54 int low = -INT_MAX, high = INT_MAX; 55 bool same_trip = false; 56 int i, ret; 57 58 lockdep_assert_held(&tz->lock); 59 60 if (!tz->ops->set_trips) 61 return; 62 63 for (i = 0; i < tz->num_trips; i++) { 64 bool low_set = false; 65 int trip_low; 66 67 ret = __thermal_zone_get_trip(tz, i , &trip); 68 if (ret) 69 return; 70 71 trip_low = trip.temperature - trip.hysteresis; 72 73 if (trip_low < tz->temperature && trip_low > low) { 74 low = trip_low; 75 low_set = true; 76 same_trip = false; 77 } 78 79 if (trip.temperature > tz->temperature && 80 trip.temperature < high) { 81 high = trip.temperature; 82 same_trip = low_set; 83 } 84 } 85 86 /* No need to change trip points */ 87 if (tz->prev_low_trip == low && tz->prev_high_trip == high) 88 return; 89 90 /* 91 * If "high" and "low" are the same, skip the change unless this is the 92 * first time. 93 */ 94 if (same_trip && (tz->prev_low_trip != -INT_MAX || 95 tz->prev_high_trip != INT_MAX)) 96 return; 97 98 tz->prev_low_trip = low; 99 tz->prev_high_trip = high; 100 101 dev_dbg(&tz->device, 102 "new temperature boundaries: %d < x < %d\n", low, high); 103 104 /* 105 * Set a temperature window. When this window is left the driver 106 * must inform the thermal core via thermal_zone_device_update. 107 */ 108 ret = tz->ops->set_trips(tz, low, high); 109 if (ret) 110 dev_err(&tz->device, "Failed to set trips: %d\n", ret); 111 } 112 113 int __thermal_zone_get_trip(struct thermal_zone_device *tz, int trip_id, 114 struct thermal_trip *trip) 115 { 116 if (!tz || !tz->trips || trip_id < 0 || trip_id >= tz->num_trips || !trip) 117 return -EINVAL; 118 119 *trip = tz->trips[trip_id]; 120 return 0; 121 } 122 EXPORT_SYMBOL_GPL(__thermal_zone_get_trip); 123 124 int thermal_zone_get_trip(struct thermal_zone_device *tz, int trip_id, 125 struct thermal_trip *trip) 126 { 127 int ret; 128 129 mutex_lock(&tz->lock); 130 ret = __thermal_zone_get_trip(tz, trip_id, trip); 131 mutex_unlock(&tz->lock); 132 133 return ret; 134 } 135 EXPORT_SYMBOL_GPL(thermal_zone_get_trip); 136 137 int thermal_zone_set_trip(struct thermal_zone_device *tz, int trip_id, 138 const struct thermal_trip *trip) 139 { 140 struct thermal_trip t; 141 int ret; 142 143 if (!tz->ops->set_trip_temp && !tz->ops->set_trip_hyst && !tz->trips) 144 return -EINVAL; 145 146 ret = __thermal_zone_get_trip(tz, trip_id, &t); 147 if (ret) 148 return ret; 149 150 if (t.type != trip->type) 151 return -EINVAL; 152 153 if (t.temperature != trip->temperature && tz->ops->set_trip_temp) { 154 ret = tz->ops->set_trip_temp(tz, trip_id, trip->temperature); 155 if (ret) 156 return ret; 157 } 158 159 if (t.hysteresis != trip->hysteresis && tz->ops->set_trip_hyst) { 160 ret = tz->ops->set_trip_hyst(tz, trip_id, trip->hysteresis); 161 if (ret) 162 return ret; 163 } 164 165 if (tz->trips && (t.temperature != trip->temperature || t.hysteresis != trip->hysteresis)) 166 tz->trips[trip_id] = *trip; 167 168 thermal_notify_tz_trip_change(tz->id, trip_id, trip->type, 169 trip->temperature, trip->hysteresis); 170 171 __thermal_zone_device_update(tz, THERMAL_TRIP_CHANGED); 172 173 return 0; 174 } 175 176 int thermal_zone_trip_id(struct thermal_zone_device *tz, 177 const struct thermal_trip *trip) 178 { 179 int i; 180 181 for (i = 0; i < tz->num_trips; i++) { 182 if (&tz->trips[i] == trip) 183 return i; 184 } 185 186 return -ENODATA; 187 } 188