2019-05-29 17:12:27 +03:00
// SPDX-License-Identifier: GPL-2.0-only
2016-01-15 12:54:59 +03:00
/*
* Driver for Linear Technology LTC2990 power monitor
*
* Copyright ( C ) 2014 Topic Embedded Products
* Author : Mike Looijmans < mike . looijmans @ topic . nl >
*/
2017-07-03 07:28:58 +03:00
# include <linux/bitops.h>
2016-01-15 12:54:59 +03:00
# include <linux/err.h>
# include <linux/hwmon.h>
# include <linux/hwmon-sysfs.h>
# include <linux/i2c.h>
# include <linux/kernel.h>
# include <linux/module.h>
2019-08-19 15:16:17 +03:00
# include <linux/property.h>
2016-01-15 12:54:59 +03:00
# define LTC2990_STATUS 0x00
# define LTC2990_CONTROL 0x01
# define LTC2990_TRIGGER 0x02
# define LTC2990_TINT_MSB 0x04
# define LTC2990_V1_MSB 0x06
# define LTC2990_V2_MSB 0x08
# define LTC2990_V3_MSB 0x0A
# define LTC2990_V4_MSB 0x0C
# define LTC2990_VCC_MSB 0x0E
2017-07-03 07:29:00 +03:00
# define LTC2990_IN0 BIT(0)
# define LTC2990_IN1 BIT(1)
# define LTC2990_IN2 BIT(2)
# define LTC2990_IN3 BIT(3)
# define LTC2990_IN4 BIT(4)
# define LTC2990_CURR1 BIT(5)
# define LTC2990_CURR2 BIT(6)
# define LTC2990_TEMP1 BIT(7)
# define LTC2990_TEMP2 BIT(8)
# define LTC2990_TEMP3 BIT(9)
# define LTC2990_NONE 0
# define LTC2990_ALL GENMASK(9, 0)
# define LTC2990_MODE0_SHIFT 0
# define LTC2990_MODE0_MASK GENMASK(2, 0)
# define LTC2990_MODE1_SHIFT 3
# define LTC2990_MODE1_MASK GENMASK(1, 0)
/* Enabled measurements for mode bits 2..0 */
static const int ltc2990_attrs_ena_0 [ ] = {
LTC2990_IN1 | LTC2990_IN2 | LTC2990_TEMP3 ,
LTC2990_CURR1 | LTC2990_TEMP3 ,
LTC2990_CURR1 | LTC2990_IN3 | LTC2990_IN4 ,
LTC2990_TEMP2 | LTC2990_IN3 | LTC2990_IN4 ,
LTC2990_TEMP2 | LTC2990_CURR2 ,
LTC2990_TEMP2 | LTC2990_TEMP3 ,
LTC2990_CURR1 | LTC2990_CURR2 ,
LTC2990_IN1 | LTC2990_IN2 | LTC2990_IN3 | LTC2990_IN4
} ;
/* Enabled measurements for mode bits 4..3 */
static const int ltc2990_attrs_ena_1 [ ] = {
LTC2990_NONE ,
LTC2990_TEMP2 | LTC2990_IN1 | LTC2990_CURR1 ,
LTC2990_TEMP3 | LTC2990_IN3 | LTC2990_CURR2 ,
LTC2990_ALL
} ;
struct ltc2990_data {
struct i2c_client * i2c ;
u32 mode [ 2 ] ;
} ;
2016-01-15 12:54:59 +03:00
/* Return the converted value from the given register in uV or mC */
2017-07-03 07:29:00 +03:00
static int ltc2990_get_value ( struct i2c_client * i2c , int index , int * result )
2016-01-15 12:54:59 +03:00
{
int val ;
2017-07-03 07:29:00 +03:00
u8 reg ;
switch ( index ) {
case LTC2990_IN0 :
reg = LTC2990_VCC_MSB ;
break ;
case LTC2990_IN1 :
case LTC2990_CURR1 :
case LTC2990_TEMP2 :
reg = LTC2990_V1_MSB ;
break ;
case LTC2990_IN2 :
reg = LTC2990_V2_MSB ;
break ;
case LTC2990_IN3 :
case LTC2990_CURR2 :
case LTC2990_TEMP3 :
reg = LTC2990_V3_MSB ;
break ;
case LTC2990_IN4 :
reg = LTC2990_V4_MSB ;
break ;
case LTC2990_TEMP1 :
reg = LTC2990_TINT_MSB ;
break ;
default :
return - EINVAL ;
}
2016-01-15 12:54:59 +03:00
val = i2c_smbus_read_word_swapped ( i2c , reg ) ;
if ( unlikely ( val < 0 ) )
return val ;
2017-07-03 07:29:00 +03:00
switch ( index ) {
case LTC2990_TEMP1 :
case LTC2990_TEMP2 :
case LTC2990_TEMP3 :
/* temp, 0.0625 degrees/LSB */
2017-07-03 07:28:58 +03:00
* result = sign_extend32 ( val , 12 ) * 1000 / 16 ;
2016-01-15 12:54:59 +03:00
break ;
2017-07-03 07:29:00 +03:00
case LTC2990_CURR1 :
case LTC2990_CURR2 :
/* Vx-Vy, 19.42uV/LSB */
2017-07-03 07:28:58 +03:00
* result = sign_extend32 ( val , 14 ) * 1942 / 100 ;
2016-01-15 12:54:59 +03:00
break ;
2017-07-03 07:29:00 +03:00
case LTC2990_IN0 :
/* Vcc, 305.18uV/LSB, 2.5V offset */
2017-07-03 07:28:58 +03:00
* result = sign_extend32 ( val , 14 ) * 30518 / ( 100 * 1000 ) + 2500 ;
2016-01-15 12:54:59 +03:00
break ;
2017-07-03 07:29:00 +03:00
case LTC2990_IN1 :
case LTC2990_IN2 :
case LTC2990_IN3 :
case LTC2990_IN4 :
/* Vx, 305.18uV/LSB */
* result = sign_extend32 ( val , 14 ) * 30518 / ( 100 * 1000 ) ;
break ;
2016-01-15 12:54:59 +03:00
default :
return - EINVAL ; /* won't happen, keep compiler happy */
}
return 0 ;
}
2018-12-11 01:02:14 +03:00
static ssize_t ltc2990_value_show ( struct device * dev ,
2016-01-15 12:54:59 +03:00
struct device_attribute * da , char * buf )
{
struct sensor_device_attribute * attr = to_sensor_dev_attr ( da ) ;
2017-07-03 07:29:00 +03:00
struct ltc2990_data * data = dev_get_drvdata ( dev ) ;
2016-01-15 12:54:59 +03:00
int value ;
int ret ;
2017-07-03 07:29:00 +03:00
ret = ltc2990_get_value ( data - > i2c , attr - > index , & value ) ;
2016-01-15 12:54:59 +03:00
if ( unlikely ( ret < 0 ) )
return ret ;
return snprintf ( buf , PAGE_SIZE , " %d \n " , value ) ;
}
2017-07-03 07:29:00 +03:00
static umode_t ltc2990_attrs_visible ( struct kobject * kobj ,
struct attribute * a , int n )
{
struct device * dev = container_of ( kobj , struct device , kobj ) ;
struct ltc2990_data * data = dev_get_drvdata ( dev ) ;
struct device_attribute * da =
container_of ( a , struct device_attribute , attr ) ;
struct sensor_device_attribute * attr = to_sensor_dev_attr ( da ) ;
int attrs_mask = LTC2990_IN0 | LTC2990_TEMP1 |
( ltc2990_attrs_ena_0 [ data - > mode [ 0 ] ] &
ltc2990_attrs_ena_1 [ data - > mode [ 1 ] ] ) ;
if ( attr - > index & attrs_mask )
return a - > mode ;
return 0 ;
}
2018-12-11 01:02:14 +03:00
static SENSOR_DEVICE_ATTR_RO ( temp1_input , ltc2990_value , LTC2990_TEMP1 ) ;
static SENSOR_DEVICE_ATTR_RO ( temp2_input , ltc2990_value , LTC2990_TEMP2 ) ;
static SENSOR_DEVICE_ATTR_RO ( temp3_input , ltc2990_value , LTC2990_TEMP3 ) ;
static SENSOR_DEVICE_ATTR_RO ( curr1_input , ltc2990_value , LTC2990_CURR1 ) ;
static SENSOR_DEVICE_ATTR_RO ( curr2_input , ltc2990_value , LTC2990_CURR2 ) ;
static SENSOR_DEVICE_ATTR_RO ( in0_input , ltc2990_value , LTC2990_IN0 ) ;
static SENSOR_DEVICE_ATTR_RO ( in1_input , ltc2990_value , LTC2990_IN1 ) ;
static SENSOR_DEVICE_ATTR_RO ( in2_input , ltc2990_value , LTC2990_IN2 ) ;
static SENSOR_DEVICE_ATTR_RO ( in3_input , ltc2990_value , LTC2990_IN3 ) ;
static SENSOR_DEVICE_ATTR_RO ( in4_input , ltc2990_value , LTC2990_IN4 ) ;
2016-01-15 12:54:59 +03:00
static struct attribute * ltc2990_attrs [ ] = {
& sensor_dev_attr_temp1_input . dev_attr . attr ,
2017-07-03 07:29:00 +03:00
& sensor_dev_attr_temp2_input . dev_attr . attr ,
& sensor_dev_attr_temp3_input . dev_attr . attr ,
2016-01-15 12:54:59 +03:00
& sensor_dev_attr_curr1_input . dev_attr . attr ,
& sensor_dev_attr_curr2_input . dev_attr . attr ,
& sensor_dev_attr_in0_input . dev_attr . attr ,
2017-07-03 07:29:00 +03:00
& sensor_dev_attr_in1_input . dev_attr . attr ,
& sensor_dev_attr_in2_input . dev_attr . attr ,
& sensor_dev_attr_in3_input . dev_attr . attr ,
& sensor_dev_attr_in4_input . dev_attr . attr ,
2016-01-15 12:54:59 +03:00
NULL ,
} ;
2017-07-03 07:29:00 +03:00
static const struct attribute_group ltc2990_group = {
. attrs = ltc2990_attrs ,
. is_visible = ltc2990_attrs_visible ,
} ;
__ATTRIBUTE_GROUPS ( ltc2990 ) ;
2016-01-15 12:54:59 +03:00
static int ltc2990_i2c_probe ( struct i2c_client * i2c ,
const struct i2c_device_id * id )
{
int ret ;
struct device * hwmon_dev ;
2017-07-03 07:29:00 +03:00
struct ltc2990_data * data ;
2016-01-15 12:54:59 +03:00
if ( ! i2c_check_functionality ( i2c - > adapter , I2C_FUNC_SMBUS_BYTE_DATA |
I2C_FUNC_SMBUS_WORD_DATA ) )
return - ENODEV ;
2017-07-03 07:29:00 +03:00
data = devm_kzalloc ( & i2c - > dev , sizeof ( struct ltc2990_data ) , GFP_KERNEL ) ;
if ( unlikely ( ! data ) )
return - ENOMEM ;
data - > i2c = i2c ;
2019-08-19 15:16:17 +03:00
if ( dev_fwnode ( & i2c - > dev ) ) {
ret = device_property_read_u32_array ( & i2c - > dev ,
" lltc,meas-mode " ,
data - > mode , 2 ) ;
2017-07-03 07:29:00 +03:00
if ( ret < 0 )
return ret ;
if ( data - > mode [ 0 ] & ~ LTC2990_MODE0_MASK | |
data - > mode [ 1 ] & ~ LTC2990_MODE1_MASK )
return - EINVAL ;
} else {
ret = i2c_smbus_read_byte_data ( i2c , LTC2990_CONTROL ) ;
if ( ret < 0 )
return ret ;
data - > mode [ 0 ] = ret > > LTC2990_MODE0_SHIFT & LTC2990_MODE0_MASK ;
data - > mode [ 1 ] = ret > > LTC2990_MODE1_SHIFT & LTC2990_MODE1_MASK ;
}
/* Setup continuous mode */
2016-01-15 12:54:59 +03:00
ret = i2c_smbus_write_byte_data ( i2c , LTC2990_CONTROL ,
2017-07-03 07:29:00 +03:00
data - > mode [ 0 ] < < LTC2990_MODE0_SHIFT |
data - > mode [ 1 ] < < LTC2990_MODE1_SHIFT ) ;
2016-01-15 12:54:59 +03:00
if ( ret < 0 ) {
dev_err ( & i2c - > dev , " Error: Failed to set control mode. \n " ) ;
return ret ;
}
/* Trigger once to start continuous conversion */
ret = i2c_smbus_write_byte_data ( i2c , LTC2990_TRIGGER , 1 ) ;
if ( ret < 0 ) {
dev_err ( & i2c - > dev , " Error: Failed to start acquisition. \n " ) ;
return ret ;
}
hwmon_dev = devm_hwmon_device_register_with_groups ( & i2c - > dev ,
i2c - > name ,
2017-07-03 07:29:00 +03:00
data ,
2016-01-15 12:54:59 +03:00
ltc2990_groups ) ;
return PTR_ERR_OR_ZERO ( hwmon_dev ) ;
}
static const struct i2c_device_id ltc2990_i2c_id [ ] = {
{ " ltc2990 " , 0 } ,
{ }
} ;
MODULE_DEVICE_TABLE ( i2c , ltc2990_i2c_id ) ;
static struct i2c_driver ltc2990_i2c_driver = {
. driver = {
. name = " ltc2990 " ,
} ,
. probe = ltc2990_i2c_probe ,
. id_table = ltc2990_i2c_id ,
} ;
module_i2c_driver ( ltc2990_i2c_driver ) ;
MODULE_DESCRIPTION ( " LTC2990 Sensor Driver " ) ;
MODULE_AUTHOR ( " Topic Embedded Products " ) ;
MODULE_LICENSE ( " GPL v2 " ) ;