2018-05-07 20:52:29 +03:00
// SPDX-License-Identifier: GPL-2.0
2016-11-08 08:09:04 +03:00
/*
* thermal_helpers . c - helper functions to handle thermal devices
*
* Copyright ( C ) 2016 Eduardo Valentin < edubezval @ gmail . com >
*
* Highly based on original thermal_core . c
* Copyright ( C ) 2008 Intel Corp
* Copyright ( C ) 2008 Zhang Rui < rui . zhang @ intel . com >
* Copyright ( C ) 2008 Sujith Thomas < sujith . thomas @ intel . com >
*/
# define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
# include <linux/device.h>
# include <linux/err.h>
2020-05-11 15:24:52 +03:00
# include <linux/export.h>
2016-11-08 08:09:04 +03:00
# include <linux/slab.h>
# include <linux/string.h>
2020-05-11 15:24:51 +03:00
# include <linux/sysfs.h>
2016-11-08 08:09:04 +03:00
# include "thermal_core.h"
2023-03-07 16:37:25 +03:00
# include "thermal_trace.h"
2016-11-08 08:09:04 +03:00
2023-10-12 21:34:50 +03:00
int get_tz_trend ( struct thermal_zone_device * tz , const struct thermal_trip * trip )
2016-11-08 08:09:04 +03:00
{
enum thermal_trend trend ;
if ( tz - > emul_temperature | | ! tz - > ops - > get_trend | |
tz - > ops - > get_trend ( tz , trip , & trend ) ) {
if ( tz - > temperature > tz - > last_temperature )
trend = THERMAL_TREND_RAISING ;
else if ( tz - > temperature < tz - > last_temperature )
trend = THERMAL_TREND_DROPPING ;
else
trend = THERMAL_TREND_STABLE ;
}
return trend ;
}
struct thermal_instance *
get_thermal_instance ( struct thermal_zone_device * tz ,
2023-09-21 20:52:44 +03:00
struct thermal_cooling_device * cdev , int trip_index )
2016-11-08 08:09:04 +03:00
{
struct thermal_instance * pos = NULL ;
struct thermal_instance * target_instance = NULL ;
2023-09-21 20:52:44 +03:00
const struct thermal_trip * trip ;
2016-11-08 08:09:04 +03:00
mutex_lock ( & tz - > lock ) ;
mutex_lock ( & cdev - > lock ) ;
2023-09-21 20:52:44 +03:00
trip = & tz - > trips [ trip_index ] ;
2016-11-08 08:09:04 +03:00
list_for_each_entry ( pos , & tz - > thermal_instances , tz_node ) {
if ( pos - > tz = = tz & & pos - > trip = = trip & & pos - > cdev = = cdev ) {
target_instance = pos ;
break ;
}
}
mutex_unlock ( & cdev - > lock ) ;
mutex_unlock ( & tz - > lock ) ;
return target_instance ;
}
EXPORT_SYMBOL ( get_thermal_instance ) ;
2022-11-10 18:24:55 +03:00
/**
* __thermal_zone_get_temp ( ) - returns the temperature of a thermal zone
* @ tz : a valid pointer to a struct thermal_zone_device
* @ temp : a valid pointer to where to store the resulting temperature .
*
* When a valid thermal zone reference is passed , it will fetch its
* temperature and fill @ temp .
*
* Both tz and tz - > ops must be valid pointers when calling this function ,
* and the tz - > ops - > get_temp callback must be provided .
* The function must be called under tz - > lock .
*
* Return : On success returns 0 , an error code otherwise
*/
2022-08-05 18:38:34 +03:00
int __thermal_zone_get_temp ( struct thermal_zone_device * tz , int * temp )
2016-11-08 08:09:04 +03:00
{
2023-12-04 22:46:35 +03:00
const struct thermal_trip * trip ;
2016-11-08 08:09:04 +03:00
int crit_temp = INT_MAX ;
2023-12-04 22:46:35 +03:00
int ret = - EINVAL ;
2016-11-08 08:09:04 +03:00
2022-08-05 18:38:34 +03:00
lockdep_assert_held ( & tz - > lock ) ;
2016-11-08 08:09:04 +03:00
ret = tz - > ops - > get_temp ( tz , temp ) ;
if ( IS_ENABLED ( CONFIG_THERMAL_EMULATION ) & & tz - > emul_temperature ) {
2023-12-04 22:46:35 +03:00
for_each_trip ( tz , trip ) {
if ( trip - > type = = THERMAL_TRIP_CRITICAL ) {
crit_temp = trip - > temperature ;
2016-11-08 08:09:04 +03:00
break ;
}
}
/*
* Only allow emulating a temperature when the real temperature
* is below the critical temperature so that the emulation code
* cannot hide critical conditions .
*/
if ( ! ret & & * temp < crit_temp )
* temp = tz - > emul_temperature ;
}
2023-03-01 23:14:33 +03:00
if ( ret )
dev_dbg ( & tz - > device , " Failed to get temperature: %d \n " , ret ) ;
2016-11-08 08:09:04 +03:00
return ret ;
}
2020-03-31 19:54:48 +03:00
/**
2022-08-05 18:38:34 +03:00
* thermal_zone_get_temp ( ) - returns the temperature of a thermal zone
* @ tz : a valid pointer to a struct thermal_zone_device
* @ temp : a valid pointer to where to store the resulting temperature .
2020-03-31 19:54:48 +03:00
*
2022-08-05 18:38:34 +03:00
* When a valid thermal zone reference is passed , it will fetch its
* temperature and fill @ temp .
2020-03-31 19:54:48 +03:00
*
2022-08-05 18:38:34 +03:00
* Return : On success returns 0 , an error code otherwise
2020-03-31 19:54:48 +03:00
*/
2022-08-05 18:38:34 +03:00
int thermal_zone_get_temp ( struct thermal_zone_device * tz , int * temp )
{
int ret ;
2022-11-10 18:24:55 +03:00
if ( IS_ERR_OR_NULL ( tz ) )
return - EINVAL ;
2022-08-05 18:38:34 +03:00
mutex_lock ( & tz - > lock ) ;
2022-11-10 18:24:54 +03:00
2022-11-10 18:24:55 +03:00
if ( ! tz - > ops - > get_temp ) {
ret = - EINVAL ;
goto unlock ;
}
2023-12-08 22:19:03 +03:00
ret = __thermal_zone_get_temp ( tz , temp ) ;
2022-11-10 18:24:54 +03:00
2022-11-10 18:24:55 +03:00
unlock :
2022-08-05 18:38:34 +03:00
mutex_unlock ( & tz - > lock ) ;
return ret ;
}
EXPORT_SYMBOL_GPL ( thermal_zone_get_temp ) ;
2024-01-09 12:41:11 +03:00
static int thermal_cdev_set_cur_state ( struct thermal_cooling_device * cdev , int state )
2020-07-06 13:55:38 +03:00
{
2024-01-09 12:41:11 +03:00
int ret ;
/*
* No check is needed for the ops - > set_cur_state as the
* registering function checked the ops are correctly set
*/
ret = cdev - > ops - > set_cur_state ( cdev , state ) ;
2024-01-09 19:42:48 +03:00
if ( ret )
return ret ;
2020-07-06 13:55:38 +03:00
2024-01-09 19:42:48 +03:00
thermal_notify_cdev_state_update ( cdev , state ) ;
thermal_cooling_device_stats_update ( cdev , state ) ;
thermal_debug_cdev_state_update ( cdev , state ) ;
return 0 ;
2020-07-06 13:55:38 +03:00
}
2021-04-22 14:43:06 +03:00
void __thermal_cdev_update ( struct thermal_cooling_device * cdev )
2016-11-08 08:09:04 +03:00
{
struct thermal_instance * instance ;
unsigned long target = 0 ;
/* Make sure cdev enters the deepest cooling state */
list_for_each_entry ( instance , & cdev - > thermal_instances , cdev_node ) {
dev_dbg ( & cdev - > device , " zone%d->target=%lu \n " ,
instance - > tz - > id , instance - > target ) ;
if ( instance - > target = = THERMAL_NO_TARGET )
continue ;
if ( instance - > target > target )
target = instance - > target ;
}
2018-04-02 13:56:25 +03:00
2020-07-06 13:55:38 +03:00
thermal_cdev_set_cur_state ( cdev , target ) ;
2018-04-02 13:56:25 +03:00
2016-11-08 08:09:04 +03:00
trace_cdev_update ( cdev , target ) ;
dev_dbg ( & cdev - > device , " set to state %lu \n " , target ) ;
}
2021-04-22 14:43:06 +03:00
/**
* thermal_cdev_update - update cooling device state if needed
* @ cdev : pointer to struct thermal_cooling_device
*
* Update the cooling device state if there is a need .
*/
void thermal_cdev_update ( struct thermal_cooling_device * cdev )
{
mutex_lock ( & cdev - > lock ) ;
if ( ! cdev - > updated ) {
__thermal_cdev_update ( cdev ) ;
cdev - > updated = true ;
}
mutex_unlock ( & cdev - > lock ) ;
}
2016-11-08 08:09:27 +03:00
/**
* thermal_zone_get_slope - return the slope attribute of the thermal zone
* @ tz : thermal zone device with the slope attribute
*
* Return : If the thermal zone device has a slope attribute , return it , else
* return 1.
*/
int thermal_zone_get_slope ( struct thermal_zone_device * tz )
{
if ( tz & & tz - > tzp )
return tz - > tzp - > slope ;
return 1 ;
}
EXPORT_SYMBOL_GPL ( thermal_zone_get_slope ) ;
/**
* thermal_zone_get_offset - return the offset attribute of the thermal zone
* @ tz : thermal zone device with the offset attribute
*
* Return : If the thermal zone device has a offset attribute , return it , else
* return 0.
*/
int thermal_zone_get_offset ( struct thermal_zone_device * tz )
{
if ( tz & & tz - > tzp )
return tz - > tzp - > offset ;
return 0 ;
}
EXPORT_SYMBOL_GPL ( thermal_zone_get_offset ) ;