mbox series

[v3,0/4] Make max expected current configurable for ina2xx drivers

Message ID 1507814282-1486-1-git-send-email-m.purski@samsung.com
Headers show
Series Make max expected current configurable for ina2xx drivers | expand

Message

Maciej Purski Oct. 12, 2017, 1:17 p.m. UTC
Hi all,

this patchset makes it possible to calibrate ina2xx drivers with different
calibration values, which are calculated using max expected current value.
It can be read from device tree, platform data or changed during run-time
using sysfs. If it isn't specified in device tree or platform data, the
driver uses default value, thanks to which the behaviour of the driver
doesn't change in this case.

There are two drivers for ina2xx: hwmon and iio. Changes are made
for both of them and their bindings as well.

These changes allow setting sensor's precision to the required by the board.
It is useful in Odroid XU3. Therefore this patchset also sets
max-expected-current in OdroidXU3 device tree to values from documentation.

Best Regards,

	Maciej Purski

---
Changes in v3:
- fix units inconsistency

Changes in v2:
- make scale attribute for current iio channel writable as instead of
  adding new attribute max_expected_current
- use currX_max standard attribute in hwmon instead of adding new
  attribute
- fix max expected current property name
- update commit messages

Maciej Purski (4):
  iio: adc: ina2xx: Make max expected current configurable
  hwmon: (ina2xx) Make max expected current configurable
  dt-bindings: hwmon: Add ti-max-expected-current-microamp property to
    ina2xx
  ARM: dts: Add ti-max-expected-current-microamp properties for ina231
    in Odroid XU3

 Documentation/devicetree/bindings/hwmon/ina2xx.txt |   4 +-
 Documentation/hwmon/ina2xx                         |   3 +
 arch/arm/boot/dts/exynos5422-odroidxu3.dts         |   4 +
 drivers/hwmon/ina2xx.c                             | 106 ++++++++++++++++++---
 drivers/iio/adc/ina2xx-adc.c                       |  97 ++++++++++++++-----
 include/linux/platform_data/ina2xx.h               |   2 +
 6 files changed, 178 insertions(+), 38 deletions(-)

Comments

Jonathan Cameron Oct. 15, 2017, 10:38 a.m. UTC | #1
On Thu, 12 Oct 2017 15:17:59 +0200
Maciej Purski <m.purski@samsung.com> wrote:

> Max expected current is used for calculating calibration register value,
> Current LSB and Power LSB according to equations found in ina datasheet:
> current_lsb = max_expected_current / 2^15
> calibration_register = 0.00512 / (current_lsb * r_shunt)
> power_lsb = 25 * current_lsb (for ina231, 230, 226)
> power_lsb = 20 * current_lsb (for older ones)
> 
> 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, but
> users have no impact on the precision of the device.
> 
> Make max expected current configurable from device tree or platform_data.
> Allow changing current_lsb from sysfs. It is exposed in sysfs as
> scale attribute for IIO current channel. Update calibration register and
> power_lsb value on each scale change.
> 
> Signed-off-by: Maciej Purski <m.purski@samsung.com>
I'm going to hold off on this until the discussion around Stefan's
point in the thread
Re: [2/3] iio: adc: ina2xx: Adhere to documented ABI, use Ohm instead of uOhm
is addressed.

There he points out that current_lsb and presumably the calibration register
are just conveniences, but that they result in lost precision.

As I understand it, Stefan's suggestion is that we always use a fixed
value and perform any 'calibration' by providing appropriate scale values
to let it be done in userspace where floating point is available.

Other than that fundamental question of whether this makes sense at all,
the below looks good to me.

Jonathan
> ---
>  drivers/iio/adc/ina2xx-adc.c         | 96 ++++++++++++++++++++++++++----------
>  include/linux/platform_data/ina2xx.h |  2 +
>  2 files changed, 73 insertions(+), 25 deletions(-)
> 
> diff --git a/drivers/iio/adc/ina2xx-adc.c b/drivers/iio/adc/ina2xx-adc.c
> index a16f8c6..be26dfc 100644
> --- a/drivers/iio/adc/ina2xx-adc.c
> +++ b/drivers/iio/adc/ina2xx-adc.c
> @@ -56,6 +56,7 @@
>  #define INA226_DEFAULT_IT		1110
>  
>  #define INA2XX_RSHUNT_DEFAULT           10000
> +#define INA2XX_MAX_EXPECTED_MA_DEFAULT	BIT(15)	/* current_lsb = 1 mA */
>  
>  /*
>   * bit masks for reading the settings in the configuration register
> @@ -114,7 +115,7 @@ struct ina2xx_config {
>  	int shunt_div;
>  	int bus_voltage_shift;
>  	int bus_voltage_lsb;	/* uV */
> -	int power_lsb;		/* uW */
> +	int power_lsb_factor;
>  	enum ina2xx_ids chip_id;
>  };
>  
> @@ -123,7 +124,9 @@ struct ina2xx_chip_info {
>  	struct task_struct *task;
>  	const struct ina2xx_config *config;
>  	struct mutex state_lock;
> -	unsigned int shunt_resistor;
> +	unsigned int shunt_resistor;		/* uOhms */
> +	int current_lsb;			/* uA */
> +	int power_lsb;				/* uW */
>  	int avg;
>  	int int_time_vbus; /* Bus voltage integration time uS */
>  	int int_time_vshunt; /* Shunt voltage integration time uS */
> @@ -137,7 +140,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,
>  		.chip_id = ina219,
>  	},
>  	[ina226] = {
> @@ -146,7 +149,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,
>  		.chip_id = ina226,
>  	},
>  };
> @@ -210,14 +213,15 @@ static int ina2xx_read_raw(struct iio_dev *indio_dev,
>  
>  		case INA2XX_POWER:
>  			/* processed (mW) = raw*lsb (uW) / 1000 */
> -			*val = chip->config->power_lsb;
> +			*val = chip->power_lsb;
>  			*val2 = 1000;
>  			return IIO_VAL_FRACTIONAL;
>  
>  		case INA2XX_CURRENT:
> -			/* processed (mA) = raw (mA) */
> -			*val = 1;
> -			return IIO_VAL_INT;
> +			/* processed (mA) = raw*lsb (uA) / 1000 */
> +			*val = chip->current_lsb;
> +			*val2 = 1000;
> +			return IIO_VAL_FRACTIONAL;
>  		}
>  	}
>  
> @@ -353,6 +357,36 @@ static int ina219_set_int_time_vshunt(struct ina2xx_chip_info *chip,
>  	return 0;
>  }
>  
> +/*
> + * 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 RShunt should be
> + * converted to mOhms in order to keep the scale.
> + * There is no need to expose the CALIBRATION register
> + * to the user for now. But we need to reset this register
> + * if the user updates RShunt or max expected current after driver
> + * init, e.g upon reading an EEPROM/Probe-type value.
> + */
> +static int ina2xx_set_calibration(struct ina2xx_chip_info *chip)
> +{
> +	unsigned int rshunt = DIV_ROUND_CLOSEST(chip->shunt_resistor, 1000);
> +	u16 regval = DIV_ROUND_CLOSEST(chip->config->calibration_factor,
> +				     chip->current_lsb * rshunt);
> +
> +	return regmap_write(chip->regmap, INA2XX_CALIBRATION, regval);
> +}
> +
> +static int ina2xx_set_scale(struct ina2xx_chip_info *chip, unsigned int val)
> +{
> +	if (val <= 0 || val > chip->config->calibration_factor)
> +		return -EINVAL;
> +
> +	chip->current_lsb = val;
> +	chip->power_lsb = chip->current_lsb * chip->config->power_lsb_factor;
> +
> +	return 0;
> +}
> +
>  static int ina2xx_write_raw(struct iio_dev *indio_dev,
>  			    struct iio_chan_spec const *chan,
>  			    int val, int val2, long mask)
> @@ -394,7 +428,18 @@ static int ina2xx_write_raw(struct iio_dev *indio_dev,
>  							       &tmp);
>  		}
>  		break;
> -
> +	case IIO_CHAN_INFO_SCALE:
> +		switch (chan->type) {
> +		case IIO_CURRENT:
> +			val = val * 1000 + DIV_ROUND_CLOSEST(val2, 1000);
> +			ret = ina2xx_set_scale(chip, val);
> +			if (!ret)
> +				ret = ina2xx_set_calibration(chip);
> +			break;
> +		default:
> +			ret = -EINVAL;
> +		}
> +		break;
>  	default:
>  		ret = -EINVAL;
>  	}
> @@ -433,22 +478,6 @@ static ssize_t ina2xx_allow_async_readout_store(struct device *dev,
>  	return len;
>  }
>  
> -/*
> - * Set current LSB to 1mA, shunt is in uOhms
> - * (equation 13 in datasheet). We hardcode a Current_LSB
> - * of 1.0 x10-6. The only remaining parameter is RShunt.
> - * There is no need to expose the CALIBRATION register
> - * to the user for now. But we need to reset this register
> - * if the user updates RShunt after driver init, e.g upon
> - * reading an EEPROM/Probe-type value.
> - */
> -static int ina2xx_set_calibration(struct ina2xx_chip_info *chip)
> -{
> -	u16 regval = DIV_ROUND_CLOSEST(chip->config->calibration_factor,
> -				   chip->shunt_resistor);
> -
> -	return regmap_write(chip->regmap, INA2XX_CALIBRATION, regval);
> -}
>  
>  static int set_shunt_resistor(struct ina2xx_chip_info *chip, unsigned int val)
>  {
> @@ -849,6 +878,23 @@ static int ina2xx_probe(struct i2c_client *client,
>  	if (ret)
>  		return ret;
>  
> +	if (of_property_read_u32(client->dev.of_node,
> +				"ti-max-expected-current-milliamp", &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_MA_DEFAULT;
> +	}
> +
> +	/* Calculate current_lsb: max-expected-current / 2^15 */
> +	val = DIV_ROUND_CLOSEST(val * 1000, (1 << 15));
> +	ret = ina2xx_set_scale(chip, val);
> +	if (ret)
> +		return ret;
> +
>  	/* Patch the current config register with default. */
>  	val = chip->config->config_default;
>  
> diff --git a/include/linux/platform_data/ina2xx.h b/include/linux/platform_data/ina2xx.h
> index 9abc0ca..f02b1d8 100644
> --- a/include/linux/platform_data/ina2xx.h
> +++ b/include/linux/platform_data/ina2xx.h
> @@ -13,7 +13,9 @@
>  /**
>   * struct ina2xx_platform_data - ina2xx info
>   * @shunt_uohms		shunt resistance in microohms
> + * @max_mA		max expected current in mA
>   */
>  struct ina2xx_platform_data {
>  	long shunt_uohms;
> +	int max_mA;
>  };

--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html