Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 023912db authored by Nicolin Chen's avatar Nicolin Chen Committed by Guenter Roeck
Browse files

hwmon: (ina3221) Add voltage conversion time settings



The CONFIG register has two 3-bit fields for conversion time
settings of Bus-voltage and Shunt-voltage, respectively. The
conversion settings, along with averaging mode, allow users
to optimize available timing requirement.

This patch adds an 'update_interval' sysfs node through the
hwmon_chip_info of hwmon core. It reflects a total hardware
conversion time:
    samples * channels * (Bus + Shunt conversion times)

Though INA3221 supports different conversion time setups for
Bus and Shunt voltages, this patch only adds the support of
a unified setting for both conversion times, by dividing the
conversion time into two equal values.

Signed-off-by: default avatarNicolin Chen <nicoleotsuka@gmail.com>
[groeck: .rst related formatting changes in documentation]
Signed-off-by: default avatarGuenter Roeck <linux@roeck-us.net>
parent 521c0b61
Loading
Loading
Loading
Loading
+13 −0
Original line number Diff line number Diff line
@@ -46,4 +46,17 @@ samples Number of samples using in the averaging mode.
                        Supports the list of number of samples:

                          1, 4, 16, 64, 128, 256, 512, 1024

update_interval         Data conversion time in millisecond, following:

                          update_interval = C x S x (BC + SC)

                          * C:	number of enabled channels
                          * S:	number of samples
                          * BC:	bus-voltage conversion time in millisecond
                          * SC:	shunt-voltage conversion time in millisecond

                        Affects both Bus- and Shunt-voltage conversion time.
                        Note that setting update_interval to 0ms sets both BC
                        and SC to 140 us (minimum conversion time).
======================= =======================================================
+50 −8
Original line number Diff line number Diff line
@@ -144,19 +144,37 @@ static const int ina3221_avg_samples[] = {
	1, 4, 16, 64, 128, 256, 512, 1024,
};

static inline int ina3221_wait_for_data(struct ina3221_data *ina)
/* Converting update_interval in msec to conversion time in usec */
static inline u32 ina3221_interval_ms_to_conv_time(u16 config, int interval)
{
	u32 channels = hweight16(config & INA3221_CONFIG_CHs_EN_MASK);
	u32 samples_idx = INA3221_CONFIG_AVG(config);
	u32 samples = ina3221_avg_samples[samples_idx];

	/* Bisect the result to Bus and Shunt conversion times */
	return DIV_ROUND_CLOSEST(interval * 1000 / 2, channels * samples);
}

/* Converting CONFIG register value to update_interval in usec */
static inline u32 ina3221_reg_to_interval_us(u16 config)
{
	u32 channels = hweight16(ina->reg_config & INA3221_CONFIG_CHs_EN_MASK);
	u32 vbus_ct_idx = INA3221_CONFIG_VBUS_CT(ina->reg_config);
	u32 vsh_ct_idx = INA3221_CONFIG_VSH_CT(ina->reg_config);
	u32 samples_idx = INA3221_CONFIG_AVG(ina->reg_config);
	u32 channels = hweight16(config & INA3221_CONFIG_CHs_EN_MASK);
	u32 vbus_ct_idx = INA3221_CONFIG_VBUS_CT(config);
	u32 vsh_ct_idx = INA3221_CONFIG_VSH_CT(config);
	u32 samples_idx = INA3221_CONFIG_AVG(config);
	u32 samples = ina3221_avg_samples[samples_idx];
	u32 vbus_ct = ina3221_conv_time[vbus_ct_idx];
	u32 vsh_ct = ina3221_conv_time[vsh_ct_idx];
	u32 wait, cvrf;

	/* Calculate total conversion time */
	wait = channels * (vbus_ct + vsh_ct) * samples;
	return channels * (vbus_ct + vsh_ct) * samples;
}

static inline int ina3221_wait_for_data(struct ina3221_data *ina)
{
	u32 wait, cvrf;

	wait = ina3221_reg_to_interval_us(ina->reg_config);

	/* Polling the CVRF bit to make sure read data is ready */
	return regmap_field_read_poll_timeout(ina->fields[F_CVRF],
@@ -197,6 +215,11 @@ static int ina3221_read_chip(struct device *dev, u32 attr, long *val)
		regval = INA3221_CONFIG_AVG(ina->reg_config);
		*val = ina3221_avg_samples[regval];
		return 0;
	case hwmon_chip_update_interval:
		/* Return in msec */
		*val = ina3221_reg_to_interval_us(ina->reg_config);
		*val = DIV_ROUND_CLOSEST(*val, 1000);
		return 0;
	default:
		return -EOPNOTSUPP;
	}
@@ -322,6 +345,23 @@ static int ina3221_write_chip(struct device *dev, u32 attr, long val)
		if (ret)
			return ret;

		/* Update reg_config accordingly */
		ina->reg_config = tmp;
		return 0;
	case hwmon_chip_update_interval:
		tmp = ina3221_interval_ms_to_conv_time(ina->reg_config, val);
		idx = find_closest(tmp, ina3221_conv_time,
				   ARRAY_SIZE(ina3221_conv_time));

		/* Update Bus and Shunt voltage conversion times */
		tmp = INA3221_CONFIG_VBUS_CT_MASK | INA3221_CONFIG_VSH_CT_MASK;
		tmp = (ina->reg_config & ~tmp) |
		      (idx << INA3221_CONFIG_VBUS_CT_SHIFT) |
		      (idx << INA3221_CONFIG_VSH_CT_SHIFT);
		ret = regmap_write(ina->regmap, INA3221_CONFIG, tmp);
		if (ret)
			return ret;

		/* Update reg_config accordingly */
		ina->reg_config = tmp;
		return 0;
@@ -483,6 +523,7 @@ static umode_t ina3221_is_visible(const void *drvdata,
	case hwmon_chip:
		switch (attr) {
		case hwmon_chip_samples:
		case hwmon_chip_update_interval:
			return 0644;
		default:
			return 0;
@@ -528,7 +569,8 @@ static umode_t ina3221_is_visible(const void *drvdata,

static const struct hwmon_channel_info *ina3221_info[] = {
	HWMON_CHANNEL_INFO(chip,
			   HWMON_C_SAMPLES),
			   HWMON_C_SAMPLES,
			   HWMON_C_UPDATE_INTERVAL),
	HWMON_CHANNEL_INFO(in,
			   /* 0: dummy, skipped in is_visible */
			   HWMON_I_INPUT,