Signed-off-by: Maciej Purski <m.purski@xxxxxxxxxxx>
---
drivers/iio/adc/ina2xx-adc.c | 110 ++++++++++++++++++++++++++++++-----
include/linux/platform_data/ina2xx.h | 2 +
2 files changed, 98 insertions(+), 14 deletions(-)
diff --git a/drivers/iio/adc/ina2xx-adc.c b/drivers/iio/adc/ina2xx-adc.c
index f387b97..883fede 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_A_DEFAULT (1 << 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,10 @@ 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 */
+ unsigned int max_expected_current; /* mA */
+ 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 +141,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 +150,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 +214,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;
}
}
@@ -434,24 +439,47 @@ static ssize_t ina2xx_allow_async_readout_store(struct device *dev,
}
/*
- * 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.
+ * 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 after driver init, e.g upon
- * reading an EEPROM/Probe-type value.
+ * 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->shunt_resistor);
+ chip->current_lsb * rshunt);
return regmap_write(chip->regmap, INA2XX_CALIBRATION, regval);
}
+/*
+ * 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_chip_info *chip,
+ unsigned int val)
+{
+ if (val <= 0 || val > chip->config->calibration_factor)
+ return -EINVAL;
+
+ chip->max_expected_current = val;
+ chip->current_lsb = DIV_ROUND_CLOSEST(chip->max_expected_current * 1000,
+ 1 << 15);
+ chip->power_lsb = chip->current_lsb * chip->config->power_lsb_factor;
+
+ return 0;
+}
+
static int set_shunt_resistor(struct ina2xx_chip_info *chip, unsigned int val)
{
+
if (val <= 0 || val > chip->config->calibration_factor)
return -EINVAL;
@@ -493,6 +521,39 @@ static ssize_t ina2xx_shunt_resistor_store(struct device *dev,
return len;
}
+static ssize_t ina2xx_max_expected_current_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct ina2xx_chip_info *chip = iio_priv(dev_to_iio_dev(dev));
+
+ return sprintf(buf, "%d\n", chip->max_expected_current);
+}
+
+static ssize_t ina2xx_max_expected_current_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t len)
+{
+ struct ina2xx_chip_info *chip = iio_priv(dev_to_iio_dev(dev));
+ unsigned long val;
+ int ret;
+
+ ret = kstrtoul((const char *) buf, 10, &val);
+ if (ret)
+ return ret;
+
+ ret = set_max_expected_current(chip, val);
+ if (ret)
+ return ret;
+
+ /* Update the Calibration register */
+ ret = ina2xx_set_calibration(chip);
+ if (ret)
+ return ret;
+
+ return len;
+}
+
#define INA219_CHAN(_type, _index, _address) { \
.type = (_type), \
.address = (_address), \
@@ -755,10 +816,15 @@ static IIO_DEVICE_ATTR(in_shunt_resistor, S_IRUGO | S_IWUSR,
ina2xx_shunt_resistor_show,
ina2xx_shunt_resistor_store, 0);
+static IIO_DEVICE_ATTR(in_max_expected_current, S_IRUGO | S_IWUSR,
+ ina2xx_max_expected_current_show,
+ ina2xx_max_expected_current_store, 0);
+
static struct attribute *ina219_attributes[] = {
&iio_dev_attr_in_allow_async_readout.dev_attr.attr,
&iio_const_attr_ina219_integration_time_available.dev_attr.attr,
&iio_dev_attr_in_shunt_resistor.dev_attr.attr,
+ &iio_dev_attr_in_max_expected_current.dev_attr.attr,
NULL,
};
@@ -766,6 +832,7 @@ static struct attribute *ina226_attributes[] = {
&iio_dev_attr_in_allow_async_readout.dev_attr.attr,
&iio_const_attr_ina226_integration_time_available.dev_attr.attr,
&iio_dev_attr_in_shunt_resistor.dev_attr.attr,
+ &iio_dev_attr_in_max_expected_current.dev_attr.attr,
NULL,
};
@@ -851,6 +918,21 @@ static int ina2xx_probe(struct i2c_client *client,
if (ret)
return ret;
+ if (of_property_read_u32(client->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(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;
};