@ -12,6 +12,7 @@
# include <linux/delay.h>
# include <linux/device.h>
# include <linux/init.h>
# include <linux/interrupt.h>
# include <linux/io.h>
# include <linux/kernel.h>
# include <linux/mfd/syscon.h>
@ -31,6 +32,8 @@
# define MISC0_REFTOP_SELBIASOFF (1 << 3)
# define TEMPSENSE0 0x0180
# define TEMPSENSE0_ALARM_VALUE_SHIFT 20
# define TEMPSENSE0_ALARM_VALUE_MASK (0xfff << TEMPSENSE0_ALARM_VALUE_SHIFT)
# define TEMPSENSE0_TEMP_CNT_SHIFT 8
# define TEMPSENSE0_TEMP_CNT_MASK (0xfff << TEMPSENSE0_TEMP_CNT_SHIFT)
# define TEMPSENSE0_FINISHED (1 << 2)
@ -66,33 +69,62 @@ struct imx_thermal_data {
int c1 , c2 ; /* See formula in imx_get_sensor_data() */
unsigned long temp_passive ;
unsigned long temp_critical ;
unsigned long alarm_temp ;
unsigned long last_temp ;
bool irq_enabled ;
int irq ;
} ;
static void imx_set_alarm_temp ( struct imx_thermal_data * data ,
signed long alarm_temp )
{
struct regmap * map = data - > tempmon ;
int alarm_value ;
data - > alarm_temp = alarm_temp ;
alarm_value = ( alarm_temp - data - > c2 ) / data - > c1 ;
regmap_write ( map , TEMPSENSE0 + REG_CLR , TEMPSENSE0_ALARM_VALUE_MASK ) ;
regmap_write ( map , TEMPSENSE0 + REG_SET , alarm_value < <
TEMPSENSE0_ALARM_VALUE_SHIFT ) ;
}
static int imx_get_temp ( struct thermal_zone_device * tz , unsigned long * temp )
{
struct imx_thermal_data * data = tz - > devdata ;
struct regmap * map = data - > tempmon ;
static unsigned long last_temp ;
unsigned int n_meas ;
bool wait ;
u32 val ;
/*
* Every time we measure the temperature , we will power on the
* temperature sensor , enable measurements , take a reading ,
* disable measurements , power off the temperature sensor .
*/
regmap_write ( map , TEMPSENSE0 + REG_CLR , TEMPSENSE0_POWER_DOWN ) ;
regmap_write ( map , TEMPSENSE0 + REG_SET , TEMPSENSE0_MEASURE_TEMP ) ;
if ( data - > mode = = THERMAL_DEVICE_ENABLED ) {
/* Check if a measurement is currently in progress */
regmap_read ( map , TEMPSENSE0 , & val ) ;
wait = ! ( val & TEMPSENSE0_FINISHED ) ;
} else {
/*
* Every time we measure the temperature , we will power on the
* temperature sensor , enable measurements , take a reading ,
* disable measurements , power off the temperature sensor .
*/
regmap_write ( map , TEMPSENSE0 + REG_CLR , TEMPSENSE0_POWER_DOWN ) ;
regmap_write ( map , TEMPSENSE0 + REG_SET , TEMPSENSE0_MEASURE_TEMP ) ;
wait = true ;
}
/*
* According to the temp sensor designers , it may require up to ~ 17u s
* to complete a measurement .
*/
usleep_range ( 20 , 50 ) ;
if ( wait )
usleep_range ( 20 , 50 ) ;
regmap_read ( map , TEMPSENSE0 , & val ) ;
regmap_write ( map , TEMPSENSE0 + REG_CLR , TEMPSENSE0_MEASURE_TEMP ) ;
regmap_write ( map , TEMPSENSE0 + REG_SET , TEMPSENSE0_POWER_DOWN ) ;
if ( data - > mode ! = THERMAL_DEVICE_ENABLED ) {
regmap_write ( map , TEMPSENSE0 + REG_CLR , TEMPSENSE0_MEASURE_TEMP ) ;
regmap_write ( map , TEMPSENSE0 + REG_SET , TEMPSENSE0_POWER_DOWN ) ;
}
if ( ( val & TEMPSENSE0_FINISHED ) = = 0 ) {
dev_dbg ( & tz - > device , " temp measurement never finished \n " ) ;
@ -104,9 +136,24 @@ static int imx_get_temp(struct thermal_zone_device *tz, unsigned long *temp)
/* See imx_get_sensor_data() for formula derivation */
* temp = data - > c2 + data - > c1 * n_meas ;
if ( * temp ! = last_temp ) {
/* Update alarm value to next higher trip point */
if ( data - > alarm_temp = = data - > temp_passive & & * temp > = data - > temp_passive )
imx_set_alarm_temp ( data , data - > temp_critical ) ;
if ( data - > alarm_temp = = data - > temp_critical & & * temp < data - > temp_passive ) {
imx_set_alarm_temp ( data , data - > temp_passive ) ;
dev_dbg ( & tz - > device , " thermal alarm off: T < %lu \n " ,
data - > alarm_temp / 1000 ) ;
}
if ( * temp ! = data - > last_temp ) {
dev_dbg ( & tz - > device , " millicelsius: %ld \n " , * temp ) ;
last_temp = * temp ;
data - > last_temp = * temp ;
}
/* Reenable alarm IRQ if temperature below alarm temperature */
if ( ! data - > irq_enabled & & * temp < data - > alarm_temp ) {
data - > irq_enabled = true ;
enable_irq ( data - > irq ) ;
}
return 0 ;
@ -126,13 +173,30 @@ static int imx_set_mode(struct thermal_zone_device *tz,
enum thermal_device_mode mode )
{
struct imx_thermal_data * data = tz - > devdata ;
struct regmap * map = data - > tempmon ;
if ( mode = = THERMAL_DEVICE_ENABLED ) {
tz - > polling_delay = IMX_POLLING_DELAY ;
tz - > passive_delay = IMX_PASSIVE_DELAY ;
regmap_write ( map , TEMPSENSE0 + REG_CLR , TEMPSENSE0_POWER_DOWN ) ;
regmap_write ( map , TEMPSENSE0 + REG_SET , TEMPSENSE0_MEASURE_TEMP ) ;
if ( ! data - > irq_enabled ) {
data - > irq_enabled = true ;
enable_irq ( data - > irq ) ;
}
} else {
regmap_write ( map , TEMPSENSE0 + REG_CLR , TEMPSENSE0_MEASURE_TEMP ) ;
regmap_write ( map , TEMPSENSE0 + REG_SET , TEMPSENSE0_POWER_DOWN ) ;
tz - > polling_delay = 0 ;
tz - > passive_delay = 0 ;
if ( data - > irq_enabled ) {
disable_irq ( data - > irq ) ;
data - > irq_enabled = false ;
}
}
data - > mode = mode ;
@ -181,6 +245,8 @@ static int imx_set_trip_temp(struct thermal_zone_device *tz, int trip,
data - > temp_passive = temp ;
imx_set_alarm_temp ( data , temp ) ;
return 0 ;
}
@ -299,11 +365,34 @@ static int imx_get_sensor_data(struct platform_device *pdev)
return 0 ;
}
static irqreturn_t imx_thermal_alarm_irq ( int irq , void * dev )
{
struct imx_thermal_data * data = dev ;
disable_irq_nosync ( irq ) ;
data - > irq_enabled = false ;
return IRQ_WAKE_THREAD ;
}
static irqreturn_t imx_thermal_alarm_irq_thread ( int irq , void * dev )
{
struct imx_thermal_data * data = dev ;
dev_dbg ( & data - > tz - > device , " THERMAL ALARM: T > %lu \n " ,
data - > alarm_temp / 1000 ) ;
thermal_zone_device_update ( data - > tz ) ;
return IRQ_HANDLED ;
}
static int imx_thermal_probe ( struct platform_device * pdev )
{
struct imx_thermal_data * data ;
struct cpumask clip_cpus ;
struct regmap * map ;
int measure_freq ;
int ret ;
data = devm_kzalloc ( & pdev - > dev , sizeof ( * data ) , GFP_KERNEL ) ;
@ -318,6 +407,18 @@ static int imx_thermal_probe(struct platform_device *pdev)
}
data - > tempmon = map ;
data - > irq = platform_get_irq ( pdev , 0 ) ;
if ( data - > irq < 0 )
return data - > irq ;
ret = devm_request_threaded_irq ( & pdev - > dev , data - > irq ,
imx_thermal_alarm_irq , imx_thermal_alarm_irq_thread ,
0 , " imx_thermal " , data ) ;
if ( ret < 0 ) {
dev_err ( & pdev - > dev , " failed to request alarm irq: %d \n " , ret ) ;
return ret ;
}
platform_set_drvdata ( pdev , data ) ;
ret = imx_get_sensor_data ( pdev ) ;
@ -356,6 +457,15 @@ static int imx_thermal_probe(struct platform_device *pdev)
return ret ;
}
/* Enable measurements at ~ 10 Hz */
regmap_write ( map , TEMPSENSE1 + REG_CLR , TEMPSENSE1_MEASURE_FREQ ) ;
measure_freq = DIV_ROUND_UP ( 32768 , 10 ) ; /* 10 Hz */
regmap_write ( map , TEMPSENSE1 + REG_SET , measure_freq ) ;
imx_set_alarm_temp ( data , data - > temp_passive ) ;
regmap_write ( map , TEMPSENSE0 + REG_CLR , TEMPSENSE0_POWER_DOWN ) ;
regmap_write ( map , TEMPSENSE0 + REG_SET , TEMPSENSE0_MEASURE_TEMP ) ;
data - > irq_enabled = true ;
data - > mode = THERMAL_DEVICE_ENABLED ;
return 0 ;
@ -364,6 +474,10 @@ static int imx_thermal_probe(struct platform_device *pdev)
static int imx_thermal_remove ( struct platform_device * pdev )
{
struct imx_thermal_data * data = platform_get_drvdata ( pdev ) ;
struct regmap * map = data - > tempmon ;
/* Disable measurements */
regmap_write ( map , TEMPSENSE0 + REG_SET , TEMPSENSE0_POWER_DOWN ) ;
thermal_zone_device_unregister ( data - > tz ) ;
cpufreq_cooling_unregister ( data - > cdev ) ;