2019-05-30 02:57:28 +03:00
// SPDX-License-Identifier: GPL-2.0-only
2009-12-09 22:35:50 +03:00
/*
* LM73 Sensor driver
* Based on LM75
*
* Copyright ( C ) 2007 , CenoSYS ( www . cenosys . com ) .
* Copyright ( C ) 2009 , Bollore telecom ( www . bolloretelecom . eu ) .
*
* Guillaume Ligneul < guillaume . ligneul @ gmail . com >
* Adrien Demarez < adrien . demarez @ bolloretelecom . eu >
* Jeremy Laine < jeremy . laine @ bolloretelecom . eu >
2013-01-05 13:41:20 +04:00
* Chris Verges < kg4ysn @ gmail . com >
2009-12-09 22:35:50 +03:00
*/
# include <linux/module.h>
# include <linux/init.h>
# include <linux/i2c.h>
# include <linux/hwmon.h>
# include <linux/hwmon-sysfs.h>
# include <linux/err.h>
/* Addresses scanned */
static const unsigned short normal_i2c [ ] = { 0x48 , 0x49 , 0x4a , 0x4c ,
0x4d , 0x4e , I2C_CLIENT_END } ;
/* LM73 registers */
# define LM73_REG_INPUT 0x00
# define LM73_REG_CONF 0x01
# define LM73_REG_MAX 0x02
# define LM73_REG_MIN 0x03
# define LM73_REG_CTRL 0x04
# define LM73_REG_ID 0x07
2011-11-04 15:00:47 +04:00
# define LM73_ID 0x9001 /* 0x0190, byte-swapped */
2009-12-09 22:35:50 +03:00
# define DRVNAME "lm73"
2013-01-11 09:16:47 +04:00
# define LM73_TEMP_MIN (-256000 / 250)
# define LM73_TEMP_MAX (255750 / 250)
2009-12-09 22:35:50 +03:00
2013-01-05 13:41:19 +04:00
# define LM73_CTRL_RES_SHIFT 5
# define LM73_CTRL_RES_MASK (BIT(5) | BIT(6))
# define LM73_CTRL_TO_MASK BIT(7)
2013-01-05 13:41:20 +04:00
# define LM73_CTRL_HI_SHIFT 2
# define LM73_CTRL_LO_SHIFT 1
2013-01-05 13:41:19 +04:00
static const unsigned short lm73_convrates [ ] = {
14 , /* 11-bits (0.25000 C/LSB): RES1 Bit = 0, RES0 Bit = 0 */
28 , /* 12-bits (0.12500 C/LSB): RES1 Bit = 0, RES0 Bit = 1 */
56 , /* 13-bits (0.06250 C/LSB): RES1 Bit = 1, RES0 Bit = 0 */
112 , /* 14-bits (0.03125 C/LSB): RES1 Bit = 1, RES0 Bit = 1 */
} ;
struct lm73_data {
2013-09-02 05:32:52 +04:00
struct i2c_client * client ;
2013-01-05 13:41:19 +04:00
struct mutex lock ;
u8 ctrl ; /* control register value */
} ;
2009-12-09 22:35:50 +03:00
2013-01-05 13:41:19 +04:00
/*-----------------------------------------------------------------------*/
2009-12-09 22:35:50 +03:00
2018-12-11 01:02:11 +03:00
static ssize_t temp_store ( struct device * dev , struct device_attribute * da ,
const char * buf , size_t count )
2009-12-09 22:35:50 +03:00
{
struct sensor_device_attribute * attr = to_sensor_dev_attr ( da ) ;
2013-09-02 05:32:52 +04:00
struct lm73_data * data = dev_get_drvdata ( dev ) ;
2009-12-09 22:35:50 +03:00
long temp ;
short value ;
2012-12-21 13:58:34 +04:00
s32 err ;
2009-12-09 22:35:50 +03:00
2012-01-04 23:58:52 +04:00
int status = kstrtol ( buf , 10 , & temp ) ;
2009-12-09 22:35:50 +03:00
if ( status < 0 )
return status ;
/* Write value */
2013-01-11 09:16:47 +04:00
value = clamp_val ( temp / 250 , LM73_TEMP_MIN , LM73_TEMP_MAX ) < < 5 ;
2013-09-02 05:32:52 +04:00
err = i2c_smbus_write_word_swapped ( data - > client , attr - > index , value ) ;
2012-12-21 13:58:34 +04:00
return ( err < 0 ) ? err : count ;
2009-12-09 22:35:50 +03:00
}
2018-12-11 01:02:11 +03:00
static ssize_t temp_show ( struct device * dev , struct device_attribute * da ,
2009-12-09 22:35:50 +03:00
char * buf )
{
struct sensor_device_attribute * attr = to_sensor_dev_attr ( da ) ;
2013-09-02 05:32:52 +04:00
struct lm73_data * data = dev_get_drvdata ( dev ) ;
2012-12-21 13:58:34 +04:00
int temp ;
2013-09-02 05:32:52 +04:00
s32 err = i2c_smbus_read_word_swapped ( data - > client , attr - > index ) ;
2012-12-21 13:58:34 +04:00
if ( err < 0 )
return err ;
2009-12-09 22:35:50 +03:00
/* use integer division instead of equivalent right shift to
guarantee arithmetic shift and preserve the sign */
2012-12-21 13:58:34 +04:00
temp = ( ( ( s16 ) err ) * 250 ) / 32 ;
return scnprintf ( buf , PAGE_SIZE , " %d \n " , temp ) ;
2009-12-09 22:35:50 +03:00
}
2018-12-11 01:02:11 +03:00
static ssize_t convrate_store ( struct device * dev , struct device_attribute * da ,
const char * buf , size_t count )
2013-01-05 13:41:19 +04:00
{
2013-09-02 05:32:52 +04:00
struct lm73_data * data = dev_get_drvdata ( dev ) ;
2013-01-05 13:41:19 +04:00
unsigned long convrate ;
s32 err ;
int res = 0 ;
err = kstrtoul ( buf , 10 , & convrate ) ;
if ( err < 0 )
return err ;
/*
* Convert the desired conversion rate into register bits .
* res is already initialized , and everything past the second - to - last
* value in the array is treated as belonging to the last value
* in the array .
*/
while ( res < ( ARRAY_SIZE ( lm73_convrates ) - 1 ) & &
convrate > lm73_convrates [ res ] )
res + + ;
mutex_lock ( & data - > lock ) ;
data - > ctrl & = LM73_CTRL_TO_MASK ;
data - > ctrl | = res < < LM73_CTRL_RES_SHIFT ;
2013-09-02 05:32:52 +04:00
err = i2c_smbus_write_byte_data ( data - > client , LM73_REG_CTRL ,
data - > ctrl ) ;
2013-01-05 13:41:19 +04:00
mutex_unlock ( & data - > lock ) ;
if ( err < 0 )
return err ;
return count ;
}
2018-12-11 01:02:11 +03:00
static ssize_t convrate_show ( struct device * dev , struct device_attribute * da ,
2013-01-05 13:41:19 +04:00
char * buf )
{
2013-09-02 05:32:52 +04:00
struct lm73_data * data = dev_get_drvdata ( dev ) ;
2013-01-05 13:41:19 +04:00
int res ;
res = ( data - > ctrl & LM73_CTRL_RES_MASK ) > > LM73_CTRL_RES_SHIFT ;
return scnprintf ( buf , PAGE_SIZE , " %hu \n " , lm73_convrates [ res ] ) ;
}
2009-12-09 22:35:50 +03:00
2018-12-11 01:02:11 +03:00
static ssize_t maxmin_alarm_show ( struct device * dev ,
2013-01-05 13:41:20 +04:00
struct device_attribute * da , char * buf )
{
struct sensor_device_attribute * attr = to_sensor_dev_attr ( da ) ;
2013-09-02 05:32:52 +04:00
struct lm73_data * data = dev_get_drvdata ( dev ) ;
2013-01-05 13:41:20 +04:00
s32 ctrl ;
mutex_lock ( & data - > lock ) ;
2013-09-02 05:32:52 +04:00
ctrl = i2c_smbus_read_byte_data ( data - > client , LM73_REG_CTRL ) ;
2013-01-05 13:41:20 +04:00
if ( ctrl < 0 )
goto abort ;
data - > ctrl = ctrl ;
mutex_unlock ( & data - > lock ) ;
return scnprintf ( buf , PAGE_SIZE , " %d \n " , ( ctrl > > attr - > index ) & 1 ) ;
abort :
mutex_unlock ( & data - > lock ) ;
return ctrl ;
}
2009-12-09 22:35:50 +03:00
/*-----------------------------------------------------------------------*/
/* sysfs attributes for hwmon */
2018-12-11 01:02:11 +03:00
static SENSOR_DEVICE_ATTR_RW ( temp1_max , temp , LM73_REG_MAX ) ;
static SENSOR_DEVICE_ATTR_RW ( temp1_min , temp , LM73_REG_MIN ) ;
static SENSOR_DEVICE_ATTR_RO ( temp1_input , temp , LM73_REG_INPUT ) ;
static SENSOR_DEVICE_ATTR_RW ( update_interval , convrate , 0 ) ;
static SENSOR_DEVICE_ATTR_RO ( temp1_max_alarm , maxmin_alarm ,
LM73_CTRL_HI_SHIFT ) ;
static SENSOR_DEVICE_ATTR_RO ( temp1_min_alarm , maxmin_alarm ,
LM73_CTRL_LO_SHIFT ) ;
2009-12-09 22:35:50 +03:00
2013-09-02 05:32:52 +04:00
static struct attribute * lm73_attrs [ ] = {
2009-12-09 22:35:50 +03:00
& sensor_dev_attr_temp1_input . dev_attr . attr ,
& sensor_dev_attr_temp1_max . dev_attr . attr ,
& sensor_dev_attr_temp1_min . dev_attr . attr ,
2013-01-05 13:41:19 +04:00
& sensor_dev_attr_update_interval . dev_attr . attr ,
2013-01-05 13:41:20 +04:00
& sensor_dev_attr_temp1_max_alarm . dev_attr . attr ,
& sensor_dev_attr_temp1_min_alarm . dev_attr . attr ,
2009-12-09 22:35:50 +03:00
NULL
} ;
2013-09-02 05:32:52 +04:00
ATTRIBUTE_GROUPS ( lm73 ) ;
2009-12-09 22:35:50 +03:00
/*-----------------------------------------------------------------------*/
/* device probe and removal */
static int
2020-08-13 19:23:00 +03:00
lm73_probe ( struct i2c_client * client )
2009-12-09 22:35:50 +03:00
{
2013-09-02 05:32:52 +04:00
struct device * dev = & client - > dev ;
struct device * hwmon_dev ;
2013-01-05 13:41:19 +04:00
struct lm73_data * data ;
int ctrl ;
2013-09-02 05:32:52 +04:00
data = devm_kzalloc ( dev , sizeof ( struct lm73_data ) , GFP_KERNEL ) ;
2013-01-05 13:41:19 +04:00
if ( ! data )
return - ENOMEM ;
2013-09-02 05:32:52 +04:00
data - > client = client ;
2013-01-05 13:41:19 +04:00
mutex_init ( & data - > lock ) ;
ctrl = i2c_smbus_read_byte_data ( client , LM73_REG_CTRL ) ;
if ( ctrl < 0 )
return ctrl ;
data - > ctrl = ctrl ;
2009-12-09 22:35:50 +03:00
2013-09-02 05:32:52 +04:00
hwmon_dev = devm_hwmon_device_register_with_groups ( dev , client - > name ,
data , lm73_groups ) ;
if ( IS_ERR ( hwmon_dev ) )
return PTR_ERR ( hwmon_dev ) ;
2009-12-09 22:35:50 +03:00
2013-09-02 05:32:52 +04:00
dev_info ( dev , " sensor '%s' \n " , client - > name ) ;
2009-12-09 22:35:50 +03:00
return 0 ;
}
static const struct i2c_device_id lm73_ids [ ] = {
2009-12-14 23:17:26 +03:00
{ " lm73 " , 0 } ,
2009-12-09 22:35:50 +03:00
{ /* LIST END */ }
} ;
MODULE_DEVICE_TABLE ( i2c , lm73_ids ) ;
/* Return 0 if detection is successful, -ENODEV otherwise */
2009-12-14 23:17:23 +03:00
static int lm73_detect ( struct i2c_client * new_client ,
2009-12-09 22:35:50 +03:00
struct i2c_board_info * info )
{
struct i2c_adapter * adapter = new_client - > adapter ;
2011-11-04 15:00:46 +04:00
int id , ctrl , conf ;
2009-12-09 22:35:50 +03:00
if ( ! i2c_check_functionality ( adapter , I2C_FUNC_SMBUS_BYTE_DATA |
I2C_FUNC_SMBUS_WORD_DATA ) )
return - ENODEV ;
2011-11-04 15:00:46 +04:00
/*
* Do as much detection as possible with byte reads first , as word
* reads can confuse other devices .
*/
ctrl = i2c_smbus_read_byte_data ( new_client , LM73_REG_CTRL ) ;
if ( ctrl < 0 | | ( ctrl & 0x10 ) )
return - ENODEV ;
conf = i2c_smbus_read_byte_data ( new_client , LM73_REG_CONF ) ;
if ( conf < 0 | | ( conf & 0x0c ) )
return - ENODEV ;
id = i2c_smbus_read_byte_data ( new_client , LM73_REG_ID ) ;
if ( id < 0 | | id ! = ( LM73_ID & 0xff ) )
return - ENODEV ;
2009-12-09 22:35:50 +03:00
/* Check device ID */
id = i2c_smbus_read_word_data ( new_client , LM73_REG_ID ) ;
2011-11-04 15:00:46 +04:00
if ( id < 0 | | id ! = LM73_ID )
2009-12-09 22:35:50 +03:00
return - ENODEV ;
strlcpy ( info - > type , " lm73 " , I2C_NAME_SIZE ) ;
return 0 ;
}
2020-02-12 06:06:15 +03:00
static const struct of_device_id lm73_of_match [ ] = {
{
. compatible = " ti,lm73 " ,
} ,
{ } ,
} ;
MODULE_DEVICE_TABLE ( of , lm73_of_match ) ;
2009-12-09 22:35:50 +03:00
static struct i2c_driver lm73_driver = {
. class = I2C_CLASS_HWMON ,
. driver = {
. name = " lm73 " ,
2020-02-12 06:06:15 +03:00
. of_match_table = lm73_of_match ,
2009-12-09 22:35:50 +03:00
} ,
2020-08-13 19:23:00 +03:00
. probe_new = lm73_probe ,
2009-12-09 22:35:50 +03:00
. id_table = lm73_ids ,
. detect = lm73_detect ,
2009-12-14 23:17:25 +03:00
. address_list = normal_i2c ,
2009-12-09 22:35:50 +03:00
} ;
2012-01-20 11:38:18 +04:00
module_i2c_driver ( lm73_driver ) ;
2009-12-09 22:35:50 +03:00
MODULE_AUTHOR ( " Guillaume Ligneul <guillaume.ligneul@gmail.com> " ) ;
MODULE_DESCRIPTION ( " LM73 driver " ) ;
MODULE_LICENSE ( " GPL " ) ;