[PATCH 2/4] hwmon: (ina2xx) Make max expected current configurable

Guenter Roeck linux at roeck-us.net
Thu Sep 28 06:25:54 PDT 2017


On 09/28/2017 05:50 AM, Maciej Purski wrote:
> Max expected current is used for calculating calibration register value,
> Current LSB and Power LSB according to equations found in ina datasheet.
> Max expected current is now implicitly set to default value,
> which is 2^15, thanks to which Current LSB is equal to 1 mA and
> Power LSB is equal to 20000 uW or 25000 uW depending on ina model.
> 
> Make max expected current configurable, just like it's already done
> with shunt resistance: from device tree, platform_data or later
> from sysfs. On each max_expected_current change, calculate new values
> for Current LSB and Power LSB. According to datasheet Current LSB should
> be calculated by dividing max expected current by 2^15, as values read
> from device registers are in this case 16-bit integers. Power LSB
> is calculated by multiplying Current LSB by a factor, which is defined
> in ina documentation.
> 
> Signed-off-by: Maciej Purski <m.purski at samsung.com>
> ---
>   drivers/hwmon/ina2xx.c | 105 +++++++++++++++++++++++++++++++++++++++++++------
>   1 file changed, 93 insertions(+), 12 deletions(-)
> 
> diff --git a/drivers/hwmon/ina2xx.c b/drivers/hwmon/ina2xx.c
> index 62e38fa..d956013 100644
> --- a/drivers/hwmon/ina2xx.c
> +++ b/drivers/hwmon/ina2xx.c
> @@ -80,6 +80,8 @@
>   /* common attrs, ina226 attrs and NULL */
>   #define INA2XX_MAX_ATTRIBUTE_GROUPS	3
>   
> +#define INA2XX_MAX_EXPECTED_A_DEFAULT  (1 << 15)       /* current_lsb = 1 mA */
> +
>   /*
>    * Both bus voltage and shunt voltage conversion times for ina226 are set
>    * to 0b0100 on POR, which translates to 2200 microseconds in total.
> @@ -100,13 +102,16 @@ struct ina2xx_config {
>   	int shunt_div;
>   	int bus_voltage_shift;
>   	int bus_voltage_lsb;	/* uV */
> -	int power_lsb;		/* uW */
> +	int power_lsb_factor;
>   };
>   
>   struct ina2xx_data {
>   	const struct ina2xx_config *config;
>   
> -	long rshunt;
> +	long rshunt;				/* uOhms */
> +	unsigned int max_expected_current;	/* mA */
> +	int current_lsb;			/* uA */
> +	int power_lsb;				/* uW */
>   	struct mutex config_lock;
>   	struct regmap *regmap;
>   
> @@ -121,7 +126,7 @@ static const struct ina2xx_config ina2xx_config[] = {
>   		.shunt_div = 100,
>   		.bus_voltage_shift = 3,
>   		.bus_voltage_lsb = 4000,
> -		.power_lsb = 20000,
> +		.power_lsb_factor = 20,
>   	},
>   	[ina226] = {
>   		.config_default = INA226_CONFIG_DEFAULT,
> @@ -130,7 +135,7 @@ static const struct ina2xx_config ina2xx_config[] = {
>   		.shunt_div = 400,
>   		.bus_voltage_shift = 0,
>   		.bus_voltage_lsb = 1250,
> -		.power_lsb = 25000,
> +		.power_lsb_factor = 25,
>   	},
>   };
>   
> @@ -169,10 +174,17 @@ static u16 ina226_interval_to_reg(int interval)
>   	return INA226_SHIFT_AVG(avg_bits);
>   }
>   
> +/*
> + * Calculate calibration value according to equation 1 in ina226 datasheet
> + * http://www.ti.com/lit/ds/symlink/ina226.pdf.
> + * Current LSB is in uA and RShunt is in uOhms, so in order to keep
> + * calibration value scaled RShunt must be converted to mOhms.
> + */
>   static int ina2xx_calibrate(struct ina2xx_data *data)
>   {
> +	int r_shunt = DIV_ROUND_CLOSEST(data->rshunt, 1000);
>   	u16 val = DIV_ROUND_CLOSEST(data->config->calibration_factor,
> -				    data->rshunt);
> +				    data->current_lsb * r_shunt);
>   
>   	return regmap_write(data->regmap, INA2XX_CALIBRATION, val);
>   }
> @@ -187,13 +199,28 @@ static int ina2xx_init(struct ina2xx_data *data)
>   	if (ret < 0)
>   		return ret;
>   
> -	/*
> -	 * Set current LSB to 1mA, shunt is in uOhms
> -	 * (equation 13 in datasheet).
> -	 */
>   	return ina2xx_calibrate(data);
>   }
>   
> +/*
> + * Set max_expected_current (mA) and calculate current_lsb (uA),
> + * according to equation 2 in ina226 datasheet. Power LSB is calculated
> + * by multiplying Current LSB by a given factor, which may vary depending
> + * on ina version.
> + */
> +static int set_max_expected_current(struct ina2xx_data *data, unsigned int val)
> +{
> +	if (val <= 0 || val > data->config->calibration_factor)
> +		return -EINVAL;
> +
> +	data->max_expected_current = val;
> +	data->current_lsb = DIV_ROUND_CLOSEST(data->max_expected_current * 1000,
> +					      1 << 15);
> +	data->power_lsb = data->current_lsb * data->config->power_lsb_factor;
> +
> +	return 0;
> +}
> +
>   static int ina2xx_read_reg(struct device *dev, int reg, unsigned int *regval)
>   {
>   	struct ina2xx_data *data = dev_get_drvdata(dev);
> @@ -268,11 +295,11 @@ static int ina2xx_get_value(struct ina2xx_data *data, u8 reg,
>   		val = DIV_ROUND_CLOSEST(val, 1000);
>   		break;
>   	case INA2XX_POWER:
> -		val = regval * data->config->power_lsb;
> +		val = regval * data->power_lsb;
>   		break;
>   	case INA2XX_CURRENT:
> -		/* signed register, LSB=1mA (selected), in mA */
> -		val = (s16)regval;
> +		val = (s16)regval * data->current_lsb;
> +		val = DIV_ROUND_CLOSEST(val, 1000);
>   		break;
>   	case INA2XX_CALIBRATION:
>   		val = DIV_ROUND_CLOSEST(data->config->calibration_factor,
> @@ -369,6 +396,39 @@ static ssize_t ina226_show_interval(struct device *dev,
>   	return snprintf(buf, PAGE_SIZE, "%d\n", ina226_reg_to_interval(regval));
>   }
>   
> +static ssize_t ina2xx_max_expected_current_show(struct device *dev,
> +					  struct device_attribute *attr,
> +					  char *buf)
> +{
> +	struct ina2xx_data *data = dev_get_drvdata(dev);
> +
> +	return sprintf(buf, "%d\n", data->max_expected_current);
> +}
> +
> +static ssize_t ina2xx_max_expected_current_set(struct device *dev,
> +					   struct device_attribute *attr,
> +					   const char *buf, size_t len)
> +{
> +	struct ina2xx_data *data = dev_get_drvdata(dev);
> +	unsigned long val;
> +	int ret;
> +
> +	ret = kstrtoul((const char *) buf, 10, &val);
> +	if (ret)
> +		return ret;
> +
> +	ret = set_max_expected_current(data, val);
> +	if (ret)
> +		return ret;
> +
> +	/* Update the Calibration register */
> +	ret = ina2xx_calibrate(data);
> +	if (ret)
> +		return ret;
> +
> +	return len;
> +}
> +
>   /* shunt voltage */
>   static SENSOR_DEVICE_ATTR(in0_input, S_IRUGO, ina2xx_show_value, NULL,
>   			  INA2XX_SHUNT_VOLTAGE);
> @@ -390,6 +450,11 @@ static SENSOR_DEVICE_ATTR(shunt_resistor, S_IRUGO | S_IWUSR,
>   			  ina2xx_show_value, ina2xx_set_shunt,
>   			  INA2XX_CALIBRATION);
>   
> +/* max expected current */
> +static SENSOR_DEVICE_ATTR(max_expected_current, S_IRUGO | S_IWUSR,
> +			  ina2xx_max_expected_current_show,
> +			  ina2xx_max_expected_current_set, 0);
> +
>   /* update interval (ina226 only) */
>   static SENSOR_DEVICE_ATTR(update_interval, S_IRUGO | S_IWUSR,
>   			  ina226_show_interval, ina226_set_interval, 0);
> @@ -401,6 +466,7 @@ static struct attribute *ina2xx_attrs[] = {
>   	&sensor_dev_attr_curr1_input.dev_attr.attr,
>   	&sensor_dev_attr_power1_input.dev_attr.attr,
>   	&sensor_dev_attr_shunt_resistor.dev_attr.attr,
> +	&sensor_dev_attr_max_expected_current.dev_attr.attr,

We do have standard attributes (presumably currX_max or possibly currX_crit).
Please use them, or explain in detail why you don't.

Guenter

>   	NULL,
>   };
>   
> @@ -453,6 +519,21 @@ static int ina2xx_probe(struct i2c_client *client,
>   
>   	data->rshunt = val;
>   
> +	if (of_property_read_u32(dev->of_node, "max-expected-current",
> +				 &val) < 0) {
> +		struct ina2xx_platform_data *pdata =
> +		    dev_get_platdata(&client->dev);
> +
> +		if (pdata && pdata->max_mA != 0)
> +			val = pdata->max_mA;
> +		else
> +			val = INA2XX_MAX_EXPECTED_A_DEFAULT;
> +	}
> +
> +	ret = set_max_expected_current(data, val);
> +	if (ret < 0)
> +		return ret;
> +
>   	ina2xx_regmap_config.max_register = data->config->registers;
>   
>   	data->regmap = devm_regmap_init_i2c(client, &ina2xx_regmap_config);
> 




More information about the linux-arm-kernel mailing list