2020-02-29 06:44:20 +03:00
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright 2020 NXP .
*
* Author : Anson Huang < Anson . Huang @ nxp . com >
*/
2020-03-20 06:26:30 +03:00
# include <linux/bitfield.h>
2020-02-29 06:44:20 +03:00
# include <linux/clk.h>
# include <linux/err.h>
# include <linux/io.h>
# include <linux/module.h>
# include <linux/of.h>
2020-03-20 06:26:30 +03:00
# include <linux/of_device.h>
2020-02-29 06:44:20 +03:00
# include <linux/platform_device.h>
# include <linux/thermal.h>
# include "thermal_core.h"
# define TER 0x0 /* TMU enable */
2020-03-20 06:26:30 +03:00
# define TPS 0x4
2020-02-29 06:44:20 +03:00
# define TRITSR 0x20 /* TMU immediate temp */
# define TER_EN BIT(31)
2020-03-20 06:26:30 +03:00
# define TRITSR_TEMP0_VAL_MASK 0xff
# define TRITSR_TEMP1_VAL_MASK 0xff0000
2020-02-29 06:44:20 +03:00
2020-03-20 06:26:30 +03:00
# define PROBE_SEL_ALL GENMASK(31, 30)
2020-02-29 06:44:20 +03:00
2020-03-20 06:26:30 +03:00
# define probe_status_offset(x) (30 + x)
# define SIGN_BIT BIT(7)
# define TEMP_VAL_MASK GENMASK(6, 0)
# define VER1_TEMP_LOW_LIMIT 10000
# define VER2_TEMP_LOW_LIMIT -40000
# define VER2_TEMP_HIGH_LIMIT 125000
# define TMU_VER1 0x1
# define TMU_VER2 0x2
struct thermal_soc_data {
u32 num_sensors ;
u32 version ;
int ( * get_temp ) ( void * , int * ) ;
} ;
struct tmu_sensor {
struct imx8mm_tmu * priv ;
u32 hw_id ;
2020-02-29 06:44:20 +03:00
struct thermal_zone_device * tzd ;
2020-03-20 06:26:30 +03:00
} ;
struct imx8mm_tmu {
2020-02-29 06:44:20 +03:00
void __iomem * base ;
struct clk * clk ;
2020-03-20 06:26:30 +03:00
const struct thermal_soc_data * socdata ;
2020-05-07 22:25:17 +03:00
struct tmu_sensor sensors [ ] ;
2020-02-29 06:44:20 +03:00
} ;
2020-03-20 06:26:30 +03:00
static int imx8mm_tmu_get_temp ( void * data , int * temp )
2020-02-29 06:44:20 +03:00
{
2020-03-20 06:26:30 +03:00
struct tmu_sensor * sensor = data ;
struct imx8mm_tmu * tmu = sensor - > priv ;
2020-02-29 06:44:20 +03:00
u32 val ;
2020-03-20 06:26:30 +03:00
val = readl_relaxed ( tmu - > base + TRITSR ) & TRITSR_TEMP0_VAL_MASK ;
* temp = val * 1000 ;
if ( * temp < VER1_TEMP_LOW_LIMIT )
2020-02-29 06:44:20 +03:00
return - EAGAIN ;
2020-03-20 06:26:30 +03:00
return 0 ;
}
static int imx8mp_tmu_get_temp ( void * data , int * temp )
{
struct tmu_sensor * sensor = data ;
struct imx8mm_tmu * tmu = sensor - > priv ;
2020-03-23 17:19:16 +03:00
unsigned long val ;
2020-03-20 06:26:30 +03:00
bool ready ;
2020-03-23 17:19:16 +03:00
val = readl_relaxed ( tmu - > base + TRITSR ) ;
ready = test_bit ( probe_status_offset ( sensor - > hw_id ) , & val ) ;
2020-03-20 06:26:30 +03:00
if ( ! ready )
return - EAGAIN ;
val = sensor - > hw_id ? FIELD_GET ( TRITSR_TEMP1_VAL_MASK , val ) :
FIELD_GET ( TRITSR_TEMP0_VAL_MASK , val ) ;
if ( val & SIGN_BIT ) /* negative */
val = ( ~ ( val & TEMP_VAL_MASK ) + 1 ) ;
2020-02-29 06:44:20 +03:00
* temp = val * 1000 ;
2020-03-20 06:26:30 +03:00
if ( * temp < VER2_TEMP_LOW_LIMIT | | * temp > VER2_TEMP_HIGH_LIMIT )
return - EAGAIN ;
2020-02-29 06:44:20 +03:00
return 0 ;
}
2020-03-20 06:26:30 +03:00
static int tmu_get_temp ( void * data , int * temp )
{
struct tmu_sensor * sensor = data ;
struct imx8mm_tmu * tmu = sensor - > priv ;
return tmu - > socdata - > get_temp ( data , temp ) ;
}
2020-02-29 06:44:20 +03:00
static struct thermal_zone_of_device_ops tmu_tz_ops = {
. get_temp = tmu_get_temp ,
} ;
2020-03-20 06:26:30 +03:00
static void imx8mm_tmu_enable ( struct imx8mm_tmu * tmu , bool enable )
{
u32 val ;
val = readl_relaxed ( tmu - > base + TER ) ;
val = enable ? ( val | TER_EN ) : ( val & ~ TER_EN ) ;
writel_relaxed ( val , tmu - > base + TER ) ;
}
static void imx8mm_tmu_probe_sel_all ( struct imx8mm_tmu * tmu )
{
u32 val ;
val = readl_relaxed ( tmu - > base + TPS ) ;
val | = PROBE_SEL_ALL ;
writel_relaxed ( val , tmu - > base + TPS ) ;
}
2020-02-29 06:44:20 +03:00
static int imx8mm_tmu_probe ( struct platform_device * pdev )
{
2020-03-20 06:26:30 +03:00
const struct thermal_soc_data * data ;
2020-02-29 06:44:20 +03:00
struct imx8mm_tmu * tmu ;
int ret ;
2020-03-20 06:26:30 +03:00
int i ;
data = of_device_get_match_data ( & pdev - > dev ) ;
2020-02-29 06:44:20 +03:00
2020-03-20 06:26:30 +03:00
tmu = devm_kzalloc ( & pdev - > dev , struct_size ( tmu , sensors ,
data - > num_sensors ) , GFP_KERNEL ) ;
2020-02-29 06:44:20 +03:00
if ( ! tmu )
return - ENOMEM ;
2020-03-20 06:26:30 +03:00
tmu - > socdata = data ;
2020-02-29 06:44:20 +03:00
tmu - > base = devm_platform_ioremap_resource ( pdev , 0 ) ;
if ( IS_ERR ( tmu - > base ) )
return PTR_ERR ( tmu - > base ) ;
tmu - > clk = devm_clk_get ( & pdev - > dev , NULL ) ;
2020-08-11 09:59:45 +03:00
if ( IS_ERR ( tmu - > clk ) )
return dev_err_probe ( & pdev - > dev , PTR_ERR ( tmu - > clk ) ,
" failed to get tmu clock \n " ) ;
2020-02-29 06:44:20 +03:00
ret = clk_prepare_enable ( tmu - > clk ) ;
if ( ret ) {
dev_err ( & pdev - > dev , " failed to enable tmu clock: %d \n " , ret ) ;
return ret ;
}
2020-03-20 06:26:30 +03:00
/* disable the monitor during initialization */
imx8mm_tmu_enable ( tmu , false ) ;
for ( i = 0 ; i < data - > num_sensors ; i + + ) {
tmu - > sensors [ i ] . priv = tmu ;
tmu - > sensors [ i ] . tzd =
devm_thermal_zone_of_sensor_register ( & pdev - > dev , i ,
& tmu - > sensors [ i ] ,
& tmu_tz_ops ) ;
if ( IS_ERR ( tmu - > sensors [ i ] . tzd ) ) {
2020-12-03 02:24:47 +03:00
ret = PTR_ERR ( tmu - > sensors [ i ] . tzd ) ;
2020-03-20 06:26:30 +03:00
dev_err ( & pdev - > dev ,
" failed to register thermal zone sensor[%d]: %d \n " ,
i , ret ) ;
2020-12-03 02:24:48 +03:00
goto disable_clk ;
2020-03-20 06:26:30 +03:00
}
tmu - > sensors [ i ] . hw_id = i ;
2020-02-29 06:44:20 +03:00
}
platform_set_drvdata ( pdev , tmu ) ;
2020-03-20 06:26:30 +03:00
/* enable all the probes for V2 TMU */
if ( tmu - > socdata - > version = = TMU_VER2 )
imx8mm_tmu_probe_sel_all ( tmu ) ;
2020-02-29 06:44:20 +03:00
/* enable the monitor */
2020-03-20 06:26:30 +03:00
imx8mm_tmu_enable ( tmu , true ) ;
2020-02-29 06:44:20 +03:00
return 0 ;
2020-12-03 02:24:48 +03:00
disable_clk :
clk_disable_unprepare ( tmu - > clk ) ;
return ret ;
2020-02-29 06:44:20 +03:00
}
static int imx8mm_tmu_remove ( struct platform_device * pdev )
{
struct imx8mm_tmu * tmu = platform_get_drvdata ( pdev ) ;
/* disable TMU */
2020-03-20 06:26:30 +03:00
imx8mm_tmu_enable ( tmu , false ) ;
2020-02-29 06:44:20 +03:00
clk_disable_unprepare ( tmu - > clk ) ;
platform_set_drvdata ( pdev , NULL ) ;
return 0 ;
}
2020-03-20 06:26:30 +03:00
static struct thermal_soc_data imx8mm_tmu_data = {
. num_sensors = 1 ,
. version = TMU_VER1 ,
. get_temp = imx8mm_tmu_get_temp ,
} ;
static struct thermal_soc_data imx8mp_tmu_data = {
. num_sensors = 2 ,
. version = TMU_VER2 ,
. get_temp = imx8mp_tmu_get_temp ,
} ;
2020-02-29 06:44:20 +03:00
static const struct of_device_id imx8mm_tmu_table [ ] = {
2020-03-20 06:26:30 +03:00
{ . compatible = " fsl,imx8mm-tmu " , . data = & imx8mm_tmu_data , } ,
{ . compatible = " fsl,imx8mp-tmu " , . data = & imx8mp_tmu_data , } ,
2020-02-29 06:44:20 +03:00
{ } ,
} ;
2020-06-17 10:47:54 +03:00
MODULE_DEVICE_TABLE ( of , imx8mm_tmu_table ) ;
2020-02-29 06:44:20 +03:00
static struct platform_driver imx8mm_tmu = {
. driver = {
. name = " i.mx8mm_thermal " ,
. of_match_table = imx8mm_tmu_table ,
} ,
. probe = imx8mm_tmu_probe ,
. remove = imx8mm_tmu_remove ,
} ;
module_platform_driver ( imx8mm_tmu ) ;
MODULE_AUTHOR ( " Anson Huang <Anson.Huang@nxp.com> " ) ;
MODULE_DESCRIPTION ( " i.MX8MM Thermal Monitor Unit driver " ) ;
MODULE_LICENSE ( " GPL v2 " ) ;