2013-06-24 10:30:44 +04:00
/*
* Copyright 2013 Freescale Semiconductor , Inc .
*
* 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 .
*
*/
2013-12-24 00:49:22 +04:00
# include <linux/clk.h>
2013-06-24 10:30:44 +04:00
# include <linux/cpu_cooling.h>
# include <linux/delay.h>
# include <linux/device.h>
# include <linux/init.h>
2013-08-01 20:33:12 +04:00
# include <linux/interrupt.h>
2013-06-24 10:30:44 +04:00
# include <linux/io.h>
# include <linux/kernel.h>
# include <linux/mfd/syscon.h>
# include <linux/module.h>
# include <linux/of.h>
2014-08-06 11:12:09 +04:00
# include <linux/of_device.h>
2013-06-24 10:30:44 +04:00
# include <linux/platform_device.h>
# include <linux/regmap.h>
# include <linux/slab.h>
# include <linux/thermal.h>
# include <linux/types.h>
# define REG_SET 0x4
# define REG_CLR 0x8
# define REG_TOG 0xc
# define MISC0 0x0150
# define MISC0_REFTOP_SELBIASOFF (1 << 3)
2014-08-06 11:12:09 +04:00
# define MISC1 0x0160
# define MISC1_IRQ_TEMPHIGH (1 << 29)
/* Below LOW and PANIC bits are only for TEMPMON_IMX6SX */
# define MISC1_IRQ_TEMPLOW (1 << 28)
# define MISC1_IRQ_TEMPPANIC (1 << 27)
2013-06-24 10:30:44 +04:00
# define TEMPSENSE0 0x0180
2013-08-01 20:33:12 +04:00
# define TEMPSENSE0_ALARM_VALUE_SHIFT 20
# define TEMPSENSE0_ALARM_VALUE_MASK (0xfff << TEMPSENSE0_ALARM_VALUE_SHIFT)
2013-06-24 10:30:44 +04:00
# define TEMPSENSE0_TEMP_CNT_SHIFT 8
# define TEMPSENSE0_TEMP_CNT_MASK (0xfff << TEMPSENSE0_TEMP_CNT_SHIFT)
# define TEMPSENSE0_FINISHED (1 << 2)
# define TEMPSENSE0_MEASURE_TEMP (1 << 1)
# define TEMPSENSE0_POWER_DOWN (1 << 0)
# define TEMPSENSE1 0x0190
# define TEMPSENSE1_MEASURE_FREQ 0xffff
2014-08-06 11:12:09 +04:00
/* Below TEMPSENSE2 is only for TEMPMON_IMX6SX */
# define TEMPSENSE2 0x0290
# define TEMPSENSE2_LOW_VALUE_SHIFT 0
# define TEMPSENSE2_LOW_VALUE_MASK 0xfff
# define TEMPSENSE2_PANIC_VALUE_SHIFT 16
# define TEMPSENSE2_PANIC_VALUE_MASK 0xfff0000
2013-06-24 10:30:44 +04:00
2015-11-19 17:49:40 +03:00
# define OCOTP_MEM0 0x0480
2013-06-24 10:30:44 +04:00
# define OCOTP_ANA1 0x04e0
/* The driver supports 1 passive trip point and 1 critical trip point */
enum imx_thermal_trip {
IMX_TRIP_PASSIVE ,
IMX_TRIP_CRITICAL ,
IMX_TRIP_NUM ,
} ;
# define IMX_POLLING_DELAY 2000 /* millisecond */
# define IMX_PASSIVE_DELAY 1000
2014-02-12 14:06:35 +04:00
# define FACTOR0 10000000
# define FACTOR1 15976
# define FACTOR2 4297157
2014-08-06 11:12:09 +04:00
# define TEMPMON_IMX6Q 1
# define TEMPMON_IMX6SX 2
struct thermal_soc_data {
u32 version ;
} ;
static struct thermal_soc_data thermal_imx6q_data = {
. version = TEMPMON_IMX6Q ,
} ;
static struct thermal_soc_data thermal_imx6sx_data = {
. version = TEMPMON_IMX6SX ,
} ;
2013-06-24 10:30:44 +04:00
struct imx_thermal_data {
struct thermal_zone_device * tz ;
struct thermal_cooling_device * cdev ;
enum thermal_device_mode mode ;
struct regmap * tempmon ;
2014-02-12 14:06:35 +04:00
u32 c1 , c2 ; /* See formula in imx_get_sensor_data() */
2015-07-24 09:12:54 +03:00
int temp_passive ;
int temp_critical ;
2015-11-19 17:49:40 +03:00
int temp_max ;
2015-07-24 09:12:54 +03:00
int alarm_temp ;
int last_temp ;
2013-08-01 20:33:12 +04:00
bool irq_enabled ;
int irq ;
2013-12-24 00:49:22 +04:00
struct clk * thermal_clk ;
2014-08-06 11:12:09 +04:00
const struct thermal_soc_data * socdata ;
2015-11-19 17:49:40 +03:00
const char * temp_grade ;
2013-06-24 10:30:44 +04:00
} ;
2014-08-06 11:12:09 +04:00
static void imx_set_panic_temp ( struct imx_thermal_data * data ,
2015-07-24 09:12:54 +03:00
int panic_temp )
2014-08-06 11:12:09 +04:00
{
struct regmap * map = data - > tempmon ;
int critical_value ;
critical_value = ( data - > c2 - panic_temp ) / data - > c1 ;
regmap_write ( map , TEMPSENSE2 + REG_CLR , TEMPSENSE2_PANIC_VALUE_MASK ) ;
regmap_write ( map , TEMPSENSE2 + REG_SET , critical_value < <
TEMPSENSE2_PANIC_VALUE_SHIFT ) ;
}
2013-08-01 20:33:12 +04:00
static void imx_set_alarm_temp ( struct imx_thermal_data * data ,
2015-07-24 09:12:54 +03:00
int alarm_temp )
2013-08-01 20:33:12 +04:00
{
struct regmap * map = data - > tempmon ;
int alarm_value ;
data - > alarm_temp = alarm_temp ;
2014-02-12 14:06:35 +04:00
alarm_value = ( data - > c2 - alarm_temp ) / data - > c1 ;
2013-08-01 20:33:12 +04:00
regmap_write ( map , TEMPSENSE0 + REG_CLR , TEMPSENSE0_ALARM_VALUE_MASK ) ;
regmap_write ( map , TEMPSENSE0 + REG_SET , alarm_value < <
TEMPSENSE0_ALARM_VALUE_SHIFT ) ;
}
2015-07-24 09:12:54 +03:00
static int imx_get_temp ( struct thermal_zone_device * tz , int * temp )
2013-06-24 10:30:44 +04:00
{
struct imx_thermal_data * data = tz - > devdata ;
struct regmap * map = data - > tempmon ;
unsigned int n_meas ;
2013-08-01 20:33:12 +04:00
bool wait ;
2013-06-24 10:30:44 +04:00
u32 val ;
2013-08-01 20:33:12 +04:00
if ( data - > mode = = THERMAL_DEVICE_ENABLED ) {
/* Check if a measurement is currently in progress */
regmap_read ( map , TEMPSENSE0 , & val ) ;
wait = ! ( val & TEMPSENSE0_FINISHED ) ;
} else {
/*
* Every time we measure the temperature , we will power on the
* temperature sensor , enable measurements , take a reading ,
* disable measurements , power off the temperature sensor .
*/
regmap_write ( map , TEMPSENSE0 + REG_CLR , TEMPSENSE0_POWER_DOWN ) ;
regmap_write ( map , TEMPSENSE0 + REG_SET , TEMPSENSE0_MEASURE_TEMP ) ;
wait = true ;
}
2013-06-24 10:30:44 +04:00
/*
* According to the temp sensor designers , it may require up to ~ 17u s
* to complete a measurement .
*/
2013-08-01 20:33:12 +04:00
if ( wait )
usleep_range ( 20 , 50 ) ;
2013-06-24 10:30:44 +04:00
regmap_read ( map , TEMPSENSE0 , & val ) ;
2013-08-01 20:33:12 +04:00
if ( data - > mode ! = THERMAL_DEVICE_ENABLED ) {
regmap_write ( map , TEMPSENSE0 + REG_CLR , TEMPSENSE0_MEASURE_TEMP ) ;
regmap_write ( map , TEMPSENSE0 + REG_SET , TEMPSENSE0_POWER_DOWN ) ;
}
2013-06-24 10:30:44 +04:00
if ( ( val & TEMPSENSE0_FINISHED ) = = 0 ) {
dev_dbg ( & tz - > device , " temp measurement never finished \n " ) ;
return - EAGAIN ;
}
n_meas = ( val & TEMPSENSE0_TEMP_CNT_MASK ) > > TEMPSENSE0_TEMP_CNT_SHIFT ;
/* See imx_get_sensor_data() for formula derivation */
2014-02-12 14:06:35 +04:00
* temp = data - > c2 - n_meas * data - > c1 ;
2013-06-24 10:30:44 +04:00
2014-08-06 11:12:09 +04:00
/* Update alarm value to next higher trip point for TEMPMON_IMX6Q */
if ( data - > socdata - > version = = TEMPMON_IMX6Q ) {
if ( data - > alarm_temp = = data - > temp_passive & &
* temp > = data - > temp_passive )
imx_set_alarm_temp ( data , data - > temp_critical ) ;
if ( data - > alarm_temp = = data - > temp_critical & &
* temp < data - > temp_passive ) {
imx_set_alarm_temp ( data , data - > temp_passive ) ;
2015-07-24 09:12:54 +03:00
dev_dbg ( & tz - > device , " thermal alarm off: T < %d \n " ,
2014-08-06 11:12:09 +04:00
data - > alarm_temp / 1000 ) ;
}
2013-08-01 20:33:12 +04:00
}
if ( * temp ! = data - > last_temp ) {
2015-07-24 09:12:54 +03:00
dev_dbg ( & tz - > device , " millicelsius: %d \n " , * temp ) ;
2013-08-01 20:33:12 +04:00
data - > last_temp = * temp ;
}
/* Reenable alarm IRQ if temperature below alarm temperature */
if ( ! data - > irq_enabled & & * temp < data - > alarm_temp ) {
data - > irq_enabled = true ;
enable_irq ( data - > irq ) ;
2013-06-24 10:30:44 +04:00
}
return 0 ;
}
static int imx_get_mode ( struct thermal_zone_device * tz ,
enum thermal_device_mode * mode )
{
struct imx_thermal_data * data = tz - > devdata ;
* mode = data - > mode ;
return 0 ;
}
static int imx_set_mode ( struct thermal_zone_device * tz ,
enum thermal_device_mode mode )
{
struct imx_thermal_data * data = tz - > devdata ;
2013-08-01 20:33:12 +04:00
struct regmap * map = data - > tempmon ;
2013-06-24 10:30:44 +04:00
if ( mode = = THERMAL_DEVICE_ENABLED ) {
tz - > polling_delay = IMX_POLLING_DELAY ;
tz - > passive_delay = IMX_PASSIVE_DELAY ;
2013-08-01 20:33:12 +04:00
regmap_write ( map , TEMPSENSE0 + REG_CLR , TEMPSENSE0_POWER_DOWN ) ;
regmap_write ( map , TEMPSENSE0 + REG_SET , TEMPSENSE0_MEASURE_TEMP ) ;
if ( ! data - > irq_enabled ) {
data - > irq_enabled = true ;
enable_irq ( data - > irq ) ;
}
2013-06-24 10:30:44 +04:00
} else {
2013-08-01 20:33:12 +04:00
regmap_write ( map , TEMPSENSE0 + REG_CLR , TEMPSENSE0_MEASURE_TEMP ) ;
regmap_write ( map , TEMPSENSE0 + REG_SET , TEMPSENSE0_POWER_DOWN ) ;
2013-06-24 10:30:44 +04:00
tz - > polling_delay = 0 ;
tz - > passive_delay = 0 ;
2013-08-01 20:33:12 +04:00
if ( data - > irq_enabled ) {
disable_irq ( data - > irq ) ;
data - > irq_enabled = false ;
}
2013-06-24 10:30:44 +04:00
}
data - > mode = mode ;
thermal_zone_device_update ( tz ) ;
return 0 ;
}
static int imx_get_trip_type ( struct thermal_zone_device * tz , int trip ,
enum thermal_trip_type * type )
{
* type = ( trip = = IMX_TRIP_PASSIVE ) ? THERMAL_TRIP_PASSIVE :
THERMAL_TRIP_CRITICAL ;
return 0 ;
}
2015-07-24 09:12:54 +03:00
static int imx_get_crit_temp ( struct thermal_zone_device * tz , int * temp )
2013-06-24 10:30:44 +04:00
{
2013-08-01 20:33:11 +04:00
struct imx_thermal_data * data = tz - > devdata ;
* temp = data - > temp_critical ;
2013-06-24 10:30:44 +04:00
return 0 ;
}
static int imx_get_trip_temp ( struct thermal_zone_device * tz , int trip ,
2015-07-24 09:12:54 +03:00
int * temp )
2013-06-24 10:30:44 +04:00
{
2013-08-01 20:33:11 +04:00
struct imx_thermal_data * data = tz - > devdata ;
* temp = ( trip = = IMX_TRIP_PASSIVE ) ? data - > temp_passive :
data - > temp_critical ;
return 0 ;
}
static int imx_set_trip_temp ( struct thermal_zone_device * tz , int trip ,
2015-07-24 09:12:54 +03:00
int temp )
2013-08-01 20:33:11 +04:00
{
struct imx_thermal_data * data = tz - > devdata ;
2015-11-19 17:49:40 +03:00
/* do not allow changing critical threshold */
2013-08-01 20:33:11 +04:00
if ( trip = = IMX_TRIP_CRITICAL )
return - EPERM ;
2015-11-19 17:49:40 +03:00
/* do not allow passive to be set higher than critical */
if ( temp < 0 | | temp > data - > temp_critical )
2013-08-01 20:33:11 +04:00
return - EINVAL ;
data - > temp_passive = temp ;
2013-08-01 20:33:12 +04:00
imx_set_alarm_temp ( data , temp ) ;
2013-06-24 10:30:44 +04:00
return 0 ;
}
static int imx_bind ( struct thermal_zone_device * tz ,
struct thermal_cooling_device * cdev )
{
int ret ;
ret = thermal_zone_bind_cooling_device ( tz , IMX_TRIP_PASSIVE , cdev ,
THERMAL_NO_LIMIT ,
2015-02-18 19:04:21 +03:00
THERMAL_NO_LIMIT ,
THERMAL_WEIGHT_DEFAULT ) ;
2013-06-24 10:30:44 +04:00
if ( ret ) {
dev_err ( & tz - > device ,
" binding zone %s with cdev %s failed:%d \n " ,
tz - > type , cdev - > type , ret ) ;
return ret ;
}
return 0 ;
}
static int imx_unbind ( struct thermal_zone_device * tz ,
struct thermal_cooling_device * cdev )
{
int ret ;
ret = thermal_zone_unbind_cooling_device ( tz , IMX_TRIP_PASSIVE , cdev ) ;
if ( ret ) {
dev_err ( & tz - > device ,
" unbinding zone %s with cdev %s failed:%d \n " ,
tz - > type , cdev - > type , ret ) ;
return ret ;
}
return 0 ;
}
2014-01-06 16:54:34 +04:00
static struct thermal_zone_device_ops imx_tz_ops = {
2013-06-24 10:30:44 +04:00
. bind = imx_bind ,
. unbind = imx_unbind ,
. get_temp = imx_get_temp ,
. get_mode = imx_get_mode ,
. set_mode = imx_set_mode ,
. get_trip_type = imx_get_trip_type ,
. get_trip_temp = imx_get_trip_temp ,
. get_crit_temp = imx_get_crit_temp ,
2013-08-01 20:33:11 +04:00
. set_trip_temp = imx_set_trip_temp ,
2013-06-24 10:30:44 +04:00
} ;
static int imx_get_sensor_data ( struct platform_device * pdev )
{
struct imx_thermal_data * data = platform_get_drvdata ( pdev ) ;
struct regmap * map ;
2014-06-20 11:03:06 +04:00
int t1 , n1 ;
2013-06-24 10:30:44 +04:00
int ret ;
u32 val ;
2014-02-12 14:06:35 +04:00
u64 temp64 ;
2013-06-24 10:30:44 +04:00
map = syscon_regmap_lookup_by_phandle ( pdev - > dev . of_node ,
" fsl,tempmon-data " ) ;
if ( IS_ERR ( map ) ) {
ret = PTR_ERR ( map ) ;
dev_err ( & pdev - > dev , " failed to get sensor regmap: %d \n " , ret ) ;
return ret ;
}
ret = regmap_read ( map , OCOTP_ANA1 , & val ) ;
if ( ret ) {
dev_err ( & pdev - > dev , " failed to read sensor data: %d \n " , ret ) ;
return ret ;
}
if ( val = = 0 | | val = = ~ 0 ) {
dev_err ( & pdev - > dev , " invalid sensor calibration data \n " ) ;
return - EINVAL ;
}
/*
* Sensor data layout :
* [ 31 : 20 ] - sensor value @ 25 C
2014-02-12 14:06:35 +04:00
* Use universal formula now and only need sensor value @ 25 C
* slope = 0.4297157 - ( 0.0015976 * 25 C fuse )
2013-06-24 10:30:44 +04:00
*/
n1 = val > > 20 ;
t1 = 25 ; /* t1 always 25C */
/*
2014-02-12 14:06:35 +04:00
* Derived from linear interpolation :
* slope = 0.4297157 - ( 0.0015976 * 25 C fuse )
* slope = ( FACTOR2 - FACTOR1 * n1 ) / FACTOR0
* ( Nmeas - n1 ) / ( Tmeas - t1 ) = slope
2013-06-24 10:30:44 +04:00
* We want to reduce this down to the minimum computation necessary
* for each temperature read . Also , we want Tmeas in millicelsius
* and we don ' t want to lose precision from integer division . So . . .
2014-02-12 14:06:35 +04:00
* Tmeas = ( Nmeas - n1 ) / slope + t1
* milli_Tmeas = 1000 * ( Nmeas - n1 ) / slope + 1000 * t1
* milli_Tmeas = - 1000 * ( n1 - Nmeas ) / slope + 1000 * t1
* Let constant c1 = ( - 1000 / slope )
* milli_Tmeas = ( n1 - Nmeas ) * c1 + 1000 * t1
* Let constant c2 = n1 * c1 + 1000 * t1
* milli_Tmeas = c2 - Nmeas * c1
2013-06-24 10:30:44 +04:00
*/
2014-02-12 14:06:35 +04:00
temp64 = FACTOR0 ;
temp64 * = 1000 ;
do_div ( temp64 , FACTOR1 * n1 - FACTOR2 ) ;
data - > c1 = temp64 ;
data - > c2 = n1 * data - > c1 + 1000 * t1 ;
2013-06-24 10:30:44 +04:00
2015-11-19 17:49:40 +03:00
/* use OTP for thermal grade */
ret = regmap_read ( map , OCOTP_MEM0 , & val ) ;
if ( ret ) {
dev_err ( & pdev - > dev , " failed to read temp grade: %d \n " , ret ) ;
return ret ;
}
/* The maximum die temp is specified by the Temperature Grade */
switch ( ( val > > 6 ) & 0x3 ) {
case 0 : /* Commercial (0 to 95C) */
data - > temp_grade = " Commercial " ;
data - > temp_max = 95000 ;
break ;
case 1 : /* Extended Commercial (-20 to 105C) */
data - > temp_grade = " Extended Commercial " ;
data - > temp_max = 105000 ;
break ;
case 2 : /* Industrial (-40 to 105C) */
data - > temp_grade = " Industrial " ;
data - > temp_max = 105000 ;
break ;
case 3 : /* Automotive (-40 to 125C) */
data - > temp_grade = " Automotive " ;
data - > temp_max = 125000 ;
break ;
}
2013-08-01 20:33:11 +04:00
/*
2015-11-19 17:49:40 +03:00
* Set the critical trip point at 5 C under max
* Set the passive trip point at 10 C under max ( can change via sysfs )
2013-08-01 20:33:11 +04:00
*/
2015-11-19 17:49:40 +03:00
data - > temp_critical = data - > temp_max - ( 1000 * 5 ) ;
data - > temp_passive = data - > temp_max - ( 1000 * 10 ) ;
2013-08-01 20:33:11 +04:00
2013-06-24 10:30:44 +04:00
return 0 ;
}
2013-08-01 20:33:12 +04:00
static irqreturn_t imx_thermal_alarm_irq ( int irq , void * dev )
{
struct imx_thermal_data * data = dev ;
disable_irq_nosync ( irq ) ;
data - > irq_enabled = false ;
return IRQ_WAKE_THREAD ;
}
static irqreturn_t imx_thermal_alarm_irq_thread ( int irq , void * dev )
{
struct imx_thermal_data * data = dev ;
2015-07-24 09:12:54 +03:00
dev_dbg ( & data - > tz - > device , " THERMAL ALARM: T > %d \n " ,
2013-08-01 20:33:12 +04:00
data - > alarm_temp / 1000 ) ;
thermal_zone_device_update ( data - > tz ) ;
return IRQ_HANDLED ;
}
2014-08-06 11:12:09 +04:00
static const struct of_device_id of_imx_thermal_match [ ] = {
{ . compatible = " fsl,imx6q-tempmon " , . data = & thermal_imx6q_data , } ,
{ . compatible = " fsl,imx6sx-tempmon " , . data = & thermal_imx6sx_data , } ,
{ /* end */ }
} ;
MODULE_DEVICE_TABLE ( of , of_imx_thermal_match ) ;
2013-06-24 10:30:44 +04:00
static int imx_thermal_probe ( struct platform_device * pdev )
{
2014-08-06 11:12:09 +04:00
const struct of_device_id * of_id =
of_match_device ( of_imx_thermal_match , & pdev - > dev ) ;
2013-06-24 10:30:44 +04:00
struct imx_thermal_data * data ;
struct regmap * map ;
2013-08-01 20:33:12 +04:00
int measure_freq ;
2013-06-24 10:30:44 +04:00
int ret ;
data = devm_kzalloc ( & pdev - > dev , sizeof ( * data ) , GFP_KERNEL ) ;
if ( ! data )
return - ENOMEM ;
map = syscon_regmap_lookup_by_phandle ( pdev - > dev . of_node , " fsl,tempmon " ) ;
if ( IS_ERR ( map ) ) {
ret = PTR_ERR ( map ) ;
dev_err ( & pdev - > dev , " failed to get tempmon regmap: %d \n " , ret ) ;
return ret ;
}
data - > tempmon = map ;
2014-08-06 11:12:09 +04:00
data - > socdata = of_id - > data ;
/* make sure the IRQ flag is clear before enabling irq on i.MX6SX */
if ( data - > socdata - > version = = TEMPMON_IMX6SX ) {
regmap_write ( map , MISC1 + REG_CLR , MISC1_IRQ_TEMPHIGH |
MISC1_IRQ_TEMPLOW | MISC1_IRQ_TEMPPANIC ) ;
/*
* reset value of LOW ALARM is incorrect , set it to lowest
* value to avoid false trigger of low alarm .
*/
regmap_write ( map , TEMPSENSE2 + REG_SET ,
TEMPSENSE2_LOW_VALUE_MASK ) ;
}
2013-08-01 20:33:12 +04:00
data - > irq = platform_get_irq ( pdev , 0 ) ;
if ( data - > irq < 0 )
return data - > irq ;
2013-06-24 10:30:44 +04:00
platform_set_drvdata ( pdev , data ) ;
ret = imx_get_sensor_data ( pdev ) ;
if ( ret ) {
dev_err ( & pdev - > dev , " failed to get sensor data \n " ) ;
return ret ;
}
/* Make sure sensor is in known good state for measurements */
regmap_write ( map , TEMPSENSE0 + REG_CLR , TEMPSENSE0_POWER_DOWN ) ;
regmap_write ( map , TEMPSENSE0 + REG_CLR , TEMPSENSE0_MEASURE_TEMP ) ;
regmap_write ( map , TEMPSENSE1 + REG_CLR , TEMPSENSE1_MEASURE_FREQ ) ;
regmap_write ( map , MISC0 + REG_SET , MISC0_REFTOP_SELBIASOFF ) ;
regmap_write ( map , TEMPSENSE0 + REG_SET , TEMPSENSE0_POWER_DOWN ) ;
2014-12-04 07:11:45 +03:00
data - > cdev = cpufreq_cooling_register ( cpu_present_mask ) ;
2013-06-24 10:30:44 +04:00
if ( IS_ERR ( data - > cdev ) ) {
ret = PTR_ERR ( data - > cdev ) ;
2014-12-12 00:11:40 +03:00
if ( ret ! = - EPROBE_DEFER )
dev_err ( & pdev - > dev ,
" failed to register cpufreq cooling device: %d \n " ,
ret ) ;
2013-06-24 10:30:44 +04:00
return ret ;
}
2014-11-08 22:35:54 +03:00
data - > thermal_clk = devm_clk_get ( & pdev - > dev , NULL ) ;
if ( IS_ERR ( data - > thermal_clk ) ) {
ret = PTR_ERR ( data - > thermal_clk ) ;
if ( ret ! = - EPROBE_DEFER )
dev_err ( & pdev - > dev ,
" failed to get thermal clk: %d \n " , ret ) ;
cpufreq_cooling_unregister ( data - > cdev ) ;
return ret ;
}
/*
* Thermal sensor needs clk on to get correct value , normally
* we should enable its clk before taking measurement and disable
* clk after measurement is done , but if alarm function is enabled ,
* hardware will auto measure the temperature periodically , so we
* need to keep the clk always on for alarm function .
*/
ret = clk_prepare_enable ( data - > thermal_clk ) ;
if ( ret ) {
dev_err ( & pdev - > dev , " failed to enable thermal clk: %d \n " , ret ) ;
cpufreq_cooling_unregister ( data - > cdev ) ;
return ret ;
}
2013-06-24 10:30:44 +04:00
data - > tz = thermal_zone_device_register ( " imx_thermal_zone " ,
2013-08-01 20:33:11 +04:00
IMX_TRIP_NUM ,
BIT ( IMX_TRIP_PASSIVE ) , data ,
2013-06-24 10:30:44 +04:00
& imx_tz_ops , NULL ,
IMX_PASSIVE_DELAY ,
IMX_POLLING_DELAY ) ;
if ( IS_ERR ( data - > tz ) ) {
ret = PTR_ERR ( data - > tz ) ;
dev_err ( & pdev - > dev ,
" failed to register thermal zone device %d \n " , ret ) ;
2014-11-08 22:35:54 +03:00
clk_disable_unprepare ( data - > thermal_clk ) ;
2013-06-24 10:30:44 +04:00
cpufreq_cooling_unregister ( data - > cdev ) ;
return ret ;
}
2015-11-19 17:49:40 +03:00
dev_info ( & pdev - > dev , " %s CPU temperature grade - max:%dC "
" critical:%dC passive:%dC \n " , data - > temp_grade ,
data - > temp_max / 1000 , data - > temp_critical / 1000 ,
data - > temp_passive / 1000 ) ;
2013-08-01 20:33:12 +04:00
/* Enable measurements at ~ 10 Hz */
regmap_write ( map , TEMPSENSE1 + REG_CLR , TEMPSENSE1_MEASURE_FREQ ) ;
measure_freq = DIV_ROUND_UP ( 32768 , 10 ) ; /* 10 Hz */
regmap_write ( map , TEMPSENSE1 + REG_SET , measure_freq ) ;
imx_set_alarm_temp ( data , data - > temp_passive ) ;
2014-08-06 11:12:09 +04:00
if ( data - > socdata - > version = = TEMPMON_IMX6SX )
imx_set_panic_temp ( data , data - > temp_critical ) ;
2013-08-01 20:33:12 +04:00
regmap_write ( map , TEMPSENSE0 + REG_CLR , TEMPSENSE0_POWER_DOWN ) ;
regmap_write ( map , TEMPSENSE0 + REG_SET , TEMPSENSE0_MEASURE_TEMP ) ;
2015-09-14 14:09:51 +03:00
ret = devm_request_threaded_irq ( & pdev - > dev , data - > irq ,
imx_thermal_alarm_irq , imx_thermal_alarm_irq_thread ,
0 , " imx_thermal " , data ) ;
if ( ret < 0 ) {
dev_err ( & pdev - > dev , " failed to request alarm irq: %d \n " , ret ) ;
clk_disable_unprepare ( data - > thermal_clk ) ;
thermal_zone_device_unregister ( data - > tz ) ;
cpufreq_cooling_unregister ( data - > cdev ) ;
return ret ;
}
2013-08-01 20:33:12 +04:00
data - > irq_enabled = true ;
2013-06-24 10:30:44 +04:00
data - > mode = THERMAL_DEVICE_ENABLED ;
return 0 ;
}
static int imx_thermal_remove ( struct platform_device * pdev )
{
struct imx_thermal_data * data = platform_get_drvdata ( pdev ) ;
2013-08-01 20:33:12 +04:00
struct regmap * map = data - > tempmon ;
/* Disable measurements */
regmap_write ( map , TEMPSENSE0 + REG_SET , TEMPSENSE0_POWER_DOWN ) ;
2013-12-24 00:49:22 +04:00
if ( ! IS_ERR ( data - > thermal_clk ) )
clk_disable_unprepare ( data - > thermal_clk ) ;
2013-06-24 10:30:44 +04:00
thermal_zone_device_unregister ( data - > tz ) ;
cpufreq_cooling_unregister ( data - > cdev ) ;
return 0 ;
}
# ifdef CONFIG_PM_SLEEP
static int imx_thermal_suspend ( struct device * dev )
{
struct imx_thermal_data * data = dev_get_drvdata ( dev ) ;
struct regmap * map = data - > tempmon ;
2013-12-24 18:43:24 +04:00
/*
* Need to disable thermal sensor , otherwise , when thermal core
* try to get temperature before thermal sensor resume , a wrong
* temperature will be read as the thermal sensor is powered
* down .
*/
regmap_write ( map , TEMPSENSE0 + REG_CLR , TEMPSENSE0_MEASURE_TEMP ) ;
regmap_write ( map , TEMPSENSE0 + REG_SET , TEMPSENSE0_POWER_DOWN ) ;
data - > mode = THERMAL_DEVICE_DISABLED ;
2015-01-06 13:50:22 +03:00
clk_disable_unprepare ( data - > thermal_clk ) ;
2013-06-24 10:30:44 +04:00
return 0 ;
}
static int imx_thermal_resume ( struct device * dev )
{
2013-12-24 18:43:24 +04:00
struct imx_thermal_data * data = dev_get_drvdata ( dev ) ;
struct regmap * map = data - > tempmon ;
2015-01-06 13:50:22 +03:00
clk_prepare_enable ( data - > thermal_clk ) ;
2013-12-24 18:43:24 +04:00
/* Enabled thermal sensor after resume */
regmap_write ( map , TEMPSENSE0 + REG_CLR , TEMPSENSE0_POWER_DOWN ) ;
regmap_write ( map , TEMPSENSE0 + REG_SET , TEMPSENSE0_MEASURE_TEMP ) ;
data - > mode = THERMAL_DEVICE_ENABLED ;
2013-06-24 10:30:44 +04:00
return 0 ;
}
# endif
static SIMPLE_DEV_PM_OPS ( imx_thermal_pm_ops ,
imx_thermal_suspend , imx_thermal_resume ) ;
static struct platform_driver imx_thermal = {
. driver = {
. name = " imx_thermal " ,
. pm = & imx_thermal_pm_ops ,
. of_match_table = of_imx_thermal_match ,
} ,
. probe = imx_thermal_probe ,
. remove = imx_thermal_remove ,
} ;
module_platform_driver ( imx_thermal ) ;
MODULE_AUTHOR ( " Freescale Semiconductor, Inc. " ) ;
MODULE_DESCRIPTION ( " Thermal driver for Freescale i.MX SoCs " ) ;
MODULE_LICENSE ( " GPL v2 " ) ;
MODULE_ALIAS ( " platform:imx-thermal " ) ;