2015-02-05 20:12:56 +03:00
/*
2018-05-24 10:19:02 +03:00
* Copyright ( c ) 2011 - 2015 , 2017 , The Linux Foundation . All rights reserved .
2015-02-05 20:12:56 +03:00
*
* This program is free software ; you can redistribute it and / or modify
* it under the terms of the GNU General Public License version 2 and
* only 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 .
*/
2018-05-24 10:19:02 +03:00
# include <linux/bitops.h>
2015-02-05 20:12:56 +03:00
# include <linux/delay.h>
# include <linux/err.h>
# include <linux/iio/consumer.h>
# include <linux/interrupt.h>
# include <linux/module.h>
# include <linux/of.h>
# include <linux/of_device.h>
# include <linux/platform_device.h>
# include <linux/regmap.h>
# include <linux/thermal.h>
# define QPNP_TM_REG_TYPE 0x04
# define QPNP_TM_REG_SUBTYPE 0x05
# define QPNP_TM_REG_STATUS 0x08
# define QPNP_TM_REG_SHUTDOWN_CTRL1 0x40
# define QPNP_TM_REG_ALARM_CTRL 0x46
# define QPNP_TM_TYPE 0x09
2018-05-24 10:19:02 +03:00
# define QPNP_TM_SUBTYPE_GEN1 0x08
# define QPNP_TM_SUBTYPE_GEN2 0x09
2015-02-05 20:12:56 +03:00
2018-05-24 10:19:02 +03:00
# define STATUS_GEN1_STAGE_MASK GENMASK(1, 0)
# define STATUS_GEN2_STATE_MASK GENMASK(6, 4)
# define STATUS_GEN2_STATE_SHIFT 4
2015-02-05 20:12:56 +03:00
2018-05-24 10:19:02 +03:00
# define SHUTDOWN_CTRL1_OVERRIDE_MASK GENMASK(7, 6)
# define SHUTDOWN_CTRL1_THRESHOLD_MASK GENMASK(1, 0)
2015-02-05 20:12:56 +03:00
2018-05-24 10:19:02 +03:00
# define ALARM_CTRL_FORCE_ENABLE BIT(7)
2015-02-05 20:12:56 +03:00
/*
* Trip point values based on threshold control
* 0 = { 105 C , 125 C , 145 C }
* 1 = { 110 C , 130 C , 150 C }
* 2 = { 115 C , 135 C , 155 C }
* 3 = { 120 C , 140 C , 160 C }
*/
# define TEMP_STAGE_STEP 20000 /* Stage step: 20.000 C */
# define TEMP_STAGE_HYSTERESIS 2000
# define TEMP_THRESH_MIN 105000 /* Threshold Min: 105 C */
# define TEMP_THRESH_STEP 5000 /* Threshold step: 5 C */
# define THRESH_MIN 0
/* Temperature in Milli Celsius reported during stage 0 if no ADC is present */
# define DEFAULT_TEMP 37000
struct qpnp_tm_chip {
struct regmap * map ;
struct thermal_zone_device * tz_dev ;
2018-05-24 10:19:02 +03:00
unsigned int subtype ;
2015-02-05 20:12:56 +03:00
long temp ;
unsigned int thresh ;
unsigned int stage ;
unsigned int prev_stage ;
unsigned int base ;
struct iio_channel * adc ;
} ;
2018-05-24 10:19:02 +03:00
/* This array maps from GEN2 alarm state to GEN1 alarm stage */
static const unsigned int alarm_state_map [ 8 ] = { 0 , 1 , 1 , 2 , 2 , 3 , 3 , 3 } ;
2015-02-05 20:12:56 +03:00
static int qpnp_tm_read ( struct qpnp_tm_chip * chip , u16 addr , u8 * data )
{
unsigned int val ;
int ret ;
ret = regmap_read ( chip - > map , chip - > base + addr , & val ) ;
if ( ret < 0 )
return ret ;
* data = val ;
return 0 ;
}
static int qpnp_tm_write ( struct qpnp_tm_chip * chip , u16 addr , u8 data )
{
return regmap_write ( chip - > map , chip - > base + addr , data ) ;
}
2018-05-24 10:19:02 +03:00
/**
* qpnp_tm_get_temp_stage ( ) - return over - temperature stage
* @ chip : Pointer to the qpnp_tm chip
*
* Return : stage ( GEN1 ) or state ( GEN2 ) on success , or errno on failure .
*/
static int qpnp_tm_get_temp_stage ( struct qpnp_tm_chip * chip )
{
int ret ;
u8 reg = 0 ;
ret = qpnp_tm_read ( chip , QPNP_TM_REG_STATUS , & reg ) ;
if ( ret < 0 )
return ret ;
if ( chip - > subtype = = QPNP_TM_SUBTYPE_GEN1 )
ret = reg & STATUS_GEN1_STAGE_MASK ;
else
ret = ( reg & STATUS_GEN2_STATE_MASK ) > > STATUS_GEN2_STATE_SHIFT ;
return ret ;
}
2015-02-05 20:12:56 +03:00
/*
* This function updates the internal temp value based on the
* current thermal stage and threshold as well as the previous stage
*/
static int qpnp_tm_update_temp_no_adc ( struct qpnp_tm_chip * chip )
{
2018-05-24 10:19:02 +03:00
unsigned int stage , stage_new , stage_old ;
2015-02-05 20:12:56 +03:00
int ret ;
2018-05-24 10:19:02 +03:00
ret = qpnp_tm_get_temp_stage ( chip ) ;
2015-02-05 20:12:56 +03:00
if ( ret < 0 )
return ret ;
2018-05-24 10:19:02 +03:00
stage = ret ;
2015-02-05 20:12:56 +03:00
2018-05-24 10:19:02 +03:00
if ( chip - > subtype = = QPNP_TM_SUBTYPE_GEN1 ) {
stage_new = stage ;
stage_old = chip - > stage ;
} else {
stage_new = alarm_state_map [ stage ] ;
stage_old = alarm_state_map [ chip - > stage ] ;
}
2015-02-05 20:12:56 +03:00
2018-05-24 10:19:02 +03:00
if ( stage_new > stage_old ) {
2015-02-05 20:12:56 +03:00
/* increasing stage, use lower bound */
2018-05-24 10:19:02 +03:00
chip - > temp = ( stage_new - 1 ) * TEMP_STAGE_STEP +
2015-02-05 20:12:56 +03:00
chip - > thresh * TEMP_THRESH_STEP +
TEMP_STAGE_HYSTERESIS + TEMP_THRESH_MIN ;
2018-05-24 10:19:02 +03:00
} else if ( stage_new < stage_old ) {
2015-02-05 20:12:56 +03:00
/* decreasing stage, use upper bound */
2018-05-24 10:19:02 +03:00
chip - > temp = stage_new * TEMP_STAGE_STEP +
2015-02-05 20:12:56 +03:00
chip - > thresh * TEMP_THRESH_STEP -
TEMP_STAGE_HYSTERESIS + TEMP_THRESH_MIN ;
}
chip - > stage = stage ;
return 0 ;
}
2015-07-24 09:12:54 +03:00
static int qpnp_tm_get_temp ( void * data , int * temp )
2015-02-05 20:12:56 +03:00
{
struct qpnp_tm_chip * chip = data ;
int ret , mili_celsius ;
if ( ! temp )
return - EINVAL ;
2017-10-19 20:06:00 +03:00
if ( ! chip - > adc ) {
2015-02-05 20:12:56 +03:00
ret = qpnp_tm_update_temp_no_adc ( chip ) ;
if ( ret < 0 )
return ret ;
} else {
ret = iio_read_channel_processed ( chip - > adc , & mili_celsius ) ;
if ( ret < 0 )
return ret ;
chip - > temp = mili_celsius ;
}
* temp = chip - > temp < 0 ? 0 : chip - > temp ;
return 0 ;
}
static const struct thermal_zone_of_device_ops qpnp_tm_sensor_ops = {
. get_temp = qpnp_tm_get_temp ,
} ;
static irqreturn_t qpnp_tm_isr ( int irq , void * data )
{
struct qpnp_tm_chip * chip = data ;
2016-08-27 02:21:16 +03:00
thermal_zone_device_update ( chip - > tz_dev , THERMAL_EVENT_UNSPECIFIED ) ;
2015-02-05 20:12:56 +03:00
return IRQ_HANDLED ;
}
/*
* This function initializes the internal temp value based on only the
* current thermal stage and threshold . Setup threshold control and
* disable shutdown override .
*/
static int qpnp_tm_init ( struct qpnp_tm_chip * chip )
{
2018-05-24 10:19:02 +03:00
unsigned int stage ;
2015-02-05 20:12:56 +03:00
int ret ;
2018-05-24 10:19:02 +03:00
u8 reg = 0 ;
2015-02-05 20:12:56 +03:00
2018-05-24 10:19:02 +03:00
ret = qpnp_tm_read ( chip , QPNP_TM_REG_SHUTDOWN_CTRL1 , & reg ) ;
if ( ret < 0 )
return ret ;
chip - > thresh = reg & SHUTDOWN_CTRL1_THRESHOLD_MASK ;
2015-02-05 20:12:56 +03:00
chip - > temp = DEFAULT_TEMP ;
2018-05-24 10:19:02 +03:00
ret = qpnp_tm_get_temp_stage ( chip ) ;
2015-02-05 20:12:56 +03:00
if ( ret < 0 )
return ret ;
2018-05-24 10:19:02 +03:00
chip - > stage = ret ;
2015-02-05 20:12:56 +03:00
2018-05-24 10:19:02 +03:00
stage = chip - > subtype = = QPNP_TM_SUBTYPE_GEN1
? chip - > stage : alarm_state_map [ chip - > stage ] ;
2015-02-05 20:12:56 +03:00
2018-05-24 10:19:02 +03:00
if ( stage )
2015-02-05 20:12:56 +03:00
chip - > temp = chip - > thresh * TEMP_THRESH_STEP +
2018-05-24 10:19:02 +03:00
( stage - 1 ) * TEMP_STAGE_STEP +
2015-02-05 20:12:56 +03:00
TEMP_THRESH_MIN ;
/*
* Set threshold and disable software override of stage 2 and 3
* shutdowns .
*/
2018-05-24 10:19:02 +03:00
chip - > thresh = THRESH_MIN ;
reg & = ~ ( SHUTDOWN_CTRL1_OVERRIDE_MASK | SHUTDOWN_CTRL1_THRESHOLD_MASK ) ;
reg | = chip - > thresh & SHUTDOWN_CTRL1_THRESHOLD_MASK ;
2015-02-05 20:12:56 +03:00
ret = qpnp_tm_write ( chip , QPNP_TM_REG_SHUTDOWN_CTRL1 , reg ) ;
if ( ret < 0 )
return ret ;
/* Enable the thermal alarm PMIC module in always-on mode. */
reg = ALARM_CTRL_FORCE_ENABLE ;
ret = qpnp_tm_write ( chip , QPNP_TM_REG_ALARM_CTRL , reg ) ;
return ret ;
}
static int qpnp_tm_probe ( struct platform_device * pdev )
{
struct qpnp_tm_chip * chip ;
struct device_node * node ;
u8 type , subtype ;
2016-10-19 02:40:19 +03:00
u32 res ;
2015-02-05 20:12:56 +03:00
int ret , irq ;
node = pdev - > dev . of_node ;
chip = devm_kzalloc ( & pdev - > dev , sizeof ( * chip ) , GFP_KERNEL ) ;
if ( ! chip )
return - ENOMEM ;
dev_set_drvdata ( & pdev - > dev , chip ) ;
chip - > map = dev_get_regmap ( pdev - > dev . parent , NULL ) ;
if ( ! chip - > map )
return - ENXIO ;
2016-10-19 02:40:19 +03:00
ret = of_property_read_u32 ( node , " reg " , & res ) ;
2015-02-05 20:12:56 +03:00
if ( ret < 0 )
return ret ;
irq = platform_get_irq ( pdev , 0 ) ;
if ( irq < 0 )
return irq ;
/* ADC based measurements are optional */
2017-10-19 20:06:00 +03:00
chip - > adc = devm_iio_channel_get ( & pdev - > dev , " thermal " ) ;
if ( IS_ERR ( chip - > adc ) ) {
ret = PTR_ERR ( chip - > adc ) ;
chip - > adc = NULL ;
if ( ret = = - EPROBE_DEFER )
return ret ;
}
2015-02-05 20:12:56 +03:00
2016-10-19 02:40:19 +03:00
chip - > base = res ;
2015-02-05 20:12:56 +03:00
ret = qpnp_tm_read ( chip , QPNP_TM_REG_TYPE , & type ) ;
if ( ret < 0 ) {
dev_err ( & pdev - > dev , " could not read type \n " ) ;
2017-10-19 20:06:00 +03:00
return ret ;
2015-02-05 20:12:56 +03:00
}
ret = qpnp_tm_read ( chip , QPNP_TM_REG_SUBTYPE , & subtype ) ;
if ( ret < 0 ) {
dev_err ( & pdev - > dev , " could not read subtype \n " ) ;
2017-10-19 20:06:00 +03:00
return ret ;
2015-02-05 20:12:56 +03:00
}
2018-05-24 10:19:02 +03:00
if ( type ! = QPNP_TM_TYPE | | ( subtype ! = QPNP_TM_SUBTYPE_GEN1
& & subtype ! = QPNP_TM_SUBTYPE_GEN2 ) ) {
2015-02-05 20:12:56 +03:00
dev_err ( & pdev - > dev , " invalid type 0x%02x or subtype 0x%02x \n " ,
type , subtype ) ;
2017-10-19 20:06:00 +03:00
return - ENODEV ;
2015-02-05 20:12:56 +03:00
}
2018-05-24 10:19:02 +03:00
chip - > subtype = subtype ;
2015-02-05 20:12:56 +03:00
ret = qpnp_tm_init ( chip ) ;
if ( ret < 0 ) {
dev_err ( & pdev - > dev , " init failed \n " ) ;
2017-10-19 20:06:00 +03:00
return ret ;
2015-02-05 20:12:56 +03:00
}
ret = devm_request_threaded_irq ( & pdev - > dev , irq , NULL , qpnp_tm_isr ,
IRQF_ONESHOT , node - > name , chip ) ;
if ( ret < 0 )
2017-10-19 20:06:00 +03:00
return ret ;
2015-02-05 20:12:56 +03:00
2016-03-10 00:08:48 +03:00
chip - > tz_dev = devm_thermal_zone_of_sensor_register ( & pdev - > dev , 0 , chip ,
2015-02-05 20:12:56 +03:00
& qpnp_tm_sensor_ops ) ;
if ( IS_ERR ( chip - > tz_dev ) ) {
dev_err ( & pdev - > dev , " failed to register sensor \n " ) ;
2017-10-19 20:06:00 +03:00
return PTR_ERR ( chip - > tz_dev ) ;
2015-02-05 20:12:56 +03:00
}
return 0 ;
}
static const struct of_device_id qpnp_tm_match_table [ ] = {
{ . compatible = " qcom,spmi-temp-alarm " } ,
{ }
} ;
MODULE_DEVICE_TABLE ( of , qpnp_tm_match_table ) ;
static struct platform_driver qpnp_tm_driver = {
. driver = {
. name = " spmi-temp-alarm " ,
. of_match_table = qpnp_tm_match_table ,
} ,
. probe = qpnp_tm_probe ,
} ;
module_platform_driver ( qpnp_tm_driver ) ;
MODULE_ALIAS ( " platform:spmi-temp-alarm " ) ;
MODULE_DESCRIPTION ( " QPNP PMIC Temperature Alarm driver " ) ;
MODULE_LICENSE ( " GPL v2 " ) ;