2015-02-05 19:12:56 +02:00
/*
2018-05-24 12:49:02 +05:30
* Copyright ( c ) 2011 - 2015 , 2017 , The Linux Foundation . All rights reserved .
2015-02-05 19:12:56 +02: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 12:49:02 +05:30
# include <linux/bitops.h>
2015-02-05 19:12:56 +02: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>
2018-07-31 11:59:15 -07:00
# include "thermal_core.h"
2015-02-05 19:12:56 +02:00
# 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 12:49:02 +05:30
# define QPNP_TM_SUBTYPE_GEN1 0x08
# define QPNP_TM_SUBTYPE_GEN2 0x09
2015-02-05 19:12:56 +02:00
2018-05-24 12:49:02 +05:30
# 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 19:12:56 +02:00
2018-07-31 11:59:15 -07:00
# define SHUTDOWN_CTRL1_OVERRIDE_S2 BIT(6)
2018-05-24 12:49:02 +05:30
# define SHUTDOWN_CTRL1_THRESHOLD_MASK GENMASK(1, 0)
2015-02-05 19:12:56 +02:00
2018-07-31 11:59:15 -07:00
# define SHUTDOWN_CTRL1_RATE_25HZ BIT(3)
2018-05-24 12:49:02 +05:30
# define ALARM_CTRL_FORCE_ENABLE BIT(7)
2015-02-05 19:12:56 +02: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
2018-07-31 11:59:15 -07:00
# define THRESH_MAX 3
/* Stage 2 Threshold Min: 125 C */
# define STAGE2_THRESHOLD_MIN 125000
/* Stage 2 Threshold Max: 140 C */
# define STAGE2_THRESHOLD_MAX 140000
2015-02-05 19:12:56 +02:00
/* Temperature in Milli Celsius reported during stage 0 if no ADC is present */
# define DEFAULT_TEMP 37000
struct qpnp_tm_chip {
struct regmap * map ;
2018-07-31 11:59:15 -07:00
struct device * dev ;
2015-02-05 19:12:56 +02:00
struct thermal_zone_device * tz_dev ;
2018-05-24 12:49:02 +05:30
unsigned int subtype ;
2015-02-05 19:12:56 +02:00
long temp ;
unsigned int thresh ;
unsigned int stage ;
unsigned int prev_stage ;
unsigned int base ;
2018-07-31 11:59:15 -07:00
/* protects .thresh, .stage and chip registers */
struct mutex lock ;
bool initialized ;
2015-02-05 19:12:56 +02:00
struct iio_channel * adc ;
} ;
2018-05-24 12:49:02 +05:30
/* 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 19:12:56 +02: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 12:49:02 +05:30
/**
* 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 19:12:56 +02: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 12:49:02 +05:30
unsigned int stage , stage_new , stage_old ;
2015-02-05 19:12:56 +02:00
int ret ;
2018-07-31 11:59:15 -07:00
WARN_ON ( ! mutex_is_locked ( & chip - > lock ) ) ;
2018-05-24 12:49:02 +05:30
ret = qpnp_tm_get_temp_stage ( chip ) ;
2015-02-05 19:12:56 +02:00
if ( ret < 0 )
return ret ;
2018-05-24 12:49:02 +05:30
stage = ret ;
2015-02-05 19:12:56 +02:00
2018-05-24 12:49:02 +05:30
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 19:12:56 +02:00
2018-05-24 12:49:02 +05:30
if ( stage_new > stage_old ) {
2015-02-05 19:12:56 +02:00
/* increasing stage, use lower bound */
2018-05-24 12:49:02 +05:30
chip - > temp = ( stage_new - 1 ) * TEMP_STAGE_STEP +
2015-02-05 19:12:56 +02:00
chip - > thresh * TEMP_THRESH_STEP +
TEMP_STAGE_HYSTERESIS + TEMP_THRESH_MIN ;
2018-05-24 12:49:02 +05:30
} else if ( stage_new < stage_old ) {
2015-02-05 19:12:56 +02:00
/* decreasing stage, use upper bound */
2018-05-24 12:49:02 +05:30
chip - > temp = stage_new * TEMP_STAGE_STEP +
2015-02-05 19:12:56 +02:00
chip - > thresh * TEMP_THRESH_STEP -
TEMP_STAGE_HYSTERESIS + TEMP_THRESH_MIN ;
}
chip - > stage = stage ;
return 0 ;
}
2015-07-24 08:12:54 +02:00
static int qpnp_tm_get_temp ( void * data , int * temp )
2015-02-05 19:12:56 +02:00
{
struct qpnp_tm_chip * chip = data ;
int ret , mili_celsius ;
if ( ! temp )
return - EINVAL ;
2018-07-31 11:59:15 -07:00
if ( ! chip - > initialized ) {
* temp = DEFAULT_TEMP ;
return 0 ;
}
2017-10-19 19:06:00 +02:00
if ( ! chip - > adc ) {
2018-07-31 11:59:15 -07:00
mutex_lock ( & chip - > lock ) ;
2015-02-05 19:12:56 +02:00
ret = qpnp_tm_update_temp_no_adc ( chip ) ;
2018-07-31 11:59:15 -07:00
mutex_unlock ( & chip - > lock ) ;
2015-02-05 19:12:56 +02:00
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 ;
}
2018-07-31 11:59:15 -07:00
static int qpnp_tm_update_critical_trip_temp ( struct qpnp_tm_chip * chip ,
int temp )
{
u8 reg ;
bool disable_s2_shutdown = false ;
WARN_ON ( ! mutex_is_locked ( & chip - > lock ) ) ;
/*
* Default : S2 and S3 shutdown enabled , thresholds at
* 105 C / 125 C / 145 C , monitoring at 25 Hz
*/
reg = SHUTDOWN_CTRL1_RATE_25HZ ;
if ( temp = = THERMAL_TEMP_INVALID | |
temp < STAGE2_THRESHOLD_MIN ) {
chip - > thresh = THRESH_MIN ;
goto skip ;
}
if ( temp < = STAGE2_THRESHOLD_MAX ) {
chip - > thresh = THRESH_MAX -
( ( STAGE2_THRESHOLD_MAX - temp ) /
TEMP_THRESH_STEP ) ;
disable_s2_shutdown = true ;
} else {
chip - > thresh = THRESH_MAX ;
if ( chip - > adc )
disable_s2_shutdown = true ;
else
dev_warn ( chip - > dev ,
" No ADC is configured and critical temperature is above the maximum stage 2 threshold of 140 C! Configuring stage 2 shutdown at 140 C. \n " ) ;
}
skip :
reg | = chip - > thresh ;
if ( disable_s2_shutdown )
reg | = SHUTDOWN_CTRL1_OVERRIDE_S2 ;
return qpnp_tm_write ( chip , QPNP_TM_REG_SHUTDOWN_CTRL1 , reg ) ;
}
static int qpnp_tm_set_trip_temp ( void * data , int trip , int temp )
{
struct qpnp_tm_chip * chip = data ;
const struct thermal_trip * trip_points ;
int ret ;
trip_points = of_thermal_get_trip_points ( chip - > tz_dev ) ;
if ( ! trip_points )
return - EINVAL ;
if ( trip_points [ trip ] . type ! = THERMAL_TRIP_CRITICAL )
return 0 ;
mutex_lock ( & chip - > lock ) ;
ret = qpnp_tm_update_critical_trip_temp ( chip , temp ) ;
mutex_unlock ( & chip - > lock ) ;
return ret ;
}
2015-02-05 19:12:56 +02:00
static const struct thermal_zone_of_device_ops qpnp_tm_sensor_ops = {
. get_temp = qpnp_tm_get_temp ,
2018-07-31 11:59:15 -07:00
. set_trip_temp = qpnp_tm_set_trip_temp ,
2015-02-05 19:12:56 +02:00
} ;
static irqreturn_t qpnp_tm_isr ( int irq , void * data )
{
struct qpnp_tm_chip * chip = data ;
2016-08-26 16:21:16 -07:00
thermal_zone_device_update ( chip - > tz_dev , THERMAL_EVENT_UNSPECIFIED ) ;
2015-02-05 19:12:56 +02:00
return IRQ_HANDLED ;
}
2018-07-31 11:59:15 -07:00
static int qpnp_tm_get_critical_trip_temp ( struct qpnp_tm_chip * chip )
{
int ntrips ;
const struct thermal_trip * trips ;
int i ;
ntrips = of_thermal_get_ntrips ( chip - > tz_dev ) ;
if ( ntrips < = 0 )
return THERMAL_TEMP_INVALID ;
trips = of_thermal_get_trip_points ( chip - > tz_dev ) ;
if ( ! trips )
return THERMAL_TEMP_INVALID ;
for ( i = 0 ; i < ntrips ; i + + ) {
if ( of_thermal_is_trip_valid ( chip - > tz_dev , i ) & &
trips [ i ] . type = = THERMAL_TRIP_CRITICAL )
return trips [ i ] . temperature ;
}
return THERMAL_TEMP_INVALID ;
}
2015-02-05 19:12:56 +02:00
/*
* 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 12:49:02 +05:30
unsigned int stage ;
2015-02-05 19:12:56 +02:00
int ret ;
2018-05-24 12:49:02 +05:30
u8 reg = 0 ;
2018-07-31 11:59:15 -07:00
int crit_temp ;
mutex_lock ( & chip - > lock ) ;
2015-02-05 19:12:56 +02:00
2018-05-24 12:49:02 +05:30
ret = qpnp_tm_read ( chip , QPNP_TM_REG_SHUTDOWN_CTRL1 , & reg ) ;
if ( ret < 0 )
2018-07-31 11:59:15 -07:00
goto out ;
2018-05-24 12:49:02 +05:30
chip - > thresh = reg & SHUTDOWN_CTRL1_THRESHOLD_MASK ;
2015-02-05 19:12:56 +02:00
chip - > temp = DEFAULT_TEMP ;
2018-05-24 12:49:02 +05:30
ret = qpnp_tm_get_temp_stage ( chip ) ;
2015-02-05 19:12:56 +02:00
if ( ret < 0 )
2018-07-31 11:59:15 -07:00
goto out ;
2018-05-24 12:49:02 +05:30
chip - > stage = ret ;
2015-02-05 19:12:56 +02:00
2018-05-24 12:49:02 +05:30
stage = chip - > subtype = = QPNP_TM_SUBTYPE_GEN1
? chip - > stage : alarm_state_map [ chip - > stage ] ;
2015-02-05 19:12:56 +02:00
2018-05-24 12:49:02 +05:30
if ( stage )
2015-02-05 19:12:56 +02:00
chip - > temp = chip - > thresh * TEMP_THRESH_STEP +
2018-05-24 12:49:02 +05:30
( stage - 1 ) * TEMP_STAGE_STEP +
2015-02-05 19:12:56 +02:00
TEMP_THRESH_MIN ;
2018-07-31 11:59:15 -07:00
crit_temp = qpnp_tm_get_critical_trip_temp ( chip ) ;
ret = qpnp_tm_update_critical_trip_temp ( chip , crit_temp ) ;
2015-02-05 19:12:56 +02:00
if ( ret < 0 )
2018-07-31 11:59:15 -07:00
goto out ;
2015-02-05 19:12:56 +02:00
/* 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 ) ;
2018-07-31 11:59:15 -07:00
chip - > initialized = true ;
out :
mutex_unlock ( & chip - > lock ) ;
2015-02-05 19:12:56 +02:00
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-18 16:40:19 -07:00
u32 res ;
2015-02-05 19:12:56 +02: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 ) ;
2018-07-31 11:59:15 -07:00
chip - > dev = & pdev - > dev ;
mutex_init ( & chip - > lock ) ;
2015-02-05 19:12:56 +02:00
chip - > map = dev_get_regmap ( pdev - > dev . parent , NULL ) ;
if ( ! chip - > map )
return - ENXIO ;
2016-10-18 16:40:19 -07:00
ret = of_property_read_u32 ( node , " reg " , & res ) ;
2015-02-05 19:12:56 +02: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 19:06:00 +02: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 19:12:56 +02:00
2016-10-18 16:40:19 -07:00
chip - > base = res ;
2015-02-05 19:12:56 +02: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 19:06:00 +02:00
return ret ;
2015-02-05 19:12:56 +02: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 19:06:00 +02:00
return ret ;
2015-02-05 19:12:56 +02:00
}
2018-05-24 12:49:02 +05:30
if ( type ! = QPNP_TM_TYPE | | ( subtype ! = QPNP_TM_SUBTYPE_GEN1
& & subtype ! = QPNP_TM_SUBTYPE_GEN2 ) ) {
2015-02-05 19:12:56 +02:00
dev_err ( & pdev - > dev , " invalid type 0x%02x or subtype 0x%02x \n " ,
type , subtype ) ;
2017-10-19 19:06:00 +02:00
return - ENODEV ;
2015-02-05 19:12:56 +02:00
}
2018-05-24 12:49:02 +05:30
chip - > subtype = subtype ;
2018-07-31 11:59:15 -07:00
/*
* Register the sensor before initializing the hardware to be able to
* read the trip points . get_temp ( ) returns the default temperature
* before the hardware initialization is completed .
*/
chip - > tz_dev = devm_thermal_zone_of_sensor_register (
& pdev - > dev , 0 , chip , & qpnp_tm_sensor_ops ) ;
if ( IS_ERR ( chip - > tz_dev ) ) {
dev_err ( & pdev - > dev , " failed to register sensor \n " ) ;
return PTR_ERR ( chip - > tz_dev ) ;
}
2015-02-05 19:12:56 +02:00
ret = qpnp_tm_init ( chip ) ;
if ( ret < 0 ) {
dev_err ( & pdev - > dev , " init failed \n " ) ;
2017-10-19 19:06:00 +02:00
return ret ;
2015-02-05 19:12:56 +02:00
}
ret = devm_request_threaded_irq ( & pdev - > dev , irq , NULL , qpnp_tm_isr ,
IRQF_ONESHOT , node - > name , chip ) ;
if ( ret < 0 )
2017-10-19 19:06:00 +02:00
return ret ;
2015-02-05 19:12:56 +02:00
2018-07-31 11:59:15 -07:00
thermal_zone_device_update ( chip - > tz_dev , THERMAL_EVENT_UNSPECIFIED ) ;
2015-02-05 19:12:56 +02: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 " ) ;