2019-11-23 11:11:26 -08:00
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Driver for MAX31730 3 - Channel Remote Temperature Sensor
*
* Copyright ( c ) 2019 Guenter Roeck < linux @ roeck - us . net >
*/
# include <linux/bits.h>
# include <linux/err.h>
# include <linux/i2c.h>
# include <linux/init.h>
# include <linux/hwmon.h>
# include <linux/module.h>
# include <linux/of_device.h>
# include <linux/of.h>
# include <linux/slab.h>
/* Addresses scanned */
static const unsigned short normal_i2c [ ] = { 0x1c , 0x1d , 0x1e , 0x1f , 0x4c ,
0x4d , 0x4e , 0x4f , I2C_CLIENT_END } ;
/* The MAX31730 registers */
# define MAX31730_REG_TEMP 0x00
# define MAX31730_REG_CONF 0x13
# define MAX31730_STOP BIT(7)
# define MAX31730_EXTRANGE BIT(1)
# define MAX31730_REG_TEMP_OFFSET 0x16
# define MAX31730_TEMP_OFFSET_BASELINE 0x77
# define MAX31730_REG_OFFSET_ENABLE 0x17
# define MAX31730_REG_TEMP_MAX 0x20
# define MAX31730_REG_TEMP_MIN 0x30
# define MAX31730_REG_STATUS_HIGH 0x32
# define MAX31730_REG_STATUS_LOW 0x33
# define MAX31730_REG_CHANNEL_ENABLE 0x35
# define MAX31730_REG_TEMP_FAULT 0x36
# define MAX31730_REG_MFG_ID 0x50
# define MAX31730_MFG_ID 0x4d
# define MAX31730_REG_MFG_REV 0x51
# define MAX31730_MFG_REV 0x01
# define MAX31730_TEMP_MIN (-128000)
# define MAX31730_TEMP_MAX 127937
/* Each client has this additional data */
struct max31730_data {
struct i2c_client * client ;
u8 orig_conf ;
u8 current_conf ;
u8 offset_enable ;
u8 channel_enable ;
} ;
/*-----------------------------------------------------------------------*/
static inline long max31730_reg_to_mc ( s16 temp )
{
return DIV_ROUND_CLOSEST ( ( temp > > 4 ) * 1000 , 16 ) ;
}
static int max31730_write_config ( struct max31730_data * data , u8 set_mask ,
u8 clr_mask )
{
u8 value ;
clr_mask | = MAX31730_EXTRANGE ;
value = data - > current_conf & ~ clr_mask ;
value | = set_mask ;
if ( data - > current_conf ! = value ) {
s32 err ;
err = i2c_smbus_write_byte_data ( data - > client , MAX31730_REG_CONF ,
value ) ;
if ( err )
return err ;
data - > current_conf = value ;
}
return 0 ;
}
static int max31730_set_enable ( struct i2c_client * client , int reg ,
u8 * confdata , int channel , bool enable )
{
u8 regval = * confdata ;
int err ;
if ( enable )
regval | = BIT ( channel ) ;
else
regval & = ~ BIT ( channel ) ;
if ( regval ! = * confdata ) {
err = i2c_smbus_write_byte_data ( client , reg , regval ) ;
if ( err )
return err ;
* confdata = regval ;
}
return 0 ;
}
static int max31730_set_offset_enable ( struct max31730_data * data , int channel ,
bool enable )
{
return max31730_set_enable ( data - > client , MAX31730_REG_OFFSET_ENABLE ,
& data - > offset_enable , channel , enable ) ;
}
static int max31730_set_channel_enable ( struct max31730_data * data , int channel ,
bool enable )
{
return max31730_set_enable ( data - > client , MAX31730_REG_CHANNEL_ENABLE ,
& data - > channel_enable , channel , enable ) ;
}
static int max31730_read ( struct device * dev , enum hwmon_sensor_types type ,
u32 attr , int channel , long * val )
{
struct max31730_data * data = dev_get_drvdata ( dev ) ;
int regval , reg , offset ;
if ( type ! = hwmon_temp )
return - EINVAL ;
switch ( attr ) {
case hwmon_temp_input :
if ( ! ( data - > channel_enable & BIT ( channel ) ) )
return - ENODATA ;
reg = MAX31730_REG_TEMP + ( channel * 2 ) ;
break ;
case hwmon_temp_max :
reg = MAX31730_REG_TEMP_MAX + ( channel * 2 ) ;
break ;
case hwmon_temp_min :
reg = MAX31730_REG_TEMP_MIN ;
break ;
case hwmon_temp_enable :
* val = ! ! ( data - > channel_enable & BIT ( channel ) ) ;
return 0 ;
case hwmon_temp_offset :
if ( ! channel )
return - EINVAL ;
if ( ! ( data - > offset_enable & BIT ( channel ) ) ) {
* val = 0 ;
return 0 ;
}
offset = i2c_smbus_read_byte_data ( data - > client ,
MAX31730_REG_TEMP_OFFSET ) ;
if ( offset < 0 )
return offset ;
* val = ( offset - MAX31730_TEMP_OFFSET_BASELINE ) * 125 ;
return 0 ;
case hwmon_temp_fault :
regval = i2c_smbus_read_byte_data ( data - > client ,
MAX31730_REG_TEMP_FAULT ) ;
if ( regval < 0 )
return regval ;
* val = ! ! ( regval & BIT ( channel ) ) ;
return 0 ;
case hwmon_temp_min_alarm :
regval = i2c_smbus_read_byte_data ( data - > client ,
MAX31730_REG_STATUS_LOW ) ;
if ( regval < 0 )
return regval ;
* val = ! ! ( regval & BIT ( channel ) ) ;
return 0 ;
case hwmon_temp_max_alarm :
regval = i2c_smbus_read_byte_data ( data - > client ,
MAX31730_REG_STATUS_HIGH ) ;
if ( regval < 0 )
return regval ;
* val = ! ! ( regval & BIT ( channel ) ) ;
return 0 ;
default :
return - EINVAL ;
}
regval = i2c_smbus_read_word_swapped ( data - > client , reg ) ;
if ( regval < 0 )
return regval ;
* val = max31730_reg_to_mc ( regval ) ;
return 0 ;
}
static int max31730_write ( struct device * dev , enum hwmon_sensor_types type ,
u32 attr , int channel , long val )
{
struct max31730_data * data = dev_get_drvdata ( dev ) ;
int reg , err ;
if ( type ! = hwmon_temp )
return - EINVAL ;
switch ( attr ) {
case hwmon_temp_max :
reg = MAX31730_REG_TEMP_MAX + channel * 2 ;
break ;
case hwmon_temp_min :
reg = MAX31730_REG_TEMP_MIN ;
break ;
case hwmon_temp_enable :
if ( val ! = 0 & & val ! = 1 )
return - EINVAL ;
return max31730_set_channel_enable ( data , channel , val ) ;
case hwmon_temp_offset :
val = clamp_val ( val , - 14875 , 17000 ) + 14875 ;
val = DIV_ROUND_CLOSEST ( val , 125 ) ;
err = max31730_set_offset_enable ( data , channel ,
val ! = MAX31730_TEMP_OFFSET_BASELINE ) ;
if ( err )
return err ;
return i2c_smbus_write_byte_data ( data - > client ,
MAX31730_REG_TEMP_OFFSET , val ) ;
default :
return - EINVAL ;
}
val = clamp_val ( val , MAX31730_TEMP_MIN , MAX31730_TEMP_MAX ) ;
val = DIV_ROUND_CLOSEST ( val < < 4 , 1000 ) < < 4 ;
return i2c_smbus_write_word_swapped ( data - > client , reg , ( u16 ) val ) ;
}
static umode_t max31730_is_visible ( const void * data ,
enum hwmon_sensor_types type ,
u32 attr , int channel )
{
switch ( type ) {
case hwmon_temp :
switch ( attr ) {
case hwmon_temp_input :
case hwmon_temp_min_alarm :
case hwmon_temp_max_alarm :
case hwmon_temp_fault :
return 0444 ;
case hwmon_temp_min :
return channel ? 0444 : 0644 ;
case hwmon_temp_offset :
case hwmon_temp_enable :
case hwmon_temp_max :
return 0644 ;
}
break ;
default :
break ;
}
return 0 ;
}
static const struct hwmon_channel_info * max31730_info [ ] = {
HWMON_CHANNEL_INFO ( chip ,
HWMON_C_REGISTER_TZ ) ,
HWMON_CHANNEL_INFO ( temp ,
HWMON_T_INPUT | HWMON_T_MIN | HWMON_T_MAX |
HWMON_T_ENABLE |
HWMON_T_MIN_ALARM | HWMON_T_MAX_ALARM ,
HWMON_T_INPUT | HWMON_T_MIN | HWMON_T_MAX |
HWMON_T_OFFSET | HWMON_T_ENABLE |
HWMON_T_MIN_ALARM | HWMON_T_MAX_ALARM |
HWMON_T_FAULT ,
HWMON_T_INPUT | HWMON_T_MIN | HWMON_T_MAX |
HWMON_T_OFFSET | HWMON_T_ENABLE |
HWMON_T_MIN_ALARM | HWMON_T_MAX_ALARM |
HWMON_T_FAULT ,
HWMON_T_INPUT | HWMON_T_MIN | HWMON_T_MAX |
HWMON_T_OFFSET | HWMON_T_ENABLE |
HWMON_T_MIN_ALARM | HWMON_T_MAX_ALARM |
HWMON_T_FAULT
) ,
NULL
} ;
static const struct hwmon_ops max31730_hwmon_ops = {
. is_visible = max31730_is_visible ,
. read = max31730_read ,
. write = max31730_write ,
} ;
static const struct hwmon_chip_info max31730_chip_info = {
. ops = & max31730_hwmon_ops ,
. info = max31730_info ,
} ;
static void max31730_remove ( void * data )
{
struct max31730_data * max31730 = data ;
struct i2c_client * client = max31730 - > client ;
i2c_smbus_write_byte_data ( client , MAX31730_REG_CONF ,
max31730 - > orig_conf ) ;
}
static int
2020-08-13 18:02:22 +02:00
max31730_probe ( struct i2c_client * client )
2019-11-23 11:11:26 -08:00
{
struct device * dev = & client - > dev ;
struct device * hwmon_dev ;
struct max31730_data * data ;
int status , err ;
if ( ! i2c_check_functionality ( client - > adapter ,
I2C_FUNC_SMBUS_BYTE_DATA | I2C_FUNC_SMBUS_WORD_DATA ) )
return - EIO ;
data = devm_kzalloc ( dev , sizeof ( struct max31730_data ) , GFP_KERNEL ) ;
if ( ! data )
return - ENOMEM ;
data - > client = client ;
/* Cache original configuration and enable status */
status = i2c_smbus_read_byte_data ( client , MAX31730_REG_CHANNEL_ENABLE ) ;
if ( status < 0 )
return status ;
data - > channel_enable = status ;
status = i2c_smbus_read_byte_data ( client , MAX31730_REG_OFFSET_ENABLE ) ;
if ( status < 0 )
return status ;
data - > offset_enable = status ;
status = i2c_smbus_read_byte_data ( client , MAX31730_REG_CONF ) ;
if ( status < 0 )
return status ;
data - > orig_conf = status ;
data - > current_conf = status ;
err = max31730_write_config ( data ,
data - > channel_enable ? 0 : MAX31730_STOP ,
data - > channel_enable ? MAX31730_STOP : 0 ) ;
if ( err )
return err ;
dev_set_drvdata ( dev , data ) ;
err = devm_add_action_or_reset ( dev , max31730_remove , data ) ;
if ( err )
return err ;
hwmon_dev = devm_hwmon_device_register_with_info ( dev , client - > name ,
data ,
& max31730_chip_info ,
NULL ) ;
return PTR_ERR_OR_ZERO ( hwmon_dev ) ;
}
static const struct i2c_device_id max31730_ids [ ] = {
{ " max31730 " , 0 , } ,
{ }
} ;
MODULE_DEVICE_TABLE ( i2c , max31730_ids ) ;
static const struct of_device_id __maybe_unused max31730_of_match [ ] = {
{
. compatible = " maxim,max31730 " ,
} ,
{ } ,
} ;
MODULE_DEVICE_TABLE ( of , max31730_of_match ) ;
static bool max31730_check_reg_temp ( struct i2c_client * client ,
int reg )
{
int regval ;
regval = i2c_smbus_read_byte_data ( client , reg + 1 ) ;
return regval < 0 | | ( regval & 0x0f ) ;
}
/* Return 0 if detection is successful, -ENODEV otherwise */
static int max31730_detect ( struct i2c_client * client ,
struct i2c_board_info * info )
{
struct i2c_adapter * adapter = client - > adapter ;
int regval ;
int i ;
if ( ! i2c_check_functionality ( adapter , I2C_FUNC_SMBUS_BYTE_DATA |
I2C_FUNC_SMBUS_WORD_DATA ) )
return - ENODEV ;
regval = i2c_smbus_read_byte_data ( client , MAX31730_REG_MFG_ID ) ;
if ( regval ! = MAX31730_MFG_ID )
return - ENODEV ;
regval = i2c_smbus_read_byte_data ( client , MAX31730_REG_MFG_REV ) ;
if ( regval ! = MAX31730_MFG_REV )
return - ENODEV ;
/* lower 4 bit of temperature and limit registers must be 0 */
if ( max31730_check_reg_temp ( client , MAX31730_REG_TEMP_MIN ) )
return - ENODEV ;
for ( i = 0 ; i < 4 ; i + + ) {
if ( max31730_check_reg_temp ( client , MAX31730_REG_TEMP + i * 2 ) )
return - ENODEV ;
if ( max31730_check_reg_temp ( client ,
MAX31730_REG_TEMP_MAX + i * 2 ) )
return - ENODEV ;
}
strlcpy ( info - > type , " max31730 " , I2C_NAME_SIZE ) ;
return 0 ;
}
static int __maybe_unused max31730_suspend ( struct device * dev )
{
struct max31730_data * data = dev_get_drvdata ( dev ) ;
return max31730_write_config ( data , MAX31730_STOP , 0 ) ;
}
static int __maybe_unused max31730_resume ( struct device * dev )
{
struct max31730_data * data = dev_get_drvdata ( dev ) ;
return max31730_write_config ( data , 0 , MAX31730_STOP ) ;
}
static SIMPLE_DEV_PM_OPS ( max31730_pm_ops , max31730_suspend , max31730_resume ) ;
static struct i2c_driver max31730_driver = {
. class = I2C_CLASS_HWMON ,
. driver = {
. name = " max31730 " ,
. of_match_table = of_match_ptr ( max31730_of_match ) ,
. pm = & max31730_pm_ops ,
} ,
2020-08-13 18:02:22 +02:00
. probe_new = max31730_probe ,
2019-11-23 11:11:26 -08:00
. id_table = max31730_ids ,
. detect = max31730_detect ,
. address_list = normal_i2c ,
} ;
module_i2c_driver ( max31730_driver ) ;
MODULE_AUTHOR ( " Guenter Roeck <linux@roeck-us.net> " ) ;
MODULE_DESCRIPTION ( " MAX31730 driver " ) ;
MODULE_LICENSE ( " GPL " ) ;