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