2018-10-05 13:08:46 +03:00
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright ( C ) STMicroelectronics 2018 - All Rights Reserved
* Author : David Hernandez Sanchez < david . hernandezsanchez @ st . com > for
* STMicroelectronics .
*/
# include <linux/clk.h>
# include <linux/clk-provider.h>
# include <linux/delay.h>
# include <linux/err.h>
# include <linux/interrupt.h>
# include <linux/io.h>
# include <linux/iopoll.h>
# include <linux/module.h>
# include <linux/of.h>
# include <linux/of_address.h>
# include <linux/of_device.h>
# include <linux/platform_device.h>
# include <linux/thermal.h>
# include "../thermal_core.h"
# include "../thermal_hwmon.h"
/* DTS register offsets */
# define DTS_CFGR1_OFFSET 0x0
# define DTS_T0VALR1_OFFSET 0x8
# define DTS_RAMPVALR_OFFSET 0X10
# define DTS_ITR1_OFFSET 0x14
# define DTS_DR_OFFSET 0x1C
# define DTS_SR_OFFSET 0x20
# define DTS_ITENR_OFFSET 0x24
2020-01-10 13:16:00 +03:00
# define DTS_ICIFR_OFFSET 0x28
2018-10-05 13:08:46 +03:00
/* DTS_CFGR1 register mask definitions */
# define HSREF_CLK_DIV_MASK GENMASK(30, 24)
# define TS1_SMP_TIME_MASK GENMASK(19, 16)
# define TS1_INTRIG_SEL_MASK GENMASK(11, 8)
/* DTS_T0VALR1 register mask definitions */
# define TS1_T0_MASK GENMASK(17, 16)
# define TS1_FMT0_MASK GENMASK(15, 0)
/* DTS_RAMPVALR register mask definitions */
# define TS1_RAMP_COEFF_MASK GENMASK(15, 0)
/* DTS_ITR1 register mask definitions */
# define TS1_HITTHD_MASK GENMASK(31, 16)
# define TS1_LITTHD_MASK GENMASK(15, 0)
/* DTS_DR register mask definitions */
# define TS1_MFREQ_MASK GENMASK(15, 0)
2020-01-10 13:16:02 +03:00
/* DTS_ITENR register mask definitions */
# define ITENR_MASK (GENMASK(2, 0) | GENMASK(6, 4))
/* DTS_ICIFR register mask definitions */
# define ICIFR_MASK (GENMASK(2, 0) | GENMASK(6, 4))
2018-10-05 13:08:46 +03:00
/* Less significant bit position definitions */
# define TS1_T0_POS 16
# define TS1_HITTHD_POS 16
2020-01-10 13:16:03 +03:00
# define TS1_LITTHD_POS 0
2018-10-05 13:08:46 +03:00
# define HSREF_CLK_DIV_POS 24
/* DTS_CFGR1 bit definitions */
# define TS1_EN BIT(0)
# define TS1_START BIT(4)
# define REFCLK_SEL BIT(20)
# define REFCLK_LSE REFCLK_SEL
# define Q_MEAS_OPT BIT(21)
# define CALIBRATION_CONTROL Q_MEAS_OPT
/* DTS_SR bit definitions */
# define TS_RDY BIT(15)
/* Bit definitions below are common for DTS_SR, DTS_ITENR and DTS_CIFR */
# define HIGH_THRESHOLD BIT(2)
# define LOW_THRESHOLD BIT(1)
/* Constants */
# define ADJUST 100
# define ONE_MHZ 1000000
# define POLL_TIMEOUT 5000
# define STARTUP_TIME 40
2020-01-10 13:16:04 +03:00
# define TS1_T0_VAL0 30000 /* 30 celsius */
# define TS1_T0_VAL1 130000 /* 130 celsius */
2018-10-05 13:08:46 +03:00
# define NO_HW_TRIG 0
2020-01-10 13:16:04 +03:00
# define SAMPLING_TIME 15
2018-10-05 13:08:46 +03:00
struct stm_thermal_sensor {
struct device * dev ;
struct thermal_zone_device * th_dev ;
enum thermal_device_mode mode ;
struct clk * clk ;
unsigned int low_temp_enabled ;
2020-01-10 13:16:03 +03:00
unsigned int high_temp_enabled ;
2018-10-05 13:08:46 +03:00
int irq ;
void __iomem * base ;
int t0 , fmt0 , ramp_coeff ;
} ;
2020-01-10 13:16:03 +03:00
static int stm_enable_irq ( struct stm_thermal_sensor * sensor )
2018-10-05 13:08:46 +03:00
{
2020-01-10 13:16:03 +03:00
u32 value ;
2018-10-05 13:08:46 +03:00
2020-01-10 13:16:03 +03:00
dev_dbg ( sensor - > dev , " low:%d high:%d \n " , sensor - > low_temp_enabled ,
sensor - > high_temp_enabled ) ;
/* Disable IT generation for low and high thresholds */
value = readl_relaxed ( sensor - > base + DTS_ITENR_OFFSET ) ;
value & = ~ ( LOW_THRESHOLD | HIGH_THRESHOLD ) ;
2018-10-05 13:08:46 +03:00
2020-01-10 13:16:03 +03:00
if ( sensor - > low_temp_enabled )
value | = HIGH_THRESHOLD ;
if ( sensor - > high_temp_enabled )
value | = LOW_THRESHOLD ;
/* Enable interrupts */
writel_relaxed ( value , sensor - > base + DTS_ITENR_OFFSET ) ;
return 0 ;
2018-10-05 13:08:46 +03:00
}
2020-01-10 13:16:03 +03:00
static irqreturn_t stm_thermal_irq_handler ( int irq , void * sdata )
2018-10-05 13:08:46 +03:00
{
struct stm_thermal_sensor * sensor = sdata ;
2020-01-10 13:16:03 +03:00
dev_dbg ( sensor - > dev , " sr:%d \n " ,
readl_relaxed ( sensor - > base + DTS_SR_OFFSET ) ) ;
2018-10-05 13:08:46 +03:00
2020-01-10 13:16:03 +03:00
thermal_zone_device_update ( sensor - > th_dev , THERMAL_EVENT_UNSPECIFIED ) ;
2018-10-05 13:08:46 +03:00
2020-01-10 13:16:03 +03:00
stm_enable_irq ( sensor ) ;
2018-10-05 13:08:46 +03:00
2020-01-10 13:16:03 +03:00
/* Acknoledge all DTS irqs */
writel_relaxed ( ICIFR_MASK , sensor - > base + DTS_ICIFR_OFFSET ) ;
2018-10-05 13:08:46 +03:00
return IRQ_HANDLED ;
}
static int stm_sensor_power_on ( struct stm_thermal_sensor * sensor )
{
int ret ;
u32 value ;
/* Enable sensor */
value = readl_relaxed ( sensor - > base + DTS_CFGR1_OFFSET ) ;
value | = TS1_EN ;
writel_relaxed ( value , sensor - > base + DTS_CFGR1_OFFSET ) ;
/*
* The DTS block can be enabled by setting TSx_EN bit in
* DTS_CFGRx register . It requires a startup time of
* 40 μ s . Use 5 ms as arbitrary timeout .
*/
ret = readl_poll_timeout ( sensor - > base + DTS_SR_OFFSET ,
value , ( value & TS_RDY ) ,
STARTUP_TIME , POLL_TIMEOUT ) ;
if ( ret )
return ret ;
/* Start continuous measuring */
value = readl_relaxed ( sensor - > base +
DTS_CFGR1_OFFSET ) ;
value | = TS1_START ;
writel_relaxed ( value , sensor - > base +
DTS_CFGR1_OFFSET ) ;
2020-01-10 13:16:01 +03:00
sensor - > mode = THERMAL_DEVICE_ENABLED ;
2018-10-05 13:08:46 +03:00
return 0 ;
}
static int stm_sensor_power_off ( struct stm_thermal_sensor * sensor )
{
u32 value ;
2020-01-10 13:16:01 +03:00
sensor - > mode = THERMAL_DEVICE_DISABLED ;
2018-10-05 13:08:46 +03:00
/* Stop measuring */
value = readl_relaxed ( sensor - > base + DTS_CFGR1_OFFSET ) ;
value & = ~ TS1_START ;
writel_relaxed ( value , sensor - > base + DTS_CFGR1_OFFSET ) ;
/* Ensure stop is taken into account */
usleep_range ( STARTUP_TIME , POLL_TIMEOUT ) ;
/* Disable sensor */
value = readl_relaxed ( sensor - > base + DTS_CFGR1_OFFSET ) ;
value & = ~ TS1_EN ;
writel_relaxed ( value , sensor - > base + DTS_CFGR1_OFFSET ) ;
/* Ensure disable is taken into account */
return readl_poll_timeout ( sensor - > base + DTS_SR_OFFSET , value ,
! ( value & TS_RDY ) ,
STARTUP_TIME , POLL_TIMEOUT ) ;
}
static int stm_thermal_calibration ( struct stm_thermal_sensor * sensor )
{
u32 value , clk_freq ;
u32 prescaler ;
/* Figure out prescaler value for PCLK during calibration */
clk_freq = clk_get_rate ( sensor - > clk ) ;
if ( ! clk_freq )
return - EINVAL ;
prescaler = 0 ;
clk_freq / = ONE_MHZ ;
if ( clk_freq ) {
while ( prescaler < = clk_freq )
prescaler + + ;
}
value = readl_relaxed ( sensor - > base + DTS_CFGR1_OFFSET ) ;
/* Clear prescaler */
value & = ~ HSREF_CLK_DIV_MASK ;
/* Set prescaler. pclk_freq/prescaler < 1MHz */
value | = ( prescaler < < HSREF_CLK_DIV_POS ) ;
/* Select PCLK as reference clock */
value & = ~ REFCLK_SEL ;
/* Set maximal sampling time for better precision */
value | = TS1_SMP_TIME_MASK ;
/* Measure with calibration */
value & = ~ CALIBRATION_CONTROL ;
/* select trigger */
value & = ~ TS1_INTRIG_SEL_MASK ;
value | = NO_HW_TRIG ;
writel_relaxed ( value , sensor - > base + DTS_CFGR1_OFFSET ) ;
return 0 ;
}
/* Fill in DTS structure with factory sensor values */
static int stm_thermal_read_factory_settings ( struct stm_thermal_sensor * sensor )
{
/* Retrieve engineering calibration temperature */
sensor - > t0 = readl_relaxed ( sensor - > base + DTS_T0VALR1_OFFSET ) &
TS1_T0_MASK ;
if ( ! sensor - > t0 )
sensor - > t0 = TS1_T0_VAL0 ;
else
sensor - > t0 = TS1_T0_VAL1 ;
/* Retrieve fmt0 and put it on Hz */
2018-12-06 16:23:32 +03:00
sensor - > fmt0 = ADJUST * ( readl_relaxed ( sensor - > base +
DTS_T0VALR1_OFFSET ) & TS1_FMT0_MASK ) ;
2018-10-05 13:08:46 +03:00
/* Retrieve ramp coefficient */
sensor - > ramp_coeff = readl_relaxed ( sensor - > base + DTS_RAMPVALR_OFFSET ) &
TS1_RAMP_COEFF_MASK ;
if ( ! sensor - > fmt0 | | ! sensor - > ramp_coeff ) {
dev_err ( sensor - > dev , " %s: wrong setting \n " , __func__ ) ;
return - EINVAL ;
}
dev_dbg ( sensor - > dev , " %s: T0 = %doC, FMT0 = %dHz, RAMP_COEFF = %dHz/oC " ,
__func__ , sensor - > t0 , sensor - > fmt0 , sensor - > ramp_coeff ) ;
return 0 ;
}
static int stm_thermal_calculate_threshold ( struct stm_thermal_sensor * sensor ,
int temp , u32 * th )
{
int freqM ;
/* Figure out the CLK_PTAT frequency for a given temperature */
2020-01-10 13:16:04 +03:00
freqM = ( ( temp - sensor - > t0 ) * sensor - > ramp_coeff ) / 1000 +
sensor - > fmt0 ;
2018-10-05 13:08:46 +03:00
/* Figure out the threshold sample number */
2020-01-10 13:16:04 +03:00
* th = clk_get_rate ( sensor - > clk ) * SAMPLING_TIME / freqM ;
2018-10-05 13:08:46 +03:00
if ( ! * th )
return - EINVAL ;
2020-01-10 13:16:04 +03:00
dev_dbg ( sensor - > dev , " freqM=%d Hz, threshold=0x%x " , freqM , * th ) ;
2018-10-05 13:08:46 +03:00
return 0 ;
}
/* Disable temperature interrupt */
static int stm_disable_irq ( struct stm_thermal_sensor * sensor )
{
u32 value ;
2020-01-10 13:16:02 +03:00
/* Disable IT generation */
2018-10-05 13:08:46 +03:00
value = readl_relaxed ( sensor - > base + DTS_ITENR_OFFSET ) ;
2020-01-10 13:16:02 +03:00
value & = ~ ITENR_MASK ;
writel_relaxed ( value , sensor - > base + DTS_ITENR_OFFSET ) ;
2018-10-05 13:08:46 +03:00
return 0 ;
}
2020-01-10 13:16:03 +03:00
static int stm_thermal_set_trips ( void * data , int low , int high )
2018-10-05 13:08:46 +03:00
{
2020-01-10 13:16:03 +03:00
struct stm_thermal_sensor * sensor = data ;
u32 itr1 , th ;
int ret ;
2018-10-05 13:08:46 +03:00
2020-01-10 13:16:03 +03:00
dev_dbg ( sensor - > dev , " set trips %d <--> %d \n " , low , high ) ;
2018-10-05 13:08:46 +03:00
2020-01-10 13:16:03 +03:00
/* Erase threshold content */
itr1 = readl_relaxed ( sensor - > base + DTS_ITR1_OFFSET ) ;
itr1 & = ~ ( TS1_LITTHD_MASK | TS1_HITTHD_MASK ) ;
2018-10-05 13:08:46 +03:00
2020-01-10 13:16:03 +03:00
/*
* Disable low - temp if " low " is too small . As per thermal framework
* API , we use - INT_MAX rather than INT_MIN .
*/
2018-10-05 13:08:46 +03:00
2020-01-10 13:16:03 +03:00
if ( low > - INT_MAX ) {
sensor - > low_temp_enabled = 1 ;
2020-01-10 13:16:05 +03:00
/* add 0.5 of hysteresis due to measurement error */
ret = stm_thermal_calculate_threshold ( sensor , low - 500 , & th ) ;
2020-01-10 13:16:03 +03:00
if ( ret )
return ret ;
2018-10-05 13:08:46 +03:00
2020-01-10 13:16:03 +03:00
itr1 | = ( TS1_HITTHD_MASK & ( th < < TS1_HITTHD_POS ) ) ;
} else {
sensor - > low_temp_enabled = 0 ;
2018-10-05 13:08:46 +03:00
}
2020-01-10 13:16:03 +03:00
/* Disable high-temp if "high" is too big. */
if ( high < INT_MAX ) {
sensor - > high_temp_enabled = 1 ;
ret = stm_thermal_calculate_threshold ( sensor , high , & th ) ;
if ( ret )
return ret ;
2018-10-05 13:08:46 +03:00
2020-01-10 13:16:03 +03:00
itr1 | = ( TS1_LITTHD_MASK & ( th < < TS1_LITTHD_POS ) ) ;
} else {
sensor - > high_temp_enabled = 0 ;
}
2018-10-05 13:08:46 +03:00
2020-01-10 13:16:03 +03:00
/* Write new threshod values*/
writel_relaxed ( itr1 , sensor - > base + DTS_ITR1_OFFSET ) ;
2018-10-05 13:08:46 +03:00
return 0 ;
}
/* Callback to get temperature from HW */
static int stm_thermal_get_temp ( void * data , int * temp )
{
struct stm_thermal_sensor * sensor = data ;
2020-01-10 13:16:04 +03:00
u32 periods ;
2018-10-05 13:08:46 +03:00
int freqM , ret ;
if ( sensor - > mode ! = THERMAL_DEVICE_ENABLED )
return - EAGAIN ;
2020-01-10 13:16:04 +03:00
/* Retrieve the number of periods sampled */
ret = readl_relaxed_poll_timeout ( sensor - > base + DTS_DR_OFFSET , periods ,
( periods & TS1_MFREQ_MASK ) ,
STARTUP_TIME , POLL_TIMEOUT ) ;
2018-10-05 13:08:46 +03:00
if ( ret )
return ret ;
/* Figure out the CLK_PTAT frequency */
2020-01-10 13:16:04 +03:00
freqM = ( clk_get_rate ( sensor - > clk ) * SAMPLING_TIME ) / periods ;
2018-10-05 13:08:46 +03:00
if ( ! freqM )
return - EINVAL ;
/* Figure out the temperature in mili celsius */
2020-01-10 13:16:04 +03:00
* temp = ( freqM - sensor - > fmt0 ) * 1000 / sensor - > ramp_coeff + sensor - > t0 ;
2018-10-05 13:08:46 +03:00
return 0 ;
}
/* Registers DTS irq to be visible by GIC */
static int stm_register_irq ( struct stm_thermal_sensor * sensor )
{
struct device * dev = sensor - > dev ;
struct platform_device * pdev = to_platform_device ( dev ) ;
int ret ;
sensor - > irq = platform_get_irq ( pdev , 0 ) ;
if ( sensor - > irq < 0 ) {
dev_err ( dev , " %s: Unable to find IRQ \n " , __func__ ) ;
return sensor - > irq ;
}
ret = devm_request_threaded_irq ( dev , sensor - > irq ,
2020-01-10 13:16:03 +03:00
NULL ,
stm_thermal_irq_handler ,
2018-10-05 13:08:46 +03:00
IRQF_ONESHOT ,
dev - > driver - > name , sensor ) ;
if ( ret ) {
dev_err ( dev , " %s: Failed to register IRQ %d \n " , __func__ ,
sensor - > irq ) ;
return ret ;
}
dev_dbg ( dev , " %s: thermal IRQ registered " , __func__ ) ;
return 0 ;
}
static int stm_thermal_sensor_off ( struct stm_thermal_sensor * sensor )
{
int ret ;
2020-01-10 13:16:03 +03:00
stm_disable_irq ( sensor ) ;
2018-10-05 13:08:46 +03:00
ret = stm_sensor_power_off ( sensor ) ;
if ( ret )
return ret ;
clk_disable_unprepare ( sensor - > clk ) ;
return 0 ;
}
static int stm_thermal_prepare ( struct stm_thermal_sensor * sensor )
{
int ret ;
ret = clk_prepare_enable ( sensor - > clk ) ;
if ( ret )
return ret ;
2018-12-06 16:23:32 +03:00
ret = stm_thermal_read_factory_settings ( sensor ) ;
if ( ret )
goto thermal_unprepare ;
2018-10-05 13:08:46 +03:00
ret = stm_thermal_calibration ( sensor ) ;
if ( ret )
goto thermal_unprepare ;
return 0 ;
thermal_unprepare :
clk_disable_unprepare ( sensor - > clk ) ;
return ret ;
}
# ifdef CONFIG_PM_SLEEP
static int stm_thermal_suspend ( struct device * dev )
{
int ret ;
2019-03-19 19:36:35 +03:00
struct stm_thermal_sensor * sensor = dev_get_drvdata ( dev ) ;
2018-10-05 13:08:46 +03:00
ret = stm_thermal_sensor_off ( sensor ) ;
if ( ret )
return ret ;
return 0 ;
}
static int stm_thermal_resume ( struct device * dev )
{
int ret ;
2019-03-19 19:36:35 +03:00
struct stm_thermal_sensor * sensor = dev_get_drvdata ( dev ) ;
2018-10-05 13:08:46 +03:00
ret = stm_thermal_prepare ( sensor ) ;
if ( ret )
return ret ;
2020-01-10 13:16:03 +03:00
ret = stm_sensor_power_on ( sensor ) ;
if ( ret )
return ret ;
thermal_zone_device_update ( sensor - > th_dev , THERMAL_EVENT_UNSPECIFIED ) ;
stm_enable_irq ( sensor ) ;
2018-10-05 13:08:46 +03:00
return 0 ;
}
# endif /* CONFIG_PM_SLEEP */
2019-10-08 14:00:21 +03:00
static SIMPLE_DEV_PM_OPS ( stm_thermal_pm_ops ,
stm_thermal_suspend , stm_thermal_resume ) ;
2018-10-05 13:08:46 +03:00
static const struct thermal_zone_of_device_ops stm_tz_ops = {
. get_temp = stm_thermal_get_temp ,
2020-01-10 13:16:03 +03:00
. set_trips = stm_thermal_set_trips ,
2018-10-05 13:08:46 +03:00
} ;
static const struct of_device_id stm_thermal_of_match [ ] = {
{ . compatible = " st,stm32-thermal " } ,
{ /* sentinel */ }
} ;
MODULE_DEVICE_TABLE ( of , stm_thermal_of_match ) ;
static int stm_thermal_probe ( struct platform_device * pdev )
{
struct stm_thermal_sensor * sensor ;
struct resource * res ;
void __iomem * base ;
2020-01-10 13:16:03 +03:00
int ret ;
2018-10-05 13:08:46 +03:00
if ( ! pdev - > dev . of_node ) {
dev_err ( & pdev - > dev , " %s: device tree node not found \n " ,
__func__ ) ;
return - EINVAL ;
}
sensor = devm_kzalloc ( & pdev - > dev , sizeof ( * sensor ) , GFP_KERNEL ) ;
if ( ! sensor )
return - ENOMEM ;
platform_set_drvdata ( pdev , sensor ) ;
sensor - > dev = & pdev - > dev ;
res = platform_get_resource ( pdev , IORESOURCE_MEM , 0 ) ;
base = devm_ioremap_resource ( & pdev - > dev , res ) ;
if ( IS_ERR ( base ) )
return PTR_ERR ( base ) ;
/* Populate sensor */
sensor - > base = base ;
sensor - > clk = devm_clk_get ( & pdev - > dev , " pclk " ) ;
if ( IS_ERR ( sensor - > clk ) ) {
dev_err ( & pdev - > dev , " %s: failed to fetch PCLK clock \n " ,
__func__ ) ;
return PTR_ERR ( sensor - > clk ) ;
}
2020-01-10 13:16:02 +03:00
stm_disable_irq ( sensor ) ;
/* Clear irq flags */
writel_relaxed ( ICIFR_MASK , sensor - > base + DTS_ICIFR_OFFSET ) ;
2020-01-10 13:16:03 +03:00
/* Configure and enable HW sensor */
ret = stm_thermal_prepare ( sensor ) ;
if ( ret ) {
2020-01-30 13:05:37 +03:00
dev_err ( & pdev - > dev , " Error prepare sensor: %d \n " , ret ) ;
2018-10-05 13:08:46 +03:00
return ret ;
2020-01-10 13:16:03 +03:00
}
ret = stm_sensor_power_on ( sensor ) ;
if ( ret ) {
dev_err ( & pdev - > dev , " Error power on sensor: %d \n " , ret ) ;
return ret ;
}
2018-10-05 13:08:46 +03:00
sensor - > th_dev = devm_thermal_zone_of_sensor_register ( & pdev - > dev , 0 ,
sensor ,
& stm_tz_ops ) ;
if ( IS_ERR ( sensor - > th_dev ) ) {
dev_err ( & pdev - > dev , " %s: thermal zone sensor registering KO \n " ,
__func__ ) ;
ret = PTR_ERR ( sensor - > th_dev ) ;
return ret ;
}
2020-01-10 13:16:03 +03:00
/* Register IRQ into GIC */
ret = stm_register_irq ( sensor ) ;
if ( ret )
2018-10-05 13:08:46 +03:00
goto err_tz ;
2020-01-10 13:16:03 +03:00
stm_enable_irq ( sensor ) ;
2018-10-05 13:08:46 +03:00
/*
* Thermal_zone doesn ' t enable hwmon as default ,
* enable it here
*/
sensor - > th_dev - > tzp - > no_hwmon = false ;
ret = thermal_add_hwmon_sysfs ( sensor - > th_dev ) ;
if ( ret )
goto err_tz ;
dev_info ( & pdev - > dev , " %s: Driver initialized successfully \n " ,
__func__ ) ;
return 0 ;
err_tz :
thermal_zone_of_sensor_unregister ( & pdev - > dev , sensor - > th_dev ) ;
return ret ;
}
static int stm_thermal_remove ( struct platform_device * pdev )
{
struct stm_thermal_sensor * sensor = platform_get_drvdata ( pdev ) ;
stm_thermal_sensor_off ( sensor ) ;
thermal_remove_hwmon_sysfs ( sensor - > th_dev ) ;
thermal_zone_of_sensor_unregister ( & pdev - > dev , sensor - > th_dev ) ;
return 0 ;
}
static struct platform_driver stm_thermal_driver = {
. driver = {
. name = " stm_thermal " ,
. pm = & stm_thermal_pm_ops ,
. of_match_table = stm_thermal_of_match ,
} ,
. probe = stm_thermal_probe ,
. remove = stm_thermal_remove ,
} ;
module_platform_driver ( stm_thermal_driver ) ;
MODULE_DESCRIPTION ( " STMicroelectronics STM32 Thermal Sensor Driver " ) ;
MODULE_AUTHOR ( " David Hernandez Sanchez <david.hernandezsanchez@st.com> " ) ;
MODULE_LICENSE ( " GPL v2 " ) ;
MODULE_ALIAS ( " platform:stm_thermal " ) ;