2018-07-28 22:17:51 +03:00
// SPDX-License-Identifier: GPL-2.0
//
// Copyright 2016 Freescale Semiconductor, Inc.
2016-06-30 06:08:38 +03:00
2019-07-30 05:21:22 +03:00
# include <linux/clk.h>
2016-06-30 06:08:38 +03:00
# include <linux/module.h>
# include <linux/platform_device.h>
# include <linux/err.h>
# include <linux/io.h>
# include <linux/of.h>
# include <linux/of_address.h>
# include <linux/thermal.h>
# include "thermal_core.h"
# define SITES_MAX 16
/*
* QorIQ TMU Registers
*/
struct qoriq_tmu_site_regs {
u32 tritsr ; /* Immediate Temperature Site Register */
u32 tratsr ; /* Average Temperature Site Register */
u8 res0 [ 0x8 ] ;
} ;
struct qoriq_tmu_regs {
u32 tmr ; /* Mode Register */
# define TMR_DISABLE 0x0
# define TMR_ME 0x80000000
# define TMR_ALPF 0x0c000000
u32 tsr ; /* Status Register */
u32 tmtmir ; /* Temperature measurement interval Register */
# define TMTMIR_DEFAULT 0x0000000f
u8 res0 [ 0x14 ] ;
u32 tier ; /* Interrupt Enable Register */
# define TIER_DISABLE 0x0
u32 tidr ; /* Interrupt Detect Register */
u32 tiscr ; /* Interrupt Site Capture Register */
u32 ticscr ; /* Interrupt Critical Site Capture Register */
u8 res1 [ 0x10 ] ;
u32 tmhtcrh ; /* High Temperature Capture Register */
u32 tmhtcrl ; /* Low Temperature Capture Register */
u8 res2 [ 0x8 ] ;
u32 tmhtitr ; /* High Temperature Immediate Threshold */
u32 tmhtatr ; /* High Temperature Average Threshold */
u32 tmhtactr ; /* High Temperature Average Crit Threshold */
u8 res3 [ 0x24 ] ;
u32 ttcfgr ; /* Temperature Configuration Register */
u32 tscfgr ; /* Sensor Configuration Register */
u8 res4 [ 0x78 ] ;
struct qoriq_tmu_site_regs site [ SITES_MAX ] ;
u8 res5 [ 0x9f8 ] ;
u32 ipbrr0 ; /* IP Block Revision Register 0 */
u32 ipbrr1 ; /* IP Block Revision Register 1 */
u8 res6 [ 0x310 ] ;
u32 ttr0cr ; /* Temperature Range 0 Control Register */
u32 ttr1cr ; /* Temperature Range 1 Control Register */
u32 ttr2cr ; /* Temperature Range 2 Control Register */
u32 ttr3cr ; /* Temperature Range 3 Control Register */
} ;
2019-01-18 08:39:40 +03:00
struct qoriq_tmu_data ;
2016-06-30 06:08:38 +03:00
/*
* Thermal zone data
*/
2019-01-18 08:39:40 +03:00
struct qoriq_sensor {
struct thermal_zone_device * tzd ;
struct qoriq_tmu_data * qdata ;
int id ;
} ;
2016-06-30 06:08:38 +03:00
struct qoriq_tmu_data {
struct qoriq_tmu_regs __iomem * regs ;
2019-07-30 05:21:22 +03:00
struct clk * clk ;
2016-06-30 06:08:38 +03:00
bool little_endian ;
2019-01-18 08:39:40 +03:00
struct qoriq_sensor * sensor [ SITES_MAX ] ;
2016-06-30 06:08:38 +03:00
} ;
static void tmu_write ( struct qoriq_tmu_data * p , u32 val , void __iomem * addr )
{
if ( p - > little_endian )
iowrite32 ( val , addr ) ;
else
iowrite32be ( val , addr ) ;
}
static u32 tmu_read ( struct qoriq_tmu_data * p , void __iomem * addr )
{
if ( p - > little_endian )
return ioread32 ( addr ) ;
else
return ioread32be ( addr ) ;
}
static int tmu_get_temp ( void * p , int * temp )
{
2019-01-18 08:39:40 +03:00
struct qoriq_sensor * qsensor = p ;
struct qoriq_tmu_data * qdata = qsensor - > qdata ;
2016-06-30 06:08:38 +03:00
u32 val ;
2019-01-18 08:39:40 +03:00
val = tmu_read ( qdata , & qdata - > regs - > site [ qsensor - > id ] . tritsr ) ;
2016-06-30 06:08:38 +03:00
* temp = ( val & 0xff ) * 1000 ;
return 0 ;
}
2019-01-18 08:39:40 +03:00
static const struct thermal_zone_of_device_ops tmu_tz_ops = {
. get_temp = tmu_get_temp ,
} ;
2016-06-30 06:08:38 +03:00
2019-01-18 08:39:40 +03:00
static int qoriq_tmu_register_tmu_zone ( struct platform_device * pdev )
{
struct qoriq_tmu_data * qdata = platform_get_drvdata ( pdev ) ;
int id , sites = 0 ;
for ( id = 0 ; id < SITES_MAX ; id + + ) {
qdata - > sensor [ id ] = devm_kzalloc ( & pdev - > dev ,
sizeof ( struct qoriq_sensor ) , GFP_KERNEL ) ;
if ( ! qdata - > sensor [ id ] )
return - ENOMEM ;
qdata - > sensor [ id ] - > id = id ;
qdata - > sensor [ id ] - > qdata = qdata ;
qdata - > sensor [ id ] - > tzd = devm_thermal_zone_of_sensor_register (
& pdev - > dev , id , qdata - > sensor [ id ] , & tmu_tz_ops ) ;
if ( IS_ERR ( qdata - > sensor [ id ] - > tzd ) ) {
if ( PTR_ERR ( qdata - > sensor [ id ] - > tzd ) = = - ENODEV )
continue ;
else
return PTR_ERR ( qdata - > sensor [ id ] - > tzd ) ;
}
sites | = 0x1 < < ( 15 - id ) ;
2016-06-30 06:08:38 +03:00
}
2019-01-18 08:39:40 +03:00
/* Enable monitoring */
if ( sites ! = 0 )
tmu_write ( qdata , sites | TMR_ME | TMR_ALPF , & qdata - > regs - > tmr ) ;
2016-06-30 06:08:38 +03:00
2019-01-18 08:39:40 +03:00
return 0 ;
2016-06-30 06:08:38 +03:00
}
static int qoriq_tmu_calibration ( struct platform_device * pdev )
{
int i , val , len ;
u32 range [ 4 ] ;
const u32 * calibration ;
struct device_node * np = pdev - > dev . of_node ;
struct qoriq_tmu_data * data = platform_get_drvdata ( pdev ) ;
if ( of_property_read_u32_array ( np , " fsl,tmu-range " , range , 4 ) ) {
dev_err ( & pdev - > dev , " missing calibration range. \n " ) ;
return - ENODEV ;
}
/* Init temperature range registers */
tmu_write ( data , range [ 0 ] , & data - > regs - > ttr0cr ) ;
tmu_write ( data , range [ 1 ] , & data - > regs - > ttr1cr ) ;
tmu_write ( data , range [ 2 ] , & data - > regs - > ttr2cr ) ;
tmu_write ( data , range [ 3 ] , & data - > regs - > ttr3cr ) ;
calibration = of_get_property ( np , " fsl,tmu-calibration " , & len ) ;
if ( calibration = = NULL | | len % 8 ) {
dev_err ( & pdev - > dev , " invalid calibration data. \n " ) ;
return - ENODEV ;
}
for ( i = 0 ; i < len ; i + = 8 , calibration + = 2 ) {
val = of_read_number ( calibration , 1 ) ;
tmu_write ( data , val , & data - > regs - > ttcfgr ) ;
val = of_read_number ( calibration + 1 , 1 ) ;
tmu_write ( data , val , & data - > regs - > tscfgr ) ;
}
return 0 ;
}
static void qoriq_tmu_init_device ( struct qoriq_tmu_data * data )
{
/* Disable interrupt, using polling instead */
tmu_write ( data , TIER_DISABLE , & data - > regs - > tier ) ;
/* Set update_interval */
tmu_write ( data , TMTMIR_DEFAULT , & data - > regs - > tmtmir ) ;
/* Disable monitoring */
tmu_write ( data , TMR_DISABLE , & data - > regs - > tmr ) ;
}
static int qoriq_tmu_probe ( struct platform_device * pdev )
{
int ret ;
struct qoriq_tmu_data * data ;
struct device_node * np = pdev - > dev . of_node ;
data = devm_kzalloc ( & pdev - > dev , sizeof ( struct qoriq_tmu_data ) ,
GFP_KERNEL ) ;
if ( ! data )
return - ENOMEM ;
platform_set_drvdata ( pdev , data ) ;
data - > little_endian = of_property_read_bool ( np , " little-endian " ) ;
2019-07-30 05:21:24 +03:00
data - > regs = devm_platform_ioremap_resource ( pdev , 0 ) ;
if ( IS_ERR ( data - > regs ) ) {
2016-06-30 06:08:38 +03:00
dev_err ( & pdev - > dev , " Failed to get memory region \n " ) ;
2019-07-30 05:21:24 +03:00
return PTR_ERR ( data - > regs ) ;
2016-06-30 06:08:38 +03:00
}
2019-07-30 05:21:22 +03:00
data - > clk = devm_clk_get_optional ( & pdev - > dev , NULL ) ;
if ( IS_ERR ( data - > clk ) )
return PTR_ERR ( data - > clk ) ;
ret = clk_prepare_enable ( data - > clk ) ;
if ( ret ) {
dev_err ( & pdev - > dev , " Failed to enable clock \n " ) ;
return ret ;
}
2016-06-30 06:08:38 +03:00
qoriq_tmu_init_device ( data ) ; /* TMU initialization */
ret = qoriq_tmu_calibration ( pdev ) ; /* TMU calibration */
if ( ret < 0 )
2019-07-30 05:21:24 +03:00
goto err ;
2016-06-30 06:08:38 +03:00
2019-01-18 08:39:40 +03:00
ret = qoriq_tmu_register_tmu_zone ( pdev ) ;
if ( ret < 0 ) {
dev_err ( & pdev - > dev , " Failed to register sensors \n " ) ;
ret = - ENODEV ;
2019-07-30 05:21:24 +03:00
goto err ;
2016-06-30 06:08:38 +03:00
}
return 0 ;
2019-07-30 05:21:24 +03:00
err :
2019-07-30 05:21:22 +03:00
clk_disable_unprepare ( data - > clk ) ;
2016-06-30 06:08:38 +03:00
platform_set_drvdata ( pdev , NULL ) ;
return ret ;
}
static int qoriq_tmu_remove ( struct platform_device * pdev )
{
struct qoriq_tmu_data * data = platform_get_drvdata ( pdev ) ;
/* Disable monitoring */
tmu_write ( data , TMR_DISABLE , & data - > regs - > tmr ) ;
2019-07-30 05:21:22 +03:00
clk_disable_unprepare ( data - > clk ) ;
2016-06-30 06:08:38 +03:00
platform_set_drvdata ( pdev , NULL ) ;
return 0 ;
}
# ifdef CONFIG_PM_SLEEP
static int qoriq_tmu_suspend ( struct device * dev )
{
u32 tmr ;
struct qoriq_tmu_data * data = dev_get_drvdata ( dev ) ;
/* Disable monitoring */
tmr = tmu_read ( data , & data - > regs - > tmr ) ;
tmr & = ~ TMR_ME ;
tmu_write ( data , tmr , & data - > regs - > tmr ) ;
2019-07-30 05:21:22 +03:00
clk_disable_unprepare ( data - > clk ) ;
2016-06-30 06:08:38 +03:00
return 0 ;
}
static int qoriq_tmu_resume ( struct device * dev )
{
u32 tmr ;
2019-07-30 05:21:22 +03:00
int ret ;
2016-06-30 06:08:38 +03:00
struct qoriq_tmu_data * data = dev_get_drvdata ( dev ) ;
2019-07-30 05:21:22 +03:00
ret = clk_prepare_enable ( data - > clk ) ;
if ( ret )
return ret ;
2016-06-30 06:08:38 +03:00
/* Enable monitoring */
tmr = tmu_read ( data , & data - > regs - > tmr ) ;
tmr | = TMR_ME ;
tmu_write ( data , tmr , & data - > regs - > tmr ) ;
return 0 ;
}
# endif
static SIMPLE_DEV_PM_OPS ( qoriq_tmu_pm_ops ,
qoriq_tmu_suspend , qoriq_tmu_resume ) ;
static const struct of_device_id qoriq_tmu_match [ ] = {
{ . compatible = " fsl,qoriq-tmu " , } ,
2018-08-30 05:14:46 +03:00
{ . compatible = " fsl,imx8mq-tmu " , } ,
2016-06-30 06:08:38 +03:00
{ } ,
} ;
MODULE_DEVICE_TABLE ( of , qoriq_tmu_match ) ;
static struct platform_driver qoriq_tmu = {
. driver = {
. name = " qoriq_thermal " ,
. pm = & qoriq_tmu_pm_ops ,
. of_match_table = qoriq_tmu_match ,
} ,
. probe = qoriq_tmu_probe ,
. remove = qoriq_tmu_remove ,
} ;
module_platform_driver ( qoriq_tmu ) ;
MODULE_AUTHOR ( " Jia Hongtao <hongtao.jia@nxp.com> " ) ;
MODULE_DESCRIPTION ( " QorIQ Thermal Monitoring Unit driver " ) ;
MODULE_LICENSE ( " GPL v2 " ) ;