2012-07-12 20:02:29 +04:00
/*
2013-03-19 18:54:21 +04:00
* TI Bandgap temperature sensor driver
2012-07-12 20:02:29 +04:00
*
* Copyright ( C ) 2011 - 2012 Texas Instruments Incorporated - http : //www.ti.com/
* Author : J Keerthy < j - keerthy @ ti . com >
* Author : Moiz Sonasath < m - sonasath @ ti . com >
* Couple of fixes , DT and MFD adaptation :
* Eduardo Valentin < eduardo . valentin @ ti . com >
*
* This program is free software ; you can redistribute it and / or
* modify it under the terms of the GNU General Public License
* version 2 as published by the Free Software Foundation .
*
* This program is distributed in the hope that it will be useful , but
* WITHOUT ANY WARRANTY ; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the GNU
* General Public License for more details .
*
* You should have received a copy of the GNU General Public License
* along with this program ; if not , write to the Free Software
* Foundation , Inc . , 51 Franklin St , Fifth Floor , Boston , MA
* 02110 - 1301 USA
*
*/
# include <linux/module.h>
# include <linux/export.h>
# include <linux/init.h>
# include <linux/kernel.h>
# include <linux/interrupt.h>
# include <linux/clk.h>
# include <linux/gpio.h>
# include <linux/platform_device.h>
# include <linux/err.h>
# include <linux/types.h>
2013-03-15 17:00:35 +04:00
# include <linux/spinlock.h>
2012-07-12 20:02:29 +04:00
# include <linux/reboot.h>
# include <linux/of_device.h>
# include <linux/of_platform.h>
# include <linux/of_irq.h>
2013-06-07 19:11:53 +04:00
# include <linux/of_gpio.h>
2012-11-13 22:10:00 +04:00
# include <linux/io.h>
2012-07-12 20:02:29 +04:00
2013-03-19 18:54:19 +04:00
# include "ti-bandgap.h"
2012-07-12 20:02:29 +04:00
2015-03-25 01:20:21 +03:00
static int ti_bandgap_force_single_read ( struct ti_bandgap * bgp , int id ) ;
2013-03-15 17:00:05 +04:00
/*** Helper functions to access registers and their bitfields ***/
2013-03-15 16:59:56 +04:00
/**
2013-03-19 18:54:21 +04:00
* ti_bandgap_readl ( ) - simple read helper function
* @ bgp : pointer to ti_bandgap structure
2013-03-15 16:59:56 +04:00
* @ reg : desired register ( offset ) to be read
*
* Helper function to read bandgap registers . It uses the io remapped area .
2013-04-01 20:04:34 +04:00
* Return : the register value .
2013-03-15 16:59:56 +04:00
*/
2013-03-19 18:54:21 +04:00
static u32 ti_bandgap_readl ( struct ti_bandgap * bgp , u32 reg )
2012-07-12 20:02:29 +04:00
{
2013-03-19 18:54:18 +04:00
return readl ( bgp - > base + reg ) ;
2012-07-12 20:02:29 +04:00
}
2013-03-15 16:59:56 +04:00
/**
2013-03-19 18:54:21 +04:00
* ti_bandgap_writel ( ) - simple write helper function
* @ bgp : pointer to ti_bandgap structure
2013-03-15 16:59:56 +04:00
* @ val : desired register value to be written
* @ reg : desired register ( offset ) to be written
*
* Helper function to write bandgap registers . It uses the io remapped area .
*/
2013-03-19 18:54:21 +04:00
static void ti_bandgap_writel ( struct ti_bandgap * bgp , u32 val , u32 reg )
2012-07-12 20:02:29 +04:00
{
2013-03-19 18:54:18 +04:00
writel ( val , bgp - > base + reg ) ;
2012-07-12 20:02:29 +04:00
}
2013-03-15 16:59:56 +04:00
/**
* DOC : macro to update bits .
*
* RMW_BITS ( ) - used to read , modify and update bandgap bitfields .
* The value passed will be shifted .
*/
2013-03-19 18:54:18 +04:00
# define RMW_BITS(bgp, id, reg, mask, val) \
2013-03-15 16:59:55 +04:00
do { \
struct temp_sensor_registers * t ; \
u32 r ; \
\
2013-03-19 18:54:18 +04:00
t = bgp - > conf - > sensors [ ( id ) ] . registers ; \
2013-03-19 18:54:21 +04:00
r = ti_bandgap_readl ( bgp , t - > reg ) ; \
2013-03-15 16:59:55 +04:00
r & = ~ t - > mask ; \
r | = ( val ) < < __ffs ( t - > mask ) ; \
2013-03-19 18:54:21 +04:00
ti_bandgap_writel ( bgp , r , t - > reg ) ; \
2013-03-15 16:59:55 +04:00
} while ( 0 )
2013-03-15 17:00:06 +04:00
/*** Basic helper functions ***/
2013-03-15 16:59:58 +04:00
/**
2013-03-19 18:54:21 +04:00
* ti_bandgap_power ( ) - controls the power state of a bandgap device
* @ bgp : pointer to ti_bandgap structure
2013-03-15 16:59:58 +04:00
* @ on : desired power state ( 1 - on , 0 - off )
*
* Used to power on / off a bandgap device instance . Only used on those
* that features tempsoff bit .
2013-04-01 20:04:34 +04:00
*
* Return : 0 on success , - ENOTSUPP if tempsoff is not supported .
2013-03-15 16:59:58 +04:00
*/
2013-03-19 18:54:21 +04:00
static int ti_bandgap_power ( struct ti_bandgap * bgp , bool on )
2012-07-12 20:02:29 +04:00
{
2015-01-18 23:17:10 +03:00
int i ;
2012-07-12 20:02:29 +04:00
2015-01-18 23:17:10 +03:00
if ( ! TI_BANDGAP_HAS ( bgp , POWER_SWITCH ) )
return - ENOTSUPP ;
2012-07-12 20:02:29 +04:00
2013-03-19 18:54:18 +04:00
for ( i = 0 ; i < bgp - > conf - > sensor_count ; i + + )
2012-07-12 20:02:29 +04:00
/* active on 0 */
2013-03-19 18:54:18 +04:00
RMW_BITS ( bgp , i , temp_sensor_ctrl , bgap_tempsoff_mask , ! on ) ;
2015-01-18 23:17:10 +03:00
return 0 ;
2012-07-12 20:02:29 +04:00
}
2015-04-22 15:51:41 +03:00
/**
* ti_errata814_bandgap_read_temp ( ) - helper function to read dra7 sensor temperature
* @ bgp : pointer to ti_bandgap structure
* @ reg : desired register ( offset ) to be read
*
* Function to read dra7 bandgap sensor temperature . This is done separately
* so as to workaround the errata " Bandgap Temperature read Dtemp can be
* corrupted " - Errata ID: i814 " .
* Read accesses to registers listed below can be corrupted due to incorrect
* resynchronization between clock domains .
* Read access to registers below can be corrupted :
* CTRL_CORE_DTEMP_MPU / GPU / CORE / DSPEVE / IVA_n ( n = 0 to 4 )
* CTRL_CORE_TEMP_SENSOR_MPU / GPU / CORE / DSPEVE / IVA_n
*
* Return : the register value .
*/
static u32 ti_errata814_bandgap_read_temp ( struct ti_bandgap * bgp , u32 reg )
{
u32 val1 , val2 ;
val1 = ti_bandgap_readl ( bgp , reg ) ;
val2 = ti_bandgap_readl ( bgp , reg ) ;
/* If both times we read the same value then that is right */
if ( val1 = = val2 )
return val1 ;
/* if val1 and val2 are different read it third time */
return ti_bandgap_readl ( bgp , reg ) ;
}
2013-03-15 16:59:59 +04:00
/**
2013-03-19 18:54:21 +04:00
* ti_bandgap_read_temp ( ) - helper function to read sensor temperature
* @ bgp : pointer to ti_bandgap structure
2013-03-15 16:59:59 +04:00
* @ id : bandgap sensor id
*
* Function to concentrate the steps to read sensor temperature register .
* This function is desired because , depending on bandgap device version ,
* it might be needed to freeze the bandgap state machine , before fetching
* the register value .
2013-04-01 20:04:34 +04:00
*
* Return : temperature in ADC values .
2013-03-15 16:59:59 +04:00
*/
2013-03-19 18:54:21 +04:00
static u32 ti_bandgap_read_temp ( struct ti_bandgap * bgp , int id )
2013-02-27 02:53:33 +04:00
{
struct temp_sensor_registers * tsr ;
2013-03-15 16:59:55 +04:00
u32 temp , reg ;
2013-02-27 02:53:33 +04:00
2013-03-19 18:54:18 +04:00
tsr = bgp - > conf - > sensors [ id ] . registers ;
2013-02-27 02:53:33 +04:00
reg = tsr - > temp_sensor_ctrl ;
2013-03-19 18:54:21 +04:00
if ( TI_BANDGAP_HAS ( bgp , FREEZE_BIT ) ) {
2013-03-19 18:54:18 +04:00
RMW_BITS ( bgp , id , bgap_mask_ctrl , mask_freeze_mask , 1 ) ;
2013-02-27 02:53:33 +04:00
/*
* In case we cannot read from cur_dtemp / dtemp_0 ,
* then we read from the last valid temp read
*/
reg = tsr - > ctrl_dtemp_1 ;
}
/* read temperature */
2015-04-22 15:51:41 +03:00
if ( TI_BANDGAP_HAS ( bgp , ERRATA_814 ) )
temp = ti_errata814_bandgap_read_temp ( bgp , reg ) ;
else
temp = ti_bandgap_readl ( bgp , reg ) ;
2013-02-27 02:53:33 +04:00
temp & = tsr - > bgap_dtemp_mask ;
2013-03-19 18:54:21 +04:00
if ( TI_BANDGAP_HAS ( bgp , FREEZE_BIT ) )
2013-03-19 18:54:18 +04:00
RMW_BITS ( bgp , id , bgap_mask_ctrl , mask_freeze_mask , 0 ) ;
2013-02-27 02:53:33 +04:00
return temp ;
}
2013-03-15 17:00:07 +04:00
/*** IRQ handlers ***/
2013-03-15 17:00:01 +04:00
/**
2013-03-19 18:54:21 +04:00
* ti_bandgap_talert_irq_handler ( ) - handles Temperature alert IRQs
2013-03-15 17:00:01 +04:00
* @ irq : IRQ number
2013-03-19 18:54:21 +04:00
* @ data : private data ( struct ti_bandgap * )
2013-03-15 17:00:01 +04:00
*
* This is the Talert handler . Use it only if bandgap device features
* HAS ( TALERT ) . This handler goes over all sensors and checks their
* conditions and acts accordingly . In case there are events pending ,
* it will reset the event mask to wait for the opposite event ( next event ) .
* Every time there is a new event , it will be reported to thermal layer .
2013-04-01 20:04:34 +04:00
*
* Return : IRQ_HANDLED
2013-03-15 17:00:01 +04:00
*/
2013-03-19 18:54:21 +04:00
static irqreturn_t ti_bandgap_talert_irq_handler ( int irq , void * data )
2012-07-12 20:02:29 +04:00
{
2013-03-19 18:54:21 +04:00
struct ti_bandgap * bgp = data ;
2012-07-12 20:02:29 +04:00
struct temp_sensor_registers * tsr ;
2013-02-27 02:53:33 +04:00
u32 t_hot = 0 , t_cold = 0 , ctrl ;
2012-07-12 20:02:29 +04:00
int i ;
2013-03-19 18:54:18 +04:00
spin_lock ( & bgp - > lock ) ;
for ( i = 0 ; i < bgp - > conf - > sensor_count ; i + + ) {
tsr = bgp - > conf - > sensors [ i ] . registers ;
2013-03-19 18:54:21 +04:00
ctrl = ti_bandgap_readl ( bgp , tsr - > bgap_status ) ;
2013-03-15 17:00:04 +04:00
/* Read the status of t_hot */
t_hot = ctrl & tsr - > status_hot_mask ;
2012-07-12 20:02:29 +04:00
/* Read the status of t_cold */
2013-03-15 17:00:04 +04:00
t_cold = ctrl & tsr - > status_cold_mask ;
2012-07-12 20:02:29 +04:00
if ( ! t_cold & & ! t_hot )
continue ;
2013-03-19 18:54:21 +04:00
ctrl = ti_bandgap_readl ( bgp , tsr - > bgap_mask_ctrl ) ;
2012-07-12 20:02:29 +04:00
/*
* One TALERT interrupt : Two sources
* If the interrupt is due to t_hot then mask t_hot and
* and unmask t_cold else mask t_cold and unmask t_hot
*/
if ( t_hot ) {
ctrl & = ~ tsr - > mask_hot_mask ;
ctrl | = tsr - > mask_cold_mask ;
} else if ( t_cold ) {
ctrl & = ~ tsr - > mask_cold_mask ;
ctrl | = tsr - > mask_hot_mask ;
}
2013-03-19 18:54:21 +04:00
ti_bandgap_writel ( bgp , ctrl , tsr - > bgap_mask_ctrl ) ;
2012-07-12 20:02:29 +04:00
2013-03-19 18:54:18 +04:00
dev_dbg ( bgp - > dev ,
2012-11-13 22:10:03 +04:00
" %s: IRQ from %s sensor: hotevent %d coldevent %d \n " ,
2013-03-19 18:54:18 +04:00
__func__ , bgp - > conf - > sensors [ i ] . domain ,
2012-11-13 22:10:03 +04:00
t_hot , t_cold ) ;
2012-07-12 20:02:29 +04:00
/* report temperature to whom may concern */
2013-03-19 18:54:18 +04:00
if ( bgp - > conf - > report_temperature )
bgp - > conf - > report_temperature ( bgp , i ) ;
2012-07-12 20:02:29 +04:00
}
2013-03-19 18:54:18 +04:00
spin_unlock ( & bgp - > lock ) ;
2012-07-12 20:02:29 +04:00
return IRQ_HANDLED ;
}
2013-03-15 17:00:02 +04:00
/**
2013-03-19 18:54:21 +04:00
* ti_bandgap_tshut_irq_handler ( ) - handles Temperature shutdown signal
2013-03-15 17:00:02 +04:00
* @ irq : IRQ number
* @ data : private data ( unused )
*
* This is the Tshut handler . Use it only if bandgap device features
* HAS ( TSHUT ) . If any sensor fires the Tshut signal , we simply shutdown
* the system .
2013-04-01 20:04:34 +04:00
*
* Return : IRQ_HANDLED
2013-03-15 17:00:02 +04:00
*/
2013-03-19 18:54:21 +04:00
static irqreturn_t ti_bandgap_tshut_irq_handler ( int irq , void * data )
2012-07-12 20:02:29 +04:00
{
2013-02-27 02:53:24 +04:00
pr_emerg ( " %s: TSHUT temperature reached. Needs shut down... \n " ,
__func__ ) ;
2012-07-12 20:02:29 +04:00
orderly_poweroff ( true ) ;
return IRQ_HANDLED ;
}
2013-03-15 17:00:08 +04:00
/*** Helper functions which manipulate conversion ADC <-> mi Celsius ***/
2013-03-15 17:00:11 +04:00
/**
2013-03-19 18:54:21 +04:00
* ti_bandgap_adc_to_mcelsius ( ) - converts an ADC value to mCelsius scale
* @ bgp : struct ti_bandgap pointer
2013-03-15 17:00:11 +04:00
* @ adc_val : value in ADC representation
* @ t : address where to write the resulting temperature in mCelsius
*
* Simple conversion from ADC representation to mCelsius . In case the ADC value
* is out of the ADC conv table range , it returns - ERANGE , 0 on success .
* The conversion table is indexed by the ADC values .
2013-04-01 20:04:34 +04:00
*
* Return : 0 if conversion was successful , else - ERANGE in case the @ adc_val
* argument is out of the ADC conv table range .
2013-03-15 17:00:11 +04:00
*/
2012-07-12 20:02:29 +04:00
static
2013-03-19 18:54:21 +04:00
int ti_bandgap_adc_to_mcelsius ( struct ti_bandgap * bgp , int adc_val , int * t )
2012-07-12 20:02:29 +04:00
{
2013-03-19 18:54:23 +04:00
const struct ti_bandgap_data * conf = bgp - > conf ;
2012-07-12 20:02:29 +04:00
/* look up for temperature in the table and return the temperature */
2015-01-18 23:17:10 +03:00
if ( adc_val < conf - > adc_start_val | | adc_val > conf - > adc_end_val )
return - ERANGE ;
2012-07-12 20:02:29 +04:00
2013-03-19 18:54:18 +04:00
* t = bgp - > conf - > conv_table [ adc_val - conf - > adc_start_val ] ;
2015-01-18 23:17:10 +03:00
return 0 ;
2012-07-12 20:02:29 +04:00
}
2013-03-15 17:00:15 +04:00
/**
2013-03-19 18:54:21 +04:00
* ti_bandgap_mcelsius_to_adc ( ) - converts a mCelsius value to ADC scale
* @ bgp : struct ti_bandgap pointer
2013-03-15 17:00:15 +04:00
* @ temp : value in mCelsius
* @ adc : address where to write the resulting temperature in ADC representation
*
* Simple conversion from mCelsius to ADC values . In case the temp value
* is out of the ADC conv table range , it returns - ERANGE , 0 on success .
* The conversion table is indexed by the ADC values .
2013-04-01 20:04:34 +04:00
*
* Return : 0 if conversion was successful , else - ERANGE in case the @ temp
* argument is out of the ADC conv table range .
2013-03-15 17:00:15 +04:00
*/
2013-03-15 17:00:12 +04:00
static
2013-03-19 18:54:21 +04:00
int ti_bandgap_mcelsius_to_adc ( struct ti_bandgap * bgp , long temp , int * adc )
2012-07-12 20:02:29 +04:00
{
2013-03-19 18:54:23 +04:00
const struct ti_bandgap_data * conf = bgp - > conf ;
2013-03-19 18:54:18 +04:00
const int * conv_table = bgp - > conf - > conv_table ;
2015-01-18 23:17:10 +03:00
int high , low , mid ;
2012-07-12 20:02:29 +04:00
low = 0 ;
2013-03-15 17:00:14 +04:00
high = conf - > adc_end_val - conf - > adc_start_val ;
2012-07-12 20:02:29 +04:00
mid = ( high + low ) / 2 ;
2015-01-18 23:17:10 +03:00
if ( temp < conv_table [ low ] | | temp > conv_table [ high ] )
return - ERANGE ;
2012-07-12 20:02:29 +04:00
while ( low < high ) {
2013-02-27 02:53:36 +04:00
if ( temp < conv_table [ mid ] )
2012-07-12 20:02:29 +04:00
high = mid - 1 ;
else
low = mid + 1 ;
mid = ( low + high ) / 2 ;
}
2013-03-15 17:00:14 +04:00
* adc = conf - > adc_start_val + low ;
2015-01-18 23:17:10 +03:00
return 0 ;
2012-07-12 20:02:29 +04:00
}
2013-03-15 17:00:17 +04:00
/**
2013-03-19 18:54:21 +04:00
* ti_bandgap_add_hyst ( ) - add hysteresis ( in mCelsius ) to an ADC value
* @ bgp : struct ti_bandgap pointer
2013-03-15 17:00:17 +04:00
* @ adc_val : temperature value in ADC representation
* @ hyst_val : hysteresis value in mCelsius
* @ sum : address where to write the resulting temperature ( in ADC scale )
*
* Adds an hysteresis value ( in mCelsius ) to a ADC temperature value .
2013-04-01 20:04:34 +04:00
*
* Return : 0 on success , - ERANGE otherwise .
2013-03-15 17:00:17 +04:00
*/
2013-03-15 17:00:16 +04:00
static
2013-03-19 18:54:21 +04:00
int ti_bandgap_add_hyst ( struct ti_bandgap * bgp , int adc_val , int hyst_val ,
u32 * sum )
2013-03-15 17:00:16 +04:00
{
int temp , ret ;
/*
* Need to add in the mcelsius domain , so we have a temperature
* the conv_table range
*/
2013-03-19 18:54:21 +04:00
ret = ti_bandgap_adc_to_mcelsius ( bgp , adc_val , & temp ) ;
2013-03-15 17:00:16 +04:00
if ( ret < 0 )
2015-01-18 23:17:10 +03:00
return ret ;
2013-03-15 17:00:16 +04:00
temp + = hyst_val ;
2013-03-19 18:54:21 +04:00
ret = ti_bandgap_mcelsius_to_adc ( bgp , temp , sum ) ;
2013-03-15 17:00:16 +04:00
return ret ;
}
2013-03-15 17:00:18 +04:00
/*** Helper functions handling device Alert/Shutdown signals ***/
2013-03-15 17:00:20 +04:00
/**
2013-03-19 18:54:21 +04:00
* ti_bandgap_unmask_interrupts ( ) - unmasks the events of thot & tcold
* @ bgp : struct ti_bandgap pointer
2013-03-19 18:54:25 +04:00
* @ id : bandgap sensor id
2013-03-15 17:00:20 +04:00
* @ t_hot : hot temperature value to trigger alert signal
* @ t_cold : cold temperature value to trigger alert signal
*
* Checks the requested t_hot and t_cold values and configures the IRQ event
* masks accordingly . Call this function only if bandgap features HAS ( TALERT ) .
*/
2013-03-19 18:54:21 +04:00
static void ti_bandgap_unmask_interrupts ( struct ti_bandgap * bgp , int id ,
u32 t_hot , u32 t_cold )
2012-07-12 20:02:29 +04:00
{
struct temp_sensor_registers * tsr ;
u32 temp , reg_val ;
/* Read the current on die temperature */
2013-03-19 18:54:21 +04:00
temp = ti_bandgap_read_temp ( bgp , id ) ;
2012-07-12 20:02:29 +04:00
2013-03-19 18:54:18 +04:00
tsr = bgp - > conf - > sensors [ id ] . registers ;
2013-03-19 18:54:21 +04:00
reg_val = ti_bandgap_readl ( bgp , tsr - > bgap_mask_ctrl ) ;
2013-02-27 02:53:33 +04:00
2012-07-12 20:02:29 +04:00
if ( temp < t_hot )
reg_val | = tsr - > mask_hot_mask ;
else
reg_val & = ~ tsr - > mask_hot_mask ;
if ( t_cold < temp )
reg_val | = tsr - > mask_cold_mask ;
else
reg_val & = ~ tsr - > mask_cold_mask ;
2013-03-19 18:54:21 +04:00
ti_bandgap_writel ( bgp , reg_val , tsr - > bgap_mask_ctrl ) ;
2012-07-12 20:02:29 +04:00
}
2013-03-15 17:00:27 +04:00
/**
2013-03-19 18:54:21 +04:00
* ti_bandgap_update_alert_threshold ( ) - sequence to update thresholds
* @ bgp : struct ti_bandgap pointer
2013-03-15 17:00:27 +04:00
* @ id : bandgap sensor id
* @ val : value ( ADC ) of a new threshold
* @ hot : desired threshold to be updated . true if threshold hot , false if
* threshold cold
*
* It will program the required thresholds ( hot and cold ) for TALERT signal .
* This function can be used to update t_hot or t_cold , depending on @ hot value .
* It checks the resulting t_hot and t_cold values , based on the new passed @ val
* and configures the thresholds so that t_hot is always greater than t_cold .
* Call this function only if bandgap features HAS ( TALERT ) .
2013-04-01 20:04:34 +04:00
*
* Return : 0 if no error , else corresponding error
2013-03-15 17:00:27 +04:00
*/
2013-03-19 18:54:21 +04:00
static int ti_bandgap_update_alert_threshold ( struct ti_bandgap * bgp , int id ,
int val , bool hot )
2012-07-12 20:02:29 +04:00
{
2013-03-19 18:54:18 +04:00
struct temp_sensor_data * ts_data = bgp - > conf - > sensors [ id ] . ts_data ;
2012-07-12 20:02:29 +04:00
struct temp_sensor_registers * tsr ;
2015-04-22 15:51:42 +03:00
u32 thresh_val , reg_val , t_hot , t_cold , ctrl ;
2013-03-15 17:00:21 +04:00
int err = 0 ;
2012-07-12 20:02:29 +04:00
2013-03-19 18:54:18 +04:00
tsr = bgp - > conf - > sensors [ id ] . registers ;
2012-07-12 20:02:29 +04:00
2013-03-15 17:00:21 +04:00
/* obtain the current value */
2013-03-19 18:54:21 +04:00
thresh_val = ti_bandgap_readl ( bgp , tsr - > bgap_threshold ) ;
2013-03-15 17:00:21 +04:00
t_cold = ( thresh_val & tsr - > threshold_tcold_mask ) > >
__ffs ( tsr - > threshold_tcold_mask ) ;
t_hot = ( thresh_val & tsr - > threshold_thot_mask ) > >
__ffs ( tsr - > threshold_thot_mask ) ;
if ( hot )
t_hot = val ;
else
t_cold = val ;
2013-03-19 18:54:26 +04:00
if ( t_cold > t_hot ) {
2013-03-15 17:00:21 +04:00
if ( hot )
2013-03-19 18:54:21 +04:00
err = ti_bandgap_add_hyst ( bgp , t_hot ,
- ts_data - > hyst_val ,
& t_cold ) ;
2013-03-15 17:00:21 +04:00
else
2013-03-19 18:54:21 +04:00
err = ti_bandgap_add_hyst ( bgp , t_cold ,
ts_data - > hyst_val ,
& t_hot ) ;
2012-07-12 20:02:29 +04:00
}
2013-03-15 17:00:21 +04:00
/* write the new threshold values */
2013-03-19 18:54:27 +04:00
reg_val = thresh_val &
~ ( tsr - > threshold_thot_mask | tsr - > threshold_tcold_mask ) ;
reg_val | = ( t_hot < < __ffs ( tsr - > threshold_thot_mask ) ) |
( t_cold < < __ffs ( tsr - > threshold_tcold_mask ) ) ;
2015-04-22 15:51:42 +03:00
/**
* Errata i813 :
* Spurious Thermal Alert : Talert can happen randomly while the device
* remains under the temperature limit defined for this event to trig .
* This spurious event is caused by a incorrect re - synchronization
* between clock domains . The comparison between configured threshold
* and current temperature value can happen while the value is
* transitioning ( metastable ) , thus causing inappropriate event
* generation . No spurious event occurs as long as the threshold value
* stays unchanged . Spurious event can be generated while a thermal
* alert threshold is modified in
* CONTROL_BANDGAP_THRESHOLD_MPU / GPU / CORE / DSPEVE / IVA_n .
*/
if ( TI_BANDGAP_HAS ( bgp , ERRATA_813 ) ) {
/* Mask t_hot and t_cold events at the IP Level */
ctrl = ti_bandgap_readl ( bgp , tsr - > bgap_mask_ctrl ) ;
if ( hot )
ctrl & = ~ tsr - > mask_hot_mask ;
else
ctrl & = ~ tsr - > mask_cold_mask ;
ti_bandgap_writel ( bgp , ctrl , tsr - > bgap_mask_ctrl ) ;
}
/* Write the threshold value */
2013-03-19 18:54:21 +04:00
ti_bandgap_writel ( bgp , reg_val , tsr - > bgap_threshold ) ;
2013-03-15 17:00:21 +04:00
2015-04-22 15:51:42 +03:00
if ( TI_BANDGAP_HAS ( bgp , ERRATA_813 ) ) {
/* Unmask t_hot and t_cold events at the IP Level */
ctrl = ti_bandgap_readl ( bgp , tsr - > bgap_mask_ctrl ) ;
if ( hot )
ctrl | = tsr - > mask_hot_mask ;
else
ctrl | = tsr - > mask_cold_mask ;
ti_bandgap_writel ( bgp , ctrl , tsr - > bgap_mask_ctrl ) ;
}
2012-07-12 20:02:29 +04:00
if ( err ) {
2013-03-19 18:54:18 +04:00
dev_err ( bgp - > dev , " failed to reprogram thot threshold \n " ) ;
2013-03-15 17:00:21 +04:00
err = - EIO ;
goto exit ;
2012-07-12 20:02:29 +04:00
}
2013-03-19 18:54:21 +04:00
ti_bandgap_unmask_interrupts ( bgp , id , t_hot , t_cold ) ;
2013-03-15 17:00:21 +04:00
exit :
return err ;
2012-07-12 20:02:29 +04:00
}
2013-03-15 17:00:38 +04:00
/**
2013-03-19 18:54:21 +04:00
* ti_bandgap_validate ( ) - helper to check the sanity of a struct ti_bandgap
* @ bgp : struct ti_bandgap pointer
2013-03-15 17:00:38 +04:00
* @ id : bandgap sensor id
*
* Checks if the bandgap pointer is valid and if the sensor id is also
* applicable .
2013-04-01 20:04:34 +04:00
*
* Return : 0 if no errors , - EINVAL for invalid @ bgp pointer or - ERANGE if
* @ id cannot index @ bgp sensors .
2013-03-15 17:00:38 +04:00
*/
2013-03-19 18:54:21 +04:00
static inline int ti_bandgap_validate ( struct ti_bandgap * bgp , int id )
2012-07-12 20:02:29 +04:00
{
2013-05-29 19:07:43 +04:00
if ( ! bgp | | IS_ERR ( bgp ) ) {
2013-03-15 17:00:21 +04:00
pr_err ( " %s: invalid bandgap pointer \n " , __func__ ) ;
2015-01-18 23:17:10 +03:00
return - EINVAL ;
2012-07-12 20:02:29 +04:00
}
2013-03-19 18:54:18 +04:00
if ( ( id < 0 ) | | ( id > = bgp - > conf - > sensor_count ) ) {
dev_err ( bgp - > dev , " %s: sensor id out of range (%d) \n " ,
2013-03-15 17:00:21 +04:00
__func__ , id ) ;
2015-01-18 23:17:10 +03:00
return - ERANGE ;
2012-07-12 20:02:29 +04:00
}
2015-01-18 23:17:10 +03:00
return 0 ;
2012-07-12 20:02:29 +04:00
}
2013-03-15 17:00:28 +04:00
/**
2013-03-19 18:54:21 +04:00
* _ti_bandgap_write_threshold ( ) - helper to update TALERT t_cold or t_hot
* @ bgp : struct ti_bandgap pointer
2013-03-15 17:00:28 +04:00
* @ id : bandgap sensor id
* @ val : value ( mCelsius ) of a new threshold
* @ hot : desired threshold to be updated . true if threshold hot , false if
* threshold cold
*
* It will update the required thresholds ( hot and cold ) for TALERT signal .
* This function can be used to update t_hot or t_cold , depending on @ hot value .
* Validates the mCelsius range and update the requested threshold .
* Call this function only if bandgap features HAS ( TALERT ) .
2013-04-01 20:04:34 +04:00
*
* Return : 0 if no error , else corresponding error value .
2013-03-15 17:00:28 +04:00
*/
2013-03-19 18:54:22 +04:00
static int _ti_bandgap_write_threshold ( struct ti_bandgap * bgp , int id , int val ,
bool hot )
2012-07-12 20:02:29 +04:00
{
2013-03-15 17:00:21 +04:00
struct temp_sensor_data * ts_data ;
struct temp_sensor_registers * tsr ;
u32 adc_val ;
int ret ;
2013-03-19 18:54:21 +04:00
ret = ti_bandgap_validate ( bgp , id ) ;
2013-03-15 17:00:21 +04:00
if ( ret )
2015-01-18 23:17:10 +03:00
return ret ;
2013-03-15 17:00:21 +04:00
2015-01-18 23:17:10 +03:00
if ( ! TI_BANDGAP_HAS ( bgp , TALERT ) )
return - ENOTSUPP ;
2012-07-12 20:02:29 +04:00
2013-03-19 18:54:18 +04:00
ts_data = bgp - > conf - > sensors [ id ] . ts_data ;
tsr = bgp - > conf - > sensors [ id ] . registers ;
2013-03-15 17:00:21 +04:00
if ( hot ) {
if ( val < ts_data - > min_temp + ts_data - > hyst_val )
ret = - EINVAL ;
} else {
if ( val > ts_data - > max_temp + ts_data - > hyst_val )
ret = - EINVAL ;
2012-07-12 20:02:29 +04:00
}
2013-03-15 17:00:21 +04:00
if ( ret )
2015-01-18 23:17:10 +03:00
return ret ;
2013-03-15 17:00:21 +04:00
2013-03-19 18:54:21 +04:00
ret = ti_bandgap_mcelsius_to_adc ( bgp , val , & adc_val ) ;
2013-03-15 17:00:21 +04:00
if ( ret < 0 )
2015-01-18 23:17:10 +03:00
return ret ;
2013-03-15 17:00:21 +04:00
2013-03-19 18:54:18 +04:00
spin_lock ( & bgp - > lock ) ;
2013-03-19 18:54:28 +04:00
ret = ti_bandgap_update_alert_threshold ( bgp , id , adc_val , hot ) ;
2013-03-19 18:54:18 +04:00
spin_unlock ( & bgp - > lock ) ;
2013-03-15 17:00:21 +04:00
return ret ;
2012-07-12 20:02:29 +04:00
}
2013-03-15 17:00:29 +04:00
/**
2013-03-19 18:54:21 +04:00
* _ti_bandgap_read_threshold ( ) - helper to read TALERT t_cold or t_hot
* @ bgp : struct ti_bandgap pointer
2013-03-15 17:00:29 +04:00
* @ id : bandgap sensor id
* @ val : value ( mCelsius ) of a threshold
* @ hot : desired threshold to be read . true if threshold hot , false if
* threshold cold
*
* It will fetch the required thresholds ( hot and cold ) for TALERT signal .
* This function can be used to read t_hot or t_cold , depending on @ hot value .
* Call this function only if bandgap features HAS ( TALERT ) .
2013-04-01 20:04:34 +04:00
*
* Return : 0 if no error , - ENOTSUPP if it has no TALERT support , or the
* corresponding error value if some operation fails .
2013-03-15 17:00:29 +04:00
*/
2013-03-19 18:54:22 +04:00
static int _ti_bandgap_read_threshold ( struct ti_bandgap * bgp , int id ,
int * val , bool hot )
2012-07-12 20:02:29 +04:00
{
struct temp_sensor_registers * tsr ;
2013-03-15 17:00:21 +04:00
u32 temp , mask ;
int ret = 0 ;
2012-07-12 20:02:29 +04:00
2013-03-19 18:54:21 +04:00
ret = ti_bandgap_validate ( bgp , id ) ;
2012-07-12 20:02:29 +04:00
if ( ret )
2013-03-15 17:00:21 +04:00
goto exit ;
2012-07-12 20:02:29 +04:00
2013-03-19 18:54:21 +04:00
if ( ! TI_BANDGAP_HAS ( bgp , TALERT ) ) {
2013-03-15 17:00:21 +04:00
ret = - ENOTSUPP ;
goto exit ;
}
2012-07-12 20:02:29 +04:00
2013-03-19 18:54:18 +04:00
tsr = bgp - > conf - > sensors [ id ] . registers ;
2013-03-15 17:00:21 +04:00
if ( hot )
mask = tsr - > threshold_thot_mask ;
else
mask = tsr - > threshold_tcold_mask ;
2013-03-19 18:54:21 +04:00
temp = ti_bandgap_readl ( bgp , tsr - > bgap_threshold ) ;
2013-03-15 17:00:21 +04:00
temp = ( temp & mask ) > > __ffs ( mask ) ;
2015-01-18 23:17:10 +03:00
ret = ti_bandgap_adc_to_mcelsius ( bgp , temp , & temp ) ;
2012-07-12 20:02:29 +04:00
if ( ret ) {
2013-03-19 18:54:18 +04:00
dev_err ( bgp - > dev , " failed to read thot \n " ) ;
2013-03-15 17:00:21 +04:00
ret = - EIO ;
goto exit ;
2012-07-12 20:02:29 +04:00
}
2013-03-15 17:00:21 +04:00
* val = temp ;
2012-07-12 20:02:29 +04:00
2013-03-15 17:00:21 +04:00
exit :
2013-03-19 18:54:17 +04:00
return ret ;
2012-07-12 20:02:29 +04:00
}
2013-03-15 17:00:21 +04:00
/*** Exposed APIs ***/
/**
2013-03-19 18:54:21 +04:00
* ti_bandgap_read_thot ( ) - reads sensor current thot
2013-03-19 18:54:25 +04:00
* @ bgp : pointer to bandgap instance
* @ id : sensor id
* @ thot : resulting current thot value
2013-03-15 17:00:21 +04:00
*
2013-04-01 20:04:34 +04:00
* Return : 0 on success or the proper error code
2013-03-15 17:00:21 +04:00
*/
2013-03-19 18:54:21 +04:00
int ti_bandgap_read_thot ( struct ti_bandgap * bgp , int id , int * thot )
2013-03-15 17:00:21 +04:00
{
2013-03-19 18:54:21 +04:00
return _ti_bandgap_read_threshold ( bgp , id , thot , true ) ;
2013-03-15 17:00:21 +04:00
}
2012-07-12 20:02:29 +04:00
/**
2013-03-19 18:54:21 +04:00
* ti_bandgap_write_thot ( ) - sets sensor current thot
2013-03-19 18:54:25 +04:00
* @ bgp : pointer to bandgap instance
* @ id : sensor id
* @ val : desired thot value
2012-07-12 20:02:29 +04:00
*
2013-04-01 20:04:34 +04:00
* Return : 0 on success or the proper error code
2012-07-12 20:02:29 +04:00
*/
2013-03-19 18:54:21 +04:00
int ti_bandgap_write_thot ( struct ti_bandgap * bgp , int id , int val )
2012-07-12 20:02:29 +04:00
{
2013-03-19 18:54:21 +04:00
return _ti_bandgap_write_threshold ( bgp , id , val , true ) ;
2012-07-12 20:02:29 +04:00
}
/**
2013-03-19 18:54:21 +04:00
* ti_bandgap_read_tcold ( ) - reads sensor current tcold
2013-03-19 18:54:25 +04:00
* @ bgp : pointer to bandgap instance
* @ id : sensor id
* @ tcold : resulting current tcold value
2012-07-12 20:02:29 +04:00
*
2013-04-01 20:04:34 +04:00
* Return : 0 on success or the proper error code
2012-07-12 20:02:29 +04:00
*/
2013-03-19 18:54:21 +04:00
int ti_bandgap_read_tcold ( struct ti_bandgap * bgp , int id , int * tcold )
2012-07-12 20:02:29 +04:00
{
2013-03-19 18:54:21 +04:00
return _ti_bandgap_read_threshold ( bgp , id , tcold , false ) ;
2012-07-12 20:02:29 +04:00
}
/**
2013-03-19 18:54:21 +04:00
* ti_bandgap_write_tcold ( ) - sets the sensor tcold
2013-03-19 18:54:25 +04:00
* @ bgp : pointer to bandgap instance
* @ id : sensor id
* @ val : desired tcold value
2012-07-12 20:02:29 +04:00
*
2013-04-01 20:04:34 +04:00
* Return : 0 on success or the proper error code
2012-07-12 20:02:29 +04:00
*/
2013-03-19 18:54:21 +04:00
int ti_bandgap_write_tcold ( struct ti_bandgap * bgp , int id , int val )
2012-07-12 20:02:29 +04:00
{
2013-03-19 18:54:21 +04:00
return _ti_bandgap_write_threshold ( bgp , id , val , false ) ;
2012-07-12 20:02:29 +04:00
}
2013-04-01 20:04:42 +04:00
/**
* ti_bandgap_read_counter ( ) - read the sensor counter
* @ bgp : pointer to bandgap instance
* @ id : sensor id
* @ interval : resulting update interval in miliseconds
*/
static void ti_bandgap_read_counter ( struct ti_bandgap * bgp , int id ,
int * interval )
{
struct temp_sensor_registers * tsr ;
int time ;
tsr = bgp - > conf - > sensors [ id ] . registers ;
time = ti_bandgap_readl ( bgp , tsr - > bgap_counter ) ;
time = ( time & tsr - > counter_mask ) > >
__ffs ( tsr - > counter_mask ) ;
time = time * 1000 / bgp - > clk_rate ;
* interval = time ;
}
/**
* ti_bandgap_read_counter_delay ( ) - read the sensor counter delay
* @ bgp : pointer to bandgap instance
* @ id : sensor id
* @ interval : resulting update interval in miliseconds
*/
static void ti_bandgap_read_counter_delay ( struct ti_bandgap * bgp , int id ,
int * interval )
{
struct temp_sensor_registers * tsr ;
int reg_val ;
tsr = bgp - > conf - > sensors [ id ] . registers ;
reg_val = ti_bandgap_readl ( bgp , tsr - > bgap_mask_ctrl ) ;
reg_val = ( reg_val & tsr - > mask_counter_delay_mask ) > >
__ffs ( tsr - > mask_counter_delay_mask ) ;
switch ( reg_val ) {
case 0 :
* interval = 0 ;
break ;
case 1 :
* interval = 1 ;
break ;
case 2 :
* interval = 10 ;
break ;
case 3 :
* interval = 100 ;
break ;
case 4 :
* interval = 250 ;
break ;
case 5 :
* interval = 500 ;
break ;
default :
dev_warn ( bgp - > dev , " Wrong counter delay value read from register %X " ,
reg_val ) ;
}
}
2012-07-12 20:02:29 +04:00
/**
2013-03-19 18:54:21 +04:00
* ti_bandgap_read_update_interval ( ) - read the sensor update interval
2013-03-19 18:54:25 +04:00
* @ bgp : pointer to bandgap instance
* @ id : sensor id
* @ interval : resulting update interval in miliseconds
2012-07-12 20:02:29 +04:00
*
2013-04-01 20:04:34 +04:00
* Return : 0 on success or the proper error code
2012-07-12 20:02:29 +04:00
*/
2013-03-19 18:54:21 +04:00
int ti_bandgap_read_update_interval ( struct ti_bandgap * bgp , int id ,
int * interval )
2012-07-12 20:02:29 +04:00
{
2013-04-01 20:04:42 +04:00
int ret = 0 ;
2012-07-12 20:02:29 +04:00
2013-03-19 18:54:21 +04:00
ret = ti_bandgap_validate ( bgp , id ) ;
2012-07-12 20:02:29 +04:00
if ( ret )
2013-04-01 20:04:42 +04:00
goto exit ;
2012-07-12 20:02:29 +04:00
2013-04-01 20:04:42 +04:00
if ( ! TI_BANDGAP_HAS ( bgp , COUNTER ) & &
! TI_BANDGAP_HAS ( bgp , COUNTER_DELAY ) ) {
ret = - ENOTSUPP ;
goto exit ;
}
2012-07-12 20:02:29 +04:00
2013-04-01 20:04:42 +04:00
if ( TI_BANDGAP_HAS ( bgp , COUNTER ) ) {
ti_bandgap_read_counter ( bgp , id , interval ) ;
goto exit ;
}
2012-07-12 20:02:29 +04:00
2013-04-01 20:04:42 +04:00
ti_bandgap_read_counter_delay ( bgp , id , interval ) ;
exit :
return ret ;
}
/**
* ti_bandgap_write_counter_delay ( ) - set the counter_delay
* @ bgp : pointer to bandgap instance
* @ id : sensor id
* @ interval : desired update interval in miliseconds
*
* Return : 0 on success or the proper error code
*/
static int ti_bandgap_write_counter_delay ( struct ti_bandgap * bgp , int id ,
u32 interval )
{
int rval ;
switch ( interval ) {
case 0 : /* Immediate conversion */
rval = 0x0 ;
break ;
case 1 : /* Conversion after ever 1ms */
rval = 0x1 ;
break ;
case 10 : /* Conversion after ever 10ms */
rval = 0x2 ;
break ;
case 100 : /* Conversion after ever 100ms */
rval = 0x3 ;
break ;
case 250 : /* Conversion after ever 250ms */
rval = 0x4 ;
break ;
case 500 : /* Conversion after ever 500ms */
rval = 0x5 ;
break ;
default :
dev_warn ( bgp - > dev , " Delay %d ms is not supported \n " , interval ) ;
return - EINVAL ;
}
spin_lock ( & bgp - > lock ) ;
RMW_BITS ( bgp , id , bgap_mask_ctrl , mask_counter_delay_mask , rval ) ;
spin_unlock ( & bgp - > lock ) ;
2012-07-12 20:02:29 +04:00
return 0 ;
}
2013-04-01 20:04:42 +04:00
/**
* ti_bandgap_write_counter ( ) - set the bandgap sensor counter
* @ bgp : pointer to bandgap instance
* @ id : sensor id
* @ interval : desired update interval in miliseconds
*/
static void ti_bandgap_write_counter ( struct ti_bandgap * bgp , int id ,
u32 interval )
{
interval = interval * bgp - > clk_rate / 1000 ;
spin_lock ( & bgp - > lock ) ;
RMW_BITS ( bgp , id , bgap_counter , counter_mask , interval ) ;
spin_unlock ( & bgp - > lock ) ;
}
2012-07-12 20:02:29 +04:00
/**
2013-03-19 18:54:21 +04:00
* ti_bandgap_write_update_interval ( ) - set the update interval
2013-03-19 18:54:25 +04:00
* @ bgp : pointer to bandgap instance
* @ id : sensor id
* @ interval : desired update interval in miliseconds
2012-07-12 20:02:29 +04:00
*
2013-04-01 20:04:34 +04:00
* Return : 0 on success or the proper error code
2012-07-12 20:02:29 +04:00
*/
2013-03-19 18:54:21 +04:00
int ti_bandgap_write_update_interval ( struct ti_bandgap * bgp ,
int id , u32 interval )
2012-07-12 20:02:29 +04:00
{
2013-03-19 18:54:21 +04:00
int ret = ti_bandgap_validate ( bgp , id ) ;
2012-07-12 20:02:29 +04:00
if ( ret )
2013-04-01 20:04:42 +04:00
goto exit ;
2012-07-12 20:02:29 +04:00
2013-04-01 20:04:42 +04:00
if ( ! TI_BANDGAP_HAS ( bgp , COUNTER ) & &
! TI_BANDGAP_HAS ( bgp , COUNTER_DELAY ) ) {
ret = - ENOTSUPP ;
goto exit ;
}
2012-07-12 20:02:29 +04:00
2013-04-01 20:04:42 +04:00
if ( TI_BANDGAP_HAS ( bgp , COUNTER ) ) {
ti_bandgap_write_counter ( bgp , id , interval ) ;
goto exit ;
}
2012-07-12 20:02:29 +04:00
2013-04-01 20:04:42 +04:00
ret = ti_bandgap_write_counter_delay ( bgp , id , interval ) ;
exit :
return ret ;
2012-07-12 20:02:29 +04:00
}
/**
2013-03-19 18:54:21 +04:00
* ti_bandgap_read_temperature ( ) - report current temperature
2013-03-19 18:54:25 +04:00
* @ bgp : pointer to bandgap instance
* @ id : sensor id
* @ temperature : resulting temperature
2012-07-12 20:02:29 +04:00
*
2013-04-01 20:04:34 +04:00
* Return : 0 on success or the proper error code
2012-07-12 20:02:29 +04:00
*/
2013-03-19 18:54:21 +04:00
int ti_bandgap_read_temperature ( struct ti_bandgap * bgp , int id ,
int * temperature )
2012-07-12 20:02:29 +04:00
{
u32 temp ;
int ret ;
2013-03-19 18:54:21 +04:00
ret = ti_bandgap_validate ( bgp , id ) ;
2012-07-12 20:02:29 +04:00
if ( ret )
return ret ;
2015-03-25 01:20:21 +03:00
if ( ! TI_BANDGAP_HAS ( bgp , MODE_CONFIG ) ) {
ret = ti_bandgap_force_single_read ( bgp , id ) ;
if ( ret )
return ret ;
}
2013-03-19 18:54:18 +04:00
spin_lock ( & bgp - > lock ) ;
2013-03-19 18:54:21 +04:00
temp = ti_bandgap_read_temp ( bgp , id ) ;
2013-03-19 18:54:18 +04:00
spin_unlock ( & bgp - > lock ) ;
2012-07-12 20:02:29 +04:00
2015-01-18 23:17:10 +03:00
ret = ti_bandgap_adc_to_mcelsius ( bgp , temp , & temp ) ;
2012-07-12 20:02:29 +04:00
if ( ret )
return - EIO ;
* temperature = temp ;
return 0 ;
}
/**
2013-03-19 18:54:21 +04:00
* ti_bandgap_set_sensor_data ( ) - helper function to store thermal
2012-07-12 20:02:29 +04:00
* framework related data .
2013-03-19 18:54:25 +04:00
* @ bgp : pointer to bandgap instance
* @ id : sensor id
* @ data : thermal framework related data to be stored
2012-07-12 20:02:29 +04:00
*
2013-04-01 20:04:34 +04:00
* Return : 0 on success or the proper error code
2012-07-12 20:02:29 +04:00
*/
2013-03-19 18:54:21 +04:00
int ti_bandgap_set_sensor_data ( struct ti_bandgap * bgp , int id , void * data )
2012-07-12 20:02:29 +04:00
{
2013-03-19 18:54:21 +04:00
int ret = ti_bandgap_validate ( bgp , id ) ;
2012-07-12 20:02:29 +04:00
if ( ret )
return ret ;
2013-03-19 18:54:23 +04:00
bgp - > regval [ id ] . data = data ;
2012-07-12 20:02:29 +04:00
return 0 ;
}
/**
2013-03-19 18:54:21 +04:00
* ti_bandgap_get_sensor_data ( ) - helper function to get thermal
2012-07-12 20:02:29 +04:00
* framework related data .
2013-03-19 18:54:25 +04:00
* @ bgp : pointer to bandgap instance
* @ id : sensor id
2012-07-12 20:02:29 +04:00
*
2013-04-01 20:04:34 +04:00
* Return : data stored by set function with sensor id on success or NULL
2012-07-12 20:02:29 +04:00
*/
2013-03-19 18:54:21 +04:00
void * ti_bandgap_get_sensor_data ( struct ti_bandgap * bgp , int id )
2012-07-12 20:02:29 +04:00
{
2013-03-19 18:54:21 +04:00
int ret = ti_bandgap_validate ( bgp , id ) ;
2012-07-12 20:02:29 +04:00
if ( ret )
return ERR_PTR ( ret ) ;
2013-03-19 18:54:23 +04:00
return bgp - > regval [ id ] . data ;
2012-07-12 20:02:29 +04:00
}
2013-03-15 17:00:22 +04:00
/*** Helper functions used during device initialization ***/
2013-03-15 17:00:26 +04:00
/**
2013-03-19 18:54:21 +04:00
* ti_bandgap_force_single_read ( ) - executes 1 single ADC conversion
* @ bgp : pointer to struct ti_bandgap
2013-03-15 17:00:26 +04:00
* @ id : sensor id which it is desired to read 1 temperature
*
* Used to initialize the conversion state machine and set it to a valid
* state . Called during device initialization and context restore events .
2013-04-01 20:04:34 +04:00
*
* Return : 0
2013-03-15 17:00:26 +04:00
*/
2012-07-12 20:02:29 +04:00
static int
2013-03-19 18:54:21 +04:00
ti_bandgap_force_single_read ( struct ti_bandgap * bgp , int id )
2012-07-12 20:02:29 +04:00
{
2015-01-18 23:20:51 +03:00
u32 counter = 1000 ;
struct temp_sensor_registers * tsr ;
2012-07-12 20:02:29 +04:00
/* Select single conversion mode */
2013-03-19 18:54:21 +04:00
if ( TI_BANDGAP_HAS ( bgp , MODE_CONFIG ) )
2013-03-19 18:54:18 +04:00
RMW_BITS ( bgp , id , bgap_mode_ctrl , mode_ctrl_mask , 0 ) ;
2012-07-12 20:02:29 +04:00
/* Start of Conversion = 1 */
2013-03-19 18:54:18 +04:00
RMW_BITS ( bgp , id , temp_sensor_ctrl , bgap_soc_mask , 1 ) ;
2013-02-27 02:53:33 +04:00
2015-01-18 23:20:51 +03:00
/* Wait for EOCZ going up */
tsr = bgp - > conf - > sensors [ id ] . registers ;
while ( - - counter ) {
if ( ti_bandgap_readl ( bgp , tsr - > temp_sensor_ctrl ) &
tsr - > bgap_eocz_mask )
break ;
}
2013-02-27 02:53:33 +04:00
2012-07-12 20:02:29 +04:00
/* Start of Conversion = 0 */
2013-03-19 18:54:18 +04:00
RMW_BITS ( bgp , id , temp_sensor_ctrl , bgap_soc_mask , 0 ) ;
2012-07-12 20:02:29 +04:00
2015-01-18 23:20:51 +03:00
/* Wait for EOCZ going down */
counter = 1000 ;
while ( - - counter ) {
if ( ! ( ti_bandgap_readl ( bgp , tsr - > temp_sensor_ctrl ) &
tsr - > bgap_eocz_mask ) )
break ;
}
2012-07-12 20:02:29 +04:00
return 0 ;
}
/**
2013-03-19 18:54:21 +04:00
* ti_bandgap_set_continous_mode ( ) - One time enabling of continuous mode
* @ bgp : pointer to struct ti_bandgap
2012-07-12 20:02:29 +04:00
*
2013-03-15 17:00:25 +04:00
* Call this function only if HAS ( MODE_CONFIG ) is set . As this driver may
* be used for junction temperature monitoring , it is desirable that the
* sensors are operational all the time , so that alerts are generated
* properly .
2013-04-01 20:04:34 +04:00
*
* Return : 0
2012-07-12 20:02:29 +04:00
*/
2013-03-19 18:54:21 +04:00
static int ti_bandgap_set_continuous_mode ( struct ti_bandgap * bgp )
2012-07-12 20:02:29 +04:00
{
int i ;
2013-03-19 18:54:18 +04:00
for ( i = 0 ; i < bgp - > conf - > sensor_count ; i + + ) {
2012-07-12 20:02:29 +04:00
/* Perform a single read just before enabling continuous */
2013-03-19 18:54:21 +04:00
ti_bandgap_force_single_read ( bgp , i ) ;
2013-03-19 18:54:18 +04:00
RMW_BITS ( bgp , i , bgap_mode_ctrl , mode_ctrl_mask , 1 ) ;
2012-07-12 20:02:29 +04:00
}
return 0 ;
}
2013-04-01 20:04:45 +04:00
/**
* ti_bandgap_get_trend ( ) - To fetch the temperature trend of a sensor
* @ bgp : pointer to struct ti_bandgap
* @ id : id of the individual sensor
* @ trend : Pointer to trend .
*
* This function needs to be called to fetch the temperature trend of a
* Particular sensor . The function computes the difference in temperature
* w . r . t time . For the bandgaps with built in history buffer the temperatures
* are read from the buffer and for those without the Buffer - ENOTSUPP is
* returned .
*
* Return : 0 if no error , else return corresponding error . If no
* error then the trend value is passed on to trend parameter
*/
int ti_bandgap_get_trend ( struct ti_bandgap * bgp , int id , int * trend )
{
struct temp_sensor_registers * tsr ;
u32 temp1 , temp2 , reg1 , reg2 ;
int t1 , t2 , interval , ret = 0 ;
ret = ti_bandgap_validate ( bgp , id ) ;
if ( ret )
goto exit ;
if ( ! TI_BANDGAP_HAS ( bgp , HISTORY_BUFFER ) | |
! TI_BANDGAP_HAS ( bgp , FREEZE_BIT ) ) {
ret = - ENOTSUPP ;
goto exit ;
}
2013-06-07 23:13:13 +04:00
spin_lock ( & bgp - > lock ) ;
2013-04-01 20:04:45 +04:00
tsr = bgp - > conf - > sensors [ id ] . registers ;
/* Freeze and read the last 2 valid readings */
2013-06-07 23:13:13 +04:00
RMW_BITS ( bgp , id , bgap_mask_ctrl , mask_freeze_mask , 1 ) ;
2013-04-01 20:04:45 +04:00
reg1 = tsr - > ctrl_dtemp_1 ;
reg2 = tsr - > ctrl_dtemp_2 ;
/* read temperature from history buffer */
temp1 = ti_bandgap_readl ( bgp , reg1 ) ;
temp1 & = tsr - > bgap_dtemp_mask ;
temp2 = ti_bandgap_readl ( bgp , reg2 ) ;
temp2 & = tsr - > bgap_dtemp_mask ;
/* Convert from adc values to mCelsius temperature */
ret = ti_bandgap_adc_to_mcelsius ( bgp , temp1 , & t1 ) ;
if ( ret )
2013-06-07 23:13:13 +04:00
goto unfreeze ;
2013-04-01 20:04:45 +04:00
ret = ti_bandgap_adc_to_mcelsius ( bgp , temp2 , & t2 ) ;
if ( ret )
2013-06-07 23:13:13 +04:00
goto unfreeze ;
2013-04-01 20:04:45 +04:00
/* Fetch the update interval */
ret = ti_bandgap_read_update_interval ( bgp , id , & interval ) ;
2013-08-23 20:08:23 +04:00
if ( ret )
2013-06-07 23:13:13 +04:00
goto unfreeze ;
2013-04-01 20:04:45 +04:00
2013-08-23 20:08:23 +04:00
/* Set the interval to 1 ms if bandgap counter delay is not set */
if ( interval = = 0 )
interval = 1 ;
2013-04-01 20:04:45 +04:00
* trend = ( t1 - t2 ) / interval ;
dev_dbg ( bgp - > dev , " The temperatures are t1 = %d and t2 = %d and trend =%d \n " ,
t1 , t2 , * trend ) ;
2013-06-07 23:13:13 +04:00
unfreeze :
RMW_BITS ( bgp , id , bgap_mask_ctrl , mask_freeze_mask , 0 ) ;
spin_unlock ( & bgp - > lock ) ;
2013-04-01 20:04:45 +04:00
exit :
return ret ;
}
2013-03-15 17:00:30 +04:00
/**
2013-03-19 18:54:21 +04:00
* ti_bandgap_tshut_init ( ) - setup and initialize tshut handling
* @ bgp : pointer to struct ti_bandgap
2013-03-15 17:00:30 +04:00
* @ pdev : pointer to device struct platform_device
*
* Call this function only in case the bandgap features HAS ( TSHUT ) .
* In this case , the driver needs to handle the TSHUT signal as an IRQ .
* The IRQ is wired as a GPIO , and for this purpose , it is required
* to specify which GPIO line is used . TSHUT IRQ is fired anytime
* one of the bandgap sensors violates the TSHUT high / hot threshold .
* And in that case , the system must go off .
2013-04-01 20:04:34 +04:00
*
* Return : 0 if no error , else error status
2013-03-15 17:00:30 +04:00
*/
2013-03-19 18:54:21 +04:00
static int ti_bandgap_tshut_init ( struct ti_bandgap * bgp ,
struct platform_device * pdev )
2012-07-12 20:02:29 +04:00
{
2013-03-19 18:54:18 +04:00
int gpio_nr = bgp - > tshut_gpio ;
2012-07-12 20:02:29 +04:00
int status ;
/* Request for gpio_86 line */
status = gpio_request ( gpio_nr , " tshut " ) ;
if ( status < 0 ) {
2013-03-19 18:54:18 +04:00
dev_err ( bgp - > dev , " Could not request for TSHUT GPIO:%i \n " , 86 ) ;
2012-07-12 20:02:29 +04:00
return status ;
}
status = gpio_direction_input ( gpio_nr ) ;
if ( status ) {
2013-03-19 18:54:18 +04:00
dev_err ( bgp - > dev , " Cannot set input TSHUT GPIO %d \n " , gpio_nr ) ;
2012-07-12 20:02:29 +04:00
return status ;
}
2013-03-19 18:54:21 +04:00
status = request_irq ( gpio_to_irq ( gpio_nr ) , ti_bandgap_tshut_irq_handler ,
IRQF_TRIGGER_RISING , " tshut " , NULL ) ;
2012-07-12 20:02:29 +04:00
if ( status ) {
gpio_free ( gpio_nr ) ;
2013-03-19 18:54:18 +04:00
dev_err ( bgp - > dev , " request irq failed for TSHUT " ) ;
2012-07-12 20:02:29 +04:00
}
return 0 ;
}
2013-03-15 17:00:31 +04:00
/**
2013-03-19 18:54:21 +04:00
* ti_bandgap_alert_init ( ) - setup and initialize talert handling
* @ bgp : pointer to struct ti_bandgap
2013-03-15 17:00:31 +04:00
* @ pdev : pointer to device struct platform_device
*
* Call this function only in case the bandgap features HAS ( TALERT ) .
* In this case , the driver needs to handle the TALERT signals as an IRQs .
* TALERT is a normal IRQ and it is fired any time thresholds ( hot or cold )
* are violated . In these situation , the driver must reprogram the thresholds ,
* accordingly to specified policy .
2013-04-01 20:04:34 +04:00
*
* Return : 0 if no error , else return corresponding error .
2013-03-15 17:00:31 +04:00
*/
2013-03-19 18:54:21 +04:00
static int ti_bandgap_talert_init ( struct ti_bandgap * bgp ,
struct platform_device * pdev )
2012-07-12 20:02:29 +04:00
{
int ret ;
2013-03-19 18:54:18 +04:00
bgp - > irq = platform_get_irq ( pdev , 0 ) ;
if ( bgp - > irq < 0 ) {
2012-07-12 20:02:29 +04:00
dev_err ( & pdev - > dev , " get_irq failed \n " ) ;
2013-03-19 18:54:18 +04:00
return bgp - > irq ;
2012-07-12 20:02:29 +04:00
}
2013-03-19 18:54:18 +04:00
ret = request_threaded_irq ( bgp - > irq , NULL ,
2013-03-19 18:54:21 +04:00
ti_bandgap_talert_irq_handler ,
2012-07-12 20:02:29 +04:00
IRQF_TRIGGER_HIGH | IRQF_ONESHOT ,
2013-03-19 18:54:18 +04:00
" talert " , bgp ) ;
2012-07-12 20:02:29 +04:00
if ( ret ) {
dev_err ( & pdev - > dev , " Request threaded irq failed. \n " ) ;
return ret ;
}
return 0 ;
}
2013-03-19 18:54:25 +04:00
static const struct of_device_id of_ti_bandgap_match [ ] ;
2013-03-15 17:00:32 +04:00
/**
2013-03-19 18:54:21 +04:00
* ti_bandgap_build ( ) - parse DT and setup a struct ti_bandgap
2013-03-15 17:00:32 +04:00
* @ pdev : pointer to device struct platform_device
*
* Used to read the device tree properties accordingly to the bandgap
* matching version . Based on bandgap version and its capabilities it
2013-03-19 18:54:21 +04:00
* will build a struct ti_bandgap out of the required DT entries .
2013-04-01 20:04:34 +04:00
*
* Return : valid bandgap structure if successful , else returns ERR_PTR
* return value must be verified with IS_ERR .
2013-03-15 17:00:32 +04:00
*/
2013-03-19 18:54:21 +04:00
static struct ti_bandgap * ti_bandgap_build ( struct platform_device * pdev )
2012-07-12 20:02:29 +04:00
{
struct device_node * node = pdev - > dev . of_node ;
const struct of_device_id * of_id ;
2013-03-19 18:54:21 +04:00
struct ti_bandgap * bgp ;
2012-07-12 20:02:29 +04:00
struct resource * res ;
int i ;
/* just for the sake */
if ( ! node ) {
dev_err ( & pdev - > dev , " no platform information available \n " ) ;
return ERR_PTR ( - EINVAL ) ;
}
2013-03-19 18:54:24 +04:00
bgp = devm_kzalloc ( & pdev - > dev , sizeof ( * bgp ) , GFP_KERNEL ) ;
2013-03-19 18:54:18 +04:00
if ( ! bgp ) {
2012-07-12 20:02:29 +04:00
dev_err ( & pdev - > dev , " Unable to allocate mem for driver ref \n " ) ;
return ERR_PTR ( - ENOMEM ) ;
}
2013-03-19 18:54:21 +04:00
of_id = of_match_device ( of_ti_bandgap_match , & pdev - > dev ) ;
2012-07-12 20:02:29 +04:00
if ( of_id )
2013-03-19 18:54:18 +04:00
bgp - > conf = of_id - > data ;
2012-07-12 20:02:29 +04:00
2013-03-19 18:54:23 +04:00
/* register shadow for context save and restore */
bgp - > regval = devm_kzalloc ( & pdev - > dev , sizeof ( * bgp - > regval ) *
bgp - > conf - > sensor_count , GFP_KERNEL ) ;
2014-06-03 01:25:30 +04:00
if ( ! bgp - > regval ) {
2013-03-19 18:54:23 +04:00
dev_err ( & pdev - > dev , " Unable to allocate mem for driver ref \n " ) ;
return ERR_PTR ( - ENOMEM ) ;
}
2012-07-12 20:02:29 +04:00
i = 0 ;
do {
void __iomem * chunk ;
res = platform_get_resource ( pdev , IORESOURCE_MEM , i ) ;
if ( ! res )
break ;
2013-01-21 14:09:19 +04:00
chunk = devm_ioremap_resource ( & pdev - > dev , res ) ;
2012-07-12 20:02:29 +04:00
if ( i = = 0 )
2013-03-19 18:54:18 +04:00
bgp - > base = chunk ;
2013-01-21 14:09:19 +04:00
if ( IS_ERR ( chunk ) )
return ERR_CAST ( chunk ) ;
2013-03-15 16:59:53 +04:00
2012-07-12 20:02:29 +04:00
i + + ;
} while ( res ) ;
2013-03-19 18:54:21 +04:00
if ( TI_BANDGAP_HAS ( bgp , TSHUT ) ) {
2013-06-07 19:11:53 +04:00
bgp - > tshut_gpio = of_get_gpio ( node , 0 ) ;
2013-03-19 18:54:18 +04:00
if ( ! gpio_is_valid ( bgp - > tshut_gpio ) ) {
2012-07-12 20:02:29 +04:00
dev_err ( & pdev - > dev , " invalid gpio for tshut (%d) \n " ,
2013-03-19 18:54:18 +04:00
bgp - > tshut_gpio ) ;
2012-07-12 20:02:29 +04:00
return ERR_PTR ( - EINVAL ) ;
}
}
2013-03-19 18:54:18 +04:00
return bgp ;
2012-07-12 20:02:29 +04:00
}
2013-03-15 17:00:23 +04:00
/*** Device driver call backs ***/
2012-07-12 20:02:29 +04:00
static
2013-03-19 18:54:21 +04:00
int ti_bandgap_probe ( struct platform_device * pdev )
2012-07-12 20:02:29 +04:00
{
2013-03-19 18:54:21 +04:00
struct ti_bandgap * bgp ;
2016-03-02 13:00:55 +03:00
int clk_rate , ret , i ;
2012-07-12 20:02:29 +04:00
2013-03-19 18:54:21 +04:00
bgp = ti_bandgap_build ( pdev ) ;
2013-05-29 19:07:43 +04:00
if ( IS_ERR ( bgp ) ) {
2012-07-12 20:02:29 +04:00
dev_err ( & pdev - > dev , " failed to fetch platform data \n " ) ;
2013-03-19 18:54:18 +04:00
return PTR_ERR ( bgp ) ;
2012-07-12 20:02:29 +04:00
}
2013-03-19 18:54:18 +04:00
bgp - > dev = & pdev - > dev ;
2012-07-12 20:02:29 +04:00
2015-04-02 17:49:07 +03:00
if ( TI_BANDGAP_HAS ( bgp , UNRELIABLE ) )
dev_warn ( & pdev - > dev ,
" This OMAP thermal sensor is unreliable. You've been warned \n " ) ;
2013-03-19 18:54:21 +04:00
if ( TI_BANDGAP_HAS ( bgp , TSHUT ) ) {
ret = ti_bandgap_tshut_init ( bgp , pdev ) ;
2012-07-12 20:02:29 +04:00
if ( ret ) {
dev_err ( & pdev - > dev ,
" failed to initialize system tshut IRQ \n " ) ;
return ret ;
}
}
2013-03-19 18:54:18 +04:00
bgp - > fclock = clk_get ( NULL , bgp - > conf - > fclock_name ) ;
2016-03-02 13:00:55 +03:00
if ( IS_ERR ( bgp - > fclock ) ) {
2012-07-12 20:02:29 +04:00
dev_err ( & pdev - > dev , " failed to request fclock reference \n " ) ;
2013-05-29 19:07:43 +04:00
ret = PTR_ERR ( bgp - > fclock ) ;
2012-07-12 20:02:29 +04:00
goto free_irqs ;
}
2015-01-18 23:17:10 +03:00
bgp - > div_clk = clk_get ( NULL , bgp - > conf - > div_ck_name ) ;
2016-03-02 13:00:55 +03:00
if ( IS_ERR ( bgp - > div_clk ) ) {
2015-01-18 23:17:10 +03:00
dev_err ( & pdev - > dev , " failed to request div_ts_ck clock ref \n " ) ;
2013-05-29 19:07:43 +04:00
ret = PTR_ERR ( bgp - > div_clk ) ;
2016-11-17 01:15:22 +03:00
goto put_fclock ;
2012-07-12 20:02:29 +04:00
}
2013-03-19 18:54:18 +04:00
for ( i = 0 ; i < bgp - > conf - > sensor_count ; i + + ) {
2012-07-12 20:02:29 +04:00
struct temp_sensor_registers * tsr ;
u32 val ;
2013-03-19 18:54:18 +04:00
tsr = bgp - > conf - > sensors [ i ] . registers ;
2012-07-12 20:02:29 +04:00
/*
* check if the efuse has a non - zero value if not
* it is an untrimmed sample and the temperatures
* may not be accurate
*/
2013-03-19 18:54:21 +04:00
val = ti_bandgap_readl ( bgp , tsr - > bgap_efuse ) ;
2016-03-02 13:00:55 +03:00
if ( ! val )
2012-07-12 20:02:29 +04:00
dev_info ( & pdev - > dev ,
" Non-trimmed BGAP, Temp not accurate \n " ) ;
}
2013-03-19 18:54:18 +04:00
clk_rate = clk_round_rate ( bgp - > div_clk ,
bgp - > conf - > sensors [ 0 ] . ts_data - > max_freq ) ;
if ( clk_rate < bgp - > conf - > sensors [ 0 ] . ts_data - > min_freq | |
2013-12-10 06:09:22 +04:00
clk_rate < = 0 ) {
2012-07-12 20:02:29 +04:00
ret = - ENODEV ;
dev_err ( & pdev - > dev , " wrong clock rate (%d) \n " , clk_rate ) ;
goto put_clks ;
}
2013-03-19 18:54:18 +04:00
ret = clk_set_rate ( bgp - > div_clk , clk_rate ) ;
2012-07-12 20:02:29 +04:00
if ( ret )
dev_err ( & pdev - > dev , " Cannot re-set clock rate. Continuing \n " ) ;
2013-03-19 18:54:18 +04:00
bgp - > clk_rate = clk_rate ;
2013-03-19 18:54:21 +04:00
if ( TI_BANDGAP_HAS ( bgp , CLK_CTRL ) )
2013-03-19 18:54:18 +04:00
clk_prepare_enable ( bgp - > fclock ) ;
2013-02-27 02:53:25 +04:00
2012-07-12 20:02:29 +04:00
2013-03-19 18:54:18 +04:00
spin_lock_init ( & bgp - > lock ) ;
bgp - > dev = & pdev - > dev ;
platform_set_drvdata ( pdev , bgp ) ;
2012-07-12 20:02:29 +04:00
2013-03-19 18:54:21 +04:00
ti_bandgap_power ( bgp , true ) ;
2012-07-12 20:02:29 +04:00
/* Set default counter to 1 for now */
2013-03-19 18:54:21 +04:00
if ( TI_BANDGAP_HAS ( bgp , COUNTER ) )
2013-03-19 18:54:18 +04:00
for ( i = 0 ; i < bgp - > conf - > sensor_count ; i + + )
RMW_BITS ( bgp , i , bgap_counter , counter_mask , 1 ) ;
2012-07-12 20:02:29 +04:00
2013-03-15 16:59:55 +04:00
/* Set default thresholds for alert and shutdown */
2013-03-19 18:54:18 +04:00
for ( i = 0 ; i < bgp - > conf - > sensor_count ; i + + ) {
2012-07-12 20:02:29 +04:00
struct temp_sensor_data * ts_data ;
2013-03-19 18:54:18 +04:00
ts_data = bgp - > conf - > sensors [ i ] . ts_data ;
2012-07-12 20:02:29 +04:00
2013-03-19 18:54:21 +04:00
if ( TI_BANDGAP_HAS ( bgp , TALERT ) ) {
2013-03-15 16:59:55 +04:00
/* Set initial Talert thresholds */
2013-03-19 18:54:18 +04:00
RMW_BITS ( bgp , i , bgap_threshold ,
2013-03-15 16:59:55 +04:00
threshold_tcold_mask , ts_data - > t_cold ) ;
2013-03-19 18:54:18 +04:00
RMW_BITS ( bgp , i , bgap_threshold ,
2013-03-15 16:59:55 +04:00
threshold_thot_mask , ts_data - > t_hot ) ;
/* Enable the alert events */
2013-03-19 18:54:18 +04:00
RMW_BITS ( bgp , i , bgap_mask_ctrl , mask_hot_mask , 1 ) ;
RMW_BITS ( bgp , i , bgap_mask_ctrl , mask_cold_mask , 1 ) ;
2013-03-15 16:59:55 +04:00
}
2013-03-19 18:54:21 +04:00
if ( TI_BANDGAP_HAS ( bgp , TSHUT_CONFIG ) ) {
2013-03-15 16:59:55 +04:00
/* Set initial Tshut thresholds */
2013-03-19 18:54:18 +04:00
RMW_BITS ( bgp , i , tshut_threshold ,
2013-03-15 16:59:55 +04:00
tshut_hot_mask , ts_data - > tshut_hot ) ;
2013-03-19 18:54:18 +04:00
RMW_BITS ( bgp , i , tshut_threshold ,
2013-03-15 16:59:55 +04:00
tshut_cold_mask , ts_data - > tshut_cold ) ;
2012-07-12 20:02:29 +04:00
}
}
2013-03-19 18:54:21 +04:00
if ( TI_BANDGAP_HAS ( bgp , MODE_CONFIG ) )
ti_bandgap_set_continuous_mode ( bgp ) ;
2012-07-12 20:02:29 +04:00
/* Set .250 seconds time as default counter */
2013-03-19 18:54:21 +04:00
if ( TI_BANDGAP_HAS ( bgp , COUNTER ) )
2013-03-19 18:54:18 +04:00
for ( i = 0 ; i < bgp - > conf - > sensor_count ; i + + )
RMW_BITS ( bgp , i , bgap_counter , counter_mask ,
bgp - > clk_rate / 4 ) ;
2012-07-12 20:02:29 +04:00
/* Every thing is good? Then expose the sensors */
2013-03-19 18:54:18 +04:00
for ( i = 0 ; i < bgp - > conf - > sensor_count ; i + + ) {
2012-07-12 20:02:29 +04:00
char * domain ;
2013-04-08 16:19:13 +04:00
if ( bgp - > conf - > sensors [ i ] . register_cooling ) {
ret = bgp - > conf - > sensors [ i ] . register_cooling ( bgp , i ) ;
if ( ret )
goto remove_sensors ;
}
2012-09-11 20:06:55 +04:00
2013-04-08 16:19:13 +04:00
if ( bgp - > conf - > expose_sensor ) {
domain = bgp - > conf - > sensors [ i ] . domain ;
ret = bgp - > conf - > expose_sensor ( bgp , i , domain ) ;
if ( ret )
goto remove_last_cooling ;
}
2012-07-12 20:02:29 +04:00
}
/*
* Enable the Interrupts once everything is set . Otherwise irq handler
* might be called as soon as it is enabled where as rest of framework
* is still getting initialised .
*/
2013-03-19 18:54:21 +04:00
if ( TI_BANDGAP_HAS ( bgp , TALERT ) ) {
ret = ti_bandgap_talert_init ( bgp , pdev ) ;
2012-07-12 20:02:29 +04:00
if ( ret ) {
dev_err ( & pdev - > dev , " failed to initialize Talert IRQ \n " ) ;
2013-03-19 18:54:18 +04:00
i = bgp - > conf - > sensor_count ;
2012-07-12 20:02:29 +04:00
goto disable_clk ;
}
}
return 0 ;
2013-04-08 16:19:13 +04:00
remove_last_cooling :
if ( bgp - > conf - > sensors [ i ] . unregister_cooling )
bgp - > conf - > sensors [ i ] . unregister_cooling ( bgp , i ) ;
remove_sensors :
for ( i - - ; i > = 0 ; i - - ) {
if ( bgp - > conf - > sensors [ i ] . unregister_cooling )
bgp - > conf - > sensors [ i ] . unregister_cooling ( bgp , i ) ;
if ( bgp - > conf - > remove_sensor )
bgp - > conf - > remove_sensor ( bgp , i ) ;
}
ti_bandgap_power ( bgp , false ) ;
2012-07-12 20:02:29 +04:00
disable_clk :
2013-03-19 18:54:21 +04:00
if ( TI_BANDGAP_HAS ( bgp , CLK_CTRL ) )
2013-03-19 18:54:18 +04:00
clk_disable_unprepare ( bgp - > fclock ) ;
2012-07-12 20:02:29 +04:00
put_clks :
2013-03-19 18:54:18 +04:00
clk_put ( bgp - > div_clk ) ;
2016-11-17 01:15:22 +03:00
put_fclock :
clk_put ( bgp - > fclock ) ;
2012-07-12 20:02:29 +04:00
free_irqs :
2013-03-19 18:54:21 +04:00
if ( TI_BANDGAP_HAS ( bgp , TSHUT ) ) {
2013-03-19 18:54:18 +04:00
free_irq ( gpio_to_irq ( bgp - > tshut_gpio ) , NULL ) ;
gpio_free ( bgp - > tshut_gpio ) ;
2012-07-12 20:02:29 +04:00
}
return ret ;
}
static
2013-03-19 18:54:21 +04:00
int ti_bandgap_remove ( struct platform_device * pdev )
2012-07-12 20:02:29 +04:00
{
2013-03-19 18:54:21 +04:00
struct ti_bandgap * bgp = platform_get_drvdata ( pdev ) ;
2012-07-12 20:02:29 +04:00
int i ;
/* First thing is to remove sensor interfaces */
2013-03-19 18:54:18 +04:00
for ( i = 0 ; i < bgp - > conf - > sensor_count ; i + + ) {
2013-04-08 16:19:14 +04:00
if ( bgp - > conf - > sensors [ i ] . unregister_cooling )
2013-03-19 18:54:18 +04:00
bgp - > conf - > sensors [ i ] . unregister_cooling ( bgp , i ) ;
2012-07-12 20:02:29 +04:00
2013-03-19 18:54:18 +04:00
if ( bgp - > conf - > remove_sensor )
bgp - > conf - > remove_sensor ( bgp , i ) ;
2012-07-12 20:02:29 +04:00
}
2013-03-19 18:54:21 +04:00
ti_bandgap_power ( bgp , false ) ;
2012-07-12 20:02:29 +04:00
2013-03-19 18:54:21 +04:00
if ( TI_BANDGAP_HAS ( bgp , CLK_CTRL ) )
2013-03-19 18:54:18 +04:00
clk_disable_unprepare ( bgp - > fclock ) ;
clk_put ( bgp - > fclock ) ;
clk_put ( bgp - > div_clk ) ;
2012-07-12 20:02:29 +04:00
2013-03-19 18:54:21 +04:00
if ( TI_BANDGAP_HAS ( bgp , TALERT ) )
2013-03-19 18:54:18 +04:00
free_irq ( bgp - > irq , bgp ) ;
2012-07-12 20:02:29 +04:00
2013-03-19 18:54:21 +04:00
if ( TI_BANDGAP_HAS ( bgp , TSHUT ) ) {
2013-03-19 18:54:18 +04:00
free_irq ( gpio_to_irq ( bgp - > tshut_gpio ) , NULL ) ;
gpio_free ( bgp - > tshut_gpio ) ;
2012-07-12 20:02:29 +04:00
}
return 0 ;
}
2015-02-06 17:55:46 +03:00
# ifdef CONFIG_PM_SLEEP
2013-03-19 18:54:21 +04:00
static int ti_bandgap_save_ctxt ( struct ti_bandgap * bgp )
2012-07-12 20:02:29 +04:00
{
int i ;
2013-03-19 18:54:18 +04:00
for ( i = 0 ; i < bgp - > conf - > sensor_count ; i + + ) {
2012-07-12 20:02:29 +04:00
struct temp_sensor_registers * tsr ;
struct temp_sensor_regval * rval ;
2013-03-19 18:54:23 +04:00
rval = & bgp - > regval [ i ] ;
2013-03-19 18:54:18 +04:00
tsr = bgp - > conf - > sensors [ i ] . registers ;
2012-07-12 20:02:29 +04:00
2013-03-19 18:54:21 +04:00
if ( TI_BANDGAP_HAS ( bgp , MODE_CONFIG ) )
rval - > bg_mode_ctrl = ti_bandgap_readl ( bgp ,
2012-09-11 20:06:52 +04:00
tsr - > bgap_mode_ctrl ) ;
2013-03-19 18:54:21 +04:00
if ( TI_BANDGAP_HAS ( bgp , COUNTER ) )
rval - > bg_counter = ti_bandgap_readl ( bgp ,
2012-09-11 20:06:52 +04:00
tsr - > bgap_counter ) ;
2013-03-19 18:54:21 +04:00
if ( TI_BANDGAP_HAS ( bgp , TALERT ) ) {
rval - > bg_threshold = ti_bandgap_readl ( bgp ,
2012-09-11 20:06:52 +04:00
tsr - > bgap_threshold ) ;
2013-03-19 18:54:21 +04:00
rval - > bg_ctrl = ti_bandgap_readl ( bgp ,
2012-09-11 20:06:52 +04:00
tsr - > bgap_mask_ctrl ) ;
2012-07-12 20:02:29 +04:00
}
2013-03-19 18:54:21 +04:00
if ( TI_BANDGAP_HAS ( bgp , TSHUT_CONFIG ) )
rval - > tshut_threshold = ti_bandgap_readl ( bgp ,
2012-09-11 20:06:52 +04:00
tsr - > tshut_threshold ) ;
2012-07-12 20:02:29 +04:00
}
return 0 ;
}
2013-03-19 18:54:21 +04:00
static int ti_bandgap_restore_ctxt ( struct ti_bandgap * bgp )
2012-07-12 20:02:29 +04:00
{
int i ;
2013-03-19 18:54:18 +04:00
for ( i = 0 ; i < bgp - > conf - > sensor_count ; i + + ) {
2012-07-12 20:02:29 +04:00
struct temp_sensor_registers * tsr ;
struct temp_sensor_regval * rval ;
u32 val = 0 ;
2013-03-19 18:54:23 +04:00
rval = & bgp - > regval [ i ] ;
2013-03-19 18:54:18 +04:00
tsr = bgp - > conf - > sensors [ i ] . registers ;
2012-07-12 20:02:29 +04:00
2013-03-19 18:54:21 +04:00
if ( TI_BANDGAP_HAS ( bgp , COUNTER ) )
val = ti_bandgap_readl ( bgp , tsr - > bgap_counter ) ;
2012-07-12 20:02:29 +04:00
2013-03-19 18:54:21 +04:00
if ( TI_BANDGAP_HAS ( bgp , TSHUT_CONFIG ) )
ti_bandgap_writel ( bgp , rval - > tshut_threshold ,
tsr - > tshut_threshold ) ;
2012-11-13 22:10:04 +04:00
/* Force immediate temperature measurement and update
* of the DTEMP field
*/
2013-03-19 18:54:21 +04:00
ti_bandgap_force_single_read ( bgp , i ) ;
if ( TI_BANDGAP_HAS ( bgp , COUNTER ) )
ti_bandgap_writel ( bgp , rval - > bg_counter ,
tsr - > bgap_counter ) ;
if ( TI_BANDGAP_HAS ( bgp , MODE_CONFIG ) )
ti_bandgap_writel ( bgp , rval - > bg_mode_ctrl ,
tsr - > bgap_mode_ctrl ) ;
if ( TI_BANDGAP_HAS ( bgp , TALERT ) ) {
ti_bandgap_writel ( bgp , rval - > bg_threshold ,
tsr - > bgap_threshold ) ;
ti_bandgap_writel ( bgp , rval - > bg_ctrl ,
tsr - > bgap_mask_ctrl ) ;
2012-07-12 20:02:29 +04:00
}
}
return 0 ;
}
2013-03-19 18:54:21 +04:00
static int ti_bandgap_suspend ( struct device * dev )
2012-07-12 20:02:29 +04:00
{
2013-03-19 18:54:21 +04:00
struct ti_bandgap * bgp = dev_get_drvdata ( dev ) ;
2012-07-12 20:02:29 +04:00
int err ;
2013-03-19 18:54:21 +04:00
err = ti_bandgap_save_ctxt ( bgp ) ;
ti_bandgap_power ( bgp , false ) ;
2013-02-27 02:53:25 +04:00
2013-03-19 18:54:21 +04:00
if ( TI_BANDGAP_HAS ( bgp , CLK_CTRL ) )
2013-03-19 18:54:18 +04:00
clk_disable_unprepare ( bgp - > fclock ) ;
2012-07-12 20:02:29 +04:00
return err ;
}
2013-03-19 18:54:21 +04:00
static int ti_bandgap_resume ( struct device * dev )
2012-07-12 20:02:29 +04:00
{
2013-03-19 18:54:21 +04:00
struct ti_bandgap * bgp = dev_get_drvdata ( dev ) ;
2012-07-12 20:02:29 +04:00
2013-03-19 18:54:21 +04:00
if ( TI_BANDGAP_HAS ( bgp , CLK_CTRL ) )
2013-03-19 18:54:18 +04:00
clk_prepare_enable ( bgp - > fclock ) ;
2013-02-27 02:53:25 +04:00
2013-03-19 18:54:21 +04:00
ti_bandgap_power ( bgp , true ) ;
2012-07-12 20:02:29 +04:00
2013-03-19 18:54:21 +04:00
return ti_bandgap_restore_ctxt ( bgp ) ;
2012-07-12 20:02:29 +04:00
}
2014-02-27 15:43:02 +04:00
static SIMPLE_DEV_PM_OPS ( ti_bandgap_dev_pm_ops , ti_bandgap_suspend ,
ti_bandgap_resume ) ;
2012-07-12 20:02:29 +04:00
2013-03-19 18:54:21 +04:00
# define DEV_PM_OPS (&ti_bandgap_dev_pm_ops)
2012-07-12 20:02:29 +04:00
# else
# define DEV_PM_OPS NULL
# endif
2013-03-19 18:54:21 +04:00
static const struct of_device_id of_ti_bandgap_match [ ] = {
2015-04-02 17:49:07 +03:00
# ifdef CONFIG_OMAP3_THERMAL
{
. compatible = " ti,omap34xx-bandgap " ,
. data = ( void * ) & omap34xx_data ,
} ,
2015-09-22 03:32:15 +03:00
{
. compatible = " ti,omap36xx-bandgap " ,
. data = ( void * ) & omap36xx_data ,
} ,
2015-04-02 17:49:07 +03:00
# endif
2012-07-12 20:02:31 +04:00
# ifdef CONFIG_OMAP4_THERMAL
{
. compatible = " ti,omap4430-bandgap " ,
. data = ( void * ) & omap4430_data ,
} ,
{
. compatible = " ti,omap4460-bandgap " ,
. data = ( void * ) & omap4460_data ,
} ,
{
. compatible = " ti,omap4470-bandgap " ,
. data = ( void * ) & omap4470_data ,
} ,
2012-07-12 20:02:32 +04:00
# endif
# ifdef CONFIG_OMAP5_THERMAL
{
. compatible = " ti,omap5430-bandgap " ,
. data = ( void * ) & omap5430_data ,
} ,
2013-05-29 19:07:45 +04:00
# endif
# ifdef CONFIG_DRA752_THERMAL
{
. compatible = " ti,dra752-bandgap " ,
. data = ( void * ) & dra752_data ,
} ,
2012-07-12 20:02:31 +04:00
# endif
2012-07-12 20:02:29 +04:00
/* Sentinel */
{ } ,
} ;
2013-03-19 18:54:21 +04:00
MODULE_DEVICE_TABLE ( of , of_ti_bandgap_match ) ;
2012-07-12 20:02:29 +04:00
2013-03-19 18:54:21 +04:00
static struct platform_driver ti_bandgap_sensor_driver = {
. probe = ti_bandgap_probe ,
. remove = ti_bandgap_remove ,
2012-07-12 20:02:29 +04:00
. driver = {
2013-03-19 18:54:21 +04:00
. name = " ti-soc-thermal " ,
2012-07-12 20:02:29 +04:00
. pm = DEV_PM_OPS ,
2013-03-19 18:54:21 +04:00
. of_match_table = of_ti_bandgap_match ,
2012-07-12 20:02:29 +04:00
} ,
} ;
2013-03-19 18:54:21 +04:00
module_platform_driver ( ti_bandgap_sensor_driver ) ;
2012-07-12 20:02:29 +04:00
MODULE_DESCRIPTION ( " OMAP4+ bandgap temperature sensor driver " ) ;
MODULE_LICENSE ( " GPL v2 " ) ;
2013-03-19 18:54:21 +04:00
MODULE_ALIAS ( " platform:ti-soc-thermal " ) ;
2012-07-12 20:02:29 +04:00
MODULE_AUTHOR ( " Texas Instrument Inc. " ) ;