2019-06-01 11:08:55 +03:00
// SPDX-License-Identifier: GPL-2.0-only
2012-05-12 12:36:38 +04:00
/*
* Driver for Texas Instruments INA219 , INA226 power monitor chips
*
* INA219 :
* Zero Drift Bi - Directional Current / Power Monitor with I2C Interface
2020-07-19 21:15:30 +03:00
* Datasheet : https : //www.ti.com/product/ina219
2012-05-12 12:36:38 +04:00
*
2012-05-12 22:33:11 +04:00
* INA220 :
* Bi - Directional Current / Power Monitor with I2C Interface
2020-07-19 21:15:30 +03:00
* Datasheet : https : //www.ti.com/product/ina220
2012-05-12 22:33:11 +04:00
*
2012-05-12 12:36:38 +04:00
* INA226 :
* Bi - Directional Current / Power Monitor with I2C Interface
2020-07-19 21:15:30 +03:00
* Datasheet : https : //www.ti.com/product/ina226
2012-05-12 12:36:38 +04:00
*
2012-05-12 22:33:11 +04:00
* INA230 :
* Bi - directional Current / Power Monitor with I2C Interface
2020-07-19 21:15:30 +03:00
* Datasheet : https : //www.ti.com/product/ina230
2012-05-12 22:33:11 +04:00
*
2018-08-14 10:09:37 +03:00
* Copyright ( C ) 2012 Lothar Felten < lothar . felten @ gmail . com >
2012-05-12 12:36:38 +04:00
* Thanks to Jan Volkering
*/
# include <linux/kernel.h>
# include <linux/module.h>
# include <linux/init.h>
# include <linux/err.h>
# include <linux/slab.h>
# include <linux/i2c.h>
# include <linux/hwmon.h>
# include <linux/hwmon-sysfs.h>
2012-10-10 17:25:56 +04:00
# include <linux/jiffies.h>
2013-06-19 10:50:20 +04:00
# include <linux/of.h>
2015-01-05 17:20:52 +03:00
# include <linux/delay.h>
2015-04-16 22:43:34 +03:00
# include <linux/util_macros.h>
2015-10-28 14:04:53 +03:00
# include <linux/regmap.h>
2012-05-12 12:36:38 +04:00
# include <linux/platform_data/ina2xx.h>
/* common register definitions */
# define INA2XX_CONFIG 0x00
# define INA2XX_SHUNT_VOLTAGE 0x01 /* readonly */
# define INA2XX_BUS_VOLTAGE 0x02 /* readonly */
# define INA2XX_POWER 0x03 /* readonly */
# define INA2XX_CURRENT 0x04 /* readonly */
# define INA2XX_CALIBRATION 0x05
/* INA226 register definitions */
# define INA226_MASK_ENABLE 0x06
# define INA226_ALERT_LIMIT 0x07
# define INA226_DIE_ID 0xFF
/* register count */
# define INA219_REGISTERS 6
# define INA226_REGISTERS 8
# define INA2XX_MAX_REGISTERS 8
/* settings - depend on use case */
# define INA219_CONFIG_DEFAULT 0x399F /* PGA=8 */
# define INA226_CONFIG_DEFAULT 0x4527 /* averages=16 */
/* worst case is 68.10 ms (~14.6Hz, ina219) */
# define INA2XX_CONVERSION_RATE 15
2015-01-05 17:20:52 +03:00
# define INA2XX_MAX_DELAY 69 /* worst case delay in ms */
# define INA2XX_RSHUNT_DEFAULT 10000
2012-05-12 12:36:38 +04:00
2015-01-09 19:03:42 +03:00
/* bit mask for reading the averaging setting in the configuration register */
# define INA226_AVG_RD_MASK 0x0E00
# define INA226_READ_AVG(reg) (((reg) & INA226_AVG_RD_MASK) >> 9)
# define INA226_SHIFT_AVG(val) ((val) << 9)
2020-05-05 03:59:45 +03:00
/* bit number of alert functions in Mask/Enable Register */
# define INA226_SHUNT_OVER_VOLTAGE_BIT 15
# define INA226_SHUNT_UNDER_VOLTAGE_BIT 14
# define INA226_BUS_OVER_VOLTAGE_BIT 13
# define INA226_BUS_UNDER_VOLTAGE_BIT 12
# define INA226_POWER_OVER_LIMIT_BIT 11
/* bit mask for alert config bits of Mask/Enable Register */
# define INA226_ALERT_CONFIG_MASK 0xFC00
# define INA226_ALERT_FUNCTION_FLAG BIT(4)
2015-01-09 19:03:42 +03:00
/* common attrs, ina226 attrs and NULL */
# define INA2XX_MAX_ATTRIBUTE_GROUPS 3
/*
* Both bus voltage and shunt voltage conversion times for ina226 are set
* to 0 b0100 on POR , which translates to 2200 microseconds in total .
*/
# define INA226_TOTAL_CONV_TIME_DEFAULT 2200
2015-10-28 14:04:53 +03:00
static struct regmap_config ina2xx_regmap_config = {
. reg_bits = 8 ,
. val_bits = 16 ,
} ;
2012-05-12 12:36:38 +04:00
enum ina2xx_ids { ina219 , ina226 } ;
2012-05-12 22:21:01 +04:00
struct ina2xx_config {
u16 config_default ;
2017-11-22 18:32:15 +03:00
int calibration_value ;
2012-05-12 22:21:01 +04:00
int registers ;
int shunt_div ;
int bus_voltage_shift ;
int bus_voltage_lsb ; /* uV */
2017-11-22 18:32:15 +03:00
int power_lsb_factor ;
2012-05-12 22:21:01 +04:00
} ;
2012-05-12 12:36:38 +04:00
struct ina2xx_data {
2012-05-12 22:21:01 +04:00
const struct ina2xx_config * config ;
2012-05-12 12:36:38 +04:00
2015-01-05 17:20:52 +03:00
long rshunt ;
2017-11-22 18:32:15 +03:00
long current_lsb_uA ;
long power_lsb_uW ;
2015-10-28 14:04:53 +03:00
struct mutex config_lock ;
struct regmap * regmap ;
2012-05-12 12:36:38 +04:00
2015-01-09 19:03:42 +03:00
const struct attribute_group * groups [ INA2XX_MAX_ATTRIBUTE_GROUPS ] ;
2012-05-12 12:36:38 +04:00
} ;
2012-05-12 22:21:01 +04:00
static const struct ina2xx_config ina2xx_config [ ] = {
[ ina219 ] = {
. config_default = INA219_CONFIG_DEFAULT ,
2017-11-22 18:32:15 +03:00
. calibration_value = 4096 ,
2012-05-12 22:21:01 +04:00
. registers = INA219_REGISTERS ,
. shunt_div = 100 ,
. bus_voltage_shift = 3 ,
. bus_voltage_lsb = 4000 ,
2017-11-22 18:32:15 +03:00
. power_lsb_factor = 20 ,
2012-05-12 22:21:01 +04:00
} ,
[ ina226 ] = {
. config_default = INA226_CONFIG_DEFAULT ,
2017-11-22 18:32:15 +03:00
. calibration_value = 2048 ,
2012-05-12 22:21:01 +04:00
. registers = INA226_REGISTERS ,
. shunt_div = 400 ,
. bus_voltage_shift = 0 ,
. bus_voltage_lsb = 1250 ,
2017-11-22 18:32:15 +03:00
. power_lsb_factor = 25 ,
2012-05-12 22:21:01 +04:00
} ,
} ;
2015-01-09 19:03:42 +03:00
/*
* Available averaging rates for ina226 . The indices correspond with
* the bit values expected by the chip ( according to the ina226 datasheet ,
* table 3 AVG bit settings , found at
2020-07-19 21:15:30 +03:00
* https : //www.ti.com/lit/ds/symlink/ina226.pdf.
2015-01-09 19:03:42 +03:00
*/
static const int ina226_avg_tab [ ] = { 1 , 4 , 16 , 64 , 128 , 256 , 512 , 1024 } ;
static int ina226_reg_to_interval ( u16 config )
{
int avg = ina226_avg_tab [ INA226_READ_AVG ( config ) ] ;
/*
* Multiply the total conversion time by the number of averages .
* Return the result in milliseconds .
*/
return DIV_ROUND_CLOSEST ( avg * INA226_TOTAL_CONV_TIME_DEFAULT , 1000 ) ;
}
2015-10-28 14:04:53 +03:00
/*
* Return the new , shifted AVG field value of CONFIG register ,
* to use with regmap_update_bits
*/
static u16 ina226_interval_to_reg ( int interval )
2015-01-09 19:03:42 +03:00
{
int avg , avg_bits ;
avg = DIV_ROUND_CLOSEST ( interval * 1000 ,
INA226_TOTAL_CONV_TIME_DEFAULT ) ;
2015-04-16 22:43:34 +03:00
avg_bits = find_closest ( avg , ina226_avg_tab ,
ARRAY_SIZE ( ina226_avg_tab ) ) ;
2015-01-09 19:03:42 +03:00
2015-10-28 14:04:53 +03:00
return INA226_SHIFT_AVG ( avg_bits ) ;
2015-01-09 19:03:42 +03:00
}
2017-11-22 18:32:15 +03:00
/*
* Calibration register is set to the best value , which eliminates
* truncation errors on calculating current register in hardware .
* According to datasheet ( eq . 3 ) the best values are 2048 for
* ina226 and 4096 for ina219 . They are hardcoded as calibration_value .
*/
2015-01-05 17:20:55 +03:00
static int ina2xx_calibrate ( struct ina2xx_data * data )
{
2017-11-22 18:32:15 +03:00
return regmap_write ( data - > regmap , INA2XX_CALIBRATION ,
data - > config - > calibration_value ) ;
2015-01-05 17:20:55 +03:00
}
2015-01-05 17:20:52 +03:00
/*
* Initialize the configuration and calibration registers .
*/
static int ina2xx_init ( struct ina2xx_data * data )
2012-05-12 12:36:38 +04:00
{
2015-10-28 14:04:53 +03:00
int ret = regmap_write ( data - > regmap , INA2XX_CONFIG ,
data - > config - > config_default ) ;
2015-01-05 17:20:52 +03:00
if ( ret < 0 )
return ret ;
2012-05-12 12:36:38 +04:00
2015-01-05 17:20:55 +03:00
return ina2xx_calibrate ( data ) ;
2015-01-05 17:20:52 +03:00
}
2012-05-12 12:36:38 +04:00
2015-10-28 14:04:53 +03:00
static int ina2xx_read_reg ( struct device * dev , int reg , unsigned int * regval )
2015-01-05 17:20:52 +03:00
{
struct ina2xx_data * data = dev_get_drvdata ( dev ) ;
2015-10-28 14:04:53 +03:00
int ret , retry ;
2012-05-12 12:36:38 +04:00
2015-10-28 14:04:53 +03:00
dev_dbg ( dev , " Starting register %d read \n " , reg ) ;
2012-05-12 12:36:38 +04:00
2015-01-05 17:20:52 +03:00
for ( retry = 5 ; retry ; retry - - ) {
2015-10-28 14:04:53 +03:00
ret = regmap_read ( data - > regmap , reg , regval ) ;
if ( ret < 0 )
return ret ;
dev_dbg ( dev , " read %d, val = 0x%04x \n " , reg , * regval ) ;
2015-01-05 17:20:52 +03:00
/*
* If the current value in the calibration register is 0 , the
* power and current registers will also remain at 0. In case
* the chip has been reset let ' s check the calibration
* register and reinitialize if needed .
2015-10-28 14:04:53 +03:00
* We do that extra read of the calibration register if there
* is some hint of a chip reset .
2015-01-05 17:20:52 +03:00
*/
2015-10-28 14:04:53 +03:00
if ( * regval = = 0 ) {
unsigned int cal ;
ret = regmap_read ( data - > regmap , INA2XX_CALIBRATION ,
& cal ) ;
if ( ret < 0 )
return ret ;
if ( cal = = 0 ) {
dev_warn ( dev , " chip not calibrated, reinitializing \n " ) ;
ret = ina2xx_init ( data ) ;
if ( ret < 0 )
return ret ;
/*
* Let ' s make sure the power and current
* registers have been updated before trying
* again .
*/
msleep ( INA2XX_MAX_DELAY ) ;
continue ;
}
2015-01-05 17:20:52 +03:00
}
return 0 ;
2012-05-12 12:36:38 +04:00
}
2015-01-05 17:20:52 +03:00
/*
* If we ' re here then although all write operations succeeded , the
* chip still returns 0 in the calibration register . Nothing more we
* can do here .
*/
dev_err ( dev , " unable to reinitialize the chip \n " ) ;
return - ENODEV ;
}
2015-10-28 14:04:53 +03:00
static int ina2xx_get_value ( struct ina2xx_data * data , u8 reg ,
unsigned int regval )
2012-05-12 12:36:38 +04:00
{
2012-05-12 22:21:01 +04:00
int val ;
2012-05-12 12:36:38 +04:00
switch ( reg ) {
case INA2XX_SHUNT_VOLTAGE :
2014-06-09 01:06:24 +04:00
/* signed register */
2015-10-28 14:04:53 +03:00
val = DIV_ROUND_CLOSEST ( ( s16 ) regval , data - > config - > shunt_div ) ;
2012-05-12 12:36:38 +04:00
break ;
case INA2XX_BUS_VOLTAGE :
2015-10-28 14:04:53 +03:00
val = ( regval > > data - > config - > bus_voltage_shift )
2012-05-12 22:21:01 +04:00
* data - > config - > bus_voltage_lsb ;
val = DIV_ROUND_CLOSEST ( val , 1000 ) ;
2012-05-12 12:36:38 +04:00
break ;
case INA2XX_POWER :
2017-11-22 18:32:15 +03:00
val = regval * data - > power_lsb_uW ;
2012-05-12 12:36:38 +04:00
break ;
case INA2XX_CURRENT :
2017-11-22 18:32:15 +03:00
/* signed register, result in mA */
2018-11-14 06:48:54 +03:00
val = ( s16 ) regval * data - > current_lsb_uA ;
2017-11-22 18:32:15 +03:00
val = DIV_ROUND_CLOSEST ( val , 1000 ) ;
2012-05-12 12:36:38 +04:00
break ;
2015-01-05 17:20:55 +03:00
case INA2XX_CALIBRATION :
2017-11-22 18:32:15 +03:00
val = regval ;
2015-01-05 17:20:55 +03:00
break ;
2012-05-12 12:36:38 +04:00
default :
/* programmer goofed */
WARN_ON_ONCE ( 1 ) ;
val = 0 ;
break ;
}
return val ;
}
2018-12-06 22:06:23 +03:00
static ssize_t ina2xx_value_show ( struct device * dev ,
2012-05-12 12:36:38 +04:00
struct device_attribute * da , char * buf )
{
struct sensor_device_attribute * attr = to_sensor_dev_attr ( da ) ;
2015-10-28 14:04:53 +03:00
struct ina2xx_data * data = dev_get_drvdata ( dev ) ;
unsigned int regval ;
2012-05-12 12:36:38 +04:00
2015-10-28 14:04:53 +03:00
int err = ina2xx_read_reg ( dev , attr - > index , & regval ) ;
if ( err < 0 )
return err ;
2012-05-12 12:36:38 +04:00
2021-03-16 14:00:57 +03:00
return sysfs_emit ( buf , " %d \n " , ina2xx_get_value ( data , attr - > index , regval ) ) ;
2012-05-12 12:36:38 +04:00
}
2020-05-05 03:59:45 +03:00
static int ina226_reg_to_alert ( struct ina2xx_data * data , u8 bit , u16 regval )
{
int reg ;
switch ( bit ) {
case INA226_SHUNT_OVER_VOLTAGE_BIT :
case INA226_SHUNT_UNDER_VOLTAGE_BIT :
reg = INA2XX_SHUNT_VOLTAGE ;
break ;
case INA226_BUS_OVER_VOLTAGE_BIT :
case INA226_BUS_UNDER_VOLTAGE_BIT :
reg = INA2XX_BUS_VOLTAGE ;
break ;
case INA226_POWER_OVER_LIMIT_BIT :
reg = INA2XX_POWER ;
break ;
default :
/* programmer goofed */
WARN_ON_ONCE ( 1 ) ;
return 0 ;
}
return ina2xx_get_value ( data , reg , regval ) ;
}
/*
* Turns alert limit values into register values .
* Opposite of the formula in ina2xx_get_value ( ) .
*/
static s16 ina226_alert_to_reg ( struct ina2xx_data * data , u8 bit , int val )
{
switch ( bit ) {
case INA226_SHUNT_OVER_VOLTAGE_BIT :
case INA226_SHUNT_UNDER_VOLTAGE_BIT :
val * = data - > config - > shunt_div ;
return clamp_val ( val , SHRT_MIN , SHRT_MAX ) ;
case INA226_BUS_OVER_VOLTAGE_BIT :
case INA226_BUS_UNDER_VOLTAGE_BIT :
val = ( val * 1000 ) < < data - > config - > bus_voltage_shift ;
val = DIV_ROUND_CLOSEST ( val , data - > config - > bus_voltage_lsb ) ;
return clamp_val ( val , 0 , SHRT_MAX ) ;
case INA226_POWER_OVER_LIMIT_BIT :
val = DIV_ROUND_CLOSEST ( val , data - > power_lsb_uW ) ;
return clamp_val ( val , 0 , USHRT_MAX ) ;
default :
/* programmer goofed */
WARN_ON_ONCE ( 1 ) ;
return 0 ;
}
}
static ssize_t ina226_alert_show ( struct device * dev ,
struct device_attribute * da , char * buf )
{
struct sensor_device_attribute * attr = to_sensor_dev_attr ( da ) ;
struct ina2xx_data * data = dev_get_drvdata ( dev ) ;
int regval ;
int val = 0 ;
int ret ;
mutex_lock ( & data - > config_lock ) ;
ret = regmap_read ( data - > regmap , INA226_MASK_ENABLE , & regval ) ;
if ( ret )
goto abort ;
if ( regval & BIT ( attr - > index ) ) {
ret = regmap_read ( data - > regmap , INA226_ALERT_LIMIT , & regval ) ;
if ( ret )
goto abort ;
val = ina226_reg_to_alert ( data , attr - > index , regval ) ;
}
hwmon: replace snprintf in show functions with sysfs_emit
coccicheck complains about the use of snprintf() in sysfs
show functions.
drivers/hwmon/ina3221.c:701:8-16: WARNING: use scnprintf or sprintf
This results in a large number of patch submissions. Fix it all in
one go using the following coccinelle rules. Use sysfs_emit instead
of scnprintf or sprintf since that makes more sense.
@depends on patch@
identifier show, dev, attr, buf;
@@
ssize_t show(struct device *dev, struct device_attribute *attr, char *buf)
{
<...
return
- snprintf(buf, \( PAGE_SIZE \| PAGE_SIZE - 1 \),
+ sysfs_emit(buf,
...);
...>
}
@depends on patch@
identifier show, dev, attr, buf, rc;
@@
ssize_t show(struct device *dev, struct device_attribute *attr, char *buf)
{
<...
rc =
- snprintf(buf, \( PAGE_SIZE \| PAGE_SIZE - 1 \),
+ sysfs_emit(buf,
...);
...>
}
While at it, remove unnecessary braces and as well as unnecessary
else after return statements to address checkpatch warnings in the
resulting patch.
Cc: Zihao Tang <tangzihao1@hisilicon.com>
Cc: Jay Fang <f.fangjian@huawei.com>
Signed-off-by: Guenter Roeck <linux@roeck-us.net>
2021-03-22 06:49:10 +03:00
ret = sysfs_emit ( buf , " %d \n " , val ) ;
2020-05-05 03:59:45 +03:00
abort :
mutex_unlock ( & data - > config_lock ) ;
return ret ;
}
static ssize_t ina226_alert_store ( struct device * dev ,
struct device_attribute * da ,
const char * buf , size_t count )
{
struct sensor_device_attribute * attr = to_sensor_dev_attr ( da ) ;
struct ina2xx_data * data = dev_get_drvdata ( dev ) ;
unsigned long val ;
int ret ;
ret = kstrtoul ( buf , 10 , & val ) ;
if ( ret < 0 )
return ret ;
/*
* Clear all alerts first to avoid accidentally triggering ALERT pin
* due to register write sequence . Then , only enable the alert
* if the value is non - zero .
*/
mutex_lock ( & data - > config_lock ) ;
ret = regmap_update_bits ( data - > regmap , INA226_MASK_ENABLE ,
INA226_ALERT_CONFIG_MASK , 0 ) ;
if ( ret < 0 )
goto abort ;
ret = regmap_write ( data - > regmap , INA226_ALERT_LIMIT ,
ina226_alert_to_reg ( data , attr - > index , val ) ) ;
if ( ret < 0 )
goto abort ;
if ( val ! = 0 ) {
ret = regmap_update_bits ( data - > regmap , INA226_MASK_ENABLE ,
INA226_ALERT_CONFIG_MASK ,
BIT ( attr - > index ) ) ;
if ( ret < 0 )
goto abort ;
}
ret = count ;
abort :
mutex_unlock ( & data - > config_lock ) ;
return ret ;
}
static ssize_t ina226_alarm_show ( struct device * dev ,
struct device_attribute * da , char * buf )
{
struct sensor_device_attribute * attr = to_sensor_dev_attr ( da ) ;
struct ina2xx_data * data = dev_get_drvdata ( dev ) ;
int regval ;
int alarm = 0 ;
int ret ;
ret = regmap_read ( data - > regmap , INA226_MASK_ENABLE , & regval ) ;
if ( ret )
return ret ;
alarm = ( regval & BIT ( attr - > index ) ) & &
( regval & INA226_ALERT_FUNCTION_FLAG ) ;
2021-03-16 14:00:57 +03:00
return sysfs_emit ( buf , " %d \n " , alarm ) ;
2020-05-05 03:59:45 +03:00
}
2017-11-22 18:32:15 +03:00
/*
* In order to keep calibration register value fixed , the product
* of current_lsb and shunt_resistor should also be fixed and equal
* to shunt_voltage_lsb = 1 / shunt_div multiplied by 10 ^ 9 in order
* to keep the scale .
*/
static int ina2xx_set_shunt ( struct ina2xx_data * data , long val )
{
unsigned int dividend = DIV_ROUND_CLOSEST ( 1000000000 ,
data - > config - > shunt_div ) ;
if ( val < = 0 | | val > dividend )
return - EINVAL ;
mutex_lock ( & data - > config_lock ) ;
data - > rshunt = val ;
data - > current_lsb_uA = DIV_ROUND_CLOSEST ( dividend , val ) ;
data - > power_lsb_uW = data - > config - > power_lsb_factor *
data - > current_lsb_uA ;
mutex_unlock ( & data - > config_lock ) ;
return 0 ;
}
2018-12-06 22:06:23 +03:00
static ssize_t ina2xx_shunt_show ( struct device * dev ,
struct device_attribute * da , char * buf )
2018-08-14 10:09:37 +03:00
{
struct ina2xx_data * data = dev_get_drvdata ( dev ) ;
2021-03-16 14:00:57 +03:00
return sysfs_emit ( buf , " %li \n " , data - > rshunt ) ;
2018-08-14 10:09:37 +03:00
}
2018-12-06 22:06:23 +03:00
static ssize_t ina2xx_shunt_store ( struct device * dev ,
2017-11-22 18:32:15 +03:00
struct device_attribute * da ,
const char * buf , size_t count )
2015-01-05 17:20:55 +03:00
{
unsigned long val ;
int status ;
2015-10-28 14:04:53 +03:00
struct ina2xx_data * data = dev_get_drvdata ( dev ) ;
2015-01-05 17:20:55 +03:00
status = kstrtoul ( buf , 10 , & val ) ;
if ( status < 0 )
return status ;
2017-11-22 18:32:15 +03:00
status = ina2xx_set_shunt ( data , val ) ;
2015-01-05 17:20:55 +03:00
if ( status < 0 )
return status ;
return count ;
}
2018-12-06 22:06:23 +03:00
static ssize_t ina226_interval_store ( struct device * dev ,
struct device_attribute * da ,
const char * buf , size_t count )
2015-01-09 19:03:42 +03:00
{
struct ina2xx_data * data = dev_get_drvdata ( dev ) ;
unsigned long val ;
int status ;
status = kstrtoul ( buf , 10 , & val ) ;
if ( status < 0 )
return status ;
if ( val > INT_MAX | | val = = 0 )
return - EINVAL ;
2015-10-28 14:04:53 +03:00
status = regmap_update_bits ( data - > regmap , INA2XX_CONFIG ,
INA226_AVG_RD_MASK ,
ina226_interval_to_reg ( val ) ) ;
2015-01-09 19:03:42 +03:00
if ( status < 0 )
return status ;
return count ;
}
2018-12-06 22:06:23 +03:00
static ssize_t ina226_interval_show ( struct device * dev ,
2015-01-09 19:03:42 +03:00
struct device_attribute * da , char * buf )
{
2015-10-28 14:04:53 +03:00
struct ina2xx_data * data = dev_get_drvdata ( dev ) ;
int status ;
unsigned int regval ;
2015-01-09 19:03:42 +03:00
2015-10-28 14:04:53 +03:00
status = regmap_read ( data - > regmap , INA2XX_CONFIG , & regval ) ;
if ( status )
return status ;
2015-01-09 19:03:42 +03:00
2021-03-16 14:00:57 +03:00
return sysfs_emit ( buf , " %d \n " , ina226_reg_to_interval ( regval ) ) ;
2015-01-09 19:03:42 +03:00
}
2012-05-12 12:36:38 +04:00
/* shunt voltage */
2018-12-06 22:06:23 +03:00
static SENSOR_DEVICE_ATTR_RO ( in0_input , ina2xx_value , INA2XX_SHUNT_VOLTAGE ) ;
2020-05-05 03:59:45 +03:00
/* shunt voltage over/under voltage alert setting and alarm */
static SENSOR_DEVICE_ATTR_RW ( in0_crit , ina226_alert ,
INA226_SHUNT_OVER_VOLTAGE_BIT ) ;
static SENSOR_DEVICE_ATTR_RW ( in0_lcrit , ina226_alert ,
INA226_SHUNT_UNDER_VOLTAGE_BIT ) ;
static SENSOR_DEVICE_ATTR_RO ( in0_crit_alarm , ina226_alarm ,
INA226_SHUNT_OVER_VOLTAGE_BIT ) ;
static SENSOR_DEVICE_ATTR_RO ( in0_lcrit_alarm , ina226_alarm ,
INA226_SHUNT_UNDER_VOLTAGE_BIT ) ;
2012-05-12 12:36:38 +04:00
/* bus voltage */
2018-12-06 22:06:23 +03:00
static SENSOR_DEVICE_ATTR_RO ( in1_input , ina2xx_value , INA2XX_BUS_VOLTAGE ) ;
2020-05-05 03:59:45 +03:00
/* bus voltage over/under voltage alert setting and alarm */
static SENSOR_DEVICE_ATTR_RW ( in1_crit , ina226_alert ,
INA226_BUS_OVER_VOLTAGE_BIT ) ;
static SENSOR_DEVICE_ATTR_RW ( in1_lcrit , ina226_alert ,
INA226_BUS_UNDER_VOLTAGE_BIT ) ;
static SENSOR_DEVICE_ATTR_RO ( in1_crit_alarm , ina226_alarm ,
INA226_BUS_OVER_VOLTAGE_BIT ) ;
static SENSOR_DEVICE_ATTR_RO ( in1_lcrit_alarm , ina226_alarm ,
INA226_BUS_UNDER_VOLTAGE_BIT ) ;
2012-05-12 12:36:38 +04:00
/* calculated current */
2018-12-06 22:06:23 +03:00
static SENSOR_DEVICE_ATTR_RO ( curr1_input , ina2xx_value , INA2XX_CURRENT ) ;
2012-05-12 12:36:38 +04:00
/* calculated power */
2018-12-06 22:06:23 +03:00
static SENSOR_DEVICE_ATTR_RO ( power1_input , ina2xx_value , INA2XX_POWER ) ;
2020-05-05 03:59:45 +03:00
/* over-limit power alert setting and alarm */
static SENSOR_DEVICE_ATTR_RW ( power1_crit , ina226_alert ,
INA226_POWER_OVER_LIMIT_BIT ) ;
static SENSOR_DEVICE_ATTR_RO ( power1_crit_alarm , ina226_alarm ,
INA226_POWER_OVER_LIMIT_BIT ) ;
2012-05-12 12:36:38 +04:00
2015-01-05 17:20:55 +03:00
/* shunt resistance */
2018-12-06 22:06:23 +03:00
static SENSOR_DEVICE_ATTR_RW ( shunt_resistor , ina2xx_shunt , INA2XX_CALIBRATION ) ;
2015-01-05 17:20:55 +03:00
2015-01-09 19:03:42 +03:00
/* update interval (ina226 only) */
2018-12-06 22:06:23 +03:00
static SENSOR_DEVICE_ATTR_RW ( update_interval , ina226_interval , 0 ) ;
2015-01-09 19:03:42 +03:00
2012-05-12 12:36:38 +04:00
/* pointers to created device attributes */
2013-09-03 00:16:19 +04:00
static struct attribute * ina2xx_attrs [ ] = {
2012-05-12 12:36:38 +04:00
& sensor_dev_attr_in0_input . dev_attr . attr ,
& sensor_dev_attr_in1_input . dev_attr . attr ,
& sensor_dev_attr_curr1_input . dev_attr . attr ,
& sensor_dev_attr_power1_input . dev_attr . attr ,
2015-01-05 17:20:55 +03:00
& sensor_dev_attr_shunt_resistor . dev_attr . attr ,
2012-05-12 12:36:38 +04:00
NULL ,
} ;
2015-01-09 19:03:42 +03:00
static const struct attribute_group ina2xx_group = {
. attrs = ina2xx_attrs ,
} ;
static struct attribute * ina226_attrs [ ] = {
2020-05-05 03:59:45 +03:00
& sensor_dev_attr_in0_crit . dev_attr . attr ,
& sensor_dev_attr_in0_lcrit . dev_attr . attr ,
& sensor_dev_attr_in0_crit_alarm . dev_attr . attr ,
& sensor_dev_attr_in0_lcrit_alarm . dev_attr . attr ,
& sensor_dev_attr_in1_crit . dev_attr . attr ,
& sensor_dev_attr_in1_lcrit . dev_attr . attr ,
& sensor_dev_attr_in1_crit_alarm . dev_attr . attr ,
& sensor_dev_attr_in1_lcrit_alarm . dev_attr . attr ,
& sensor_dev_attr_power1_crit . dev_attr . attr ,
& sensor_dev_attr_power1_crit_alarm . dev_attr . attr ,
2015-01-09 19:03:42 +03:00
& sensor_dev_attr_update_interval . dev_attr . attr ,
NULL ,
} ;
static const struct attribute_group ina226_group = {
. attrs = ina226_attrs ,
} ;
2012-05-12 12:36:38 +04:00
2020-08-13 19:02:22 +03:00
static const struct i2c_device_id ina2xx_id [ ] ;
static int ina2xx_probe ( struct i2c_client * client )
2012-05-12 12:36:38 +04:00
{
2013-09-03 00:16:19 +04:00
struct device * dev = & client - > dev ;
struct ina2xx_data * data ;
struct device * hwmon_dev ;
u32 val ;
2015-01-09 19:03:42 +03:00
int ret , group = 0 ;
2017-02-24 16:13:00 +03:00
enum ina2xx_ids chip ;
if ( client - > dev . of_node )
2023-08-10 12:31:46 +03:00
chip = ( uintptr_t ) of_device_get_match_data ( & client - > dev ) ;
2017-02-24 16:13:00 +03:00
else
2020-08-13 19:02:22 +03:00
chip = i2c_match_id ( ina2xx_id , client ) - > driver_data ;
2012-05-12 12:36:38 +04:00
2013-09-03 00:16:19 +04:00
data = devm_kzalloc ( dev , sizeof ( * data ) , GFP_KERNEL ) ;
2012-05-12 12:36:38 +04:00
if ( ! data )
return - ENOMEM ;
/* set the device type */
2017-02-24 16:13:00 +03:00
data - > config = & ina2xx_config [ chip ] ;
2018-01-15 16:58:21 +03:00
mutex_init ( & data - > config_lock ) ;
2015-01-09 19:03:42 +03:00
2015-10-27 12:51:08 +03:00
if ( of_property_read_u32 ( dev - > of_node , " shunt-resistor " , & val ) < 0 ) {
struct ina2xx_platform_data * pdata = dev_get_platdata ( dev ) ;
if ( pdata )
val = pdata - > shunt_uohms ;
else
val = INA2XX_RSHUNT_DEFAULT ;
}
2017-11-22 18:32:15 +03:00
ina2xx_set_shunt ( data , val ) ;
2015-10-27 12:51:08 +03:00
2015-10-28 14:04:53 +03:00
ina2xx_regmap_config . max_register = data - > config - > registers ;
data - > regmap = devm_regmap_init_i2c ( client , & ina2xx_regmap_config ) ;
if ( IS_ERR ( data - > regmap ) ) {
dev_err ( dev , " failed to allocate register map \n " ) ;
return PTR_ERR ( data - > regmap ) ;
}
2023-04-07 19:05:08 +03:00
ret = devm_regulator_get_enable ( dev , " vs " ) ;
if ( ret )
return dev_err_probe ( dev , ret , " failed to enable vs regulator \n " ) ;
2015-01-05 17:20:52 +03:00
ret = ina2xx_init ( data ) ;
2014-11-27 12:59:06 +03:00
if ( ret < 0 ) {
2015-01-05 17:20:52 +03:00
dev_err ( dev , " error configuring the device: %d \n " , ret ) ;
2014-11-27 12:59:06 +03:00
return - ENODEV ;
}
2012-05-12 12:36:38 +04:00
2015-01-09 19:03:42 +03:00
data - > groups [ group + + ] = & ina2xx_group ;
2018-11-10 03:42:14 +03:00
if ( chip = = ina226 )
2015-01-09 19:03:42 +03:00
data - > groups [ group + + ] = & ina226_group ;
2013-09-03 00:16:19 +04:00
hwmon_dev = devm_hwmon_device_register_with_groups ( dev , client - > name ,
2015-01-09 19:03:42 +03:00
data , data - > groups ) ;
2013-09-03 00:16:19 +04:00
if ( IS_ERR ( hwmon_dev ) )
return PTR_ERR ( hwmon_dev ) ;
2012-05-12 12:36:38 +04:00
2013-09-03 00:16:19 +04:00
dev_info ( dev , " power monitor %s (Rshunt = %li uOhm) \n " ,
2018-11-10 03:42:14 +03:00
client - > name , data - > rshunt ) ;
2012-05-12 22:21:01 +04:00
2012-05-12 12:36:38 +04:00
return 0 ;
}
static const struct i2c_device_id ina2xx_id [ ] = {
{ " ina219 " , ina219 } ,
2012-05-12 22:33:11 +04:00
{ " ina220 " , ina219 } ,
2012-05-12 12:36:38 +04:00
{ " ina226 " , ina226 } ,
2012-05-12 22:33:11 +04:00
{ " ina230 " , ina226 } ,
2015-01-15 04:34:58 +03:00
{ " ina231 " , ina226 } ,
2012-05-12 12:36:38 +04:00
{ }
} ;
MODULE_DEVICE_TABLE ( i2c , ina2xx_id ) ;
2019-04-04 18:00:57 +03:00
static const struct of_device_id __maybe_unused ina2xx_of_match [ ] = {
2017-02-24 16:13:00 +03:00
{
. compatible = " ti,ina219 " ,
. data = ( void * ) ina219
} ,
{
. compatible = " ti,ina220 " ,
. data = ( void * ) ina219
} ,
{
. compatible = " ti,ina226 " ,
. data = ( void * ) ina226
} ,
{
. compatible = " ti,ina230 " ,
. data = ( void * ) ina226
} ,
{
. compatible = " ti,ina231 " ,
. data = ( void * ) ina226
} ,
{ } ,
} ;
MODULE_DEVICE_TABLE ( of , ina2xx_of_match ) ;
2012-05-12 12:36:38 +04:00
static struct i2c_driver ina2xx_driver = {
. driver = {
. name = " ina2xx " ,
2017-02-24 16:13:00 +03:00
. of_match_table = of_match_ptr ( ina2xx_of_match ) ,
2012-05-12 12:36:38 +04:00
} ,
2023-05-05 16:17:18 +03:00
. probe = ina2xx_probe ,
2012-05-12 12:36:38 +04:00
. id_table = ina2xx_id ,
} ;
2012-10-08 16:40:35 +04:00
module_i2c_driver ( ina2xx_driver ) ;
2012-05-12 12:36:38 +04:00
MODULE_AUTHOR ( " Lothar Felten <l-felten@ti.com> " ) ;
MODULE_DESCRIPTION ( " ina2xx driver " ) ;
MODULE_LICENSE ( " GPL " ) ;