2011-09-07 18:49:08 +09:00
/*
2013-06-24 16:20:26 +05:30
* exynos_tmu . c - Samsung EXYNOS TMU ( Thermal Management Unit )
2011-09-07 18:49:08 +09:00
*
2015-01-23 13:10:08 +01:00
* Copyright ( C ) 2014 Samsung Electronics
* Bartlomiej Zolnierkiewicz < b . zolnierkie @ samsung . com >
* Lukasz Majewski < l . majewski @ samsung . com >
*
2011-09-07 18:49:08 +09:00
* Copyright ( C ) 2011 Samsung Electronics
* Donggeun Kim < dg77 . kim @ samsung . com >
2012-08-16 17:11:41 +05:30
* Amit Daniel Kachhap < amit . kachhap @ linaro . org >
2011-09-07 18:49:08 +09:00
*
* This program is free software ; you can redistribute it and / or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation ; either version 2 of the License , or
* ( at your option ) any later version .
*
* 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 . , 59 Temple Place , Suite 330 , Boston , MA 02111 - 1307 USA
*
*/
# include <linux/clk.h>
# include <linux/io.h>
2013-06-24 16:20:25 +05:30
# include <linux/interrupt.h>
# include <linux/module.h>
2018-04-16 12:11:54 +02:00
# include <linux/of_device.h>
2013-06-24 16:20:39 +05:30
# include <linux/of_address.h>
# include <linux/of_irq.h>
2013-06-24 16:20:25 +05:30
# include <linux/platform_device.h>
2013-06-24 16:20:47 +05:30
# include <linux/regulator/consumer.h>
2013-06-24 16:20:25 +05:30
2013-06-24 16:20:27 +05:30
# include "exynos_tmu.h"
2015-01-23 13:10:08 +01:00
# include "../thermal_core.h"
2014-11-13 16:01:28 +01:00
/* Exynos generic registers */
# define EXYNOS_TMU_REG_TRIMINFO 0x0
# define EXYNOS_TMU_REG_CONTROL 0x20
# define EXYNOS_TMU_REG_STATUS 0x28
# define EXYNOS_TMU_REG_CURRENT_TEMP 0x40
# define EXYNOS_TMU_REG_INTEN 0x70
# define EXYNOS_TMU_REG_INTSTAT 0x74
# define EXYNOS_TMU_REG_INTCLEAR 0x78
# define EXYNOS_TMU_TEMP_MASK 0xff
# define EXYNOS_TMU_REF_VOLTAGE_SHIFT 24
# define EXYNOS_TMU_REF_VOLTAGE_MASK 0x1f
# define EXYNOS_TMU_BUF_SLOPE_SEL_MASK 0xf
# define EXYNOS_TMU_BUF_SLOPE_SEL_SHIFT 8
# define EXYNOS_TMU_CORE_EN_SHIFT 0
/* Exynos3250 specific registers */
# define EXYNOS_TMU_TRIMINFO_CON1 0x10
/* Exynos4210 specific registers */
# define EXYNOS4210_TMU_REG_THRESHOLD_TEMP 0x44
# define EXYNOS4210_TMU_REG_TRIG_LEVEL0 0x50
/* Exynos5250, Exynos4412, Exynos3250 specific registers */
# define EXYNOS_TMU_TRIMINFO_CON2 0x14
# define EXYNOS_THD_TEMP_RISE 0x50
# define EXYNOS_THD_TEMP_FALL 0x54
# define EXYNOS_EMUL_CON 0x80
# define EXYNOS_TRIMINFO_RELOAD_ENABLE 1
# define EXYNOS_TRIMINFO_25_SHIFT 0
# define EXYNOS_TRIMINFO_85_SHIFT 8
# define EXYNOS_TMU_TRIP_MODE_SHIFT 13
# define EXYNOS_TMU_TRIP_MODE_MASK 0x7
# define EXYNOS_TMU_THERM_TRIP_EN_SHIFT 12
# define EXYNOS_TMU_INTEN_RISE0_SHIFT 0
# define EXYNOS_TMU_INTEN_RISE1_SHIFT 4
# define EXYNOS_TMU_INTEN_RISE2_SHIFT 8
# define EXYNOS_TMU_INTEN_RISE3_SHIFT 12
# define EXYNOS_TMU_INTEN_FALL0_SHIFT 16
# define EXYNOS_EMUL_TIME 0x57F0
# define EXYNOS_EMUL_TIME_MASK 0xffff
# define EXYNOS_EMUL_TIME_SHIFT 16
# define EXYNOS_EMUL_DATA_SHIFT 8
# define EXYNOS_EMUL_DATA_MASK 0xFF
# define EXYNOS_EMUL_ENABLE 0x1
/* Exynos5260 specific */
# define EXYNOS5260_TMU_REG_INTEN 0xC0
# define EXYNOS5260_TMU_REG_INTSTAT 0xC4
# define EXYNOS5260_TMU_REG_INTCLEAR 0xC8
# define EXYNOS5260_EMUL_CON 0x100
/* Exynos4412 specific */
# define EXYNOS4412_MUX_ADDR_VALUE 6
# define EXYNOS4412_MUX_ADDR_SHIFT 20
2015-03-10 11:23:44 +09:00
/* Exynos5433 specific registers */
# define EXYNOS5433_TMU_REG_CONTROL1 0x024
# define EXYNOS5433_TMU_SAMPLING_INTERVAL 0x02c
# define EXYNOS5433_TMU_COUNTER_VALUE0 0x030
# define EXYNOS5433_TMU_COUNTER_VALUE1 0x034
# define EXYNOS5433_TMU_REG_CURRENT_TEMP1 0x044
# define EXYNOS5433_THD_TEMP_RISE3_0 0x050
# define EXYNOS5433_THD_TEMP_RISE7_4 0x054
# define EXYNOS5433_THD_TEMP_FALL3_0 0x060
# define EXYNOS5433_THD_TEMP_FALL7_4 0x064
# define EXYNOS5433_TMU_REG_INTEN 0x0c0
# define EXYNOS5433_TMU_REG_INTPEND 0x0c8
# define EXYNOS5433_TMU_EMUL_CON 0x110
# define EXYNOS5433_TMU_PD_DET_EN 0x130
# define EXYNOS5433_TRIMINFO_SENSOR_ID_SHIFT 16
# define EXYNOS5433_TRIMINFO_CALIB_SEL_SHIFT 23
# define EXYNOS5433_TRIMINFO_SENSOR_ID_MASK \
( 0xf < < EXYNOS5433_TRIMINFO_SENSOR_ID_SHIFT )
# define EXYNOS5433_TRIMINFO_CALIB_SEL_MASK BIT(23)
# define EXYNOS5433_TRIMINFO_ONE_POINT_TRIMMING 0
# define EXYNOS5433_TRIMINFO_TWO_POINT_TRIMMING 1
# define EXYNOS5433_PD_DET_EN 1
2014-11-13 16:01:28 +01:00
/*exynos5440 specific registers*/
# define EXYNOS5440_TMU_S0_7_TRIM 0x000
# define EXYNOS5440_TMU_S0_7_CTRL 0x020
# define EXYNOS5440_TMU_S0_7_DEBUG 0x040
# define EXYNOS5440_TMU_S0_7_TEMP 0x0f0
# define EXYNOS5440_TMU_S0_7_TH0 0x110
# define EXYNOS5440_TMU_S0_7_TH1 0x130
# define EXYNOS5440_TMU_S0_7_TH2 0x150
# define EXYNOS5440_TMU_S0_7_IRQEN 0x210
# define EXYNOS5440_TMU_S0_7_IRQ 0x230
/* exynos5440 common registers */
# define EXYNOS5440_TMU_IRQ_STATUS 0x000
# define EXYNOS5440_TMU_PMIN 0x004
# define EXYNOS5440_TMU_INTEN_RISE0_SHIFT 0
# define EXYNOS5440_TMU_INTEN_RISE1_SHIFT 1
# define EXYNOS5440_TMU_INTEN_RISE2_SHIFT 2
# define EXYNOS5440_TMU_INTEN_RISE3_SHIFT 3
# define EXYNOS5440_TMU_INTEN_FALL0_SHIFT 4
# define EXYNOS5440_TMU_TH_RISE4_SHIFT 24
# define EXYNOS5440_EFUSE_SWAP_OFFSET 8
2012-08-16 17:11:42 +05:30
2015-01-27 11:18:22 +05:30
/* Exynos7 specific registers */
# define EXYNOS7_THD_TEMP_RISE7_6 0x50
# define EXYNOS7_THD_TEMP_FALL7_6 0x60
# define EXYNOS7_TMU_REG_INTEN 0x110
# define EXYNOS7_TMU_REG_INTPEND 0x118
# define EXYNOS7_TMU_REG_EMUL_CON 0x160
# define EXYNOS7_TMU_TEMP_MASK 0x1ff
# define EXYNOS7_PD_DET_EN_SHIFT 23
# define EXYNOS7_TMU_INTEN_RISE0_SHIFT 0
# define EXYNOS7_TMU_INTEN_RISE1_SHIFT 1
# define EXYNOS7_TMU_INTEN_RISE2_SHIFT 2
# define EXYNOS7_TMU_INTEN_RISE3_SHIFT 3
# define EXYNOS7_TMU_INTEN_RISE4_SHIFT 4
# define EXYNOS7_TMU_INTEN_RISE5_SHIFT 5
# define EXYNOS7_TMU_INTEN_RISE6_SHIFT 6
# define EXYNOS7_TMU_INTEN_RISE7_SHIFT 7
# define EXYNOS7_EMUL_DATA_SHIFT 7
# define EXYNOS7_EMUL_DATA_MASK 0x1ff
2015-01-23 13:10:08 +01:00
# define MCELSIUS 1000
2013-06-24 16:20:39 +05:30
/**
* struct exynos_tmu_data : A structure to hold the private data of the TMU
driver
* @ id : identifier of the one instance of the TMU controller .
* @ pdata : pointer to the tmu platform / configuration data
* @ base : base address of the single instance of the TMU controller .
2013-12-19 11:36:08 +05:30
* @ base_second : base address of the common registers of the TMU controller .
2013-06-24 16:20:39 +05:30
* @ irq : irq number of the TMU controller .
* @ soc : id of the SOC type .
* @ irq_work : pointer to the irq work structure .
* @ lock : lock to implement synchronization .
* @ clk : pointer to the clock structure .
2013-12-19 11:36:31 +05:30
* @ clk_sec : pointer to the clock structure for accessing the base_second .
2015-01-27 11:18:22 +05:30
* @ sclk : pointer to the clock structure for accessing the tmu special clk .
2013-06-24 16:20:39 +05:30
* @ temp_error1 : fused value of the first point trim .
* @ temp_error2 : fused value of the second point trim .
2013-06-24 16:20:47 +05:30
* @ regulator : pointer to the TMU regulator structure .
2013-06-24 16:20:39 +05:30
* @ reg_conf : pointer to structure to register with core thermal .
2016-02-18 14:14:20 +09:00
* @ ntrip : number of supported trip points .
2018-04-16 12:11:52 +02:00
* @ enabled : current status of TMU device
2014-11-13 16:01:13 +01:00
* @ tmu_initialize : SoC specific TMU initialization method
2014-11-13 16:01:15 +01:00
* @ tmu_control : SoC specific TMU control method
2014-11-13 16:01:16 +01:00
* @ tmu_read : SoC specific TMU temperature read method
2014-11-13 16:01:18 +01:00
* @ tmu_set_emulation : SoC specific TMU emulation setting method
2014-11-13 16:01:19 +01:00
* @ tmu_clear_irqs : SoC specific TMU interrupts clearing method
2013-06-24 16:20:39 +05:30
*/
2012-08-16 17:11:42 +05:30
struct exynos_tmu_data {
2013-06-24 16:20:39 +05:30
int id ;
2012-08-16 17:11:42 +05:30
struct exynos_tmu_platform_data * pdata ;
2011-09-07 18:49:08 +09:00
void __iomem * base ;
2013-12-19 11:36:08 +05:30
void __iomem * base_second ;
2011-09-07 18:49:08 +09:00
int irq ;
2012-08-16 17:11:42 +05:30
enum soc_type soc ;
2011-09-07 18:49:08 +09:00
struct work_struct irq_work ;
struct mutex lock ;
2015-01-27 11:18:22 +05:30
struct clk * clk , * clk_sec , * sclk ;
u16 temp_error1 , temp_error2 ;
2013-06-24 16:20:47 +05:30
struct regulator * regulator ;
2015-01-23 13:10:08 +01:00
struct thermal_zone_device * tzd ;
2016-02-18 14:14:20 +09:00
unsigned int ntrip ;
2018-04-16 12:11:52 +02:00
bool enabled ;
2015-01-23 13:10:08 +01:00
2014-11-13 16:01:13 +01:00
int ( * tmu_initialize ) ( struct platform_device * pdev ) ;
2014-11-13 16:01:15 +01:00
void ( * tmu_control ) ( struct platform_device * pdev , bool on ) ;
2014-11-13 16:01:16 +01:00
int ( * tmu_read ) ( struct exynos_tmu_data * data ) ;
2015-07-24 08:12:54 +02:00
void ( * tmu_set_emulation ) ( struct exynos_tmu_data * data , int temp ) ;
2014-11-13 16:01:19 +01:00
void ( * tmu_clear_irqs ) ( struct exynos_tmu_data * data ) ;
2011-09-07 18:49:08 +09:00
} ;
2015-01-23 13:10:08 +01:00
static void exynos_report_trigger ( struct exynos_tmu_data * p )
{
char data [ 10 ] , * envp [ ] = { data , NULL } ;
struct thermal_zone_device * tz = p - > tzd ;
2015-07-24 08:12:54 +02:00
int temp ;
2015-01-23 13:10:08 +01:00
unsigned int i ;
2015-01-28 16:25:22 +01:00
if ( ! tz ) {
pr_err ( " No thermal zone device defined \n " ) ;
2015-01-23 13:10:08 +01:00
return ;
}
2016-08-26 16:21:16 -07:00
thermal_zone_device_update ( tz , THERMAL_EVENT_UNSPECIFIED ) ;
2015-01-23 13:10:08 +01:00
mutex_lock ( & tz - > lock ) ;
/* Find the level for which trip happened */
for ( i = 0 ; i < of_thermal_get_ntrips ( tz ) ; i + + ) {
tz - > ops - > get_trip_temp ( tz , i , & temp ) ;
if ( tz - > last_temperature < temp )
break ;
}
snprintf ( data , sizeof ( data ) , " %u " , i ) ;
kobject_uevent_env ( & tz - > device . kobj , KOBJ_CHANGE , envp ) ;
mutex_unlock ( & tz - > lock ) ;
}
2011-09-07 18:49:08 +09:00
/*
* TMU treats temperature as a mapped temperature code .
* The temperature is converted differently depending on the calibration type .
*/
2012-08-16 17:11:42 +05:30
static int temp_to_code ( struct exynos_tmu_data * data , u8 temp )
2011-09-07 18:49:08 +09:00
{
2012-08-16 17:11:42 +05:30
struct exynos_tmu_platform_data * pdata = data - > pdata ;
2011-09-07 18:49:08 +09:00
int temp_code ;
switch ( pdata - > cal_type ) {
case TYPE_TWO_POINT_TRIMMING :
2013-06-24 16:20:30 +05:30
temp_code = ( temp - pdata - > first_point_trim ) *
( data - > temp_error2 - data - > temp_error1 ) /
( pdata - > second_point_trim - pdata - > first_point_trim ) +
data - > temp_error1 ;
2011-09-07 18:49:08 +09:00
break ;
case TYPE_ONE_POINT_TRIMMING :
2013-06-24 16:20:30 +05:30
temp_code = temp + data - > temp_error1 - pdata - > first_point_trim ;
2011-09-07 18:49:08 +09:00
break ;
default :
2013-06-24 16:20:30 +05:30
temp_code = temp + pdata - > default_temp_offset ;
2011-09-07 18:49:08 +09:00
break ;
}
2014-07-31 19:11:03 +02:00
2011-09-07 18:49:08 +09:00
return temp_code ;
}
/*
* Calculate a temperature value from a temperature code .
* The unit of the temperature is degree Celsius .
*/
2015-01-27 11:18:22 +05:30
static int code_to_temp ( struct exynos_tmu_data * data , u16 temp_code )
2011-09-07 18:49:08 +09:00
{
2012-08-16 17:11:42 +05:30
struct exynos_tmu_platform_data * pdata = data - > pdata ;
2011-09-07 18:49:08 +09:00
int temp ;
switch ( pdata - > cal_type ) {
case TYPE_TWO_POINT_TRIMMING :
2013-06-24 16:20:30 +05:30
temp = ( temp_code - data - > temp_error1 ) *
( pdata - > second_point_trim - pdata - > first_point_trim ) /
( data - > temp_error2 - data - > temp_error1 ) +
pdata - > first_point_trim ;
2011-09-07 18:49:08 +09:00
break ;
case TYPE_ONE_POINT_TRIMMING :
2013-06-24 16:20:30 +05:30
temp = temp_code - data - > temp_error1 + pdata - > first_point_trim ;
2011-09-07 18:49:08 +09:00
break ;
default :
2013-06-24 16:20:30 +05:30
temp = temp_code - pdata - > default_temp_offset ;
2011-09-07 18:49:08 +09:00
break ;
}
2014-07-31 19:11:03 +02:00
2011-09-07 18:49:08 +09:00
return temp ;
}
2014-11-13 16:01:11 +01:00
static void sanitize_temp_error ( struct exynos_tmu_data * data , u32 trim_info )
2011-09-07 18:49:08 +09:00
{
2012-08-16 17:11:42 +05:30
struct exynos_tmu_platform_data * pdata = data - > pdata ;
2011-09-07 18:49:08 +09:00
2013-06-24 16:20:31 +05:30
data - > temp_error1 = trim_info & EXYNOS_TMU_TEMP_MASK ;
2014-07-31 19:11:06 +02:00
data - > temp_error2 = ( ( trim_info > > EXYNOS_TRIMINFO_85_SHIFT ) &
2013-06-24 16:20:31 +05:30
EXYNOS_TMU_TEMP_MASK ) ;
2012-08-16 17:11:42 +05:30
2013-06-24 16:20:45 +05:30
if ( ! data - > temp_error1 | |
( pdata - > min_efuse_value > data - > temp_error1 ) | |
( data - > temp_error1 > pdata - > max_efuse_value ) )
data - > temp_error1 = pdata - > efuse_value & EXYNOS_TMU_TEMP_MASK ;
if ( ! data - > temp_error2 )
data - > temp_error2 =
2014-07-31 19:11:06 +02:00
( pdata - > efuse_value > > EXYNOS_TRIMINFO_85_SHIFT ) &
2013-06-24 16:20:45 +05:30
EXYNOS_TMU_TEMP_MASK ;
2014-11-13 16:01:11 +01:00
}
2012-08-16 17:11:42 +05:30
2014-11-13 16:01:12 +01:00
static u32 get_th_reg ( struct exynos_tmu_data * data , u32 threshold , bool falling )
{
2015-01-23 13:10:08 +01:00
struct thermal_zone_device * tz = data - > tzd ;
const struct thermal_trip * const trips =
of_thermal_get_trip_points ( tz ) ;
unsigned long temp ;
2014-11-13 16:01:12 +01:00
int i ;
2014-04-14 11:08:15 +05:30
2015-01-23 13:10:08 +01:00
if ( ! trips ) {
pr_err ( " %s: Cannot get trip points from of-thermal.c! \n " ,
__func__ ) ;
return 0 ;
}
2012-08-16 17:11:42 +05:30
2015-01-23 13:10:08 +01:00
for ( i = 0 ; i < of_thermal_get_ntrips ( tz ) ; i + + ) {
if ( trips [ i ] . type = = THERMAL_TRIP_CRITICAL )
continue ;
temp = trips [ i ] . temperature / MCELSIUS ;
2014-11-13 16:01:12 +01:00
if ( falling )
2015-01-23 13:10:08 +01:00
temp - = ( trips [ i ] . hysteresis / MCELSIUS ) ;
2014-11-13 16:01:12 +01:00
else
threshold & = ~ ( 0xff < < 8 * i ) ;
2012-08-16 17:11:42 +05:30
2014-11-13 16:01:12 +01:00
threshold | = temp_to_code ( data , temp ) < < 8 * i ;
2011-09-07 18:49:08 +09:00
}
2014-11-13 16:01:12 +01:00
return threshold ;
}
2012-08-16 17:11:42 +05:30
static int exynos_tmu_initialize ( struct platform_device * pdev )
2011-09-07 18:49:08 +09:00
{
2012-08-16 17:11:42 +05:30
struct exynos_tmu_data * data = platform_get_drvdata ( pdev ) ;
2014-11-13 16:01:13 +01:00
int ret ;
2011-09-07 18:49:08 +09:00
2016-02-18 14:14:20 +09:00
if ( of_thermal_get_ntrips ( data - > tzd ) > data - > ntrip ) {
dev_info ( & pdev - > dev ,
" More trip points than supported by this TMU. \n " ) ;
dev_info ( & pdev - > dev ,
" %d trip points should be configured in polling mode. \n " ,
( of_thermal_get_ntrips ( data - > tzd ) - data - > ntrip ) ) ;
}
2011-09-07 18:49:08 +09:00
mutex_lock ( & data - > lock ) ;
clk_enable ( data - > clk ) ;
2013-12-19 11:36:31 +05:30
if ( ! IS_ERR ( data - > clk_sec ) )
clk_enable ( data - > clk_sec ) ;
2014-11-13 16:01:13 +01:00
ret = data - > tmu_initialize ( pdev ) ;
2011-09-07 18:49:08 +09:00
clk_disable ( data - > clk ) ;
mutex_unlock ( & data - > lock ) ;
2013-12-19 11:36:31 +05:30
if ( ! IS_ERR ( data - > clk_sec ) )
clk_disable ( data - > clk_sec ) ;
2011-09-07 18:49:08 +09:00
return ret ;
}
2014-11-13 16:01:14 +01:00
static u32 get_con_reg ( struct exynos_tmu_data * data , u32 con )
2011-09-07 18:49:08 +09:00
{
2012-08-16 17:11:42 +05:30
struct exynos_tmu_platform_data * pdata = data - > pdata ;
2011-09-07 18:49:08 +09:00
2014-11-13 16:01:25 +01:00
if ( data - > soc = = SOC_ARCH_EXYNOS4412 | |
data - > soc = = SOC_ARCH_EXYNOS3250 )
con | = ( EXYNOS4412_MUX_ADDR_VALUE < < EXYNOS4412_MUX_ADDR_SHIFT ) ;
2013-10-09 08:29:52 +02:00
2014-07-31 19:11:06 +02:00
con & = ~ ( EXYNOS_TMU_REF_VOLTAGE_MASK < < EXYNOS_TMU_REF_VOLTAGE_SHIFT ) ;
con | = pdata - > reference_voltage < < EXYNOS_TMU_REF_VOLTAGE_SHIFT ;
2013-06-24 16:20:29 +05:30
2014-07-31 19:11:06 +02:00
con & = ~ ( EXYNOS_TMU_BUF_SLOPE_SEL_MASK < < EXYNOS_TMU_BUF_SLOPE_SEL_SHIFT ) ;
con | = ( pdata - > gain < < EXYNOS_TMU_BUF_SLOPE_SEL_SHIFT ) ;
2013-06-24 16:20:29 +05:30
if ( pdata - > noise_cancel_mode ) {
2014-11-13 16:01:01 +01:00
con & = ~ ( EXYNOS_TMU_TRIP_MODE_MASK < < EXYNOS_TMU_TRIP_MODE_SHIFT ) ;
con | = ( pdata - > noise_cancel_mode < < EXYNOS_TMU_TRIP_MODE_SHIFT ) ;
2012-08-16 17:11:42 +05:30
}
2014-11-13 16:01:14 +01:00
return con ;
}
static void exynos_tmu_control ( struct platform_device * pdev , bool on )
{
struct exynos_tmu_data * data = platform_get_drvdata ( pdev ) ;
2011-09-07 18:49:08 +09:00
2014-11-13 16:01:14 +01:00
mutex_lock ( & data - > lock ) ;
clk_enable ( data - > clk ) ;
2014-11-13 16:01:15 +01:00
data - > tmu_control ( pdev , on ) ;
2018-04-16 12:11:52 +02:00
data - > enabled = on ;
2011-09-07 18:49:08 +09:00
clk_disable ( data - > clk ) ;
mutex_unlock ( & data - > lock ) ;
}
2014-11-13 16:01:13 +01:00
static int exynos4210_tmu_initialize ( struct platform_device * pdev )
2011-09-07 18:49:08 +09:00
{
2014-11-13 16:01:13 +01:00
struct exynos_tmu_data * data = platform_get_drvdata ( pdev ) ;
2015-01-23 13:10:08 +01:00
struct thermal_zone_device * tz = data - > tzd ;
const struct thermal_trip * const trips =
of_thermal_get_trip_points ( tz ) ;
2014-11-13 16:01:13 +01:00
int ret = 0 , threshold_code , i ;
2015-01-23 13:10:08 +01:00
unsigned long reference , temp ;
unsigned int status ;
if ( ! trips ) {
pr_err ( " %s: Cannot get trip points from of-thermal.c! \n " ,
__func__ ) ;
ret = - ENODEV ;
goto out ;
}
2011-09-07 18:49:08 +09:00
2014-11-13 16:01:13 +01:00
status = readb ( data - > base + EXYNOS_TMU_REG_STATUS ) ;
if ( ! status ) {
ret = - EBUSY ;
goto out ;
}
2011-09-07 18:49:08 +09:00
2014-11-13 16:01:13 +01:00
sanitize_temp_error ( data , readl ( data - > base + EXYNOS_TMU_REG_TRIMINFO ) ) ;
2011-09-07 18:49:08 +09:00
2014-11-13 16:01:13 +01:00
/* Write temperature code for threshold */
2015-01-23 13:10:08 +01:00
reference = trips [ 0 ] . temperature / MCELSIUS ;
threshold_code = temp_to_code ( data , reference ) ;
if ( threshold_code < 0 ) {
ret = threshold_code ;
goto out ;
}
2014-11-13 16:01:13 +01:00
writeb ( threshold_code , data - > base + EXYNOS4210_TMU_REG_THRESHOLD_TEMP ) ;
2015-01-23 13:10:08 +01:00
for ( i = 0 ; i < of_thermal_get_ntrips ( tz ) ; i + + ) {
temp = trips [ i ] . temperature / MCELSIUS ;
writeb ( temp - reference , data - > base +
2014-11-13 16:01:13 +01:00
EXYNOS4210_TMU_REG_TRIG_LEVEL0 + i * 4 ) ;
2015-01-23 13:10:08 +01:00
}
2014-11-13 16:01:13 +01:00
2014-11-13 16:01:19 +01:00
data - > tmu_clear_irqs ( data ) ;
2014-11-13 16:01:13 +01:00
out :
return ret ;
}
static int exynos4412_tmu_initialize ( struct platform_device * pdev )
{
struct exynos_tmu_data * data = platform_get_drvdata ( pdev ) ;
2015-01-23 13:10:08 +01:00
const struct thermal_trip * const trips =
of_thermal_get_trip_points ( data - > tzd ) ;
2014-11-13 16:01:13 +01:00
unsigned int status , trim_info , con , ctrl , rising_threshold ;
int ret = 0 , threshold_code , i ;
2015-01-23 13:10:08 +01:00
unsigned long crit_temp = 0 ;
2014-11-13 16:01:13 +01:00
status = readb ( data - > base + EXYNOS_TMU_REG_STATUS ) ;
if ( ! status ) {
ret = - EBUSY ;
goto out ;
}
if ( data - > soc = = SOC_ARCH_EXYNOS3250 | |
data - > soc = = SOC_ARCH_EXYNOS4412 | |
data - > soc = = SOC_ARCH_EXYNOS5250 ) {
if ( data - > soc = = SOC_ARCH_EXYNOS3250 ) {
ctrl = readl ( data - > base + EXYNOS_TMU_TRIMINFO_CON1 ) ;
ctrl | = EXYNOS_TRIMINFO_RELOAD_ENABLE ;
writel ( ctrl , data - > base + EXYNOS_TMU_TRIMINFO_CON1 ) ;
2014-07-31 19:11:03 +02:00
}
2014-11-13 16:01:13 +01:00
ctrl = readl ( data - > base + EXYNOS_TMU_TRIMINFO_CON2 ) ;
ctrl | = EXYNOS_TRIMINFO_RELOAD_ENABLE ;
writel ( ctrl , data - > base + EXYNOS_TMU_TRIMINFO_CON2 ) ;
}
2014-07-31 19:11:03 +02:00
2014-11-13 16:01:13 +01:00
/* On exynos5420 the triminfo register is in the shared space */
if ( data - > soc = = SOC_ARCH_EXYNOS5420_TRIMINFO )
trim_info = readl ( data - > base_second + EXYNOS_TMU_REG_TRIMINFO ) ;
else
trim_info = readl ( data - > base + EXYNOS_TMU_REG_TRIMINFO ) ;
sanitize_temp_error ( data , trim_info ) ;
/* Write temperature code for rising and falling threshold */
rising_threshold = readl ( data - > base + EXYNOS_THD_TEMP_RISE ) ;
rising_threshold = get_th_reg ( data , rising_threshold , false ) ;
writel ( rising_threshold , data - > base + EXYNOS_THD_TEMP_RISE ) ;
writel ( get_th_reg ( data , 0 , true ) , data - > base + EXYNOS_THD_TEMP_FALL ) ;
2014-11-13 16:01:19 +01:00
data - > tmu_clear_irqs ( data ) ;
2014-11-13 16:01:13 +01:00
/* if last threshold limit is also present */
2015-01-23 13:10:08 +01:00
for ( i = 0 ; i < of_thermal_get_ntrips ( data - > tzd ) ; i + + ) {
if ( trips [ i ] . type = = THERMAL_TRIP_CRITICAL ) {
crit_temp = trips [ i ] . temperature ;
break ;
}
2014-11-13 16:01:13 +01:00
}
2015-01-23 13:10:08 +01:00
if ( i = = of_thermal_get_ntrips ( data - > tzd ) ) {
pr_err ( " %s: No CRITICAL trip point defined at of-thermal.c! \n " ,
__func__ ) ;
ret = - EINVAL ;
goto out ;
}
threshold_code = temp_to_code ( data , crit_temp / MCELSIUS ) ;
/* 1-4 level to be assigned in th0 reg */
rising_threshold & = ~ ( 0xff < < 8 * i ) ;
rising_threshold | = threshold_code < < 8 * i ;
writel ( rising_threshold , data - > base + EXYNOS_THD_TEMP_RISE ) ;
con = readl ( data - > base + EXYNOS_TMU_REG_CONTROL ) ;
con | = ( 1 < < EXYNOS_TMU_THERM_TRIP_EN_SHIFT ) ;
writel ( con , data - > base + EXYNOS_TMU_REG_CONTROL ) ;
2014-07-31 19:11:03 +02:00
out :
2014-11-13 16:01:13 +01:00
return ret ;
}
2011-09-07 18:49:08 +09:00
2015-03-10 11:23:44 +09:00
static int exynos5433_tmu_initialize ( struct platform_device * pdev )
{
struct exynos_tmu_data * data = platform_get_drvdata ( pdev ) ;
struct exynos_tmu_platform_data * pdata = data - > pdata ;
struct thermal_zone_device * tz = data - > tzd ;
unsigned int status , trim_info ;
unsigned int rising_threshold = 0 , falling_threshold = 0 ;
2015-07-24 08:12:54 +02:00
int temp , temp_hist ;
2015-03-10 11:23:44 +09:00
int ret = 0 , threshold_code , i , sensor_id , cal_type ;
status = readb ( data - > base + EXYNOS_TMU_REG_STATUS ) ;
if ( ! status ) {
ret = - EBUSY ;
goto out ;
}
trim_info = readl ( data - > base + EXYNOS_TMU_REG_TRIMINFO ) ;
sanitize_temp_error ( data , trim_info ) ;
/* Read the temperature sensor id */
sensor_id = ( trim_info & EXYNOS5433_TRIMINFO_SENSOR_ID_MASK )
> > EXYNOS5433_TRIMINFO_SENSOR_ID_SHIFT ;
dev_info ( & pdev - > dev , " Temperature sensor ID: 0x%x \n " , sensor_id ) ;
/* Read the calibration mode */
writel ( trim_info , data - > base + EXYNOS_TMU_REG_TRIMINFO ) ;
cal_type = ( trim_info & EXYNOS5433_TRIMINFO_CALIB_SEL_MASK )
> > EXYNOS5433_TRIMINFO_CALIB_SEL_SHIFT ;
switch ( cal_type ) {
case EXYNOS5433_TRIMINFO_ONE_POINT_TRIMMING :
pdata - > cal_type = TYPE_ONE_POINT_TRIMMING ;
break ;
case EXYNOS5433_TRIMINFO_TWO_POINT_TRIMMING :
pdata - > cal_type = TYPE_TWO_POINT_TRIMMING ;
break ;
default :
pdata - > cal_type = TYPE_ONE_POINT_TRIMMING ;
break ;
2015-10-08 14:34:05 +09:00
}
2015-03-10 11:23:44 +09:00
dev_info ( & pdev - > dev , " Calibration type is %d-point calibration \n " ,
cal_type ? 2 : 1 ) ;
/* Write temperature code for rising and falling threshold */
for ( i = 0 ; i < of_thermal_get_ntrips ( tz ) ; i + + ) {
int rising_reg_offset , falling_reg_offset ;
int j = 0 ;
switch ( i ) {
case 0 :
case 1 :
case 2 :
case 3 :
rising_reg_offset = EXYNOS5433_THD_TEMP_RISE3_0 ;
falling_reg_offset = EXYNOS5433_THD_TEMP_FALL3_0 ;
j = i ;
break ;
case 4 :
case 5 :
case 6 :
case 7 :
rising_reg_offset = EXYNOS5433_THD_TEMP_RISE7_4 ;
falling_reg_offset = EXYNOS5433_THD_TEMP_FALL7_4 ;
j = i - 4 ;
break ;
default :
continue ;
}
/* Write temperature code for rising threshold */
tz - > ops - > get_trip_temp ( tz , i , & temp ) ;
temp / = MCELSIUS ;
threshold_code = temp_to_code ( data , temp ) ;
rising_threshold = readl ( data - > base + rising_reg_offset ) ;
rising_threshold | = ( threshold_code < < j * 8 ) ;
writel ( rising_threshold , data - > base + rising_reg_offset ) ;
/* Write temperature code for falling threshold */
tz - > ops - > get_trip_hyst ( tz , i , & temp_hist ) ;
temp_hist = temp - ( temp_hist / MCELSIUS ) ;
threshold_code = temp_to_code ( data , temp_hist ) ;
falling_threshold = readl ( data - > base + falling_reg_offset ) ;
falling_threshold & = ~ ( 0xff < < j * 8 ) ;
falling_threshold | = ( threshold_code < < j * 8 ) ;
writel ( falling_threshold , data - > base + falling_reg_offset ) ;
}
data - > tmu_clear_irqs ( data ) ;
out :
return ret ;
}
2014-11-13 16:01:13 +01:00
static int exynos5440_tmu_initialize ( struct platform_device * pdev )
{
struct exynos_tmu_data * data = platform_get_drvdata ( pdev ) ;
unsigned int trim_info = 0 , con , rising_threshold ;
2015-10-08 14:34:06 +09:00
int threshold_code ;
2015-07-24 08:12:54 +02:00
int crit_temp = 0 ;
2014-11-13 16:01:13 +01:00
/*
* For exynos5440 soc triminfo value is swapped between TMU0 and
* TMU2 , so the below logic is needed .
*/
switch ( data - > id ) {
case 0 :
trim_info = readl ( data - > base + EXYNOS5440_EFUSE_SWAP_OFFSET +
EXYNOS5440_TMU_S0_7_TRIM ) ;
break ;
case 1 :
trim_info = readl ( data - > base + EXYNOS5440_TMU_S0_7_TRIM ) ;
break ;
case 2 :
trim_info = readl ( data - > base - EXYNOS5440_EFUSE_SWAP_OFFSET +
EXYNOS5440_TMU_S0_7_TRIM ) ;
}
sanitize_temp_error ( data , trim_info ) ;
/* Write temperature code for rising and falling threshold */
rising_threshold = readl ( data - > base + EXYNOS5440_TMU_S0_7_TH0 ) ;
rising_threshold = get_th_reg ( data , rising_threshold , false ) ;
writel ( rising_threshold , data - > base + EXYNOS5440_TMU_S0_7_TH0 ) ;
writel ( 0 , data - > base + EXYNOS5440_TMU_S0_7_TH1 ) ;
2014-11-13 16:01:19 +01:00
data - > tmu_clear_irqs ( data ) ;
2014-11-13 16:01:13 +01:00
/* if last threshold limit is also present */
2015-01-23 13:10:08 +01:00
if ( ! data - > tzd - > ops - > get_crit_temp ( data - > tzd , & crit_temp ) ) {
threshold_code = temp_to_code ( data , crit_temp / MCELSIUS ) ;
2014-11-13 16:01:13 +01:00
/* 5th level to be assigned in th2 reg */
rising_threshold =
threshold_code < < EXYNOS5440_TMU_TH_RISE4_SHIFT ;
writel ( rising_threshold , data - > base + EXYNOS5440_TMU_S0_7_TH2 ) ;
con = readl ( data - > base + EXYNOS5440_TMU_S0_7_CTRL ) ;
con | = ( 1 < < EXYNOS_TMU_THERM_TRIP_EN_SHIFT ) ;
writel ( con , data - > base + EXYNOS5440_TMU_S0_7_CTRL ) ;
}
/* Clear the PMIN in the common TMU register */
if ( ! data - > id )
writel ( 0 , data - > base_second + EXYNOS5440_TMU_PMIN ) ;
2015-10-08 14:34:06 +09:00
return 0 ;
2011-09-07 18:49:08 +09:00
}
2015-01-27 11:18:22 +05:30
static int exynos7_tmu_initialize ( struct platform_device * pdev )
{
struct exynos_tmu_data * data = platform_get_drvdata ( pdev ) ;
struct thermal_zone_device * tz = data - > tzd ;
struct exynos_tmu_platform_data * pdata = data - > pdata ;
unsigned int status , trim_info ;
unsigned int rising_threshold = 0 , falling_threshold = 0 ;
int ret = 0 , threshold_code , i ;
2015-07-24 08:12:54 +02:00
int temp , temp_hist ;
2015-01-27 11:18:22 +05:30
unsigned int reg_off , bit_off ;
status = readb ( data - > base + EXYNOS_TMU_REG_STATUS ) ;
if ( ! status ) {
ret = - EBUSY ;
goto out ;
}
trim_info = readl ( data - > base + EXYNOS_TMU_REG_TRIMINFO ) ;
data - > temp_error1 = trim_info & EXYNOS7_TMU_TEMP_MASK ;
if ( ! data - > temp_error1 | |
( pdata - > min_efuse_value > data - > temp_error1 ) | |
( data - > temp_error1 > pdata - > max_efuse_value ) )
data - > temp_error1 = pdata - > efuse_value & EXYNOS_TMU_TEMP_MASK ;
/* Write temperature code for rising and falling threshold */
for ( i = ( of_thermal_get_ntrips ( tz ) - 1 ) ; i > = 0 ; i - - ) {
/*
* On exynos7 there are 4 rising and 4 falling threshold
* registers ( 0x50 - 0x5c and 0x60 - 0x6c respectively ) . Each
* register holds the value of two threshold levels ( at bit
* offsets 0 and 16 ) . Based on the fact that there are atmost
* eight possible trigger levels , calculate the register and
* bit offsets where the threshold levels are to be written .
*
* e . g . EXYNOS7_THD_TEMP_RISE7_6 ( 0x50 )
* [ 24 : 16 ] - Threshold level 7
* [ 8 : 0 ] - Threshold level 6
* e . g . EXYNOS7_THD_TEMP_RISE5_4 ( 0x54 )
* [ 24 : 16 ] - Threshold level 5
* [ 8 : 0 ] - Threshold level 4
*
* and similarly for falling thresholds .
*
* Based on the above , calculate the register and bit offsets
* for rising / falling threshold levels and populate them .
*/
reg_off = ( ( 7 - i ) / 2 ) * 4 ;
bit_off = ( ( 8 - i ) % 2 ) ;
tz - > ops - > get_trip_temp ( tz , i , & temp ) ;
temp / = MCELSIUS ;
tz - > ops - > get_trip_hyst ( tz , i , & temp_hist ) ;
temp_hist = temp - ( temp_hist / MCELSIUS ) ;
/* Set 9-bit temperature code for rising threshold levels */
threshold_code = temp_to_code ( data , temp ) ;
rising_threshold = readl ( data - > base +
EXYNOS7_THD_TEMP_RISE7_6 + reg_off ) ;
rising_threshold & = ~ ( EXYNOS7_TMU_TEMP_MASK < < ( 16 * bit_off ) ) ;
rising_threshold | = threshold_code < < ( 16 * bit_off ) ;
writel ( rising_threshold ,
data - > base + EXYNOS7_THD_TEMP_RISE7_6 + reg_off ) ;
/* Set 9-bit temperature code for falling threshold levels */
threshold_code = temp_to_code ( data , temp_hist ) ;
falling_threshold & = ~ ( EXYNOS7_TMU_TEMP_MASK < < ( 16 * bit_off ) ) ;
falling_threshold | = threshold_code < < ( 16 * bit_off ) ;
writel ( falling_threshold ,
data - > base + EXYNOS7_THD_TEMP_FALL7_6 + reg_off ) ;
}
data - > tmu_clear_irqs ( data ) ;
out :
return ret ;
}
2014-11-13 16:01:15 +01:00
static void exynos4210_tmu_control ( struct platform_device * pdev , bool on )
2013-02-11 03:54:23 +00:00
{
2014-11-13 16:01:15 +01:00
struct exynos_tmu_data * data = platform_get_drvdata ( pdev ) ;
2015-01-23 13:10:08 +01:00
struct thermal_zone_device * tz = data - > tzd ;
2014-11-13 16:01:15 +01:00
unsigned int con , interrupt_en ;
2013-02-11 03:54:23 +00:00
2014-11-13 16:01:15 +01:00
con = get_con_reg ( data , readl ( data - > base + EXYNOS_TMU_REG_CONTROL ) ) ;
2013-02-11 03:54:23 +00:00
2014-11-13 16:01:15 +01:00
if ( on ) {
con | = ( 1 < < EXYNOS_TMU_CORE_EN_SHIFT ) ;
interrupt_en =
2015-01-23 13:10:08 +01:00
( of_thermal_is_trip_valid ( tz , 3 )
< < EXYNOS_TMU_INTEN_RISE3_SHIFT ) |
( of_thermal_is_trip_valid ( tz , 2 )
< < EXYNOS_TMU_INTEN_RISE2_SHIFT ) |
( of_thermal_is_trip_valid ( tz , 1 )
< < EXYNOS_TMU_INTEN_RISE1_SHIFT ) |
( of_thermal_is_trip_valid ( tz , 0 )
< < EXYNOS_TMU_INTEN_RISE0_SHIFT ) ;
2014-11-13 16:01:20 +01:00
if ( data - > soc ! = SOC_ARCH_EXYNOS4210 )
2014-11-13 16:01:15 +01:00
interrupt_en | =
interrupt_en < < EXYNOS_TMU_INTEN_FALL0_SHIFT ;
} else {
con & = ~ ( 1 < < EXYNOS_TMU_CORE_EN_SHIFT ) ;
interrupt_en = 0 ; /* Disable all interrupts */
}
writel ( interrupt_en , data - > base + EXYNOS_TMU_REG_INTEN ) ;
writel ( con , data - > base + EXYNOS_TMU_REG_CONTROL ) ;
}
2015-03-10 11:23:44 +09:00
static void exynos5433_tmu_control ( struct platform_device * pdev , bool on )
{
struct exynos_tmu_data * data = platform_get_drvdata ( pdev ) ;
struct thermal_zone_device * tz = data - > tzd ;
unsigned int con , interrupt_en , pd_det_en ;
con = get_con_reg ( data , readl ( data - > base + EXYNOS_TMU_REG_CONTROL ) ) ;
if ( on ) {
con | = ( 1 < < EXYNOS_TMU_CORE_EN_SHIFT ) ;
interrupt_en =
( of_thermal_is_trip_valid ( tz , 7 )
< < EXYNOS7_TMU_INTEN_RISE7_SHIFT ) |
( of_thermal_is_trip_valid ( tz , 6 )
< < EXYNOS7_TMU_INTEN_RISE6_SHIFT ) |
( of_thermal_is_trip_valid ( tz , 5 )
< < EXYNOS7_TMU_INTEN_RISE5_SHIFT ) |
( of_thermal_is_trip_valid ( tz , 4 )
< < EXYNOS7_TMU_INTEN_RISE4_SHIFT ) |
( of_thermal_is_trip_valid ( tz , 3 )
< < EXYNOS7_TMU_INTEN_RISE3_SHIFT ) |
( of_thermal_is_trip_valid ( tz , 2 )
< < EXYNOS7_TMU_INTEN_RISE2_SHIFT ) |
( of_thermal_is_trip_valid ( tz , 1 )
< < EXYNOS7_TMU_INTEN_RISE1_SHIFT ) |
( of_thermal_is_trip_valid ( tz , 0 )
< < EXYNOS7_TMU_INTEN_RISE0_SHIFT ) ;
interrupt_en | =
interrupt_en < < EXYNOS_TMU_INTEN_FALL0_SHIFT ;
} else {
con & = ~ ( 1 < < EXYNOS_TMU_CORE_EN_SHIFT ) ;
interrupt_en = 0 ; /* Disable all interrupts */
}
pd_det_en = on ? EXYNOS5433_PD_DET_EN : 0 ;
writel ( pd_det_en , data - > base + EXYNOS5433_TMU_PD_DET_EN ) ;
writel ( interrupt_en , data - > base + EXYNOS5433_TMU_REG_INTEN ) ;
writel ( con , data - > base + EXYNOS_TMU_REG_CONTROL ) ;
}
2014-11-13 16:01:15 +01:00
static void exynos5440_tmu_control ( struct platform_device * pdev , bool on )
{
struct exynos_tmu_data * data = platform_get_drvdata ( pdev ) ;
2015-01-23 13:10:08 +01:00
struct thermal_zone_device * tz = data - > tzd ;
2014-11-13 16:01:15 +01:00
unsigned int con , interrupt_en ;
con = get_con_reg ( data , readl ( data - > base + EXYNOS5440_TMU_S0_7_CTRL ) ) ;
if ( on ) {
con | = ( 1 < < EXYNOS_TMU_CORE_EN_SHIFT ) ;
interrupt_en =
2015-01-23 13:10:08 +01:00
( of_thermal_is_trip_valid ( tz , 3 )
< < EXYNOS5440_TMU_INTEN_RISE3_SHIFT ) |
( of_thermal_is_trip_valid ( tz , 2 )
< < EXYNOS5440_TMU_INTEN_RISE2_SHIFT ) |
( of_thermal_is_trip_valid ( tz , 1 )
< < EXYNOS5440_TMU_INTEN_RISE1_SHIFT ) |
( of_thermal_is_trip_valid ( tz , 0 )
< < EXYNOS5440_TMU_INTEN_RISE0_SHIFT ) ;
interrupt_en | =
interrupt_en < < EXYNOS5440_TMU_INTEN_FALL0_SHIFT ;
2014-11-13 16:01:15 +01:00
} else {
con & = ~ ( 1 < < EXYNOS_TMU_CORE_EN_SHIFT ) ;
interrupt_en = 0 ; /* Disable all interrupts */
}
writel ( interrupt_en , data - > base + EXYNOS5440_TMU_S0_7_IRQEN ) ;
writel ( con , data - > base + EXYNOS5440_TMU_S0_7_CTRL ) ;
}
2015-01-27 11:18:22 +05:30
static void exynos7_tmu_control ( struct platform_device * pdev , bool on )
{
struct exynos_tmu_data * data = platform_get_drvdata ( pdev ) ;
struct thermal_zone_device * tz = data - > tzd ;
unsigned int con , interrupt_en ;
con = get_con_reg ( data , readl ( data - > base + EXYNOS_TMU_REG_CONTROL ) ) ;
if ( on ) {
con | = ( 1 < < EXYNOS_TMU_CORE_EN_SHIFT ) ;
2015-02-24 13:56:54 +09:00
con | = ( 1 < < EXYNOS7_PD_DET_EN_SHIFT ) ;
2015-01-27 11:18:22 +05:30
interrupt_en =
( of_thermal_is_trip_valid ( tz , 7 )
< < EXYNOS7_TMU_INTEN_RISE7_SHIFT ) |
( of_thermal_is_trip_valid ( tz , 6 )
< < EXYNOS7_TMU_INTEN_RISE6_SHIFT ) |
( of_thermal_is_trip_valid ( tz , 5 )
< < EXYNOS7_TMU_INTEN_RISE5_SHIFT ) |
( of_thermal_is_trip_valid ( tz , 4 )
< < EXYNOS7_TMU_INTEN_RISE4_SHIFT ) |
( of_thermal_is_trip_valid ( tz , 3 )
< < EXYNOS7_TMU_INTEN_RISE3_SHIFT ) |
( of_thermal_is_trip_valid ( tz , 2 )
< < EXYNOS7_TMU_INTEN_RISE2_SHIFT ) |
( of_thermal_is_trip_valid ( tz , 1 )
< < EXYNOS7_TMU_INTEN_RISE1_SHIFT ) |
( of_thermal_is_trip_valid ( tz , 0 )
< < EXYNOS7_TMU_INTEN_RISE0_SHIFT ) ;
interrupt_en | =
interrupt_en < < EXYNOS_TMU_INTEN_FALL0_SHIFT ;
} else {
con & = ~ ( 1 < < EXYNOS_TMU_CORE_EN_SHIFT ) ;
2015-02-24 13:56:54 +09:00
con & = ~ ( 1 < < EXYNOS7_PD_DET_EN_SHIFT ) ;
2015-01-27 11:18:22 +05:30
interrupt_en = 0 ; /* Disable all interrupts */
}
writel ( interrupt_en , data - > base + EXYNOS7_TMU_REG_INTEN ) ;
writel ( con , data - > base + EXYNOS_TMU_REG_CONTROL ) ;
}
2015-07-24 08:12:54 +02:00
static int exynos_get_temp ( void * p , int * temp )
2011-09-07 18:49:08 +09:00
{
2015-01-23 13:10:08 +01:00
struct exynos_tmu_data * data = p ;
2018-04-16 12:11:53 +02:00
int value , ret = 0 ;
2015-01-23 13:10:08 +01:00
2018-04-16 12:11:52 +02:00
if ( ! data | | ! data - > tmu_read | | ! data - > enabled )
2015-01-23 13:10:08 +01:00
return - EINVAL ;
2013-02-11 03:54:23 +00:00
mutex_lock ( & data - > lock ) ;
clk_enable ( data - > clk ) ;
2015-01-23 13:10:08 +01:00
2018-04-16 12:11:53 +02:00
value = data - > tmu_read ( data ) ;
if ( value < 0 )
ret = value ;
else
* temp = code_to_temp ( data , value ) * MCELSIUS ;
2015-01-23 13:10:08 +01:00
2011-09-07 18:49:08 +09:00
clk_disable ( data - > clk ) ;
mutex_unlock ( & data - > lock ) ;
2013-02-11 03:54:23 +00:00
2018-04-16 12:11:53 +02:00
return ret ;
2011-09-07 18:49:08 +09:00
}
2013-02-11 03:54:23 +00:00
# ifdef CONFIG_THERMAL_EMULATION
2014-11-13 16:01:17 +01:00
static u32 get_emul_con_reg ( struct exynos_tmu_data * data , unsigned int val ,
2015-07-24 08:12:54 +02:00
int temp )
2014-11-13 16:01:17 +01:00
{
2013-02-11 03:54:23 +00:00
if ( temp ) {
temp / = MCELSIUS ;
2014-11-13 16:01:21 +01:00
if ( data - > soc ! = SOC_ARCH_EXYNOS5440 ) {
2014-11-13 16:01:17 +01:00
val & = ~ ( EXYNOS_EMUL_TIME_MASK < < EXYNOS_EMUL_TIME_SHIFT ) ;
val | = ( EXYNOS_EMUL_TIME < < EXYNOS_EMUL_TIME_SHIFT ) ;
2013-06-24 16:20:40 +05:30
}
2015-01-27 11:18:22 +05:30
if ( data - > soc = = SOC_ARCH_EXYNOS7 ) {
val & = ~ ( EXYNOS7_EMUL_DATA_MASK < <
EXYNOS7_EMUL_DATA_SHIFT ) ;
val | = ( temp_to_code ( data , temp ) < <
EXYNOS7_EMUL_DATA_SHIFT ) |
EXYNOS_EMUL_ENABLE ;
} else {
val & = ~ ( EXYNOS_EMUL_DATA_MASK < <
EXYNOS_EMUL_DATA_SHIFT ) ;
val | = ( temp_to_code ( data , temp ) < <
EXYNOS_EMUL_DATA_SHIFT ) |
EXYNOS_EMUL_ENABLE ;
}
2013-02-11 03:54:23 +00:00
} else {
2013-06-24 16:20:31 +05:30
val & = ~ EXYNOS_EMUL_ENABLE ;
2013-02-11 03:54:23 +00:00
}
2014-11-13 16:01:17 +01:00
return val ;
}
2014-11-13 16:01:18 +01:00
static void exynos4412_tmu_set_emulation ( struct exynos_tmu_data * data ,
2015-07-24 08:12:54 +02:00
int temp )
2014-11-13 16:01:18 +01:00
{
unsigned int val ;
u32 emul_con ;
if ( data - > soc = = SOC_ARCH_EXYNOS5260 )
emul_con = EXYNOS5260_EMUL_CON ;
2015-10-17 08:08:56 +09:00
else if ( data - > soc = = SOC_ARCH_EXYNOS5433 )
2015-03-10 11:23:44 +09:00
emul_con = EXYNOS5433_TMU_EMUL_CON ;
2015-01-27 11:18:22 +05:30
else if ( data - > soc = = SOC_ARCH_EXYNOS7 )
emul_con = EXYNOS7_TMU_REG_EMUL_CON ;
2014-11-13 16:01:18 +01:00
else
emul_con = EXYNOS_EMUL_CON ;
val = readl ( data - > base + emul_con ) ;
val = get_emul_con_reg ( data , val , temp ) ;
writel ( val , data - > base + emul_con ) ;
}
static void exynos5440_tmu_set_emulation ( struct exynos_tmu_data * data ,
2015-07-24 08:12:54 +02:00
int temp )
2014-11-13 16:01:18 +01:00
{
unsigned int val ;
val = readl ( data - > base + EXYNOS5440_TMU_S0_7_DEBUG ) ;
val = get_emul_con_reg ( data , val , temp ) ;
writel ( val , data - > base + EXYNOS5440_TMU_S0_7_DEBUG ) ;
}
2015-07-24 08:12:54 +02:00
static int exynos_tmu_set_emulation ( void * drv_data , int temp )
2013-02-11 03:54:23 +00:00
{
struct exynos_tmu_data * data = drv_data ;
int ret = - EINVAL ;
2014-11-13 16:01:22 +01:00
if ( data - > soc = = SOC_ARCH_EXYNOS4210 )
2013-02-11 03:54:23 +00:00
goto out ;
if ( temp & & temp < MCELSIUS )
goto out ;
mutex_lock ( & data - > lock ) ;
clk_enable ( data - > clk ) ;
2014-11-13 16:01:18 +01:00
data - > tmu_set_emulation ( data , temp ) ;
2013-02-11 03:54:23 +00:00
clk_disable ( data - > clk ) ;
mutex_unlock ( & data - > lock ) ;
return 0 ;
out :
return ret ;
}
# else
2014-11-13 16:01:18 +01:00
# define exynos4412_tmu_set_emulation NULL
# define exynos5440_tmu_set_emulation NULL
2015-07-24 08:12:54 +02:00
static int exynos_tmu_set_emulation ( void * drv_data , int temp )
2013-02-11 03:54:23 +00:00
{ return - EINVAL ; }
2015-01-23 13:09:54 +01:00
# endif /* CONFIG_THERMAL_EMULATION */
2013-02-11 03:54:23 +00:00
2014-11-13 16:01:16 +01:00
static int exynos4210_tmu_read ( struct exynos_tmu_data * data )
{
int ret = readb ( data - > base + EXYNOS_TMU_REG_CURRENT_TEMP ) ;
/* "temp_code" should range between 75 and 175 */
return ( ret < 75 | | ret > 175 ) ? - ENODATA : ret ;
}
static int exynos4412_tmu_read ( struct exynos_tmu_data * data )
{
return readb ( data - > base + EXYNOS_TMU_REG_CURRENT_TEMP ) ;
}
static int exynos5440_tmu_read ( struct exynos_tmu_data * data )
{
return readb ( data - > base + EXYNOS5440_TMU_S0_7_TEMP ) ;
}
2015-01-27 11:18:22 +05:30
static int exynos7_tmu_read ( struct exynos_tmu_data * data )
{
return readw ( data - > base + EXYNOS_TMU_REG_CURRENT_TEMP ) &
EXYNOS7_TMU_TEMP_MASK ;
}
2012-08-16 17:11:42 +05:30
static void exynos_tmu_work ( struct work_struct * work )
2011-09-07 18:49:08 +09:00
{
2012-08-16 17:11:42 +05:30
struct exynos_tmu_data * data = container_of ( work ,
struct exynos_tmu_data , irq_work ) ;
2014-10-03 18:17:17 +02:00
unsigned int val_type ;
2013-06-24 16:20:43 +05:30
2013-12-19 11:36:31 +05:30
if ( ! IS_ERR ( data - > clk_sec ) )
clk_enable ( data - > clk_sec ) ;
2013-06-24 16:20:43 +05:30
/* Find which sensor generated this interrupt */
2014-11-13 16:01:05 +01:00
if ( data - > soc = = SOC_ARCH_EXYNOS5440 ) {
val_type = readl ( data - > base_second + EXYNOS5440_TMU_IRQ_STATUS ) ;
2013-06-24 16:20:43 +05:30
if ( ! ( ( val_type > > data - > id ) & 0x1 ) )
goto out ;
}
2013-12-19 11:36:31 +05:30
if ( ! IS_ERR ( data - > clk_sec ) )
clk_disable ( data - > clk_sec ) ;
2011-09-07 18:49:08 +09:00
2015-01-23 13:10:08 +01:00
exynos_report_trigger ( data ) ;
2011-09-07 18:49:08 +09:00
mutex_lock ( & data - > lock ) ;
clk_enable ( data - > clk ) ;
2013-06-24 16:20:31 +05:30
2013-06-24 16:20:33 +05:30
/* TODO: take action based on particular interrupt */
2014-11-13 16:01:19 +01:00
data - > tmu_clear_irqs ( data ) ;
2013-06-24 16:20:31 +05:30
2011-09-07 18:49:08 +09:00
clk_disable ( data - > clk ) ;
mutex_unlock ( & data - > lock ) ;
2013-06-24 16:20:43 +05:30
out :
2012-08-16 17:11:42 +05:30
enable_irq ( data - > irq ) ;
2011-09-07 18:49:08 +09:00
}
2014-11-13 16:01:19 +01:00
static void exynos4210_tmu_clear_irqs ( struct exynos_tmu_data * data )
{
unsigned int val_irq ;
u32 tmu_intstat , tmu_intclear ;
if ( data - > soc = = SOC_ARCH_EXYNOS5260 ) {
tmu_intstat = EXYNOS5260_TMU_REG_INTSTAT ;
tmu_intclear = EXYNOS5260_TMU_REG_INTCLEAR ;
2015-01-27 11:18:22 +05:30
} else if ( data - > soc = = SOC_ARCH_EXYNOS7 ) {
tmu_intstat = EXYNOS7_TMU_REG_INTPEND ;
tmu_intclear = EXYNOS7_TMU_REG_INTPEND ;
2015-03-10 11:23:44 +09:00
} else if ( data - > soc = = SOC_ARCH_EXYNOS5433 ) {
tmu_intstat = EXYNOS5433_TMU_REG_INTPEND ;
tmu_intclear = EXYNOS5433_TMU_REG_INTPEND ;
2014-11-13 16:01:19 +01:00
} else {
tmu_intstat = EXYNOS_TMU_REG_INTSTAT ;
tmu_intclear = EXYNOS_TMU_REG_INTCLEAR ;
}
val_irq = readl ( data - > base + tmu_intstat ) ;
/*
* Clear the interrupts . Please note that the documentation for
* Exynos3250 , Exynos4412 , Exynos5250 and Exynos5260 incorrectly
* states that INTCLEAR register has a different placing of bits
* responsible for FALL IRQs than INTSTAT register . Exynos5420
* and Exynos5440 documentation is correct ( Exynos4210 doesn ' t
* support FALL IRQs at all ) .
*/
writel ( val_irq , data - > base + tmu_intclear ) ;
}
static void exynos5440_tmu_clear_irqs ( struct exynos_tmu_data * data )
{
unsigned int val_irq ;
val_irq = readl ( data - > base + EXYNOS5440_TMU_S0_7_IRQ ) ;
/* clear the interrupts */
writel ( val_irq , data - > base + EXYNOS5440_TMU_S0_7_IRQ ) ;
}
2012-08-16 17:11:42 +05:30
static irqreturn_t exynos_tmu_irq ( int irq , void * id )
2011-09-07 18:49:08 +09:00
{
2012-08-16 17:11:42 +05:30
struct exynos_tmu_data * data = id ;
2011-09-07 18:49:08 +09:00
disable_irq_nosync ( irq ) ;
schedule_work ( & data - > irq_work ) ;
return IRQ_HANDLED ;
}
2012-08-16 17:11:44 +05:30
static const struct of_device_id exynos_tmu_match [ ] = {
2018-04-16 12:11:54 +02:00
{
. compatible = " samsung,exynos3250-tmu " ,
. data = ( const void * ) SOC_ARCH_EXYNOS3250 ,
} , {
. compatible = " samsung,exynos4210-tmu " ,
. data = ( const void * ) SOC_ARCH_EXYNOS4210 ,
} , {
. compatible = " samsung,exynos4412-tmu " ,
. data = ( const void * ) SOC_ARCH_EXYNOS4412 ,
} , {
. compatible = " samsung,exynos5250-tmu " ,
. data = ( const void * ) SOC_ARCH_EXYNOS5250 ,
} , {
. compatible = " samsung,exynos5260-tmu " ,
. data = ( const void * ) SOC_ARCH_EXYNOS5260 ,
} , {
. compatible = " samsung,exynos5420-tmu " ,
. data = ( const void * ) SOC_ARCH_EXYNOS5420 ,
} , {
. compatible = " samsung,exynos5420-tmu-ext-triminfo " ,
. data = ( const void * ) SOC_ARCH_EXYNOS5420_TRIMINFO ,
} , {
. compatible = " samsung,exynos5433-tmu " ,
. data = ( const void * ) SOC_ARCH_EXYNOS5433 ,
} , {
. compatible = " samsung,exynos5440-tmu " ,
. data = ( const void * ) SOC_ARCH_EXYNOS5440 ,
} , {
. compatible = " samsung,exynos7-tmu " ,
. data = ( const void * ) SOC_ARCH_EXYNOS7 ,
} ,
{ } ,
2012-08-16 17:11:44 +05:30
} ;
MODULE_DEVICE_TABLE ( of , exynos_tmu_match ) ;
2015-01-23 13:10:08 +01:00
static int exynos_of_sensor_conf ( struct device_node * np ,
struct exynos_tmu_platform_data * pdata )
{
u32 value ;
int ret ;
of_node_get ( np ) ;
ret = of_property_read_u32 ( np , " samsung,tmu_gain " , & value ) ;
pdata - > gain = ( u8 ) value ;
of_property_read_u32 ( np , " samsung,tmu_reference_voltage " , & value ) ;
pdata - > reference_voltage = ( u8 ) value ;
of_property_read_u32 ( np , " samsung,tmu_noise_cancel_mode " , & value ) ;
pdata - > noise_cancel_mode = ( u8 ) value ;
of_property_read_u32 ( np , " samsung,tmu_efuse_value " ,
& pdata - > efuse_value ) ;
of_property_read_u32 ( np , " samsung,tmu_min_efuse_value " ,
& pdata - > min_efuse_value ) ;
of_property_read_u32 ( np , " samsung,tmu_max_efuse_value " ,
& pdata - > max_efuse_value ) ;
of_property_read_u32 ( np , " samsung,tmu_first_point_trim " , & value ) ;
pdata - > first_point_trim = ( u8 ) value ;
of_property_read_u32 ( np , " samsung,tmu_second_point_trim " , & value ) ;
pdata - > second_point_trim = ( u8 ) value ;
of_property_read_u32 ( np , " samsung,tmu_default_temp_offset " , & value ) ;
pdata - > default_temp_offset = ( u8 ) value ;
of_property_read_u32 ( np , " samsung,tmu_cal_type " , & pdata - > cal_type ) ;
of_node_put ( np ) ;
return 0 ;
2012-08-16 17:11:43 +05:30
}
2012-11-21 13:31:01 +09:00
2013-06-24 16:20:39 +05:30
static int exynos_map_dt_data ( struct platform_device * pdev )
2011-09-07 18:49:08 +09:00
{
2013-06-24 16:20:39 +05:30
struct exynos_tmu_data * data = platform_get_drvdata ( pdev ) ;
struct exynos_tmu_platform_data * pdata ;
struct resource res ;
2013-08-19 11:58:43 +05:30
if ( ! data | | ! pdev - > dev . of_node )
2013-06-24 16:20:39 +05:30
return - ENODEV ;
2011-09-07 18:49:08 +09:00
2013-06-24 16:20:39 +05:30
data - > id = of_alias_get_id ( pdev - > dev . of_node , " tmuctrl " ) ;
if ( data - > id < 0 )
data - > id = 0 ;
2012-08-16 17:11:44 +05:30
2013-06-24 16:20:39 +05:30
data - > irq = irq_of_parse_and_map ( pdev - > dev . of_node , 0 ) ;
if ( data - > irq < = 0 ) {
dev_err ( & pdev - > dev , " failed to get IRQ \n " ) ;
return - ENODEV ;
}
if ( of_address_to_resource ( pdev - > dev . of_node , 0 , & res ) ) {
dev_err ( & pdev - > dev , " failed to get Resource 0 \n " ) ;
return - ENODEV ;
}
data - > base = devm_ioremap ( & pdev - > dev , res . start , resource_size ( & res ) ) ;
if ( ! data - > base ) {
dev_err ( & pdev - > dev , " Failed to ioremap memory \n " ) ;
return - EADDRNOTAVAIL ;
}
2015-01-23 13:10:08 +01:00
pdata = devm_kzalloc ( & pdev - > dev ,
sizeof ( struct exynos_tmu_platform_data ) ,
GFP_KERNEL ) ;
if ( ! pdata )
return - ENOMEM ;
2014-11-13 16:01:23 +01:00
2015-01-23 13:10:08 +01:00
exynos_of_sensor_conf ( pdev - > dev . of_node , pdata ) ;
2013-06-24 16:20:39 +05:30
data - > pdata = pdata ;
2018-04-16 12:11:54 +02:00
data - > soc = ( enum soc_type ) of_device_get_match_data ( & pdev - > dev ) ;
2014-11-13 16:01:23 +01:00
switch ( data - > soc ) {
case SOC_ARCH_EXYNOS4210 :
data - > tmu_initialize = exynos4210_tmu_initialize ;
data - > tmu_control = exynos4210_tmu_control ;
data - > tmu_read = exynos4210_tmu_read ;
data - > tmu_clear_irqs = exynos4210_tmu_clear_irqs ;
2016-02-18 14:14:20 +09:00
data - > ntrip = 4 ;
2014-11-13 16:01:23 +01:00
break ;
case SOC_ARCH_EXYNOS3250 :
case SOC_ARCH_EXYNOS4412 :
case SOC_ARCH_EXYNOS5250 :
case SOC_ARCH_EXYNOS5260 :
case SOC_ARCH_EXYNOS5420 :
case SOC_ARCH_EXYNOS5420_TRIMINFO :
data - > tmu_initialize = exynos4412_tmu_initialize ;
data - > tmu_control = exynos4210_tmu_control ;
data - > tmu_read = exynos4412_tmu_read ;
data - > tmu_set_emulation = exynos4412_tmu_set_emulation ;
data - > tmu_clear_irqs = exynos4210_tmu_clear_irqs ;
2016-02-18 14:14:20 +09:00
data - > ntrip = 4 ;
2014-11-13 16:01:23 +01:00
break ;
2015-03-10 11:23:44 +09:00
case SOC_ARCH_EXYNOS5433 :
data - > tmu_initialize = exynos5433_tmu_initialize ;
data - > tmu_control = exynos5433_tmu_control ;
data - > tmu_read = exynos4412_tmu_read ;
data - > tmu_set_emulation = exynos4412_tmu_set_emulation ;
data - > tmu_clear_irqs = exynos4210_tmu_clear_irqs ;
2016-02-18 14:14:20 +09:00
data - > ntrip = 8 ;
2015-03-10 11:23:44 +09:00
break ;
2014-11-13 16:01:23 +01:00
case SOC_ARCH_EXYNOS5440 :
data - > tmu_initialize = exynos5440_tmu_initialize ;
data - > tmu_control = exynos5440_tmu_control ;
data - > tmu_read = exynos5440_tmu_read ;
data - > tmu_set_emulation = exynos5440_tmu_set_emulation ;
data - > tmu_clear_irqs = exynos5440_tmu_clear_irqs ;
2016-02-18 14:14:20 +09:00
data - > ntrip = 4 ;
2014-11-13 16:01:23 +01:00
break ;
2015-01-27 11:18:22 +05:30
case SOC_ARCH_EXYNOS7 :
data - > tmu_initialize = exynos7_tmu_initialize ;
data - > tmu_control = exynos7_tmu_control ;
data - > tmu_read = exynos7_tmu_read ;
data - > tmu_set_emulation = exynos4412_tmu_set_emulation ;
data - > tmu_clear_irqs = exynos4210_tmu_clear_irqs ;
2016-02-18 14:14:20 +09:00
data - > ntrip = 8 ;
2015-01-27 11:18:22 +05:30
break ;
2014-11-13 16:01:23 +01:00
default :
dev_err ( & pdev - > dev , " Platform not supported \n " ) ;
return - EINVAL ;
}
2013-06-24 16:20:42 +05:30
/*
* Check if the TMU shares some registers and then try to map the
* memory of common registers .
*/
2014-11-13 16:01:23 +01:00
if ( data - > soc ! = SOC_ARCH_EXYNOS5420_TRIMINFO & &
data - > soc ! = SOC_ARCH_EXYNOS5440 )
2013-06-24 16:20:42 +05:30
return 0 ;
if ( of_address_to_resource ( pdev - > dev . of_node , 1 , & res ) ) {
dev_err ( & pdev - > dev , " failed to get Resource 1 \n " ) ;
return - ENODEV ;
}
2013-12-19 11:36:08 +05:30
data - > base_second = devm_ioremap ( & pdev - > dev , res . start ,
2013-06-24 16:20:42 +05:30
resource_size ( & res ) ) ;
2013-12-19 11:36:08 +05:30
if ( ! data - > base_second ) {
2013-06-24 16:20:42 +05:30
dev_err ( & pdev - > dev , " Failed to ioremap memory \n " ) ;
return - ENOMEM ;
}
2013-06-24 16:20:39 +05:30
return 0 ;
}
2017-08-08 17:08:59 +02:00
static const struct thermal_zone_of_device_ops exynos_sensor_ops = {
2015-01-23 13:10:08 +01:00
. get_temp = exynos_get_temp ,
. set_emul_temp = exynos_tmu_set_emulation ,
} ;
2013-06-24 16:20:39 +05:30
static int exynos_tmu_probe ( struct platform_device * pdev )
{
2015-01-23 13:10:08 +01:00
struct exynos_tmu_data * data ;
int ret ;
2013-06-24 16:20:39 +05:30
2012-08-16 05:41:45 -06:00
data = devm_kzalloc ( & pdev - > dev , sizeof ( struct exynos_tmu_data ) ,
GFP_KERNEL ) ;
2014-05-07 15:04:48 +09:00
if ( ! data )
2011-09-07 18:49:08 +09:00
return - ENOMEM ;
2013-06-24 16:20:39 +05:30
platform_set_drvdata ( pdev , data ) ;
mutex_init ( & data - > lock ) ;
2011-09-07 18:49:08 +09:00
2015-10-08 14:34:02 +09:00
/*
* Try enabling the regulator if found
* TODO : Add regulator as an SOC feature , so that regulator enable
* is a compulsory call .
*/
2016-02-18 15:19:10 -03:00
data - > regulator = devm_regulator_get_optional ( & pdev - > dev , " vtmu " ) ;
2015-10-08 14:34:02 +09:00
if ( ! IS_ERR ( data - > regulator ) ) {
ret = regulator_enable ( data - > regulator ) ;
if ( ret ) {
dev_err ( & pdev - > dev , " failed to enable vtmu \n " ) ;
return ret ;
}
} else {
2016-02-18 15:19:11 -03:00
if ( PTR_ERR ( data - > regulator ) = = - EPROBE_DEFER )
return - EPROBE_DEFER ;
2015-10-08 14:34:02 +09:00
dev_info ( & pdev - > dev , " Regulator node (vtmu) not found \n " ) ;
2015-01-23 13:10:08 +01:00
}
2015-10-08 14:34:02 +09:00
2013-06-24 16:20:39 +05:30
ret = exynos_map_dt_data ( pdev ) ;
if ( ret )
2015-01-23 13:10:08 +01:00
goto err_sensor ;
2011-09-07 18:49:08 +09:00
2013-06-24 16:20:39 +05:30
INIT_WORK ( & data - > irq_work , exynos_tmu_work ) ;
2011-09-07 18:49:08 +09:00
2013-04-18 11:37:58 +00:00
data - > clk = devm_clk_get ( & pdev - > dev , " tmu_apbif " ) ;
2011-09-07 18:49:08 +09:00
if ( IS_ERR ( data - > clk ) ) {
dev_err ( & pdev - > dev , " Failed to get clock \n " ) ;
2015-01-23 13:10:08 +01:00
ret = PTR_ERR ( data - > clk ) ;
goto err_sensor ;
2011-09-07 18:49:08 +09:00
}
2013-12-19 11:36:31 +05:30
data - > clk_sec = devm_clk_get ( & pdev - > dev , " tmu_triminfo_apbif " ) ;
if ( IS_ERR ( data - > clk_sec ) ) {
if ( data - > soc = = SOC_ARCH_EXYNOS5420_TRIMINFO ) {
dev_err ( & pdev - > dev , " Failed to get triminfo clock \n " ) ;
2015-01-23 13:10:08 +01:00
ret = PTR_ERR ( data - > clk_sec ) ;
goto err_sensor ;
2013-12-19 11:36:31 +05:30
}
} else {
ret = clk_prepare ( data - > clk_sec ) ;
if ( ret ) {
dev_err ( & pdev - > dev , " Failed to get clock \n " ) ;
2015-01-23 13:10:08 +01:00
goto err_sensor ;
2013-12-19 11:36:31 +05:30
}
}
2013-04-18 11:37:58 +00:00
ret = clk_prepare ( data - > clk ) ;
2013-12-19 11:36:31 +05:30
if ( ret ) {
dev_err ( & pdev - > dev , " Failed to get clock \n " ) ;
goto err_clk_sec ;
}
2013-04-18 11:37:58 +00:00
2015-03-10 11:23:44 +09:00
switch ( data - > soc ) {
case SOC_ARCH_EXYNOS5433 :
case SOC_ARCH_EXYNOS7 :
2015-01-27 11:18:22 +05:30
data - > sclk = devm_clk_get ( & pdev - > dev , " tmu_sclk " ) ;
if ( IS_ERR ( data - > sclk ) ) {
dev_err ( & pdev - > dev , " Failed to get sclk \n " ) ;
goto err_clk ;
} else {
ret = clk_prepare_enable ( data - > sclk ) ;
if ( ret ) {
dev_err ( & pdev - > dev , " Failed to enable sclk \n " ) ;
goto err_clk ;
}
}
2015-03-10 11:23:44 +09:00
break ;
default :
break ;
2015-10-08 14:34:05 +09:00
}
2015-01-27 11:18:22 +05:30
2015-10-08 14:34:03 +09:00
/*
* data - > tzd must be registered before calling exynos_tmu_initialize ( ) ,
* requesting irq and calling exynos_tmu_control ( ) .
*/
data - > tzd = thermal_zone_of_sensor_register ( & pdev - > dev , 0 , data ,
& exynos_sensor_ops ) ;
if ( IS_ERR ( data - > tzd ) ) {
ret = PTR_ERR ( data - > tzd ) ;
dev_err ( & pdev - > dev , " Failed to register sensor: %d \n " , ret ) ;
goto err_sclk ;
}
2015-01-27 11:18:22 +05:30
2012-08-16 17:11:42 +05:30
ret = exynos_tmu_initialize ( pdev ) ;
2011-09-07 18:49:08 +09:00
if ( ret ) {
dev_err ( & pdev - > dev , " Failed to initialize TMU \n " ) ;
2015-10-08 14:34:03 +09:00
goto err_thermal ;
2011-09-07 18:49:08 +09:00
}
2013-06-24 16:20:39 +05:30
ret = devm_request_irq ( & pdev - > dev , data - > irq , exynos_tmu_irq ,
IRQF_TRIGGER_RISING | IRQF_SHARED , dev_name ( & pdev - > dev ) , data ) ;
if ( ret ) {
dev_err ( & pdev - > dev , " Failed to request irq: %d \n " , data - > irq ) ;
2015-10-08 14:34:03 +09:00
goto err_thermal ;
2013-06-24 16:20:39 +05:30
}
2012-11-21 13:31:01 +09:00
2015-01-23 13:10:08 +01:00
exynos_tmu_control ( pdev , true ) ;
2011-09-07 18:49:08 +09:00
return 0 ;
2015-10-08 14:34:03 +09:00
err_thermal :
thermal_zone_of_sensor_unregister ( & pdev - > dev , data - > tzd ) ;
2015-01-27 11:18:22 +05:30
err_sclk :
clk_disable_unprepare ( data - > sclk ) ;
2011-09-07 18:49:08 +09:00
err_clk :
2013-04-18 11:37:58 +00:00
clk_unprepare ( data - > clk ) ;
2013-12-19 11:36:31 +05:30
err_clk_sec :
if ( ! IS_ERR ( data - > clk_sec ) )
clk_unprepare ( data - > clk_sec ) ;
2015-01-23 13:10:08 +01:00
err_sensor :
2015-10-08 14:34:04 +09:00
if ( ! IS_ERR ( data - > regulator ) )
2015-06-08 10:35:49 +09:00
regulator_disable ( data - > regulator ) ;
2015-01-23 13:10:08 +01:00
2011-09-07 18:49:08 +09:00
return ret ;
}
2012-12-21 13:15:52 -08:00
static int exynos_tmu_remove ( struct platform_device * pdev )
2011-09-07 18:49:08 +09:00
{
2012-08-16 17:11:42 +05:30
struct exynos_tmu_data * data = platform_get_drvdata ( pdev ) ;
2015-01-23 13:10:08 +01:00
struct thermal_zone_device * tzd = data - > tzd ;
2011-09-07 18:49:08 +09:00
2015-01-23 13:10:08 +01:00
thermal_zone_of_sensor_unregister ( & pdev - > dev , tzd ) ;
2014-07-08 15:09:56 +02:00
exynos_tmu_control ( pdev , false ) ;
2015-01-27 11:18:22 +05:30
clk_disable_unprepare ( data - > sclk ) ;
2013-04-18 11:37:58 +00:00
clk_unprepare ( data - > clk ) ;
2013-12-19 11:36:31 +05:30
if ( ! IS_ERR ( data - > clk_sec ) )
clk_unprepare ( data - > clk_sec ) ;
2011-09-07 18:49:08 +09:00
2013-06-24 16:20:47 +05:30
if ( ! IS_ERR ( data - > regulator ) )
regulator_disable ( data - > regulator ) ;
2011-09-07 18:49:08 +09:00
return 0 ;
}
2012-07-08 21:48:15 +02:00
# ifdef CONFIG_PM_SLEEP
2012-08-16 17:11:42 +05:30
static int exynos_tmu_suspend ( struct device * dev )
2011-09-07 18:49:08 +09:00
{
2012-08-16 17:11:42 +05:30
exynos_tmu_control ( to_platform_device ( dev ) , false ) ;
2011-09-07 18:49:08 +09:00
return 0 ;
}
2012-08-16 17:11:42 +05:30
static int exynos_tmu_resume ( struct device * dev )
2011-09-07 18:49:08 +09:00
{
2012-07-08 21:48:15 +02:00
struct platform_device * pdev = to_platform_device ( dev ) ;
2012-08-16 17:11:42 +05:30
exynos_tmu_initialize ( pdev ) ;
exynos_tmu_control ( pdev , true ) ;
2011-09-07 18:49:08 +09:00
return 0 ;
}
2012-07-08 21:48:15 +02:00
2012-08-16 17:11:42 +05:30
static SIMPLE_DEV_PM_OPS ( exynos_tmu_pm ,
exynos_tmu_suspend , exynos_tmu_resume ) ;
# define EXYNOS_TMU_PM (&exynos_tmu_pm)
2011-09-07 18:49:08 +09:00
# else
2012-08-16 17:11:42 +05:30
# define EXYNOS_TMU_PM NULL
2011-09-07 18:49:08 +09:00
# endif
2012-08-16 17:11:42 +05:30
static struct platform_driver exynos_tmu_driver = {
2011-09-07 18:49:08 +09:00
. driver = {
2012-08-16 17:11:42 +05:30
. name = " exynos-tmu " ,
. pm = EXYNOS_TMU_PM ,
2013-08-19 11:58:43 +05:30
. of_match_table = exynos_tmu_match ,
2011-09-07 18:49:08 +09:00
} ,
2012-08-16 17:11:42 +05:30
. probe = exynos_tmu_probe ,
2012-12-21 13:15:52 -08:00
. remove = exynos_tmu_remove ,
2011-09-07 18:49:08 +09:00
} ;
2012-08-16 17:11:42 +05:30
module_platform_driver ( exynos_tmu_driver ) ;
2011-09-07 18:49:08 +09:00
2012-08-16 17:11:42 +05:30
MODULE_DESCRIPTION ( " EXYNOS TMU Driver " ) ;
2011-09-07 18:49:08 +09:00
MODULE_AUTHOR ( " Donggeun Kim <dg77.kim@samsung.com> " ) ;
MODULE_LICENSE ( " GPL " ) ;
2012-08-16 17:11:42 +05:30
MODULE_ALIAS ( " platform:exynos-tmu " ) ;