2013-04-03 16:18:12 +04:00
/*
* Copyright ( C ) ST - Ericsson 2010 - 2013
* Author : Martin Persson < martin . persson @ stericsson . com >
* Hongbo Zhang < hongbo . zhang @ linaro . org >
* License Terms : GNU General Public License v2
*
* When the AB8500 thermal warning temperature is reached ( threshold cannot
* be changed by SW ) , an interrupt is set , and if no further action is taken
2014-09-30 06:45:02 +04:00
* within a certain time frame , kernel_power_off will be called .
2013-04-03 16:18:12 +04:00
*
* When AB8500 thermal shutdown temperature is reached a hardware shutdown of
* the AB8500 will occur .
*/
# include <linux/err.h>
# include <linux/hwmon.h>
# include <linux/hwmon-sysfs.h>
# include <linux/mfd/abx500.h>
# include <linux/mfd/abx500/ab8500-bm.h>
# include <linux/mfd/abx500/ab8500-gpadc.h>
# include <linux/module.h>
# include <linux/platform_device.h>
# include <linux/power/ab8500.h>
2014-09-30 06:45:02 +04:00
# include <linux/reboot.h>
2013-04-03 16:18:12 +04:00
# include <linux/slab.h>
# include <linux/sysfs.h>
# include "abx500.h"
# define DEFAULT_POWER_OFF_DELAY (HZ * 10)
# define THERMAL_VCC 1800
# define PULL_UP_RESISTOR 47000
/* Number of monitored sensors should not greater than NUM_SENSORS */
# define NUM_MONITORED_SENSORS 4
struct ab8500_gpadc_cfg {
const struct abx500_res_to_temp * temp_tbl ;
int tbl_sz ;
int vcc ;
int r_up ;
} ;
struct ab8500_temp {
struct ab8500_gpadc * gpadc ;
struct ab8500_btemp * btemp ;
struct delayed_work power_off_work ;
struct ab8500_gpadc_cfg cfg ;
struct abx500_temp * abx500_data ;
} ;
/*
* The hardware connection is like this :
* VCC - - - - [ R_up ] - - - - - [ NTC ] - - - - GND
* where R_up is pull - up resistance , and GPADC measures voltage on NTC .
* and res_to_temp table is strictly sorted by falling resistance values .
*/
static int ab8500_voltage_to_temp ( struct ab8500_gpadc_cfg * cfg ,
int v_ntc , int * temp )
{
int r_ntc , i = 0 , tbl_sz = cfg - > tbl_sz ;
const struct abx500_res_to_temp * tbl = cfg - > temp_tbl ;
if ( cfg - > vcc < 0 | | v_ntc > = cfg - > vcc )
return - EINVAL ;
r_ntc = v_ntc * cfg - > r_up / ( cfg - > vcc - v_ntc ) ;
if ( r_ntc > tbl [ 0 ] . resist | | r_ntc < tbl [ tbl_sz - 1 ] . resist )
return - EINVAL ;
while ( ! ( r_ntc < = tbl [ i ] . resist & & r_ntc > tbl [ i + 1 ] . resist ) & &
i < tbl_sz - 2 )
i + + ;
/* return milli-Celsius */
* temp = tbl [ i ] . temp * 1000 + ( ( tbl [ i + 1 ] . temp - tbl [ i ] . temp ) * 1000 *
( r_ntc - tbl [ i ] . resist ) ) / ( tbl [ i + 1 ] . resist - tbl [ i ] . resist ) ;
return 0 ;
}
static int ab8500_read_sensor ( struct abx500_temp * data , u8 sensor , int * temp )
{
int voltage , ret ;
struct ab8500_temp * ab8500_data = data - > plat_data ;
if ( sensor = = BAT_CTRL ) {
* temp = ab8500_btemp_get_batctrl_temp ( ab8500_data - > btemp ) ;
} else if ( sensor = = BTEMP_BALL ) {
* temp = ab8500_btemp_get_temp ( ab8500_data - > btemp ) ;
} else {
voltage = ab8500_gpadc_convert ( ab8500_data - > gpadc , sensor ) ;
if ( voltage < 0 )
return voltage ;
ret = ab8500_voltage_to_temp ( & ab8500_data - > cfg , voltage , temp ) ;
if ( ret < 0 )
return ret ;
}
return 0 ;
}
static void ab8500_thermal_power_off ( struct work_struct * work )
{
struct ab8500_temp * ab8500_data = container_of ( work ,
struct ab8500_temp , power_off_work . work ) ;
struct abx500_temp * abx500_data = ab8500_data - > abx500_data ;
dev_warn ( & abx500_data - > pdev - > dev , " Power off due to critical temp \n " ) ;
2014-09-30 06:45:02 +04:00
kernel_power_off ( ) ;
2013-04-03 16:18:12 +04:00
}
static ssize_t ab8500_show_name ( struct device * dev ,
struct device_attribute * devattr , char * buf )
{
return sprintf ( buf , " ab8500 \n " ) ;
}
static ssize_t ab8500_show_label ( struct device * dev ,
struct device_attribute * devattr , char * buf )
{
char * label ;
struct sensor_device_attribute * attr = to_sensor_dev_attr ( devattr ) ;
int index = attr - > index ;
switch ( index ) {
case 1 :
label = " ext_adc1 " ;
break ;
case 2 :
label = " ext_adc2 " ;
break ;
case 3 :
label = " bat_temp " ;
break ;
case 4 :
label = " bat_ctrl " ;
break ;
default :
return - EINVAL ;
}
return sprintf ( buf , " %s \n " , label ) ;
}
static int ab8500_temp_irq_handler ( int irq , struct abx500_temp * data )
{
struct ab8500_temp * ab8500_data = data - > plat_data ;
dev_warn ( & data - > pdev - > dev , " Power off in %d s \n " ,
DEFAULT_POWER_OFF_DELAY / HZ ) ;
schedule_delayed_work ( & ab8500_data - > power_off_work ,
DEFAULT_POWER_OFF_DELAY ) ;
return 0 ;
}
int abx500_hwmon_init ( struct abx500_temp * data )
{
struct ab8500_temp * ab8500_data ;
ab8500_data = devm_kzalloc ( & data - > pdev - > dev , sizeof ( * ab8500_data ) ,
GFP_KERNEL ) ;
if ( ! ab8500_data )
return - ENOMEM ;
ab8500_data - > gpadc = ab8500_gpadc_get ( " ab8500-gpadc.0 " ) ;
if ( IS_ERR ( ab8500_data - > gpadc ) )
return PTR_ERR ( ab8500_data - > gpadc ) ;
ab8500_data - > btemp = ab8500_btemp_get ( ) ;
if ( IS_ERR ( ab8500_data - > btemp ) )
return PTR_ERR ( ab8500_data - > btemp ) ;
INIT_DELAYED_WORK ( & ab8500_data - > power_off_work ,
ab8500_thermal_power_off ) ;
ab8500_data - > cfg . vcc = THERMAL_VCC ;
ab8500_data - > cfg . r_up = PULL_UP_RESISTOR ;
ab8500_data - > cfg . temp_tbl = ab8500_temp_tbl_a_thermistor ;
ab8500_data - > cfg . tbl_sz = ab8500_temp_tbl_a_size ;
data - > plat_data = ab8500_data ;
/*
* ADC_AUX1 and ADC_AUX2 , connected to external NTC
* BTEMP_BALL and BAT_CTRL , fixed usage
*/
data - > gpadc_addr [ 0 ] = ADC_AUX1 ;
data - > gpadc_addr [ 1 ] = ADC_AUX2 ;
data - > gpadc_addr [ 2 ] = BTEMP_BALL ;
data - > gpadc_addr [ 3 ] = BAT_CTRL ;
data - > monitored_sensors = NUM_MONITORED_SENSORS ;
data - > ops . read_sensor = ab8500_read_sensor ;
data - > ops . irq_handler = ab8500_temp_irq_handler ;
data - > ops . show_name = ab8500_show_name ;
data - > ops . show_label = ab8500_show_label ;
data - > ops . is_visible = NULL ;
return 0 ;
}
EXPORT_SYMBOL ( abx500_hwmon_init ) ;
MODULE_AUTHOR ( " Hongbo Zhang <hongbo.zhang@linaro.org> " ) ;
MODULE_DESCRIPTION ( " AB8500 temperature driver " ) ;
MODULE_LICENSE ( " GPL " ) ;