2019-05-27 08:55:05 +02:00
// SPDX-License-Identifier: GPL-2.0-or-later
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
*/
# 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
2018-04-16 16:22:19 +02:00
# include <dt-bindings/thermal/thermal_exynos.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_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_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
2018-04-16 12:12:00 +02:00
# define EXYNOS5433_G3D_BASE 0x10070000
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_EMUL_DATA_SHIFT 7
# define EXYNOS7_EMUL_DATA_MASK 0x1ff
2018-04-16 12:11:57 +02:00
# define EXYNOS_FIRST_POINT_TRIM 25
# define EXYNOS_SECOND_POINT_TRIM 85
2018-04-16 12:11:58 +02:00
# define EXYNOS_NOISE_CANCEL_MODE 4
2015-01-23 13:10:08 +01:00
# define MCELSIUS 1000
2018-04-16 16:22:19 +02:00
enum soc_type {
SOC_ARCH_EXYNOS3250 = 1 ,
SOC_ARCH_EXYNOS4210 ,
SOC_ARCH_EXYNOS4412 ,
SOC_ARCH_EXYNOS5250 ,
SOC_ARCH_EXYNOS5260 ,
SOC_ARCH_EXYNOS5420 ,
SOC_ARCH_EXYNOS5420_TRIMINFO ,
SOC_ARCH_EXYNOS5433 ,
SOC_ARCH_EXYNOS7 ,
} ;
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 .
* @ 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 .
2018-04-16 12:12:02 +02:00
* @ cal_type : calibration type for temperature
2018-04-16 12:11:59 +02:00
* @ efuse_value : SoC defined fuse value
* @ min_efuse_value : minimum valid trimming data
* @ max_efuse_value : maximum valid trimming data
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 .
2018-04-16 12:12:01 +02:00
* @ gain : gain of amplifier in the positive - TC generator block
* 0 < gain < = 15
2018-04-16 12:12:00 +02:00
* @ reference_voltage : reference voltage of amplifier
* in the positive - TC generator block
* 0 < reference_voltage < = 31
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 ;
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 ;
2018-04-16 12:12:02 +02:00
u32 cal_type ;
2018-04-16 12:11:59 +02:00
u32 efuse_value ;
u32 min_efuse_value ;
u32 max_efuse_value ;
2015-01-27 11:18:22 +05:30
u16 temp_error1 , temp_error2 ;
2018-04-16 12:12:01 +02:00
u8 gain ;
2018-04-16 12:12:00 +02:00
u8 reference_voltage ;
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
2018-04-26 13:51:29 +02:00
void ( * tmu_set_trip_temp ) ( struct exynos_tmu_data * data , int trip ,
u8 temp ) ;
void ( * tmu_set_trip_hyst ) ( struct exynos_tmu_data * data , int trip ,
u8 temp , u8 hyst ) ;
2018-04-26 13:51:23 +02:00
void ( * 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
} ;
/*
* 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
{
2018-04-16 12:12:02 +02:00
if ( data - > cal_type = = TYPE_ONE_POINT_TRIMMING )
2018-04-16 12:11:57 +02:00
return temp + data - > temp_error1 - EXYNOS_FIRST_POINT_TRIM ;
2014-07-31 19:11:03 +02:00
2018-04-16 12:11:57 +02:00
return ( temp - EXYNOS_FIRST_POINT_TRIM ) *
2018-04-16 12:11:56 +02:00
( data - > temp_error2 - data - > temp_error1 ) /
2018-04-16 12:11:57 +02:00
( EXYNOS_SECOND_POINT_TRIM - EXYNOS_FIRST_POINT_TRIM ) +
2018-04-16 12:11:56 +02:00
data - > temp_error1 ;
2011-09-07 18:49:08 +09:00
}
/*
* 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
{
2018-04-16 12:12:02 +02:00
if ( data - > cal_type = = TYPE_ONE_POINT_TRIMMING )
2018-04-16 12:11:57 +02:00
return temp_code - data - > temp_error1 + EXYNOS_FIRST_POINT_TRIM ;
2014-07-31 19:11:03 +02:00
2018-04-16 12:11:56 +02:00
return ( temp_code - data - > temp_error1 ) *
2018-04-16 12:11:57 +02:00
( EXYNOS_SECOND_POINT_TRIM - EXYNOS_FIRST_POINT_TRIM ) /
2018-04-16 12:11:56 +02:00
( data - > temp_error2 - data - > temp_error1 ) +
2018-04-16 12:11:57 +02:00
EXYNOS_FIRST_POINT_TRIM ;
2011-09-07 18:49:08 +09:00
}
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
{
2018-04-26 13:51:20 +02:00
u16 tmu_temp_mask =
( data - > soc = = SOC_ARCH_EXYNOS7 ) ? EXYNOS7_TMU_TEMP_MASK
: EXYNOS_TMU_TEMP_MASK ;
2011-09-07 18:49:08 +09:00
2018-04-26 13:51:20 +02:00
data - > temp_error1 = trim_info & 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 | |
2018-04-16 12:11:59 +02:00
( data - > min_efuse_value > data - > temp_error1 ) | |
( data - > temp_error1 > data - > max_efuse_value ) )
data - > temp_error1 = data - > efuse_value & EXYNOS_TMU_TEMP_MASK ;
2013-06-24 16:20:45 +05:30
if ( ! data - > temp_error2 )
data - > temp_error2 =
2018-04-16 12:11:59 +02:00
( data - > 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
static int exynos_tmu_initialize ( struct platform_device * pdev )
2014-11-13 16:01:12 +01:00
{
2012-08-16 17:11:42 +05:30
struct exynos_tmu_data * data = platform_get_drvdata ( pdev ) ;
2018-04-26 13:51:17 +02:00
struct thermal_zone_device * tzd = data - > tzd ;
2015-01-23 13:10:08 +01:00
const struct thermal_trip * const trips =
2018-04-26 13:51:17 +02:00
of_thermal_get_trip_points ( tzd ) ;
2018-04-26 13:51:19 +02:00
unsigned int status ;
2018-04-26 13:51:29 +02:00
int ret = 0 , temp , hyst ;
2014-04-14 11:08:15 +05:30
2015-01-23 13:10:08 +01:00
if ( ! trips ) {
2018-04-26 13:51:17 +02:00
dev_err ( & pdev - > dev ,
" Cannot get trip points from device tree! \n " ) ;
return - ENODEV ;
2015-01-23 13:10:08 +01:00
}
2012-08-16 17:11:42 +05:30
2018-04-26 13:51:18 +02:00
if ( data - > soc ! = SOC_ARCH_EXYNOS5433 ) /* FIXME */
ret = tzd - > ops - > get_crit_temp ( tzd , & temp ) ;
if ( ret ) {
dev_err ( & pdev - > dev ,
" No CRITICAL trip point defined in device tree! \n " ) ;
goto out ;
2011-09-07 18:49:08 +09:00
}
2014-11-13 16:01:12 +01:00
2018-04-26 13:51:17 +02:00
if ( of_thermal_get_ntrips ( tzd ) > data - > ntrip ) {
2016-02-18 14:14:20 +09:00
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 " ,
2018-04-26 13:51:17 +02:00
( of_thermal_get_ntrips ( tzd ) - data - > ntrip ) ) ;
2016-02-18 14:14:20 +09:00
}
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 ) ;
2018-04-26 13:51:19 +02:00
status = readb ( data - > base + EXYNOS_TMU_REG_STATUS ) ;
2018-04-26 13:51:25 +02:00
if ( ! status ) {
2018-04-26 13:51:19 +02:00
ret = - EBUSY ;
2018-04-26 13:51:25 +02:00
} else {
2018-04-26 13:51:29 +02:00
int i , ntrips =
min_t ( int , of_thermal_get_ntrips ( tzd ) , data - > ntrip ) ;
2018-04-26 13:51:23 +02:00
data - > tmu_initialize ( pdev ) ;
2018-04-26 13:51:29 +02:00
/* Write temperature code for rising and falling threshold */
for ( i = 0 ; i < ntrips ; i + + ) {
/* Write temperature code for rising threshold */
2018-04-26 13:51:30 +02:00
ret = tzd - > ops - > get_trip_temp ( tzd , i , & temp ) ;
if ( ret )
goto err ;
2018-04-26 13:51:29 +02:00
temp / = MCELSIUS ;
data - > tmu_set_trip_temp ( data , i , temp ) ;
/* Write temperature code for falling threshold */
2018-04-26 13:51:30 +02:00
ret = tzd - > ops - > get_trip_hyst ( tzd , i , & hyst ) ;
if ( ret )
goto err ;
2018-04-26 13:51:29 +02:00
hyst / = MCELSIUS ;
data - > tmu_set_trip_hyst ( data , i , temp , hyst ) ;
}
2018-04-26 13:51:25 +02:00
data - > tmu_clear_irqs ( data ) ;
}
2018-04-26 13:51:30 +02:00
err :
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 ) ;
2018-04-26 13:51:18 +02:00
out :
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
{
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 ) ;
2018-04-16 12:12:00 +02:00
con | = data - > 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 ) ;
2018-04-16 12:12:01 +02:00
con | = ( data - > gain < < EXYNOS_TMU_BUF_SLOPE_SEL_SHIFT ) ;
2013-06-24 16:20:29 +05:30
2018-04-16 12:11:58 +02:00
con & = ~ ( EXYNOS_TMU_TRIP_MODE_MASK < < EXYNOS_TMU_TRIP_MODE_SHIFT ) ;
con | = ( EXYNOS_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 ) ;
}
2018-04-26 13:51:26 +02:00
static void exynos4210_tmu_set_trip_temp ( struct exynos_tmu_data * data ,
int trip , u8 temp )
2011-09-07 18:49:08 +09:00
{
2015-01-23 13:10:08 +01:00
const struct thermal_trip * const trips =
2018-04-26 13:51:26 +02:00
of_thermal_get_trip_points ( data - > tzd ) ;
u8 ref , th_code ;
2015-01-23 13:10:08 +01:00
2018-04-26 13:51:26 +02:00
ref = trips [ 0 ] . temperature / MCELSIUS ;
2011-09-07 18:49:08 +09:00
2018-04-26 13:51:26 +02:00
if ( trip = = 0 ) {
th_code = temp_to_code ( data , ref ) ;
writeb ( th_code , data - > base + EXYNOS4210_TMU_REG_THRESHOLD_TEMP ) ;
2014-11-13 16:01:13 +01:00
}
2011-09-07 18:49:08 +09:00
2018-04-26 13:51:26 +02:00
temp - = ref ;
writeb ( temp , data - > base + EXYNOS4210_TMU_REG_TRIG_LEVEL0 + trip * 4 ) ;
}
2018-04-26 13:51:29 +02:00
/* failing thresholds are not supported on Exynos4210 */
static void exynos4210_tmu_set_trip_hyst ( struct exynos_tmu_data * data ,
int trip , u8 temp , u8 hyst )
{
}
2018-04-26 13:51:23 +02:00
static void 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 ) ;
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
2018-04-26 13:51:26 +02:00
static void exynos4412_tmu_set_trip_temp ( struct exynos_tmu_data * data ,
int trip , u8 temp )
{
u32 th , con ;
2014-11-13 16:01:13 +01:00
2018-04-26 13:51:26 +02:00
th = readl ( data - > base + EXYNOS_THD_TEMP_RISE ) ;
th & = ~ ( 0xff < < 8 * trip ) ;
th | = temp_to_code ( data , temp ) < < 8 * trip ;
writel ( th , data - > base + EXYNOS_THD_TEMP_RISE ) ;
if ( trip = = 3 ) {
con = readl ( data - > base + EXYNOS_TMU_REG_CONTROL ) ;
con | = ( 1 < < EXYNOS_TMU_THERM_TRIP_EN_SHIFT ) ;
writel ( con , data - > base + EXYNOS_TMU_REG_CONTROL ) ;
2015-01-23 13:10:08 +01:00
}
2018-04-26 13:51:26 +02:00
}
2014-11-13 16:01:13 +01:00
2018-04-26 13:51:26 +02:00
static void exynos4412_tmu_set_trip_hyst ( struct exynos_tmu_data * data ,
int trip , u8 temp , u8 hyst )
{
u32 th ;
th = readl ( data - > base + EXYNOS_THD_TEMP_FALL ) ;
th & = ~ ( 0xff < < 8 * trip ) ;
if ( hyst )
th | = temp_to_code ( data , temp - hyst ) < < 8 * trip ;
writel ( th , data - > base + EXYNOS_THD_TEMP_FALL ) ;
2014-11-13 16:01:13 +01:00
}
2018-04-26 13:51:23 +02:00
static void exynos4412_tmu_initialize ( struct platform_device * pdev )
2014-11-13 16:01:13 +01:00
{
struct exynos_tmu_data * data = platform_get_drvdata ( pdev ) ;
2018-04-26 13:51:26 +02:00
unsigned int trim_info , ctrl ;
2014-11-13 16:01:13 +01:00
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 ) ;
2018-04-26 13:51:26 +02:00
}
2014-11-13 16:01:13 +01:00
2018-04-26 13:51:26 +02:00
static void exynos5433_tmu_set_trip_temp ( struct exynos_tmu_data * data ,
int trip , u8 temp )
{
unsigned int reg_off , j ;
u32 th ;
2014-11-13 16:01:13 +01:00
2018-04-26 13:51:26 +02:00
if ( trip > 3 ) {
reg_off = EXYNOS5433_THD_TEMP_RISE7_4 ;
j = trip - 4 ;
} else {
reg_off = EXYNOS5433_THD_TEMP_RISE3_0 ;
j = trip ;
2014-11-13 16:01:13 +01:00
}
2015-01-23 13:10:08 +01:00
2018-04-26 13:51:26 +02:00
th = readl ( data - > base + reg_off ) ;
th & = ~ ( 0xff < < j * 8 ) ;
th | = ( temp_to_code ( data , temp ) < < j * 8 ) ;
writel ( th , data - > base + reg_off ) ;
}
2015-01-23 13:10:08 +01:00
2018-04-26 13:51:26 +02:00
static void exynos5433_tmu_set_trip_hyst ( struct exynos_tmu_data * data ,
int trip , u8 temp , u8 hyst )
{
unsigned int reg_off , j ;
u32 th ;
2015-01-23 13:10:08 +01:00
2018-04-26 13:51:26 +02:00
if ( trip > 3 ) {
reg_off = EXYNOS5433_THD_TEMP_FALL7_4 ;
j = trip - 4 ;
} else {
reg_off = EXYNOS5433_THD_TEMP_FALL3_0 ;
j = trip ;
2014-11-13 16:01:13 +01:00
}
2015-01-23 13:10:08 +01:00
2018-04-26 13:51:26 +02:00
th = readl ( data - > base + reg_off ) ;
th & = ~ ( 0xff < < j * 8 ) ;
th | = ( temp_to_code ( data , temp - hyst ) < < j * 8 ) ;
writel ( th , data - > base + reg_off ) ;
2014-11-13 16:01:13 +01:00
}
2011-09-07 18:49:08 +09:00
2018-04-26 13:51:23 +02:00
static void exynos5433_tmu_initialize ( struct platform_device * pdev )
2015-03-10 11:23:44 +09:00
{
struct exynos_tmu_data * data = platform_get_drvdata ( pdev ) ;
2018-04-26 13:51:19 +02:00
unsigned int trim_info ;
2018-04-26 13:51:29 +02:00
int sensor_id , cal_type ;
2015-03-10 11:23:44 +09:00
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_TWO_POINT_TRIMMING :
2018-04-16 12:12:02 +02:00
data - > cal_type = TYPE_TWO_POINT_TRIMMING ;
2015-03-10 11:23:44 +09:00
break ;
2018-04-16 12:12:02 +02:00
case EXYNOS5433_TRIMINFO_ONE_POINT_TRIMMING :
2015-03-10 11:23:44 +09:00
default :
2018-04-16 12:12:02 +02:00
data - > cal_type = TYPE_ONE_POINT_TRIMMING ;
2015-03-10 11:23:44 +09:00
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 ) ;
}
2018-04-26 13:51:26 +02:00
static void exynos7_tmu_set_trip_temp ( struct exynos_tmu_data * data ,
int trip , u8 temp )
2014-11-13 16:01:13 +01:00
{
2018-04-26 13:51:26 +02:00
unsigned int reg_off , bit_off ;
u32 th ;
2014-11-13 16:01:13 +01:00
2018-04-26 13:51:26 +02:00
reg_off = ( ( 7 - trip ) / 2 ) * 4 ;
bit_off = ( ( 8 - trip ) % 2 ) ;
2014-11-13 16:01:13 +01:00
2018-04-26 13:51:26 +02:00
th = readl ( data - > base + EXYNOS7_THD_TEMP_RISE7_6 + reg_off ) ;
th & = ~ ( EXYNOS7_TMU_TEMP_MASK < < ( 16 * bit_off ) ) ;
th | = temp_to_code ( data , temp ) < < ( 16 * bit_off ) ;
writel ( th , data - > base + EXYNOS7_THD_TEMP_RISE7_6 + reg_off ) ;
}
2014-11-13 16:01:13 +01:00
2018-04-26 13:51:26 +02:00
static void exynos7_tmu_set_trip_hyst ( struct exynos_tmu_data * data ,
int trip , u8 temp , u8 hyst )
{
unsigned int reg_off , bit_off ;
u32 th ;
2014-11-13 16:01:13 +01:00
2018-04-26 13:51:26 +02:00
reg_off = ( ( 7 - trip ) / 2 ) * 4 ;
bit_off = ( ( 8 - trip ) % 2 ) ;
2015-10-08 14:34:06 +09:00
2018-04-26 13:51:26 +02:00
th = readl ( data - > base + EXYNOS7_THD_TEMP_FALL7_6 + reg_off ) ;
th & = ~ ( EXYNOS7_TMU_TEMP_MASK < < ( 16 * bit_off ) ) ;
th | = temp_to_code ( data , temp - hyst ) < < ( 16 * bit_off ) ;
writel ( th , data - > base + EXYNOS7_THD_TEMP_FALL7_6 + reg_off ) ;
2011-09-07 18:49:08 +09:00
}
2018-04-26 13:51:23 +02:00
static void exynos7_tmu_initialize ( struct platform_device * pdev )
2015-01-27 11:18:22 +05:30
{
struct exynos_tmu_data * data = platform_get_drvdata ( pdev ) ;
2018-04-26 13:51:19 +02:00
unsigned int trim_info ;
2015-01-27 11:18:22 +05:30
trim_info = readl ( data - > base + EXYNOS_TMU_REG_TRIMINFO ) ;
2018-04-26 13:51:20 +02:00
sanitize_temp_error ( data , trim_info ) ;
2015-01-27 11:18:22 +05:30
}
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 ;
2018-04-26 13:51:31 +02:00
unsigned int con , interrupt_en = 0 , i ;
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 ) {
2018-04-26 13:51:31 +02:00
for ( i = 0 ; i < data - > ntrip ; i + + ) {
if ( ! of_thermal_is_trip_valid ( tz , i ) )
continue ;
interrupt_en | =
( 1 < < ( EXYNOS_TMU_INTEN_RISE0_SHIFT + i * 4 ) ) ;
}
2015-01-23 13:10:08 +01:00
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 ;
2018-04-26 13:51:31 +02:00
con | = ( 1 < < EXYNOS_TMU_CORE_EN_SHIFT ) ;
2014-11-13 16:01:15 +01:00
} else {
con & = ~ ( 1 < < EXYNOS_TMU_CORE_EN_SHIFT ) ;
}
2018-04-26 13:51:31 +02:00
2014-11-13 16:01:15 +01:00
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 ;
2018-04-26 13:51:31 +02:00
unsigned int con , interrupt_en = 0 , pd_det_en , i ;
2015-03-10 11:23:44 +09:00
con = get_con_reg ( data , readl ( data - > base + EXYNOS_TMU_REG_CONTROL ) ) ;
if ( on ) {
2018-04-26 13:51:31 +02:00
for ( i = 0 ; i < data - > ntrip ; i + + ) {
if ( ! of_thermal_is_trip_valid ( tz , i ) )
continue ;
interrupt_en | =
( 1 < < ( EXYNOS7_TMU_INTEN_RISE0_SHIFT + i ) ) ;
}
2015-03-10 11:23:44 +09:00
interrupt_en | =
interrupt_en < < EXYNOS_TMU_INTEN_FALL0_SHIFT ;
2018-04-26 13:51:31 +02:00
con | = ( 1 < < EXYNOS_TMU_CORE_EN_SHIFT ) ;
} else
2015-03-10 11:23:44 +09:00
con & = ~ ( 1 < < EXYNOS_TMU_CORE_EN_SHIFT ) ;
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 ) ;
}
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 ;
2018-04-26 13:51:31 +02:00
unsigned int con , interrupt_en = 0 , i ;
2015-01-27 11:18:22 +05:30
con = get_con_reg ( data , readl ( data - > base + EXYNOS_TMU_REG_CONTROL ) ) ;
if ( on ) {
2018-04-26 13:51:31 +02:00
for ( i = 0 ; i < data - > ntrip ; i + + ) {
if ( ! of_thermal_is_trip_valid ( tz , i ) )
continue ;
interrupt_en | =
( 1 < < ( EXYNOS7_TMU_INTEN_RISE0_SHIFT + i ) ) ;
}
2015-01-27 11:18:22 +05:30
interrupt_en | =
interrupt_en < < EXYNOS_TMU_INTEN_FALL0_SHIFT ;
2018-04-26 13:51:31 +02:00
con | = ( 1 < < EXYNOS_TMU_CORE_EN_SHIFT ) ;
con | = ( 1 < < EXYNOS7_PD_DET_EN_SHIFT ) ;
2015-01-27 11:18:22 +05:30
} 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
}
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
2019-01-22 16:47:41 +01:00
if ( ! data | | ! data - > tmu_read )
2015-01-23 13:10:08 +01:00
return - EINVAL ;
2018-05-13 19:54:02 +02:00
else if ( ! data - > enabled )
/*
* Called too early , probably
* from thermal_zone_of_sensor_register ( ) .
*/
return - EAGAIN ;
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 ;
2018-04-26 13:21:12 +02:00
val & = ~ ( EXYNOS_EMUL_TIME_MASK < < EXYNOS_EMUL_TIME_SHIFT ) ;
val | = ( EXYNOS_EMUL_TIME < < EXYNOS_EMUL_TIME_SHIFT ) ;
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 ) ;
}
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
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 ) ;
}
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 ) ;
2013-06-24 16:20:43 +05:30
2018-04-26 13:51:33 +02:00
thermal_zone_device_update ( data - > tzd , THERMAL_EVENT_UNSPECIFIED ) ;
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 ) ;
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 ) ;
}
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,exynos7-tmu " ,
. data = ( const void * ) SOC_ARCH_EXYNOS7 ,
} ,
{ } ,
2012-08-16 17:11:44 +05:30
} ;
MODULE_DEVICE_TABLE ( of , exynos_tmu_match ) ;
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 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 ;
}
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 :
2018-04-26 13:51:29 +02:00
data - > tmu_set_trip_temp = exynos4210_tmu_set_trip_temp ;
data - > tmu_set_trip_hyst = exynos4210_tmu_set_trip_hyst ;
2014-11-13 16:01:23 +01:00
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 ;
2018-04-16 12:12:01 +02:00
data - > gain = 15 ;
2018-04-16 12:12:00 +02:00
data - > reference_voltage = 7 ;
2018-04-16 12:11:59 +02:00
data - > efuse_value = 55 ;
data - > min_efuse_value = 40 ;
data - > max_efuse_value = 100 ;
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 :
2018-04-26 13:51:29 +02:00
data - > tmu_set_trip_temp = exynos4412_tmu_set_trip_temp ;
data - > tmu_set_trip_hyst = exynos4412_tmu_set_trip_hyst ;
2014-11-13 16:01:23 +01:00
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 ;
2018-04-16 12:12:01 +02:00
data - > gain = 8 ;
2018-04-16 12:12:00 +02:00
data - > reference_voltage = 16 ;
2018-04-16 12:11:59 +02:00
data - > efuse_value = 55 ;
if ( data - > soc ! = SOC_ARCH_EXYNOS5420 & &
data - > soc ! = SOC_ARCH_EXYNOS5420_TRIMINFO )
data - > min_efuse_value = 40 ;
else
data - > min_efuse_value = 0 ;
data - > max_efuse_value = 100 ;
2014-11-13 16:01:23 +01:00
break ;
2015-03-10 11:23:44 +09:00
case SOC_ARCH_EXYNOS5433 :
2018-04-26 13:51:29 +02:00
data - > tmu_set_trip_temp = exynos5433_tmu_set_trip_temp ;
data - > tmu_set_trip_hyst = exynos5433_tmu_set_trip_hyst ;
2015-03-10 11:23:44 +09:00
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 ;
2018-04-16 12:12:01 +02:00
data - > gain = 8 ;
2018-04-16 12:12:00 +02:00
if ( res . start = = EXYNOS5433_G3D_BASE )
data - > reference_voltage = 23 ;
else
data - > reference_voltage = 16 ;
2018-04-16 12:11:59 +02:00
data - > efuse_value = 75 ;
data - > min_efuse_value = 40 ;
data - > max_efuse_value = 150 ;
2014-11-13 16:01:23 +01:00
break ;
2015-01-27 11:18:22 +05:30
case SOC_ARCH_EXYNOS7 :
2018-04-26 13:51:29 +02:00
data - > tmu_set_trip_temp = exynos7_tmu_set_trip_temp ;
data - > tmu_set_trip_hyst = exynos7_tmu_set_trip_hyst ;
2015-01-27 11:18:22 +05:30
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 ;
2018-04-16 12:12:01 +02:00
data - > gain = 9 ;
2018-04-16 12:12:00 +02:00
data - > reference_voltage = 17 ;
2018-04-16 12:11:59 +02:00
data - > efuse_value = 75 ;
data - > min_efuse_value = 15 ;
data - > max_efuse_value = 100 ;
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 ;
}
2018-04-16 12:12:02 +02:00
data - > cal_type = TYPE_ONE_POINT_TRIMMING ;
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 .
*/
2018-04-26 13:21:12 +02:00
if ( data - > soc ! = SOC_ARCH_EXYNOS5420_TRIMINFO )
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 " ) ;