2012-07-12 19:02:30 +03:00
/*
* OMAP thermal driver interface
*
* Copyright ( C ) 2012 Texas Instruments Incorporated - http : //www.ti.com/
* Contact :
* Eduardo Valentin < eduardo . valentin @ ti . com >
*
* This program is free software ; you can redistribute it and / or
* modify it under the terms of the GNU General Public License
* version 2 as published by the Free Software Foundation .
*
* This program is distributed in the hope that it will be useful , but
* WITHOUT ANY WARRANTY ; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the GNU
* General Public License for more details .
*
* You should have received a copy of the GNU General Public License
* along with this program ; if not , write to the Free Software
* Foundation , Inc . , 51 Franklin St , Fifth Floor , Boston , MA
* 02110 - 1301 USA
*
*/
# include <linux/device.h>
# include <linux/err.h>
# include <linux/mutex.h>
# include <linux/gfp.h>
# include <linux/kernel.h>
# include <linux/workqueue.h>
# include <linux/thermal.h>
2012-11-13 14:10:02 -04:00
# include <linux/cpumask.h>
2012-07-12 19:02:30 +03:00
# include <linux/cpu_cooling.h>
2013-07-04 16:43:54 -04:00
# include <linux/of.h>
2012-07-12 19:02:30 +03:00
2013-03-19 10:54:19 -04:00
# include "ti-thermal.h"
# include "ti-bandgap.h"
2012-07-12 19:02:30 +03:00
/* common data structures */
2013-03-19 10:54:21 -04:00
struct ti_thermal_data {
struct thermal_zone_device * ti_thermal ;
2013-05-29 15:07:41 +00:00
struct thermal_zone_device * pcb_tz ;
2012-07-12 19:02:30 +03:00
struct thermal_cooling_device * cool_dev ;
2013-03-19 10:54:21 -04:00
struct ti_bandgap * bgp ;
2012-07-12 19:02:30 +03:00
enum thermal_device_mode mode ;
struct work_struct thermal_wq ;
int sensor_id ;
2013-07-04 16:43:54 -04:00
bool our_zone ;
2012-07-12 19:02:30 +03:00
} ;
2013-03-19 10:54:21 -04:00
static void ti_thermal_work ( struct work_struct * work )
2012-07-12 19:02:30 +03:00
{
2013-03-19 10:54:21 -04:00
struct ti_thermal_data * data = container_of ( work ,
struct ti_thermal_data , thermal_wq ) ;
2012-07-12 19:02:30 +03:00
2016-08-26 16:21:16 -07:00
thermal_zone_device_update ( data - > ti_thermal , THERMAL_EVENT_UNSPECIFIED ) ;
2012-07-12 19:02:30 +03:00
2013-03-19 10:54:21 -04:00
dev_dbg ( & data - > ti_thermal - > device , " updated thermal zone %s \n " ,
data - > ti_thermal - > type ) ;
2012-07-12 19:02:30 +03:00
}
/**
2013-03-19 10:54:21 -04:00
* ti_thermal_hotspot_temperature - returns sensor extrapolated temperature
2012-07-12 19:02:30 +03:00
* @ t : omap sensor temperature
* @ s : omap sensor slope value
* @ c : omap sensor const value
*/
2013-03-19 10:54:21 -04:00
static inline int ti_thermal_hotspot_temperature ( int t , int s , int c )
2012-07-12 19:02:30 +03:00
{
int delta = t * s / 1000 + c ;
if ( delta < 0 )
delta = 0 ;
return t + delta ;
}
/* thermal zone ops */
2015-01-18 21:17:10 +01:00
/* Get temperature callback function for thermal zone */
2015-07-24 08:12:54 +02:00
static inline int __ti_thermal_get_temp ( void * devdata , int * temp )
2012-07-12 19:02:30 +03:00
{
2013-05-29 15:07:41 +00:00
struct thermal_zone_device * pcb_tz = NULL ;
2013-07-04 16:43:54 -04:00
struct ti_thermal_data * data = devdata ;
2013-03-19 10:54:21 -04:00
struct ti_bandgap * bgp ;
2013-03-19 10:54:23 -04:00
const struct ti_temp_sensor * s ;
2013-05-29 15:07:41 +00:00
int ret , tmp , slope , constant ;
2015-07-24 08:12:54 +02:00
int pcb_temp ;
2012-07-12 19:02:30 +03:00
2012-09-11 19:06:55 +03:00
if ( ! data )
return 0 ;
2013-03-19 10:54:18 -04:00
bgp = data - > bgp ;
s = & bgp - > conf - > sensors [ data - > sensor_id ] ;
2012-09-11 19:06:55 +03:00
2013-03-19 10:54:21 -04:00
ret = ti_bandgap_read_temperature ( bgp , data - > sensor_id , & tmp ) ;
2012-07-12 19:02:30 +03:00
if ( ret )
return ret ;
2013-05-29 15:07:41 +00:00
/* Default constants */
2017-03-09 13:36:00 +05:30
slope = thermal_zone_get_slope ( data - > ti_thermal ) ;
constant = thermal_zone_get_offset ( data - > ti_thermal ) ;
2013-05-29 15:07:41 +00:00
pcb_tz = data - > pcb_tz ;
2012-07-12 19:02:30 +03:00
/* In case pcb zone is available, use the extrapolation rule with it */
2013-05-29 15:07:43 +00:00
if ( ! IS_ERR ( pcb_tz ) ) {
2013-05-29 15:07:41 +00:00
ret = thermal_zone_get_temp ( pcb_tz , & pcb_temp ) ;
if ( ! ret ) {
tmp - = pcb_temp ; /* got a valid PCB temp */
slope = s - > slope_pcb ;
constant = s - > constant_pcb ;
} else {
dev_err ( bgp - > dev ,
" Failed to read PCB state. Using defaults \n " ) ;
2013-09-15 15:21:51 -04:00
ret = 0 ;
2013-05-29 15:07:41 +00:00
}
2012-07-12 19:02:30 +03:00
}
2013-03-19 10:54:21 -04:00
* temp = ti_thermal_hotspot_temperature ( tmp , slope , constant ) ;
2012-07-12 19:02:30 +03:00
return ret ;
}
2013-07-04 16:43:54 -04:00
static inline int ti_thermal_get_temp ( struct thermal_zone_device * thermal ,
2015-07-24 08:12:54 +02:00
int * temp )
2013-07-04 16:43:54 -04:00
{
struct ti_thermal_data * data = thermal - > devdata ;
return __ti_thermal_get_temp ( data , temp ) ;
}
2016-06-22 16:42:03 +08:00
static int __ti_thermal_get_trend ( void * p , int trip , enum thermal_trend * trend )
2013-04-01 12:04:46 -04:00
{
2013-07-04 16:43:54 -04:00
struct ti_thermal_data * data = p ;
2013-04-01 12:04:46 -04:00
struct ti_bandgap * bgp ;
int id , tr , ret = 0 ;
bgp = data - > bgp ;
id = data - > sensor_id ;
ret = ti_bandgap_get_trend ( bgp , id , & tr ) ;
if ( ret )
return ret ;
if ( tr > 0 )
* trend = THERMAL_TREND_RAISING ;
else if ( tr < 0 )
* trend = THERMAL_TREND_DROPPING ;
else
* trend = THERMAL_TREND_STABLE ;
return 0 ;
}
2014-11-07 21:24:39 -04:00
static const struct thermal_zone_of_device_ops ti_of_thermal_ops = {
. get_temp = __ti_thermal_get_temp ,
. get_trend = __ti_thermal_get_trend ,
} ;
2013-03-19 10:54:21 -04:00
static struct ti_thermal_data
* ti_thermal_build_data ( struct ti_bandgap * bgp , int id )
2012-07-12 19:02:30 +03:00
{
2013-03-19 10:54:21 -04:00
struct ti_thermal_data * data ;
2012-07-12 19:02:30 +03:00
2013-03-19 10:54:18 -04:00
data = devm_kzalloc ( bgp - > dev , sizeof ( * data ) , GFP_KERNEL ) ;
2012-07-12 19:02:30 +03:00
if ( ! data ) {
2013-03-19 10:54:18 -04:00
dev_err ( bgp - > dev , " kzalloc fail \n " ) ;
2012-09-11 19:06:55 +03:00
return NULL ;
2012-07-12 19:02:30 +03:00
}
data - > sensor_id = id ;
2013-03-19 10:54:18 -04:00
data - > bgp = bgp ;
2012-07-12 19:02:30 +03:00
data - > mode = THERMAL_DEVICE_ENABLED ;
2013-05-29 15:07:43 +00:00
/* pcb_tz will be either valid or PTR_ERR() */
2013-05-29 15:07:41 +00:00
data - > pcb_tz = thermal_zone_get_zone_by_name ( " pcb " ) ;
2013-03-19 10:54:21 -04:00
INIT_WORK ( & data - > thermal_wq , ti_thermal_work ) ;
2012-07-12 19:02:30 +03:00
2012-09-11 19:06:55 +03:00
return data ;
}
2013-03-19 10:54:21 -04:00
int ti_thermal_expose_sensor ( struct ti_bandgap * bgp , int id ,
char * domain )
2012-09-11 19:06:55 +03:00
{
2013-03-19 10:54:21 -04:00
struct ti_thermal_data * data ;
2012-09-11 19:06:55 +03:00
2013-03-19 10:54:21 -04:00
data = ti_bandgap_get_sensor_data ( bgp , id ) ;
2012-09-11 19:06:55 +03:00
2013-05-29 15:07:43 +00:00
if ( ! data | | IS_ERR ( data ) )
2013-03-19 10:54:21 -04:00
data = ti_thermal_build_data ( bgp , id ) ;
2012-09-11 19:06:55 +03:00
if ( ! data )
return - EINVAL ;
2013-07-04 16:43:54 -04:00
/* in case this is specified by DT */
2016-03-09 13:12:52 -08:00
data - > ti_thermal = devm_thermal_zone_of_sensor_register ( bgp - > dev , id ,
2014-11-07 21:24:39 -04:00
data , & ti_of_thermal_ops ) ;
2013-07-04 16:43:54 -04:00
if ( IS_ERR ( data - > ti_thermal ) ) {
2017-03-09 13:36:02 +05:30
dev_err ( bgp - > dev , " thermal zone device is NULL \n " ) ;
return PTR_ERR ( data - > ti_thermal ) ;
2012-07-12 19:02:30 +03:00
}
2017-03-09 13:36:02 +05:30
2013-03-19 10:54:21 -04:00
ti_bandgap_set_sensor_data ( bgp , id , data ) ;
2013-08-23 11:08:22 -05:00
ti_bandgap_write_update_interval ( bgp , data - > sensor_id ,
data - > ti_thermal - > polling_delay ) ;
2012-07-12 19:02:30 +03:00
return 0 ;
}
2013-03-19 10:54:21 -04:00
int ti_thermal_remove_sensor ( struct ti_bandgap * bgp , int id )
2012-07-12 19:02:30 +03:00
{
2013-03-19 10:54:21 -04:00
struct ti_thermal_data * data ;
2012-07-12 19:02:30 +03:00
2013-03-19 10:54:21 -04:00
data = ti_bandgap_get_sensor_data ( bgp , id ) ;
2012-07-12 19:02:30 +03:00
2013-07-04 16:43:54 -04:00
if ( data & & data - > ti_thermal ) {
if ( data - > our_zone )
thermal_zone_device_unregister ( data - > ti_thermal ) ;
}
2012-07-12 19:02:30 +03:00
return 0 ;
}
2013-03-19 10:54:21 -04:00
int ti_thermal_report_sensor_temperature ( struct ti_bandgap * bgp , int id )
2012-07-12 19:02:30 +03:00
{
2013-03-19 10:54:21 -04:00
struct ti_thermal_data * data ;
2012-07-12 19:02:30 +03:00
2013-03-19 10:54:21 -04:00
data = ti_bandgap_get_sensor_data ( bgp , id ) ;
2012-07-12 19:02:30 +03:00
schedule_work ( & data - > thermal_wq ) ;
return 0 ;
}
2013-03-19 10:54:21 -04:00
int ti_thermal_register_cpu_cooling ( struct ti_bandgap * bgp , int id )
2012-07-12 19:02:30 +03:00
{
2013-03-19 10:54:21 -04:00
struct ti_thermal_data * data ;
2013-07-04 16:43:54 -04:00
struct device_node * np = bgp - > dev - > of_node ;
/*
* We are assuming here that if one deploys the zone
* using DT , then it must be aware that the cooling device
* loading has to happen via cpufreq driver .
*/
if ( of_find_property ( np , " #thermal-sensor-cells " , NULL ) )
return 0 ;
2012-07-12 19:02:30 +03:00
2013-03-19 10:54:21 -04:00
data = ti_bandgap_get_sensor_data ( bgp , id ) ;
2013-05-29 15:07:43 +00:00
if ( ! data | | IS_ERR ( data ) )
2013-03-19 10:54:21 -04:00
data = ti_thermal_build_data ( bgp , id ) ;
2012-09-11 19:06:55 +03:00
if ( ! data )
return - EINVAL ;
2012-07-12 19:02:30 +03:00
/* Register cooling device */
2012-11-13 14:10:02 -04:00
data - > cool_dev = cpufreq_cooling_register ( cpu_present_mask ) ;
2013-05-29 15:07:43 +00:00
if ( IS_ERR ( data - > cool_dev ) ) {
2014-12-12 10:05:39 -04:00
int ret = PTR_ERR ( data - > cool_dev ) ;
if ( ret ! = - EPROBE_DEFER )
dev_err ( bgp - > dev ,
" Failed to register cpu cooling device %d \n " ,
ret ) ;
return ret ;
2012-07-12 19:02:30 +03:00
}
2013-03-19 10:54:21 -04:00
ti_bandgap_set_sensor_data ( bgp , id , data ) ;
2012-07-12 19:02:30 +03:00
return 0 ;
}
2013-03-19 10:54:21 -04:00
int ti_thermal_unregister_cpu_cooling ( struct ti_bandgap * bgp , int id )
2012-07-12 19:02:30 +03:00
{
2013-03-19 10:54:21 -04:00
struct ti_thermal_data * data ;
2012-07-12 19:02:30 +03:00
2013-03-19 10:54:21 -04:00
data = ti_bandgap_get_sensor_data ( bgp , id ) ;
2013-07-04 16:43:54 -04:00
2015-02-03 11:15:14 +01:00
if ( data )
2013-07-04 16:43:54 -04:00
cpufreq_cooling_unregister ( data - > cool_dev ) ;
2012-07-12 19:02:30 +03:00
return 0 ;
}