2018-09-12 15:22:47 +05:30
// SPDX-License-Identifier: GPL-2.0
2016-05-05 14:21:39 +05:30
/*
* Copyright ( c ) 2015 , The Linux Foundation . All rights reserved .
*/
# include <linux/err.h>
# include <linux/io.h>
# include <linux/nvmem-consumer.h>
# include <linux/of_address.h>
2018-07-18 12:13:09 +05:30
# include <linux/of_platform.h>
2016-05-05 14:21:39 +05:30
# include <linux/platform_device.h>
# include <linux/regmap.h>
# include "tsens.h"
char * qfprom_read ( struct device * dev , const char * cname )
{
struct nvmem_cell * cell ;
ssize_t data ;
char * ret ;
cell = nvmem_cell_get ( dev , cname ) ;
if ( IS_ERR ( cell ) )
return ERR_CAST ( cell ) ;
ret = nvmem_cell_read ( cell , & data ) ;
nvmem_cell_put ( cell ) ;
return ret ;
}
/*
* Use this function on devices where slope and offset calculations
* depend on calibration data read from qfprom . On others the slope
* and offset values are derived from tz - > tzp - > slope and tz - > tzp - > offset
* resp .
*/
2019-03-20 18:47:44 +05:30
void compute_intercept_slope ( struct tsens_priv * priv , u32 * p1 ,
2016-05-05 14:21:39 +05:30
u32 * p2 , u32 mode )
{
int i ;
int num , den ;
2019-03-20 18:47:44 +05:30
for ( i = 0 ; i < priv - > num_sensors ; i + + ) {
dev_dbg ( priv - > dev ,
2016-05-05 14:21:39 +05:30
" sensor%d - data_point1:%#x data_point2:%#x \n " ,
i , p1 [ i ] , p2 [ i ] ) ;
2019-03-20 18:47:44 +05:30
priv - > sensor [ i ] . slope = SLOPE_DEFAULT ;
2016-05-05 14:21:39 +05:30
if ( mode = = TWO_PT_CALIB ) {
/*
* slope ( m ) = adc_code2 - adc_code1 ( y2 - y1 ) /
* temp_120_degc - temp_30_degc ( x2 - x1 )
*/
num = p2 [ i ] - p1 [ i ] ;
num * = SLOPE_FACTOR ;
den = CAL_DEGC_PT2 - CAL_DEGC_PT1 ;
2019-03-20 18:47:44 +05:30
priv - > sensor [ i ] . slope = num / den ;
2016-05-05 14:21:39 +05:30
}
2019-03-20 18:47:44 +05:30
priv - > sensor [ i ] . offset = ( p1 [ i ] * SLOPE_FACTOR ) -
2016-05-05 14:21:39 +05:30
( CAL_DEGC_PT1 *
2019-03-20 18:47:44 +05:30
priv - > sensor [ i ] . slope ) ;
dev_dbg ( priv - > dev , " offset:%d \n " , priv - > sensor [ i ] . offset ) ;
2016-05-05 14:21:39 +05:30
}
}
static inline int code_to_degc ( u32 adc_code , const struct tsens_sensor * s )
{
int degc , num , den ;
num = ( adc_code * SLOPE_FACTOR ) - s - > offset ;
den = s - > slope ;
if ( num > 0 )
degc = num + ( den / 2 ) ;
else if ( num < 0 )
degc = num - ( den / 2 ) ;
else
degc = num ;
degc / = den ;
return degc ;
}
2019-03-20 18:47:57 +05:30
int get_temp_tsens_valid ( struct tsens_priv * priv , int i , int * temp )
{
struct tsens_sensor * s = & priv - > sensor [ i ] ;
u32 temp_idx = LAST_TEMP_0 + s - > hw_id ;
u32 valid_idx = VALID_0 + s - > hw_id ;
u32 last_temp = 0 , valid , mask ;
int ret ;
ret = regmap_field_read ( priv - > rf [ valid_idx ] , & valid ) ;
if ( ret )
return ret ;
while ( ! valid ) {
/* Valid bit is 0 for 6 AHB clock cycles.
* At 19.2 MHz , 1 AHB clock is ~ 60 ns .
* We should enter this loop very , very rarely .
*/
ndelay ( 400 ) ;
ret = regmap_field_read ( priv - > rf [ valid_idx ] , & valid ) ;
if ( ret )
return ret ;
}
/* Valid bit is set, OK to read the temperature */
ret = regmap_field_read ( priv - > rf [ temp_idx ] , & last_temp ) ;
if ( ret )
return ret ;
2019-03-20 18:47:58 +05:30
if ( priv - > feat - > adc ) {
/* Convert temperature from ADC code to milliCelsius */
* temp = code_to_degc ( last_temp , s ) * 1000 ;
} else {
mask = GENMASK ( priv - > fields [ LAST_TEMP_0 ] . msb ,
priv - > fields [ LAST_TEMP_0 ] . lsb ) ;
/* Convert temperature from deciCelsius to milliCelsius */
* temp = sign_extend32 ( last_temp , fls ( mask ) - 1 ) * 100 ;
}
2019-03-20 18:47:57 +05:30
return 0 ;
}
2019-03-20 18:47:50 +05:30
int get_temp_common ( struct tsens_priv * priv , int i , int * temp )
2016-05-05 14:21:39 +05:30
{
2019-03-20 18:47:50 +05:30
struct tsens_sensor * s = & priv - > sensor [ i ] ;
2016-05-05 14:21:39 +05:30
int last_temp = 0 , ret ;
2019-03-20 18:47:50 +05:30
ret = regmap_field_read ( priv - > rf [ LAST_TEMP_0 + s - > hw_id ] , & last_temp ) ;
2016-05-05 14:21:39 +05:30
if ( ret )
return ret ;
* temp = code_to_degc ( last_temp , s ) * 1000 ;
return 0 ;
}
static const struct regmap_config tsens_config = {
2018-11-15 17:43:30 +00:00
. name = " tm " ,
. reg_bits = 32 ,
. val_bits = 32 ,
. reg_stride = 4 ,
} ;
static const struct regmap_config tsens_srot_config = {
. name = " srot " ,
2016-05-05 14:21:39 +05:30
. reg_bits = 32 ,
. val_bits = 32 ,
. reg_stride = 4 ,
} ;
2019-03-20 18:47:44 +05:30
int __init init_common ( struct tsens_priv * priv )
2016-05-05 14:21:39 +05:30
{
2018-09-12 15:22:50 +05:30
void __iomem * tm_base , * srot_base ;
2019-03-20 18:47:51 +05:30
struct device * dev = priv - > dev ;
2018-07-26 16:03:09 +05:30
struct resource * res ;
2019-03-20 18:47:50 +05:30
u32 enabled ;
int ret , i , j ;
2019-03-20 18:47:44 +05:30
struct platform_device * op = of_find_device_by_node ( priv - > dev - > of_node ) ;
2016-05-05 14:21:39 +05:30
2018-07-18 12:13:09 +05:30
if ( ! op )
return - EINVAL ;
2016-05-05 14:21:39 +05:30
2018-07-18 12:13:09 +05:30
if ( op - > num_resources > 1 ) {
2018-09-12 15:22:50 +05:30
/* DT with separate SROT and TM address space */
2019-03-20 18:47:44 +05:30
priv - > tm_offset = 0 ;
2018-09-12 15:22:50 +05:30
res = platform_get_resource ( op , IORESOURCE_MEM , 1 ) ;
srot_base = devm_ioremap_resource ( & op - > dev , res ) ;
2019-02-13 22:53:29 +08:00
if ( IS_ERR ( srot_base ) ) {
ret = PTR_ERR ( srot_base ) ;
goto err_put_device ;
}
2018-09-12 15:22:50 +05:30
2019-03-20 18:47:51 +05:30
priv - > srot_map = devm_regmap_init_mmio ( dev , srot_base ,
2018-11-15 17:43:30 +00:00
& tsens_srot_config ) ;
2019-03-20 18:47:44 +05:30
if ( IS_ERR ( priv - > srot_map ) ) {
ret = PTR_ERR ( priv - > srot_map ) ;
2019-02-13 22:53:29 +08:00
goto err_put_device ;
}
2018-07-18 12:13:09 +05:30
} else {
/* old DTs where SROT and TM were in a contiguous 2K block */
2019-03-20 18:47:44 +05:30
priv - > tm_offset = 0x1000 ;
2018-07-18 12:13:09 +05:30
}
2018-07-26 16:03:09 +05:30
res = platform_get_resource ( op , IORESOURCE_MEM , 0 ) ;
2018-09-12 15:22:49 +05:30
tm_base = devm_ioremap_resource ( & op - > dev , res ) ;
2019-02-13 22:53:29 +08:00
if ( IS_ERR ( tm_base ) ) {
ret = PTR_ERR ( tm_base ) ;
goto err_put_device ;
}
2018-07-26 16:03:09 +05:30
2019-03-20 18:47:51 +05:30
priv - > tm_map = devm_regmap_init_mmio ( dev , tm_base , & tsens_config ) ;
2019-03-20 18:47:44 +05:30
if ( IS_ERR ( priv - > tm_map ) ) {
ret = PTR_ERR ( priv - > tm_map ) ;
2019-02-13 22:53:29 +08:00
goto err_put_device ;
}
2016-05-05 14:21:39 +05:30
2019-03-20 18:47:51 +05:30
priv - > rf [ TSENS_EN ] = devm_regmap_field_alloc ( dev , priv - > srot_map ,
2019-03-20 18:47:50 +05:30
priv - > fields [ TSENS_EN ] ) ;
if ( IS_ERR ( priv - > rf [ TSENS_EN ] ) ) {
ret = PTR_ERR ( priv - > rf [ TSENS_EN ] ) ;
goto err_put_device ;
}
ret = regmap_field_read ( priv - > rf [ TSENS_EN ] , & enabled ) ;
if ( ret )
goto err_put_device ;
if ( ! enabled ) {
2019-03-20 18:47:51 +05:30
dev_err ( dev , " tsens device is not enabled \n " ) ;
2019-03-20 18:47:50 +05:30
ret = - ENODEV ;
goto err_put_device ;
}
2019-03-20 18:47:51 +05:30
priv - > rf [ SENSOR_EN ] = devm_regmap_field_alloc ( dev , priv - > srot_map ,
2019-03-20 18:47:50 +05:30
priv - > fields [ SENSOR_EN ] ) ;
if ( IS_ERR ( priv - > rf [ SENSOR_EN ] ) ) {
ret = PTR_ERR ( priv - > rf [ SENSOR_EN ] ) ;
goto err_put_device ;
}
/* now alloc regmap_fields in tm_map */
2019-03-20 18:47:55 +05:30
for ( i = 0 , j = LAST_TEMP_0 ; i < priv - > feat - > max_sensors ; i + + , j + + ) {
2019-03-20 18:47:51 +05:30
priv - > rf [ j ] = devm_regmap_field_alloc ( dev , priv - > tm_map ,
2019-03-20 18:47:50 +05:30
priv - > fields [ j ] ) ;
if ( IS_ERR ( priv - > rf [ j ] ) ) {
ret = PTR_ERR ( priv - > rf [ j ] ) ;
2019-02-13 22:53:29 +08:00
goto err_put_device ;
2019-03-20 18:47:50 +05:30
}
}
2019-03-20 18:47:55 +05:30
for ( i = 0 , j = VALID_0 ; i < priv - > feat - > max_sensors ; i + + , j + + ) {
2019-03-20 18:47:51 +05:30
priv - > rf [ j ] = devm_regmap_field_alloc ( dev , priv - > tm_map ,
2019-03-20 18:47:50 +05:30
priv - > fields [ j ] ) ;
if ( IS_ERR ( priv - > rf [ j ] ) ) {
ret = PTR_ERR ( priv - > rf [ j ] ) ;
2019-02-13 22:53:29 +08:00
goto err_put_device ;
2018-09-12 15:22:53 +05:30
}
}
2016-05-05 14:21:39 +05:30
return 0 ;
2019-02-13 22:53:29 +08:00
err_put_device :
put_device ( & op - > dev ) ;
return ret ;
2016-05-05 14:21:39 +05:30
}