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
2013-03-19 10:54:21 -04:00
thermal_zone_device_update ( data - > ti_thermal ) ;
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 */
/* Get temperature callback function for thermal zone*/
2013-07-04 16:43:54 -04:00
static inline int __ti_thermal_get_temp ( void * devdata , long * 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 ;
unsigned long 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 */
slope = s - > slope ;
constant = s - > constant ;
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 ,
unsigned long * temp )
{
struct ti_thermal_data * data = thermal - > devdata ;
return __ti_thermal_get_temp ( data , temp ) ;
}
2012-07-12 19:02:30 +03:00
/* Bind callback functions for thermal zone */
2013-03-19 10:54:21 -04:00
static int ti_thermal_bind ( struct thermal_zone_device * thermal ,
struct thermal_cooling_device * cdev )
2012-07-12 19:02:30 +03:00
{
2013-03-19 10:54:21 -04:00
struct ti_thermal_data * data = thermal - > devdata ;
2012-11-13 14:10:02 -04:00
int id ;
2012-07-12 19:02:30 +03:00
2013-05-29 15:07:43 +00:00
if ( ! data | | IS_ERR ( data ) )
2012-07-12 19:02:30 +03:00
return - ENODEV ;
/* check if this is the cooling device we registered */
if ( data - > cool_dev ! = cdev )
return 0 ;
id = data - > sensor_id ;
/* Simple thing, two trips, one passive another critical */
2012-08-23 08:37:59 +08:00
return thermal_zone_bind_cooling_device ( thermal , 0 , cdev ,
2013-04-08 08:19:08 -04:00
/* bind with min and max states defined by cpu_cooling */
2012-08-23 08:37:59 +08:00
THERMAL_NO_LIMIT ,
2015-02-18 16:04:21 +00:00
THERMAL_NO_LIMIT ,
THERMAL_WEIGHT_DEFAULT ) ;
2012-07-12 19:02:30 +03:00
}
/* Unbind callback functions for thermal zone */
2013-03-19 10:54:21 -04:00
static int ti_thermal_unbind ( struct thermal_zone_device * thermal ,
struct thermal_cooling_device * cdev )
2012-07-12 19:02:30 +03:00
{
2013-03-19 10:54:21 -04:00
struct ti_thermal_data * data = thermal - > devdata ;
2012-07-12 19:02:30 +03:00
2013-05-29 15:07:43 +00:00
if ( ! data | | IS_ERR ( data ) )
2012-07-12 19:02:30 +03:00
return - ENODEV ;
/* check if this is the cooling device we registered */
if ( data - > cool_dev ! = cdev )
return 0 ;
/* Simple thing, two trips, one passive another critical */
return thermal_zone_unbind_cooling_device ( thermal , 0 , cdev ) ;
}
/* Get mode callback functions for thermal zone */
2013-03-19 10:54:21 -04:00
static int ti_thermal_get_mode ( struct thermal_zone_device * thermal ,
enum thermal_device_mode * mode )
2012-07-12 19:02:30 +03:00
{
2013-03-19 10:54:21 -04:00
struct ti_thermal_data * data = thermal - > devdata ;
2012-07-12 19:02:30 +03:00
if ( data )
* mode = data - > mode ;
return 0 ;
}
/* Set mode callback functions for thermal zone */
2013-03-19 10:54:21 -04:00
static int ti_thermal_set_mode ( struct thermal_zone_device * thermal ,
enum thermal_device_mode mode )
2012-07-12 19:02:30 +03:00
{
2013-03-19 10:54:21 -04:00
struct ti_thermal_data * data = thermal - > devdata ;
2013-08-23 11:08:22 -05:00
struct ti_bandgap * bgp ;
bgp = data - > bgp ;
2012-07-12 19:02:30 +03:00
2013-03-19 10:54:21 -04:00
if ( ! data - > ti_thermal ) {
2012-07-12 19:02:30 +03:00
dev_notice ( & thermal - > device , " thermal zone not registered \n " ) ;
return 0 ;
}
2013-03-19 10:54:21 -04:00
mutex_lock ( & data - > ti_thermal - > lock ) ;
2012-07-12 19:02:30 +03:00
if ( mode = = THERMAL_DEVICE_ENABLED )
2013-03-19 10:54:21 -04:00
data - > ti_thermal - > polling_delay = FAST_TEMP_MONITORING_RATE ;
2012-07-12 19:02:30 +03:00
else
2013-03-19 10:54:21 -04:00
data - > ti_thermal - > polling_delay = 0 ;
2012-07-12 19:02:30 +03:00
2013-03-19 10:54:21 -04:00
mutex_unlock ( & data - > ti_thermal - > lock ) ;
2012-07-12 19:02:30 +03:00
data - > mode = mode ;
2013-08-23 11:08:22 -05:00
ti_bandgap_write_update_interval ( bgp , data - > sensor_id ,
data - > ti_thermal - > polling_delay ) ;
2013-03-19 10:54:21 -04:00
thermal_zone_device_update ( data - > ti_thermal ) ;
2012-07-12 19:02:30 +03:00
dev_dbg ( & thermal - > device , " thermal polling set for duration=%d msec \n " ,
2013-03-19 10:54:21 -04:00
data - > ti_thermal - > polling_delay ) ;
2012-07-12 19:02:30 +03:00
return 0 ;
}
/* Get trip type callback functions for thermal zone */
2013-03-19 10:54:21 -04:00
static int ti_thermal_get_trip_type ( struct thermal_zone_device * thermal ,
int trip , enum thermal_trip_type * type )
2012-07-12 19:02:30 +03:00
{
2013-03-19 10:54:21 -04:00
if ( ! ti_thermal_is_valid_trip ( trip ) )
2012-07-12 19:02:30 +03:00
return - EINVAL ;
if ( trip + 1 = = OMAP_TRIP_NUMBER )
* type = THERMAL_TRIP_CRITICAL ;
else
* type = THERMAL_TRIP_PASSIVE ;
return 0 ;
}
/* Get trip temperature callback functions for thermal zone */
2013-03-19 10:54:21 -04:00
static int ti_thermal_get_trip_temp ( struct thermal_zone_device * thermal ,
int trip , unsigned long * temp )
2012-07-12 19:02:30 +03:00
{
2013-03-19 10:54:21 -04:00
if ( ! ti_thermal_is_valid_trip ( trip ) )
2012-07-12 19:02:30 +03:00
return - EINVAL ;
2013-03-19 10:54:21 -04:00
* temp = ti_thermal_get_trip_value ( trip ) ;
2012-07-12 19:02:30 +03:00
return 0 ;
}
2013-07-04 16:43:54 -04:00
static int __ti_thermal_get_trend ( void * p , long * 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 ;
2013-07-04 16:43:54 -04:00
* trend = tr ;
return 0 ;
}
/* Get the temperature trend callback functions for thermal zone */
static int ti_thermal_get_trend ( struct thermal_zone_device * thermal ,
int trip , enum thermal_trend * trend )
{
int ret ;
long tr ;
ret = __ti_thermal_get_trend ( thermal - > devdata , & tr ) ;
if ( ret )
return ret ;
2013-04-01 12:04:46 -04:00
if ( tr > 0 )
* trend = THERMAL_TREND_RAISING ;
else if ( tr < 0 )
* trend = THERMAL_TREND_DROPPING ;
else
* trend = THERMAL_TREND_STABLE ;
return 0 ;
}
2012-07-12 19:02:30 +03:00
/* Get critical temperature callback functions for thermal zone */
2013-03-19 10:54:21 -04:00
static int ti_thermal_get_crit_temp ( struct thermal_zone_device * thermal ,
unsigned long * temp )
2012-07-12 19:02:30 +03:00
{
/* shutdown zone */
2013-03-19 10:54:21 -04:00
return ti_thermal_get_trip_temp ( thermal , OMAP_TRIP_NUMBER - 1 , temp ) ;
2012-07-12 19:02:30 +03:00
}
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 thermal_zone_device_ops ti_thermal_ops = {
. get_temp = ti_thermal_get_temp ,
2013-04-01 12:04:46 -04:00
. get_trend = ti_thermal_get_trend ,
2013-03-19 10:54:21 -04:00
. bind = ti_thermal_bind ,
. unbind = ti_thermal_unbind ,
. get_mode = ti_thermal_get_mode ,
. set_mode = ti_thermal_set_mode ,
. get_trip_type = ti_thermal_get_trip_type ,
. get_trip_temp = ti_thermal_get_trip_temp ,
. get_crit_temp = ti_thermal_get_crit_temp ,
2012-07-12 19:02:30 +03:00
} ;
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 */
data - > ti_thermal = 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 ) ) {
/* Create thermal zone */
data - > ti_thermal = thermal_zone_device_register ( domain ,
2013-03-19 10:54:21 -04:00
OMAP_TRIP_NUMBER , 0 , data , & ti_thermal_ops ,
2012-09-18 11:04:56 +05:30
NULL , FAST_TEMP_MONITORING_RATE ,
2012-09-11 19:06:54 +03:00
FAST_TEMP_MONITORING_RATE ) ;
2013-07-04 16:43:54 -04:00
if ( IS_ERR ( data - > ti_thermal ) ) {
dev_err ( bgp - > dev , " thermal zone device is NULL \n " ) ;
return PTR_ERR ( data - > ti_thermal ) ;
}
data - > ti_thermal - > polling_delay = FAST_TEMP_MONITORING_RATE ;
data - > our_zone = true ;
2012-07-12 19:02:30 +03:00
}
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 ) ;
else
thermal_zone_of_sensor_unregister ( bgp - > dev ,
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 ;
}