Branch data Line data Source code
1 : : // SPDX-License-Identifier: GPL-2.0 2 : : /* 3 : : * thermal_helpers.c - helper functions to handle thermal devices 4 : : * 5 : : * Copyright (C) 2016 Eduardo Valentin <edubezval@gmail.com> 6 : : * 7 : : * Highly based on original thermal_core.c 8 : : * Copyright (C) 2008 Intel Corp 9 : : * Copyright (C) 2008 Zhang Rui <rui.zhang@intel.com> 10 : : * Copyright (C) 2008 Sujith Thomas <sujith.thomas@intel.com> 11 : : */ 12 : : 13 : : #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 14 : : 15 : : #include <linux/sysfs.h> 16 : : #include <linux/device.h> 17 : : #include <linux/err.h> 18 : : #include <linux/slab.h> 19 : : #include <linux/string.h> 20 : : 21 : : #include <trace/events/thermal.h> 22 : : 23 : : #include "thermal_core.h" 24 : : 25 : 0 : int get_tz_trend(struct thermal_zone_device *tz, int trip) 26 : : { 27 : : enum thermal_trend trend; 28 : : 29 : 0 : if (tz->emul_temperature || !tz->ops->get_trend || 30 : 0 : tz->ops->get_trend(tz, trip, &trend)) { 31 : 0 : if (tz->temperature > tz->last_temperature) 32 : 0 : trend = THERMAL_TREND_RAISING; 33 : 0 : else if (tz->temperature < tz->last_temperature) 34 : 0 : trend = THERMAL_TREND_DROPPING; 35 : : else 36 : 0 : trend = THERMAL_TREND_STABLE; 37 : : } 38 : : 39 : 0 : return trend; 40 : : } 41 : : EXPORT_SYMBOL(get_tz_trend); 42 : : 43 : : struct thermal_instance * 44 : 0 : get_thermal_instance(struct thermal_zone_device *tz, 45 : : struct thermal_cooling_device *cdev, int trip) 46 : : { 47 : : struct thermal_instance *pos = NULL; 48 : : struct thermal_instance *target_instance = NULL; 49 : : 50 : 0 : mutex_lock(&tz->lock); 51 : 0 : mutex_lock(&cdev->lock); 52 : : 53 : 0 : list_for_each_entry(pos, &tz->thermal_instances, tz_node) { 54 : 0 : if (pos->tz == tz && pos->trip == trip && pos->cdev == cdev) { 55 : 0 : target_instance = pos; 56 : 0 : break; 57 : : } 58 : : } 59 : : 60 : 0 : mutex_unlock(&cdev->lock); 61 : 0 : mutex_unlock(&tz->lock); 62 : : 63 : 0 : return target_instance; 64 : : } 65 : : EXPORT_SYMBOL(get_thermal_instance); 66 : : 67 : : /** 68 : : * thermal_zone_get_temp() - returns the temperature of a thermal zone 69 : : * @tz: a valid pointer to a struct thermal_zone_device 70 : : * @temp: a valid pointer to where to store the resulting temperature. 71 : : * 72 : : * When a valid thermal zone reference is passed, it will fetch its 73 : : * temperature and fill @temp. 74 : : * 75 : : * Return: On success returns 0, an error code otherwise 76 : : */ 77 : 3 : int thermal_zone_get_temp(struct thermal_zone_device *tz, int *temp) 78 : : { 79 : : int ret = -EINVAL; 80 : : int count; 81 : : int crit_temp = INT_MAX; 82 : : enum thermal_trip_type type; 83 : : 84 : 3 : if (!tz || IS_ERR(tz) || !tz->ops->get_temp) 85 : : goto exit; 86 : : 87 : 3 : mutex_lock(&tz->lock); 88 : : 89 : 3 : ret = tz->ops->get_temp(tz, temp); 90 : : 91 : : if (IS_ENABLED(CONFIG_THERMAL_EMULATION) && tz->emul_temperature) { 92 : : for (count = 0; count < tz->trips; count++) { 93 : : ret = tz->ops->get_trip_type(tz, count, &type); 94 : : if (!ret && type == THERMAL_TRIP_CRITICAL) { 95 : : ret = tz->ops->get_trip_temp(tz, count, 96 : : &crit_temp); 97 : : break; 98 : : } 99 : : } 100 : : 101 : : /* 102 : : * Only allow emulating a temperature when the real temperature 103 : : * is below the critical temperature so that the emulation code 104 : : * cannot hide critical conditions. 105 : : */ 106 : : if (!ret && *temp < crit_temp) 107 : : *temp = tz->emul_temperature; 108 : : } 109 : : 110 : 3 : mutex_unlock(&tz->lock); 111 : : exit: 112 : 3 : return ret; 113 : : } 114 : : EXPORT_SYMBOL_GPL(thermal_zone_get_temp); 115 : : 116 : 3 : void thermal_zone_set_trips(struct thermal_zone_device *tz) 117 : : { 118 : : int low = -INT_MAX; 119 : : int high = INT_MAX; 120 : : int trip_temp, hysteresis; 121 : : int i, ret; 122 : : 123 : 3 : mutex_lock(&tz->lock); 124 : : 125 : 3 : if (!tz->ops->set_trips || !tz->ops->get_trip_hyst) 126 : : goto exit; 127 : : 128 : 0 : for (i = 0; i < tz->trips; i++) { 129 : : int trip_low; 130 : : 131 : 0 : tz->ops->get_trip_temp(tz, i, &trip_temp); 132 : 0 : tz->ops->get_trip_hyst(tz, i, &hysteresis); 133 : : 134 : 0 : trip_low = trip_temp - hysteresis; 135 : : 136 : 0 : if (trip_low < tz->temperature && trip_low > low) 137 : : low = trip_low; 138 : : 139 : 0 : if (trip_temp > tz->temperature && trip_temp < high) 140 : : high = trip_temp; 141 : : } 142 : : 143 : : /* No need to change trip points */ 144 : 0 : if (tz->prev_low_trip == low && tz->prev_high_trip == high) 145 : : goto exit; 146 : : 147 : 0 : tz->prev_low_trip = low; 148 : 0 : tz->prev_high_trip = high; 149 : : 150 : : dev_dbg(&tz->device, 151 : : "new temperature boundaries: %d < x < %d\n", low, high); 152 : : 153 : : /* 154 : : * Set a temperature window. When this window is left the driver 155 : : * must inform the thermal core via thermal_zone_device_update. 156 : : */ 157 : 0 : ret = tz->ops->set_trips(tz, low, high); 158 : 0 : if (ret) 159 : 0 : dev_err(&tz->device, "Failed to set trips: %d\n", ret); 160 : : 161 : : exit: 162 : 3 : mutex_unlock(&tz->lock); 163 : 3 : } 164 : : EXPORT_SYMBOL_GPL(thermal_zone_set_trips); 165 : : 166 : 0 : void thermal_cdev_update(struct thermal_cooling_device *cdev) 167 : : { 168 : : struct thermal_instance *instance; 169 : : unsigned long target = 0; 170 : : 171 : 0 : mutex_lock(&cdev->lock); 172 : : /* cooling device is updated*/ 173 : 0 : if (cdev->updated) { 174 : 0 : mutex_unlock(&cdev->lock); 175 : 0 : return; 176 : : } 177 : : 178 : : /* Make sure cdev enters the deepest cooling state */ 179 : 0 : list_for_each_entry(instance, &cdev->thermal_instances, cdev_node) { 180 : : dev_dbg(&cdev->device, "zone%d->target=%lu\n", 181 : : instance->tz->id, instance->target); 182 : 0 : if (instance->target == THERMAL_NO_TARGET) 183 : 0 : continue; 184 : 0 : if (instance->target > target) 185 : : target = instance->target; 186 : : } 187 : : 188 : 0 : if (!cdev->ops->set_cur_state(cdev, target)) 189 : : thermal_cooling_device_stats_update(cdev, target); 190 : : 191 : 0 : cdev->updated = true; 192 : 0 : mutex_unlock(&cdev->lock); 193 : 0 : trace_cdev_update(cdev, target); 194 : : dev_dbg(&cdev->device, "set to state %lu\n", target); 195 : : } 196 : : EXPORT_SYMBOL(thermal_cdev_update); 197 : : 198 : : /** 199 : : * thermal_zone_get_slope - return the slope attribute of the thermal zone 200 : : * @tz: thermal zone device with the slope attribute 201 : : * 202 : : * Return: If the thermal zone device has a slope attribute, return it, else 203 : : * return 1. 204 : : */ 205 : 3 : int thermal_zone_get_slope(struct thermal_zone_device *tz) 206 : : { 207 : 3 : if (tz && tz->tzp) 208 : 1 : return tz->tzp->slope; 209 : : return 1; 210 : : } 211 : : EXPORT_SYMBOL_GPL(thermal_zone_get_slope); 212 : : 213 : : /** 214 : : * thermal_zone_get_offset - return the offset attribute of the thermal zone 215 : : * @tz: thermal zone device with the offset attribute 216 : : * 217 : : * Return: If the thermal zone device has a offset attribute, return it, else 218 : : * return 0. 219 : : */ 220 : 3 : int thermal_zone_get_offset(struct thermal_zone_device *tz) 221 : : { 222 : 3 : if (tz && tz->tzp) 223 : 1 : return tz->tzp->offset; 224 : : return 0; 225 : : } 226 : : EXPORT_SYMBOL_GPL(thermal_zone_get_offset);