2019-05-27 09:55:06 +03:00
// SPDX-License-Identifier: GPL-2.0-or-later
2014-01-14 09:11:56 +04:00
/*
* Driver for Linear Technology LTC4222 Dual Hot Swap controller
*
* Copyright ( c ) 2014 Guenter Roeck
*/
# include <linux/kernel.h>
# include <linux/module.h>
# include <linux/err.h>
# include <linux/slab.h>
# include <linux/bitops.h>
# include <linux/i2c.h>
# include <linux/hwmon.h>
# include <linux/hwmon-sysfs.h>
# include <linux/jiffies.h>
# include <linux/regmap.h>
/* chip registers */
# define LTC4222_CONTROL1 0xd0
# define LTC4222_ALERT1 0xd1
# define LTC4222_STATUS1 0xd2
# define LTC4222_FAULT1 0xd3
# define LTC4222_CONTROL2 0xd4
# define LTC4222_ALERT2 0xd5
# define LTC4222_STATUS2 0xd6
# define LTC4222_FAULT2 0xd7
# define LTC4222_SOURCE1 0xd8
# define LTC4222_SOURCE2 0xda
# define LTC4222_ADIN1 0xdc
# define LTC4222_ADIN2 0xde
# define LTC4222_SENSE1 0xe0
# define LTC4222_SENSE2 0xe2
# define LTC4222_ADC_CONTROL 0xe4
/*
* Fault register bits
*/
# define FAULT_OV BIT(0)
# define FAULT_UV BIT(1)
# define FAULT_OC BIT(2)
# define FAULT_POWER_BAD BIT(3)
# define FAULT_FET_BAD BIT(5)
/* Return the voltage from the given register in mV or mA */
static int ltc4222_get_value ( struct device * dev , u8 reg )
{
struct regmap * regmap = dev_get_drvdata ( dev ) ;
unsigned int val ;
u8 buf [ 2 ] ;
int ret ;
ret = regmap_bulk_read ( regmap , reg , buf , 2 ) ;
if ( ret < 0 )
return ret ;
val = ( ( buf [ 0 ] < < 8 ) + buf [ 1 ] ) > > 6 ;
switch ( reg ) {
case LTC4222_ADIN1 :
case LTC4222_ADIN2 :
/* 1.25 mV resolution. Convert to mV. */
val = DIV_ROUND_CLOSEST ( val * 5 , 4 ) ;
break ;
case LTC4222_SOURCE1 :
case LTC4222_SOURCE2 :
/* 31.25 mV resolution. Convert to mV. */
val = DIV_ROUND_CLOSEST ( val * 125 , 4 ) ;
break ;
case LTC4222_SENSE1 :
case LTC4222_SENSE2 :
/*
* 62.5 uV resolution . Convert to current as measured with
* an 1 mOhm sense resistor , in mA . If a different sense
* resistor is installed , calculate the actual current by
* dividing the reported current by the sense resistor value
* in mOhm .
*/
val = DIV_ROUND_CLOSEST ( val * 125 , 2 ) ;
break ;
default :
return - EINVAL ;
}
return val ;
}
2018-12-11 01:02:14 +03:00
static ssize_t ltc4222_value_show ( struct device * dev ,
2014-01-14 09:11:56 +04:00
struct device_attribute * da , char * buf )
{
struct sensor_device_attribute * attr = to_sensor_dev_attr ( da ) ;
int value ;
value = ltc4222_get_value ( dev , attr - > index ) ;
if ( value < 0 )
return value ;
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
return sysfs_emit ( buf , " %d \n " , value ) ;
2014-01-14 09:11:56 +04:00
}
2018-12-11 01:02:14 +03:00
static ssize_t ltc4222_bool_show ( struct device * dev ,
2014-01-14 09:11:56 +04:00
struct device_attribute * da , char * buf )
{
struct sensor_device_attribute_2 * attr = to_sensor_dev_attr_2 ( da ) ;
struct regmap * regmap = dev_get_drvdata ( dev ) ;
unsigned int fault ;
int ret ;
ret = regmap_read ( regmap , attr - > nr , & fault ) ;
if ( ret < 0 )
return ret ;
fault & = attr - > index ;
if ( fault ) /* Clear reported faults in chip register */
regmap_update_bits ( regmap , attr - > nr , attr - > index , 0 ) ;
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
return sysfs_emit ( buf , " %d \n " , ! ! fault ) ;
2014-01-14 09:11:56 +04:00
}
/* Voltages */
2018-12-11 01:02:14 +03:00
static SENSOR_DEVICE_ATTR_RO ( in1_input , ltc4222_value , LTC4222_SOURCE1 ) ;
static SENSOR_DEVICE_ATTR_RO ( in2_input , ltc4222_value , LTC4222_ADIN1 ) ;
static SENSOR_DEVICE_ATTR_RO ( in3_input , ltc4222_value , LTC4222_SOURCE2 ) ;
static SENSOR_DEVICE_ATTR_RO ( in4_input , ltc4222_value , LTC4222_ADIN2 ) ;
2014-01-14 09:11:56 +04:00
/*
* Voltage alarms
* UV / OV faults are associated with the input voltage , and power bad and fet
* faults are associated with the output voltage .
*/
2018-12-11 01:02:14 +03:00
static SENSOR_DEVICE_ATTR_2_RO ( in1_min_alarm , ltc4222_bool , LTC4222_FAULT1 ,
FAULT_UV ) ;
static SENSOR_DEVICE_ATTR_2_RO ( in1_max_alarm , ltc4222_bool , LTC4222_FAULT1 ,
FAULT_OV ) ;
static SENSOR_DEVICE_ATTR_2_RO ( in2_alarm , ltc4222_bool , LTC4222_FAULT1 ,
FAULT_POWER_BAD | FAULT_FET_BAD ) ;
static SENSOR_DEVICE_ATTR_2_RO ( in3_min_alarm , ltc4222_bool , LTC4222_FAULT2 ,
FAULT_UV ) ;
static SENSOR_DEVICE_ATTR_2_RO ( in3_max_alarm , ltc4222_bool , LTC4222_FAULT2 ,
FAULT_OV ) ;
static SENSOR_DEVICE_ATTR_2_RO ( in4_alarm , ltc4222_bool , LTC4222_FAULT2 ,
FAULT_POWER_BAD | FAULT_FET_BAD ) ;
2014-01-14 09:11:56 +04:00
/* Current (via sense resistor) */
2018-12-11 01:02:14 +03:00
static SENSOR_DEVICE_ATTR_RO ( curr1_input , ltc4222_value , LTC4222_SENSE1 ) ;
static SENSOR_DEVICE_ATTR_RO ( curr2_input , ltc4222_value , LTC4222_SENSE2 ) ;
2014-01-14 09:11:56 +04:00
/* Overcurrent alarm */
2018-12-11 01:02:14 +03:00
static SENSOR_DEVICE_ATTR_2_RO ( curr1_max_alarm , ltc4222_bool , LTC4222_FAULT1 ,
FAULT_OC ) ;
static SENSOR_DEVICE_ATTR_2_RO ( curr2_max_alarm , ltc4222_bool , LTC4222_FAULT2 ,
FAULT_OC ) ;
2014-01-14 09:11:56 +04:00
static struct attribute * ltc4222_attrs [ ] = {
& sensor_dev_attr_in1_input . dev_attr . attr ,
& sensor_dev_attr_in1_min_alarm . dev_attr . attr ,
& sensor_dev_attr_in1_max_alarm . dev_attr . attr ,
& sensor_dev_attr_in2_input . dev_attr . attr ,
& sensor_dev_attr_in2_alarm . dev_attr . attr ,
& sensor_dev_attr_in3_input . dev_attr . attr ,
& sensor_dev_attr_in3_min_alarm . dev_attr . attr ,
& sensor_dev_attr_in3_max_alarm . dev_attr . attr ,
& sensor_dev_attr_in4_input . dev_attr . attr ,
& sensor_dev_attr_in4_alarm . dev_attr . attr ,
& sensor_dev_attr_curr1_input . dev_attr . attr ,
& sensor_dev_attr_curr1_max_alarm . dev_attr . attr ,
& sensor_dev_attr_curr2_input . dev_attr . attr ,
& sensor_dev_attr_curr2_max_alarm . dev_attr . attr ,
NULL ,
} ;
ATTRIBUTE_GROUPS ( ltc4222 ) ;
2014-07-02 04:42:18 +04:00
static const struct regmap_config ltc4222_regmap_config = {
2014-01-14 09:11:56 +04:00
. reg_bits = 8 ,
. val_bits = 8 ,
. max_register = LTC4222_ADC_CONTROL ,
} ;
2020-08-13 19:02:22 +03:00
static int ltc4222_probe ( struct i2c_client * client )
2014-01-14 09:11:56 +04:00
{
struct device * dev = & client - > dev ;
struct device * hwmon_dev ;
struct regmap * regmap ;
regmap = devm_regmap_init_i2c ( client , & ltc4222_regmap_config ) ;
if ( IS_ERR ( regmap ) ) {
dev_err ( dev , " failed to allocate register map \n " ) ;
return PTR_ERR ( regmap ) ;
}
/* Clear faults */
regmap_write ( regmap , LTC4222_FAULT1 , 0x00 ) ;
regmap_write ( regmap , LTC4222_FAULT2 , 0x00 ) ;
hwmon_dev = devm_hwmon_device_register_with_groups ( dev , client - > name ,
regmap ,
ltc4222_groups ) ;
return PTR_ERR_OR_ZERO ( hwmon_dev ) ;
}
static const struct i2c_device_id ltc4222_id [ ] = {
{ " ltc4222 " , 0 } ,
{ }
} ;
MODULE_DEVICE_TABLE ( i2c , ltc4222_id ) ;
static struct i2c_driver ltc4222_driver = {
. driver = {
. name = " ltc4222 " ,
} ,
2023-05-05 16:17:18 +03:00
. probe = ltc4222_probe ,
2014-01-14 09:11:56 +04:00
. id_table = ltc4222_id ,
} ;
module_i2c_driver ( ltc4222_driver ) ;
MODULE_AUTHOR ( " Guenter Roeck <linux@roeck-us.net> " ) ;
MODULE_DESCRIPTION ( " LTC4222 driver " ) ;
MODULE_LICENSE ( " GPL " ) ;