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