linux/drivers/thermal/thermal_helpers.c
<<
>>
Prefs
   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
  25int get_tz_trend(struct thermal_zone_device *tz, int trip)
  26{
  27        enum thermal_trend trend;
  28
  29        if (tz->emul_temperature || !tz->ops->get_trend ||
  30            tz->ops->get_trend(tz, trip, &trend)) {
  31                if (tz->temperature > tz->last_temperature)
  32                        trend = THERMAL_TREND_RAISING;
  33                else if (tz->temperature < tz->last_temperature)
  34                        trend = THERMAL_TREND_DROPPING;
  35                else
  36                        trend = THERMAL_TREND_STABLE;
  37        }
  38
  39        return trend;
  40}
  41EXPORT_SYMBOL(get_tz_trend);
  42
  43struct thermal_instance *
  44get_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        mutex_lock(&tz->lock);
  51        mutex_lock(&cdev->lock);
  52
  53        list_for_each_entry(pos, &tz->thermal_instances, tz_node) {
  54                if (pos->tz == tz && pos->trip == trip && pos->cdev == cdev) {
  55                        target_instance = pos;
  56                        break;
  57                }
  58        }
  59
  60        mutex_unlock(&cdev->lock);
  61        mutex_unlock(&tz->lock);
  62
  63        return target_instance;
  64}
  65EXPORT_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 */
  77int 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        if (!tz || IS_ERR(tz) || !tz->ops->get_temp)
  85                goto exit;
  86
  87        mutex_lock(&tz->lock);
  88
  89        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        mutex_unlock(&tz->lock);
 111exit:
 112        return ret;
 113}
 114EXPORT_SYMBOL_GPL(thermal_zone_get_temp);
 115
 116void 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        mutex_lock(&tz->lock);
 124
 125        if (!tz->ops->set_trips || !tz->ops->get_trip_hyst)
 126                goto exit;
 127
 128        for (i = 0; i < tz->trips; i++) {
 129                int trip_low;
 130
 131                tz->ops->get_trip_temp(tz, i, &trip_temp);
 132                tz->ops->get_trip_hyst(tz, i, &hysteresis);
 133
 134                trip_low = trip_temp - hysteresis;
 135
 136                if (trip_low < tz->temperature && trip_low > low)
 137                        low = trip_low;
 138
 139                if (trip_temp > tz->temperature && trip_temp < high)
 140                        high = trip_temp;
 141        }
 142
 143        /* No need to change trip points */
 144        if (tz->prev_low_trip == low && tz->prev_high_trip == high)
 145                goto exit;
 146
 147        tz->prev_low_trip = low;
 148        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        ret = tz->ops->set_trips(tz, low, high);
 158        if (ret)
 159                dev_err(&tz->device, "Failed to set trips: %d\n", ret);
 160
 161exit:
 162        mutex_unlock(&tz->lock);
 163}
 164EXPORT_SYMBOL_GPL(thermal_zone_set_trips);
 165
 166void thermal_cdev_update(struct thermal_cooling_device *cdev)
 167{
 168        struct thermal_instance *instance;
 169        unsigned long target = 0;
 170
 171        mutex_lock(&cdev->lock);
 172        /* cooling device is updated*/
 173        if (cdev->updated) {
 174                mutex_unlock(&cdev->lock);
 175                return;
 176        }
 177
 178        /* Make sure cdev enters the deepest cooling state */
 179        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                if (instance->target == THERMAL_NO_TARGET)
 183                        continue;
 184                if (instance->target > target)
 185                        target = instance->target;
 186        }
 187
 188        if (!cdev->ops->set_cur_state(cdev, target))
 189                thermal_cooling_device_stats_update(cdev, target);
 190
 191        cdev->updated = true;
 192        mutex_unlock(&cdev->lock);
 193        trace_cdev_update(cdev, target);
 194        dev_dbg(&cdev->device, "set to state %lu\n", target);
 195}
 196EXPORT_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 */
 205int thermal_zone_get_slope(struct thermal_zone_device *tz)
 206{
 207        if (tz && tz->tzp)
 208                return tz->tzp->slope;
 209        return 1;
 210}
 211EXPORT_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 */
 220int thermal_zone_get_offset(struct thermal_zone_device *tz)
 221{
 222        if (tz && tz->tzp)
 223                return tz->tzp->offset;
 224        return 0;
 225}
 226EXPORT_SYMBOL_GPL(thermal_zone_get_offset);
 227