2019-11-13 01:30:01 +03:00
// SPDX-License-Identifier: GPL-2.0
/*
* Driver for Texas Instruments TMP512 , TMP513 power monitor chips
*
* TMP513 :
* Thermal / Power Management with Triple Remote and
* Local Temperature Sensor and Current Shunt Monitor
2020-07-03 21:56:57 +03:00
* Datasheet : https : //www.ti.com/lit/gpn/tmp513
2019-11-13 01:30:01 +03:00
*
* TMP512 :
* Thermal / Power Management with Dual Remote
* and Local Temperature Sensor and Current Shunt Monitor
2020-07-03 21:56:57 +03:00
* Datasheet : https : //www.ti.com/lit/gpn/tmp512
2019-11-13 01:30:01 +03:00
*
* Copyright ( C ) 2019 Eric Tremblay < etremblay @ distech - controls . com >
*
* This program is free software ; you can redistribute it and / or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation ; version 2 of the License .
*/
# include <linux/err.h>
# include <linux/hwmon.h>
# include <linux/i2c.h>
# include <linux/init.h>
# include <linux/kernel.h>
# include <linux/module.h>
# include <linux/regmap.h>
# include <linux/slab.h>
# include <linux/util_macros.h>
// Common register definition
# define TMP51X_SHUNT_CONFIG 0x00
# define TMP51X_TEMP_CONFIG 0x01
# define TMP51X_STATUS 0x02
# define TMP51X_SMBUS_ALERT 0x03
# define TMP51X_SHUNT_CURRENT_RESULT 0x04
# define TMP51X_BUS_VOLTAGE_RESULT 0x05
# define TMP51X_POWER_RESULT 0x06
# define TMP51X_BUS_CURRENT_RESULT 0x07
# define TMP51X_LOCAL_TEMP_RESULT 0x08
# define TMP51X_REMOTE_TEMP_RESULT_1 0x09
# define TMP51X_REMOTE_TEMP_RESULT_2 0x0A
# define TMP51X_SHUNT_CURRENT_H_LIMIT 0x0C
# define TMP51X_SHUNT_CURRENT_L_LIMIT 0x0D
# define TMP51X_BUS_VOLTAGE_H_LIMIT 0x0E
# define TMP51X_BUS_VOLTAGE_L_LIMIT 0x0F
# define TMP51X_POWER_LIMIT 0x10
# define TMP51X_LOCAL_TEMP_LIMIT 0x11
# define TMP51X_REMOTE_TEMP_LIMIT_1 0x12
# define TMP51X_REMOTE_TEMP_LIMIT_2 0x13
# define TMP51X_SHUNT_CALIBRATION 0x15
# define TMP51X_N_FACTOR_AND_HYST_1 0x16
# define TMP51X_N_FACTOR_2 0x17
# define TMP51X_MAN_ID_REG 0xFE
# define TMP51X_DEVICE_ID_REG 0xFF
// TMP513 specific register definition
# define TMP513_REMOTE_TEMP_RESULT_3 0x0B
# define TMP513_REMOTE_TEMP_LIMIT_3 0x14
# define TMP513_N_FACTOR_3 0x18
// Common attrs, and NULL
# define TMP51X_MANUFACTURER_ID 0x55FF
# define TMP512_DEVICE_ID 0x22FF
# define TMP513_DEVICE_ID 0x23FF
// Default config
# define TMP51X_SHUNT_CONFIG_DEFAULT 0x399F
# define TMP51X_SHUNT_VALUE_DEFAULT 1000
# define TMP51X_VBUS_RANGE_DEFAULT TMP51X_VBUS_RANGE_32V
# define TMP51X_PGA_DEFAULT 8
# define TMP51X_MAX_REGISTER_ADDR 0xFF
# define TMP512_TEMP_CONFIG_DEFAULT 0xBF80
# define TMP513_TEMP_CONFIG_DEFAULT 0xFF80
// Mask and shift
# define CURRENT_SENSE_VOLTAGE_320_MASK 0x1800
# define CURRENT_SENSE_VOLTAGE_160_MASK 0x1000
# define CURRENT_SENSE_VOLTAGE_80_MASK 0x0800
# define CURRENT_SENSE_VOLTAGE_40_MASK 0
# define TMP51X_BUS_VOLTAGE_MASK 0x2000
# define TMP51X_NFACTOR_MASK 0xFF00
# define TMP51X_HYST_MASK 0x00FF
# define TMP51X_BUS_VOLTAGE_SHIFT 3
# define TMP51X_TEMP_SHIFT 3
// Alarms
# define TMP51X_SHUNT_CURRENT_H_LIMIT_POS 15
# define TMP51X_SHUNT_CURRENT_L_LIMIT_POS 14
# define TMP51X_BUS_VOLTAGE_H_LIMIT_POS 13
# define TMP51X_BUS_VOLTAGE_L_LIMIT_POS 12
# define TMP51X_POWER_LIMIT_POS 11
# define TMP51X_LOCAL_TEMP_LIMIT_POS 10
# define TMP51X_REMOTE_TEMP_LIMIT_1_POS 9
# define TMP51X_REMOTE_TEMP_LIMIT_2_POS 8
# define TMP513_REMOTE_TEMP_LIMIT_3_POS 7
# define TMP51X_VBUS_RANGE_32V 32000000
# define TMP51X_VBUS_RANGE_16V 16000000
// Max and Min value
# define MAX_BUS_VOLTAGE_32_LIMIT 32764
# define MAX_BUS_VOLTAGE_16_LIMIT 16382
// Max possible value is -256 to +256 but datasheet indicated -40 to 125.
# define MAX_TEMP_LIMIT 125000
# define MIN_TEMP_LIMIT -40000
# define MAX_TEMP_HYST 127500
static const u8 TMP51X_TEMP_INPUT [ 4 ] = {
TMP51X_LOCAL_TEMP_RESULT ,
TMP51X_REMOTE_TEMP_RESULT_1 ,
TMP51X_REMOTE_TEMP_RESULT_2 ,
TMP513_REMOTE_TEMP_RESULT_3
} ;
static const u8 TMP51X_TEMP_CRIT [ 4 ] = {
TMP51X_LOCAL_TEMP_LIMIT ,
TMP51X_REMOTE_TEMP_LIMIT_1 ,
TMP51X_REMOTE_TEMP_LIMIT_2 ,
TMP513_REMOTE_TEMP_LIMIT_3
} ;
static const u8 TMP51X_TEMP_CRIT_ALARM [ 4 ] = {
TMP51X_LOCAL_TEMP_LIMIT_POS ,
TMP51X_REMOTE_TEMP_LIMIT_1_POS ,
TMP51X_REMOTE_TEMP_LIMIT_2_POS ,
TMP513_REMOTE_TEMP_LIMIT_3_POS
} ;
static const u8 TMP51X_TEMP_CRIT_HYST [ 4 ] = {
TMP51X_N_FACTOR_AND_HYST_1 ,
TMP51X_N_FACTOR_AND_HYST_1 ,
TMP51X_N_FACTOR_AND_HYST_1 ,
TMP51X_N_FACTOR_AND_HYST_1
} ;
static const u8 TMP51X_CURR_INPUT [ 2 ] = {
TMP51X_SHUNT_CURRENT_RESULT ,
TMP51X_BUS_CURRENT_RESULT
} ;
static struct regmap_config tmp51x_regmap_config = {
. reg_bits = 8 ,
. val_bits = 16 ,
. max_register = TMP51X_MAX_REGISTER_ADDR ,
} ;
enum tmp51x_ids {
tmp512 , tmp513
} ;
struct tmp51x_data {
u16 shunt_config ;
u16 pga_gain ;
u32 vbus_range_uvolt ;
u16 temp_config ;
u32 nfactor [ 3 ] ;
u32 shunt_uohms ;
u32 curr_lsb_ua ;
u32 pwr_lsb_uw ;
enum tmp51x_ids id ;
struct regmap * regmap ;
} ;
// Set the shift based on the gain 8=4, 4=3, 2=2, 1=1
static inline u8 tmp51x_get_pga_shift ( struct tmp51x_data * data )
{
return 5 - ffs ( data - > pga_gain ) ;
}
static int tmp51x_get_value ( struct tmp51x_data * data , u8 reg , u8 pos ,
unsigned int regval , long * val )
{
switch ( reg ) {
case TMP51X_STATUS :
* val = ( regval > > pos ) & 1 ;
break ;
case TMP51X_SHUNT_CURRENT_RESULT :
case TMP51X_SHUNT_CURRENT_H_LIMIT :
case TMP51X_SHUNT_CURRENT_L_LIMIT :
/*
* The valus is read in voltage in the chip but reported as
* current to the user .
* 2 ' s compliment number shifted by one to four depending
* on the pga gain setting . 1l sb = 10u V
*/
* val = sign_extend32 ( regval , 17 - tmp51x_get_pga_shift ( data ) ) ;
* val = DIV_ROUND_CLOSEST ( * val * 10000 , data - > shunt_uohms ) ;
break ;
case TMP51X_BUS_VOLTAGE_RESULT :
case TMP51X_BUS_VOLTAGE_H_LIMIT :
case TMP51X_BUS_VOLTAGE_L_LIMIT :
// 1lsb = 4mV
* val = ( regval > > TMP51X_BUS_VOLTAGE_SHIFT ) * 4 ;
break ;
case TMP51X_POWER_RESULT :
case TMP51X_POWER_LIMIT :
// Power = (current * BusVoltage) / 5000
* val = regval * data - > pwr_lsb_uw ;
break ;
case TMP51X_BUS_CURRENT_RESULT :
// Current = (ShuntVoltage * CalibrationRegister) / 4096
* val = sign_extend32 ( regval , 16 ) * data - > curr_lsb_ua ;
* val = DIV_ROUND_CLOSEST ( * val , 1000 ) ;
break ;
case TMP51X_LOCAL_TEMP_RESULT :
case TMP51X_REMOTE_TEMP_RESULT_1 :
case TMP51X_REMOTE_TEMP_RESULT_2 :
case TMP513_REMOTE_TEMP_RESULT_3 :
case TMP51X_LOCAL_TEMP_LIMIT :
case TMP51X_REMOTE_TEMP_LIMIT_1 :
case TMP51X_REMOTE_TEMP_LIMIT_2 :
case TMP513_REMOTE_TEMP_LIMIT_3 :
// 1lsb = 0.0625 degrees centigrade
* val = sign_extend32 ( regval , 16 ) > > TMP51X_TEMP_SHIFT ;
* val = DIV_ROUND_CLOSEST ( * val * 625 , 10 ) ;
break ;
case TMP51X_N_FACTOR_AND_HYST_1 :
// 1lsb = 0.5 degrees centigrade
* val = ( regval & TMP51X_HYST_MASK ) * 500 ;
break ;
default :
// Programmer goofed
WARN_ON_ONCE ( 1 ) ;
* val = 0 ;
return - EOPNOTSUPP ;
}
return 0 ;
}
static int tmp51x_set_value ( struct tmp51x_data * data , u8 reg , long val )
{
int regval , max_val ;
u32 mask = 0 ;
switch ( reg ) {
case TMP51X_SHUNT_CURRENT_H_LIMIT :
case TMP51X_SHUNT_CURRENT_L_LIMIT :
/*
* The user enter current value and we convert it to
* voltage . 1l sb = 10u V
*/
val = DIV_ROUND_CLOSEST ( val * data - > shunt_uohms , 10000 ) ;
max_val = U16_MAX > > tmp51x_get_pga_shift ( data ) ;
regval = clamp_val ( val , - max_val , max_val ) ;
break ;
case TMP51X_BUS_VOLTAGE_H_LIMIT :
case TMP51X_BUS_VOLTAGE_L_LIMIT :
// 1lsb = 4mV
max_val = ( data - > vbus_range_uvolt = = TMP51X_VBUS_RANGE_32V ) ?
MAX_BUS_VOLTAGE_32_LIMIT : MAX_BUS_VOLTAGE_16_LIMIT ;
val = clamp_val ( DIV_ROUND_CLOSEST ( val , 4 ) , 0 , max_val ) ;
regval = val < < TMP51X_BUS_VOLTAGE_SHIFT ;
break ;
case TMP51X_POWER_LIMIT :
regval = clamp_val ( DIV_ROUND_CLOSEST ( val , data - > pwr_lsb_uw ) , 0 ,
U16_MAX ) ;
break ;
case TMP51X_LOCAL_TEMP_LIMIT :
case TMP51X_REMOTE_TEMP_LIMIT_1 :
case TMP51X_REMOTE_TEMP_LIMIT_2 :
case TMP513_REMOTE_TEMP_LIMIT_3 :
// 1lsb = 0.0625 degrees centigrade
val = clamp_val ( val , MIN_TEMP_LIMIT , MAX_TEMP_LIMIT ) ;
regval = DIV_ROUND_CLOSEST ( val * 10 , 625 ) < < TMP51X_TEMP_SHIFT ;
break ;
case TMP51X_N_FACTOR_AND_HYST_1 :
// 1lsb = 0.5 degrees centigrade
val = clamp_val ( val , 0 , MAX_TEMP_HYST ) ;
regval = DIV_ROUND_CLOSEST ( val , 500 ) ;
mask = TMP51X_HYST_MASK ;
break ;
default :
// Programmer goofed
WARN_ON_ONCE ( 1 ) ;
return - EOPNOTSUPP ;
}
if ( mask = = 0 )
return regmap_write ( data - > regmap , reg , regval ) ;
else
return regmap_update_bits ( data - > regmap , reg , mask , regval ) ;
}
static u8 tmp51x_get_reg ( enum hwmon_sensor_types type , u32 attr , int channel )
{
switch ( type ) {
case hwmon_temp :
switch ( attr ) {
case hwmon_temp_input :
return TMP51X_TEMP_INPUT [ channel ] ;
case hwmon_temp_crit_alarm :
return TMP51X_STATUS ;
case hwmon_temp_crit :
return TMP51X_TEMP_CRIT [ channel ] ;
case hwmon_temp_crit_hyst :
return TMP51X_TEMP_CRIT_HYST [ channel ] ;
}
break ;
case hwmon_in :
switch ( attr ) {
case hwmon_in_input :
return TMP51X_BUS_VOLTAGE_RESULT ;
case hwmon_in_lcrit_alarm :
case hwmon_in_crit_alarm :
return TMP51X_STATUS ;
case hwmon_in_lcrit :
return TMP51X_BUS_VOLTAGE_L_LIMIT ;
case hwmon_in_crit :
return TMP51X_BUS_VOLTAGE_H_LIMIT ;
}
break ;
case hwmon_curr :
switch ( attr ) {
case hwmon_curr_input :
return TMP51X_CURR_INPUT [ channel ] ;
case hwmon_curr_lcrit_alarm :
case hwmon_curr_crit_alarm :
return TMP51X_STATUS ;
case hwmon_curr_lcrit :
return TMP51X_SHUNT_CURRENT_L_LIMIT ;
case hwmon_curr_crit :
return TMP51X_SHUNT_CURRENT_H_LIMIT ;
}
break ;
case hwmon_power :
switch ( attr ) {
case hwmon_power_input :
return TMP51X_POWER_RESULT ;
case hwmon_power_crit_alarm :
return TMP51X_STATUS ;
case hwmon_power_crit :
return TMP51X_POWER_LIMIT ;
}
break ;
default :
break ;
}
return 0 ;
}
static u8 tmp51x_get_status_pos ( enum hwmon_sensor_types type , u32 attr ,
int channel )
{
switch ( type ) {
case hwmon_temp :
switch ( attr ) {
case hwmon_temp_crit_alarm :
return TMP51X_TEMP_CRIT_ALARM [ channel ] ;
}
break ;
case hwmon_in :
switch ( attr ) {
case hwmon_in_lcrit_alarm :
return TMP51X_BUS_VOLTAGE_L_LIMIT_POS ;
case hwmon_in_crit_alarm :
return TMP51X_BUS_VOLTAGE_H_LIMIT_POS ;
}
break ;
case hwmon_curr :
switch ( attr ) {
case hwmon_curr_lcrit_alarm :
return TMP51X_SHUNT_CURRENT_L_LIMIT_POS ;
case hwmon_curr_crit_alarm :
return TMP51X_SHUNT_CURRENT_H_LIMIT_POS ;
}
break ;
case hwmon_power :
switch ( attr ) {
case hwmon_power_crit_alarm :
return TMP51X_POWER_LIMIT_POS ;
}
break ;
default :
break ;
}
return 0 ;
}
static int tmp51x_read ( struct device * dev , enum hwmon_sensor_types type ,
u32 attr , int channel , long * val )
{
struct tmp51x_data * data = dev_get_drvdata ( dev ) ;
int ret ;
u32 regval ;
u8 pos = 0 , reg = 0 ;
reg = tmp51x_get_reg ( type , attr , channel ) ;
if ( reg = = 0 )
return - EOPNOTSUPP ;
if ( reg = = TMP51X_STATUS )
pos = tmp51x_get_status_pos ( type , attr , channel ) ;
ret = regmap_read ( data - > regmap , reg , & regval ) ;
if ( ret < 0 )
return ret ;
return tmp51x_get_value ( data , reg , pos , regval , val ) ;
}
static int tmp51x_write ( struct device * dev , enum hwmon_sensor_types type ,
u32 attr , int channel , long val )
{
u8 reg = 0 ;
reg = tmp51x_get_reg ( type , attr , channel ) ;
if ( reg = = 0 )
return - EOPNOTSUPP ;
return tmp51x_set_value ( dev_get_drvdata ( dev ) , reg , val ) ;
}
static umode_t tmp51x_is_visible ( const void * _data ,
enum hwmon_sensor_types type , u32 attr ,
int channel )
{
const struct tmp51x_data * data = _data ;
switch ( type ) {
case hwmon_temp :
if ( data - > id = = tmp512 & & channel = = 4 )
return 0 ;
switch ( attr ) {
case hwmon_temp_input :
case hwmon_temp_crit_alarm :
return 0444 ;
case hwmon_temp_crit :
return 0644 ;
case hwmon_temp_crit_hyst :
if ( channel = = 0 )
return 0644 ;
return 0444 ;
}
break ;
case hwmon_in :
switch ( attr ) {
case hwmon_in_input :
case hwmon_in_lcrit_alarm :
case hwmon_in_crit_alarm :
return 0444 ;
case hwmon_in_lcrit :
case hwmon_in_crit :
return 0644 ;
}
break ;
case hwmon_curr :
if ( ! data - > shunt_uohms )
return 0 ;
switch ( attr ) {
case hwmon_curr_input :
case hwmon_curr_lcrit_alarm :
case hwmon_curr_crit_alarm :
return 0444 ;
case hwmon_curr_lcrit :
case hwmon_curr_crit :
return 0644 ;
}
break ;
case hwmon_power :
if ( ! data - > shunt_uohms )
return 0 ;
switch ( attr ) {
case hwmon_power_input :
case hwmon_power_crit_alarm :
return 0444 ;
case hwmon_power_crit :
return 0644 ;
}
break ;
default :
break ;
}
return 0 ;
}
static const struct hwmon_channel_info * tmp51x_info [ ] = {
HWMON_CHANNEL_INFO ( temp ,
HWMON_T_INPUT | HWMON_T_CRIT | HWMON_T_CRIT_ALARM |
HWMON_T_CRIT_HYST ,
HWMON_T_INPUT | HWMON_T_CRIT | HWMON_T_CRIT_ALARM |
HWMON_T_CRIT_HYST ,
HWMON_T_INPUT | HWMON_T_CRIT | HWMON_T_CRIT_ALARM |
HWMON_T_CRIT_HYST ,
HWMON_T_INPUT | HWMON_T_CRIT | HWMON_T_CRIT_ALARM |
HWMON_T_CRIT_HYST ) ,
HWMON_CHANNEL_INFO ( in ,
HWMON_I_INPUT | HWMON_I_LCRIT | HWMON_I_LCRIT_ALARM |
HWMON_I_CRIT | HWMON_I_CRIT_ALARM ) ,
HWMON_CHANNEL_INFO ( curr ,
HWMON_C_INPUT | HWMON_C_LCRIT | HWMON_C_LCRIT_ALARM |
HWMON_C_CRIT | HWMON_C_CRIT_ALARM ,
HWMON_C_INPUT ) ,
HWMON_CHANNEL_INFO ( power ,
HWMON_P_INPUT | HWMON_P_CRIT | HWMON_P_CRIT_ALARM ) ,
NULL
} ;
static const struct hwmon_ops tmp51x_hwmon_ops = {
. is_visible = tmp51x_is_visible ,
. read = tmp51x_read ,
. write = tmp51x_write ,
} ;
static const struct hwmon_chip_info tmp51x_chip_info = {
. ops = & tmp51x_hwmon_ops ,
. info = tmp51x_info ,
} ;
/*
* Calibrate the tmp51x following the datasheet method
*/
static int tmp51x_calibrate ( struct tmp51x_data * data )
{
int vshunt_max = data - > pga_gain * 40 ;
u64 max_curr_ma ;
u32 div ;
/*
* If shunt_uohms is equal to 0 , the calibration should be set to 0.
* The consequence will be that the current and power measurement engine
* of the sensor will not work . Temperature and voltage sensing will
* continue to work .
*/
if ( data - > shunt_uohms = = 0 )
return regmap_write ( data - > regmap , TMP51X_SHUNT_CALIBRATION , 0 ) ;
max_curr_ma = DIV_ROUND_CLOSEST_ULL ( vshunt_max * 1000 * 1000 ,
data - > shunt_uohms ) ;
/*
* Calculate the minimal bit resolution for the current and the power .
* Those values will be used during register interpretation .
*/
data - > curr_lsb_ua = DIV_ROUND_CLOSEST_ULL ( max_curr_ma * 1000 , 32767 ) ;
data - > pwr_lsb_uw = 20 * data - > curr_lsb_ua ;
div = DIV_ROUND_CLOSEST_ULL ( data - > curr_lsb_ua * data - > shunt_uohms ,
1000 * 1000 ) ;
return regmap_write ( data - > regmap , TMP51X_SHUNT_CALIBRATION ,
DIV_ROUND_CLOSEST ( 40960 , div ) ) ;
}
/*
* Initialize the configuration and calibration registers .
*/
static int tmp51x_init ( struct tmp51x_data * data )
{
unsigned int regval ;
int ret = regmap_write ( data - > regmap , TMP51X_SHUNT_CONFIG ,
data - > shunt_config ) ;
if ( ret < 0 )
return ret ;
ret = regmap_write ( data - > regmap , TMP51X_TEMP_CONFIG , data - > temp_config ) ;
if ( ret < 0 )
return ret ;
// nFactor configuration
ret = regmap_update_bits ( data - > regmap , TMP51X_N_FACTOR_AND_HYST_1 ,
TMP51X_NFACTOR_MASK , data - > nfactor [ 0 ] < < 8 ) ;
if ( ret < 0 )
return ret ;
ret = regmap_write ( data - > regmap , TMP51X_N_FACTOR_2 ,
data - > nfactor [ 1 ] < < 8 ) ;
if ( ret < 0 )
return ret ;
if ( data - > id = = tmp513 ) {
ret = regmap_write ( data - > regmap , TMP513_N_FACTOR_3 ,
data - > nfactor [ 2 ] < < 8 ) ;
if ( ret < 0 )
return ret ;
}
ret = tmp51x_calibrate ( data ) ;
if ( ret < 0 )
return ret ;
// Read the status register before using as the datasheet propose
return regmap_read ( data - > regmap , TMP51X_STATUS , & regval ) ;
}
static const struct i2c_device_id tmp51x_id [ ] = {
{ " tmp512 " , tmp512 } ,
{ " tmp513 " , tmp513 } ,
{ }
} ;
MODULE_DEVICE_TABLE ( i2c , tmp51x_id ) ;
static const struct of_device_id tmp51x_of_match [ ] = {
{
. compatible = " ti,tmp512 " ,
. data = ( void * ) tmp512
} ,
{
. compatible = " ti,tmp513 " ,
. data = ( void * ) tmp513
} ,
{ } ,
} ;
MODULE_DEVICE_TABLE ( of , tmp51x_of_match ) ;
static int tmp51x_vbus_range_to_reg ( struct device * dev ,
struct tmp51x_data * data )
{
if ( data - > vbus_range_uvolt = = TMP51X_VBUS_RANGE_32V ) {
data - > shunt_config | = TMP51X_BUS_VOLTAGE_MASK ;
} else if ( data - > vbus_range_uvolt = = TMP51X_VBUS_RANGE_16V ) {
data - > shunt_config & = ~ TMP51X_BUS_VOLTAGE_MASK ;
} else {
dev_err ( dev , " ti,bus-range-microvolt is invalid: %u \n " ,
data - > vbus_range_uvolt ) ;
return - EINVAL ;
}
return 0 ;
}
static int tmp51x_pga_gain_to_reg ( struct device * dev , struct tmp51x_data * data )
{
if ( data - > pga_gain = = 8 ) {
data - > shunt_config | = CURRENT_SENSE_VOLTAGE_320_MASK ;
} else if ( data - > pga_gain = = 4 ) {
data - > shunt_config | = CURRENT_SENSE_VOLTAGE_160_MASK ;
} else if ( data - > pga_gain = = 2 ) {
data - > shunt_config | = CURRENT_SENSE_VOLTAGE_80_MASK ;
} else if ( data - > pga_gain = = 1 ) {
data - > shunt_config | = CURRENT_SENSE_VOLTAGE_40_MASK ;
} else {
dev_err ( dev , " ti,pga-gain is invalid: %u \n " , data - > pga_gain ) ;
return - EINVAL ;
}
return 0 ;
}
static int tmp51x_read_properties ( struct device * dev , struct tmp51x_data * data )
{
int ret ;
u32 nfactor [ 3 ] ;
u32 val ;
ret = device_property_read_u32 ( dev , " shunt-resistor-micro-ohms " , & val ) ;
data - > shunt_uohms = ( ret > = 0 ) ? val : TMP51X_SHUNT_VALUE_DEFAULT ;
ret = device_property_read_u32 ( dev , " ti,bus-range-microvolt " , & val ) ;
data - > vbus_range_uvolt = ( ret > = 0 ) ? val : TMP51X_VBUS_RANGE_DEFAULT ;
ret = tmp51x_vbus_range_to_reg ( dev , data ) ;
if ( ret < 0 )
return ret ;
ret = device_property_read_u32 ( dev , " ti,pga-gain " , & val ) ;
data - > pga_gain = ( ret > = 0 ) ? val : TMP51X_PGA_DEFAULT ;
ret = tmp51x_pga_gain_to_reg ( dev , data ) ;
if ( ret < 0 )
return ret ;
ret = device_property_read_u32_array ( dev , " ti,nfactor " , nfactor ,
( data - > id = = tmp513 ) ? 3 : 2 ) ;
if ( ret > = 0 )
memcpy ( data - > nfactor , nfactor , ( data - > id = = tmp513 ) ? 3 : 2 ) ;
// Check if shunt value is compatible with pga-gain
if ( data - > shunt_uohms > data - > pga_gain * 40 * 1000 * 1000 ) {
dev_err ( dev , " shunt-resistor: %u too big for pga_gain: %u \n " ,
data - > shunt_uohms , data - > pga_gain ) ;
return - EINVAL ;
}
return 0 ;
}
static void tmp51x_use_default ( struct tmp51x_data * data )
{
data - > vbus_range_uvolt = TMP51X_VBUS_RANGE_DEFAULT ;
data - > pga_gain = TMP51X_PGA_DEFAULT ;
data - > shunt_uohms = TMP51X_SHUNT_VALUE_DEFAULT ;
}
static int tmp51x_configure ( struct device * dev , struct tmp51x_data * data )
{
data - > shunt_config = TMP51X_SHUNT_CONFIG_DEFAULT ;
data - > temp_config = ( data - > id = = tmp513 ) ?
TMP513_TEMP_CONFIG_DEFAULT : TMP512_TEMP_CONFIG_DEFAULT ;
if ( dev - > of_node )
return tmp51x_read_properties ( dev , data ) ;
tmp51x_use_default ( data ) ;
return 0 ;
}
static int tmp51x_probe ( struct i2c_client * client ,
const struct i2c_device_id * id )
{
struct device * dev = & client - > dev ;
struct tmp51x_data * data ;
struct device * hwmon_dev ;
int ret ;
data = devm_kzalloc ( dev , sizeof ( * data ) , GFP_KERNEL ) ;
if ( ! data )
return - ENOMEM ;
if ( client - > dev . of_node )
data - > id = ( enum tmp51x_ids ) device_get_match_data ( & client - > dev ) ;
else
data - > id = id - > driver_data ;
ret = tmp51x_configure ( dev , data ) ;
if ( ret < 0 ) {
dev_err ( dev , " error configuring the device: %d \n " , ret ) ;
return ret ;
}
data - > regmap = devm_regmap_init_i2c ( client , & tmp51x_regmap_config ) ;
if ( IS_ERR ( data - > regmap ) ) {
dev_err ( dev , " failed to allocate register map \n " ) ;
return PTR_ERR ( data - > regmap ) ;
}
ret = tmp51x_init ( data ) ;
if ( ret < 0 ) {
dev_err ( dev , " error configuring the device: %d \n " , ret ) ;
return - ENODEV ;
}
hwmon_dev = devm_hwmon_device_register_with_info ( dev , client - > name ,
data ,
& tmp51x_chip_info ,
NULL ) ;
if ( IS_ERR ( hwmon_dev ) )
return PTR_ERR ( hwmon_dev ) ;
dev_dbg ( dev , " power monitor %s \n " , id - > name ) ;
return 0 ;
}
static struct i2c_driver tmp51x_driver = {
. driver = {
. name = " tmp51x " ,
. of_match_table = of_match_ptr ( tmp51x_of_match ) ,
} ,
. probe = tmp51x_probe ,
. id_table = tmp51x_id ,
} ;
module_i2c_driver ( tmp51x_driver ) ;
MODULE_AUTHOR ( " Eric Tremblay <etremblay@distechcontrols.com> " ) ;
MODULE_DESCRIPTION ( " tmp51x driver " ) ;
MODULE_LICENSE ( " GPL " ) ;