lkml.org 
[lkml]   [2018]   [Apr]   [11]   [last100]   RSS Feed
Views: [wrap][no wrap]   [headers]  [forward] 
 
Messages in this thread
    Patch in this message
    /
    From
    Subject[PATCH 4.9 130/310] hwmon: (ina2xx) Make calibration register value fixed
    Date
    4.9-stable review patch.  If anyone has any objections, please let me know.

    ------------------

    From: Maciej Purski <m.purski@samsung.com>


    [ Upstream commit 5d389b125186cf254ad5b8015763ac07c151aea4 ]

    Calibration register is used for calculating current register in
    hardware according to datasheet:
    current = shunt_volt * calib_register / 2048 (ina 226)
    current = shunt_volt * calib_register / 4096 (ina 219)

    Fix calib_register value to 2048 for ina226 and 4096 for ina 219 in
    order to avoid truncation error and provide best precision allowed
    by shunt_voltage measurement. Make current scale value follow changes
    of shunt_resistor from sysfs as calib_register value is now fixed.

    Power_lsb value should also follow shunt_resistor changes as stated in
    datasheet:
    power_lsb = 25 * current_lsb (ina 226)
    power_lsb = 20 * current_lsb (ina 219)

    Signed-off-by: Maciej Purski <m.purski@samsung.com>
    Signed-off-by: Guenter Roeck <linux@roeck-us.net>
    Signed-off-by: Sasha Levin <alexander.levin@microsoft.com>
    Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
    ---
    drivers/hwmon/ina2xx.c | 87 ++++++++++++++++++++++++++++---------------------
    1 file changed, 50 insertions(+), 37 deletions(-)

    --- a/drivers/hwmon/ina2xx.c
    +++ b/drivers/hwmon/ina2xx.c
    @@ -94,18 +94,20 @@ enum ina2xx_ids { ina219, ina226 };

    struct ina2xx_config {
    u16 config_default;
    - int calibration_factor;
    + int calibration_value;
    int registers;
    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 current_lsb_uA;
    + long power_lsb_uW;
    struct mutex config_lock;
    struct regmap *regmap;

    @@ -115,21 +117,21 @@ struct ina2xx_data {
    static const struct ina2xx_config ina2xx_config[] = {
    [ina219] = {
    .config_default = INA219_CONFIG_DEFAULT,
    - .calibration_factor = 40960000,
    + .calibration_value = 4096,
    .registers = INA219_REGISTERS,
    .shunt_div = 100,
    .bus_voltage_shift = 3,
    .bus_voltage_lsb = 4000,
    - .power_lsb = 20000,
    + .power_lsb_factor = 20,
    },
    [ina226] = {
    .config_default = INA226_CONFIG_DEFAULT,
    - .calibration_factor = 5120000,
    + .calibration_value = 2048,
    .registers = INA226_REGISTERS,
    .shunt_div = 400,
    .bus_voltage_shift = 0,
    .bus_voltage_lsb = 1250,
    - .power_lsb = 25000,
    + .power_lsb_factor = 25,
    },
    };

    @@ -168,12 +170,16 @@ static u16 ina226_interval_to_reg(int in
    return INA226_SHIFT_AVG(avg_bits);
    }

    +/*
    + * Calibration register is set to the best value, which eliminates
    + * truncation errors on calculating current register in hardware.
    + * According to datasheet (eq. 3) the best values are 2048 for
    + * ina226 and 4096 for ina219. They are hardcoded as calibration_value.
    + */
    static int ina2xx_calibrate(struct ina2xx_data *data)
    {
    - u16 val = DIV_ROUND_CLOSEST(data->config->calibration_factor,
    - data->rshunt);
    -
    - return regmap_write(data->regmap, INA2XX_CALIBRATION, val);
    + return regmap_write(data->regmap, INA2XX_CALIBRATION,
    + data->config->calibration_value);
    }

    /*
    @@ -186,10 +192,6 @@ static int ina2xx_init(struct ina2xx_dat
    if (ret < 0)
    return ret;

    - /*
    - * Set current LSB to 1mA, shunt is in uOhms
    - * (equation 13 in datasheet).
    - */
    return ina2xx_calibrate(data);
    }

    @@ -267,15 +269,15 @@ static int ina2xx_get_value(struct ina2x
    val = DIV_ROUND_CLOSEST(val, 1000);
    break;
    case INA2XX_POWER:
    - val = regval * data->config->power_lsb;
    + val = regval * data->power_lsb_uW;
    break;
    case INA2XX_CURRENT:
    - /* signed register, LSB=1mA (selected), in mA */
    - val = (s16)regval;
    + /* signed register, result in mA */
    + val = regval * data->current_lsb_uA;
    + val = DIV_ROUND_CLOSEST(val, 1000);
    break;
    case INA2XX_CALIBRATION:
    - val = DIV_ROUND_CLOSEST(data->config->calibration_factor,
    - regval);
    + val = regval;
    break;
    default:
    /* programmer goofed */
    @@ -303,9 +305,32 @@ static ssize_t ina2xx_show_value(struct
    ina2xx_get_value(data, attr->index, regval));
    }

    -static ssize_t ina2xx_set_shunt(struct device *dev,
    - struct device_attribute *da,
    - const char *buf, size_t count)
    +/*
    + * In order to keep calibration register value fixed, the product
    + * of current_lsb and shunt_resistor should also be fixed and equal
    + * to shunt_voltage_lsb = 1 / shunt_div multiplied by 10^9 in order
    + * to keep the scale.
    + */
    +static int ina2xx_set_shunt(struct ina2xx_data *data, long val)
    +{
    + unsigned int dividend = DIV_ROUND_CLOSEST(1000000000,
    + data->config->shunt_div);
    + if (val <= 0 || val > dividend)
    + return -EINVAL;
    +
    + mutex_lock(&data->config_lock);
    + data->rshunt = val;
    + data->current_lsb_uA = DIV_ROUND_CLOSEST(dividend, val);
    + data->power_lsb_uW = data->config->power_lsb_factor *
    + data->current_lsb_uA;
    + mutex_unlock(&data->config_lock);
    +
    + return 0;
    +}
    +
    +static ssize_t ina2xx_store_shunt(struct device *dev,
    + struct device_attribute *da,
    + const char *buf, size_t count)
    {
    unsigned long val;
    int status;
    @@ -315,18 +340,9 @@ static ssize_t ina2xx_set_shunt(struct d
    if (status < 0)
    return status;

    - if (val == 0 ||
    - /* Values greater than the calibration factor make no sense. */
    - val > data->config->calibration_factor)
    - return -EINVAL;
    -
    - mutex_lock(&data->config_lock);
    - data->rshunt = val;
    - status = ina2xx_calibrate(data);
    - mutex_unlock(&data->config_lock);
    + status = ina2xx_set_shunt(data, val);
    if (status < 0)
    return status;
    -
    return count;
    }

    @@ -386,7 +402,7 @@ static SENSOR_DEVICE_ATTR(power1_input,

    /* shunt resistance */
    static SENSOR_DEVICE_ATTR(shunt_resistor, S_IRUGO | S_IWUSR,
    - ina2xx_show_value, ina2xx_set_shunt,
    + ina2xx_show_value, ina2xx_store_shunt,
    INA2XX_CALIBRATION);

    /* update interval (ina226 only) */
    @@ -441,10 +457,7 @@ static int ina2xx_probe(struct i2c_clien
    val = INA2XX_RSHUNT_DEFAULT;
    }

    - if (val <= 0 || val > data->config->calibration_factor)
    - return -ENODEV;
    -
    - data->rshunt = val;
    + ina2xx_set_shunt(data, val);

    ina2xx_regmap_config.max_register = data->config->registers;


    \
     
     \ /
      Last update: 2018-04-11 21:52    [W:4.141 / U:0.740 seconds]
    ©2003-2020 Jasper Spaans|hosted at Digital Ocean and TransIP|Read the blog|Advertise on this site