2021-11-30 18:57:57 +03:00
// SPDX-License-Identifier: GPL-2.0
/*
* Renesas RZ / G2L TSU Thermal Sensor Driver
*
* Copyright ( C ) 2021 Renesas Electronics Corporation
*/
# include <linux/delay.h>
# include <linux/err.h>
# include <linux/io.h>
# include <linux/iopoll.h>
# include <linux/math.h>
# include <linux/module.h>
# include <linux/of_device.h>
# include <linux/platform_device.h>
# include <linux/pm_runtime.h>
# include <linux/reset.h>
# include <linux/thermal.h>
# include <linux/units.h>
# include "thermal_hwmon.h"
# define CTEMP_MASK 0xFFF
/* default calibration values, if FUSE values are missing */
# define SW_CALIB0_VAL 3148
# define SW_CALIB1_VAL 503
/* Register offsets */
# define TSU_SM 0x00
# define TSU_ST 0x04
# define TSU_SAD 0x0C
# define TSU_SS 0x10
# define OTPTSUTRIM_REG(n) (0x18 + ((n) * 0x4))
2022-04-28 12:33:46 +03:00
# define OTPTSUTRIM_EN_MASK BIT(31)
# define OTPTSUTRIM_MASK GENMASK(11, 0)
2021-11-30 18:57:57 +03:00
/* Sensor Mode Register(TSU_SM) */
# define TSU_SM_EN_TS BIT(0)
# define TSU_SM_ADC_EN_TS BIT(1)
# define TSU_SM_NORMAL_MODE (TSU_SM_EN_TS | TSU_SM_ADC_EN_TS)
/* TSU_ST bits */
# define TSU_ST_START BIT(0)
# define TSU_SS_CONV_RUNNING BIT(0)
# define TS_CODE_AVE_SCALE(x) ((x) * 1000000)
# define MCELSIUS(temp) ((temp) * MILLIDEGREE_PER_DEGREE)
2022-07-18 15:14:40 +03:00
# define TS_CODE_CAP_TIMES 8 /* Total number of ADC data samples */
2021-11-30 18:57:57 +03:00
# define RZG2L_THERMAL_GRAN 500 /* milli Celsius */
# define RZG2L_TSU_SS_TIMEOUT_US 1000
# define CURVATURE_CORRECTION_CONST 13
struct rzg2l_thermal_priv {
struct device * dev ;
void __iomem * base ;
struct thermal_zone_device * zone ;
struct reset_control * rstc ;
u32 calib0 , calib1 ;
} ;
static inline u32 rzg2l_thermal_read ( struct rzg2l_thermal_priv * priv , u32 reg )
{
return ioread32 ( priv - > base + reg ) ;
}
static inline void rzg2l_thermal_write ( struct rzg2l_thermal_priv * priv , u32 reg ,
u32 data )
{
iowrite32 ( data , priv - > base + reg ) ;
}
2022-08-05 01:43:34 +03:00
static int rzg2l_thermal_get_temp ( struct thermal_zone_device * tz , int * temp )
2021-11-30 18:57:57 +03:00
{
2022-08-05 01:43:34 +03:00
struct rzg2l_thermal_priv * priv = tz - > devdata ;
2021-11-30 18:57:57 +03:00
u32 result = 0 , dsensor , ts_code_ave ;
int val , i ;
for ( i = 0 ; i < TS_CODE_CAP_TIMES ; i + + ) {
2022-07-18 15:14:40 +03:00
/*
* TSU repeats measurement at 20 microseconds intervals and
2021-11-30 18:57:57 +03:00
* automatically updates the results of measurement . As per
* the HW manual for measuring temperature we need to read 8
* values consecutively and then take the average .
* ts_code_ave = ( ts_code [ 0 ] + ⋯ + ts_code [ 7 ] ) / 8
*/
result + = rzg2l_thermal_read ( priv , TSU_SAD ) & CTEMP_MASK ;
usleep_range ( 20 , 30 ) ;
}
ts_code_ave = result / TS_CODE_CAP_TIMES ;
2022-07-18 15:14:40 +03:00
/*
* Calculate actual sensor value by applying curvature correction formula
2021-11-30 18:57:57 +03:00
* dsensor = ts_code_ave / ( 1 + ts_code_ave * 0.000013 ) . Here we are doing
* integer calculation by scaling all the values by 1000000.
*/
dsensor = TS_CODE_AVE_SCALE ( ts_code_ave ) /
( TS_CODE_AVE_SCALE ( 1 ) + ( ts_code_ave * CURVATURE_CORRECTION_CONST ) ) ;
2022-07-18 15:14:40 +03:00
/*
* The temperature Tj is calculated by the formula
2021-11-30 18:57:57 +03:00
* Tj = ( dsensor − calib1 ) * 165 / ( calib0 − calib1 ) − 40
2022-07-18 15:14:40 +03:00
* where calib0 and calib1 are the calibration values .
2021-11-30 18:57:57 +03:00
*/
val = ( ( dsensor - priv - > calib1 ) * ( MCELSIUS ( 165 ) /
( priv - > calib0 - priv - > calib1 ) ) ) - MCELSIUS ( 40 ) ;
* temp = roundup ( val , RZG2L_THERMAL_GRAN ) ;
return 0 ;
}
2022-08-05 01:43:34 +03:00
static const struct thermal_zone_device_ops rzg2l_tz_of_ops = {
2021-11-30 18:57:57 +03:00
. get_temp = rzg2l_thermal_get_temp ,
} ;
static int rzg2l_thermal_init ( struct rzg2l_thermal_priv * priv )
{
u32 reg_val ;
rzg2l_thermal_write ( priv , TSU_SM , TSU_SM_NORMAL_MODE ) ;
rzg2l_thermal_write ( priv , TSU_ST , 0 ) ;
2022-07-18 15:14:40 +03:00
/*
* Before setting the START bit , TSU should be in normal operating
2021-11-30 18:57:57 +03:00
* mode . As per the HW manual , it will take 60 µ s to place the TSU
* into normal operating mode .
*/
usleep_range ( 60 , 80 ) ;
reg_val = rzg2l_thermal_read ( priv , TSU_ST ) ;
reg_val | = TSU_ST_START ;
rzg2l_thermal_write ( priv , TSU_ST , reg_val ) ;
return readl_poll_timeout ( priv - > base + TSU_SS , reg_val ,
reg_val = = TSU_SS_CONV_RUNNING , 50 ,
RZG2L_TSU_SS_TIMEOUT_US ) ;
}
static void rzg2l_thermal_reset_assert_pm_disable_put ( struct platform_device * pdev )
{
struct rzg2l_thermal_priv * priv = dev_get_drvdata ( & pdev - > dev ) ;
pm_runtime_put ( & pdev - > dev ) ;
pm_runtime_disable ( & pdev - > dev ) ;
reset_control_assert ( priv - > rstc ) ;
}
static int rzg2l_thermal_remove ( struct platform_device * pdev )
{
struct rzg2l_thermal_priv * priv = dev_get_drvdata ( & pdev - > dev ) ;
thermal_remove_hwmon_sysfs ( priv - > zone ) ;
rzg2l_thermal_reset_assert_pm_disable_put ( pdev ) ;
return 0 ;
}
static int rzg2l_thermal_probe ( struct platform_device * pdev )
{
struct thermal_zone_device * zone ;
struct rzg2l_thermal_priv * priv ;
struct device * dev = & pdev - > dev ;
int ret ;
priv = devm_kzalloc ( dev , sizeof ( * priv ) , GFP_KERNEL ) ;
if ( ! priv )
return - ENOMEM ;
priv - > base = devm_platform_ioremap_resource ( pdev , 0 ) ;
if ( IS_ERR ( priv - > base ) )
return PTR_ERR ( priv - > base ) ;
priv - > dev = dev ;
2021-12-08 19:40:09 +03:00
priv - > rstc = devm_reset_control_get_exclusive ( dev , NULL ) ;
2021-11-30 18:57:57 +03:00
if ( IS_ERR ( priv - > rstc ) )
return dev_err_probe ( dev , PTR_ERR ( priv - > rstc ) ,
" failed to get cpg reset " ) ;
2021-12-08 19:40:09 +03:00
ret = reset_control_deassert ( priv - > rstc ) ;
if ( ret )
return dev_err_probe ( dev , ret , " failed to deassert " ) ;
2021-11-30 18:57:57 +03:00
pm_runtime_enable ( dev ) ;
pm_runtime_get_sync ( dev ) ;
priv - > calib0 = rzg2l_thermal_read ( priv , OTPTSUTRIM_REG ( 0 ) ) ;
2022-04-28 12:33:46 +03:00
if ( priv - > calib0 & OTPTSUTRIM_EN_MASK )
priv - > calib0 & = OTPTSUTRIM_MASK ;
else
2021-11-30 18:57:57 +03:00
priv - > calib0 = SW_CALIB0_VAL ;
priv - > calib1 = rzg2l_thermal_read ( priv , OTPTSUTRIM_REG ( 1 ) ) ;
2022-04-28 12:33:46 +03:00
if ( priv - > calib1 & OTPTSUTRIM_EN_MASK )
priv - > calib1 & = OTPTSUTRIM_MASK ;
else
2021-11-30 18:57:57 +03:00
priv - > calib1 = SW_CALIB1_VAL ;
platform_set_drvdata ( pdev , priv ) ;
ret = rzg2l_thermal_init ( priv ) ;
if ( ret ) {
dev_err ( dev , " Failed to start TSU " ) ;
goto err ;
}
2022-08-05 01:43:34 +03:00
zone = devm_thermal_of_zone_register ( dev , 0 , priv ,
& rzg2l_tz_of_ops ) ;
2021-11-30 18:57:57 +03:00
if ( IS_ERR ( zone ) ) {
dev_err ( dev , " Can't register thermal zone " ) ;
ret = PTR_ERR ( zone ) ;
goto err ;
}
priv - > zone = zone ;
priv - > zone - > tzp - > no_hwmon = false ;
ret = thermal_add_hwmon_sysfs ( priv - > zone ) ;
if ( ret )
goto err ;
2022-07-18 15:14:40 +03:00
dev_dbg ( dev , " TSU probed with %s calibration values " ,
2021-11-30 18:57:57 +03:00
rzg2l_thermal_read ( priv , OTPTSUTRIM_REG ( 0 ) ) ? " hw " : " sw " ) ;
return 0 ;
err :
rzg2l_thermal_reset_assert_pm_disable_put ( pdev ) ;
return ret ;
}
static const struct of_device_id rzg2l_thermal_dt_ids [ ] = {
{ . compatible = " renesas,rzg2l-tsu " , } ,
{ /* sentinel */ }
} ;
MODULE_DEVICE_TABLE ( of , rzg2l_thermal_dt_ids ) ;
static struct platform_driver rzg2l_thermal_driver = {
. driver = {
. name = " rzg2l_thermal " ,
. of_match_table = rzg2l_thermal_dt_ids ,
} ,
. probe = rzg2l_thermal_probe ,
. remove = rzg2l_thermal_remove ,
} ;
module_platform_driver ( rzg2l_thermal_driver ) ;
MODULE_DESCRIPTION ( " Renesas RZ/G2L TSU Thermal Sensor Driver " ) ;
MODULE_AUTHOR ( " Biju Das <biju.das.jz@bp.renesas.com> " ) ;
MODULE_LICENSE ( " GPL v2 " ) ;