Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

hwmon: (ina238) Pre-calculate current, power, and energy LSB

Current, power, and energy LSB do not change during runtime, so we can
pre-calculate the respective values. The power LSB can be derived from
the current LSB using the equation in the datasheets. Similar, the
energy LSB can be derived from the power LSB.

Also add support for chips with built-in shunt resistor by providing
a chip specific configuration parameter for the current LSB. The
relationship of current -> power -> energy LSB values in those chips
is the same as in chips with external shunt resistor, so configuration
parameters for power and energy LSB are not needed.

Use ROUND_CLOSEST functions instead of divide operations to reduce
rounding errors.

Reviewed-by: Chris Packham <chris.packham@alliedtelesis.co.nz>
Tested-by: Chris Packham <chris.packham@alliedtelesis.co.nz> # INA780
Signed-off-by: Guenter Roeck <linux@roeck-us.net>

+27 -20
+27 -20
drivers/hwmon/ina238.c
··· 118 118 bool has_power_highest; /* chip detection power peak */ 119 119 bool has_energy; /* chip detection energy */ 120 120 u8 temp_resolution; /* temperature register resolution in bit */ 121 - u32 power_calculate_factor; /* fixed parameters for power calculate */ 121 + u32 power_calculate_factor; /* fixed parameter for power calculation, from datasheet */ 122 122 u16 config_default; /* Power-on default state */ 123 123 int bus_voltage_lsb; /* use for temperature calculate, uV/lsb */ 124 + int current_lsb; /* current LSB, in uA */ 124 125 }; 125 126 126 127 struct ina238_data { ··· 131 130 struct regmap *regmap; 132 131 u32 rshunt; 133 132 int gain; 133 + int current_lsb; /* current LSB, in uA */ 134 + int power_lsb; /* power LSB, in uW */ 135 + int energy_lsb; /* energy LSB, in uJ */ 134 136 }; 135 137 136 138 static const struct ina238_config ina238_config[] = { ··· 426 422 regval = (s16)regval; 427 423 } 428 424 429 - /* Signed register, fixed 1mA current lsb. result in mA */ 430 - *val = div_s64((s64)regval * INA238_FIXED_SHUNT * data->gain, 431 - data->rshunt * 4); 425 + /* Signed register. Result in mA */ 426 + *val = DIV_S64_ROUND_CLOSEST((s64)regval * data->current_lsb, 1000); 432 427 433 428 /* Account for 4 bit offset */ 434 429 if (data->config->has_20bit_voltage_current) ··· 453 450 if (err) 454 451 return err; 455 452 456 - /* Fixed 1mA lsb, scaled by 1000000 to have result in uW */ 457 - power = div_u64(regval * 1000ULL * INA238_FIXED_SHUNT * data->gain * 458 - data->config->power_calculate_factor, 4 * 100 * data->rshunt); 453 + power = (long long)regval * data->power_lsb; 459 454 /* Clamp value to maximum value of long */ 460 455 *val = clamp_val(power, 0, LONG_MAX); 461 456 break; ··· 462 461 if (err) 463 462 return err; 464 463 465 - /* Fixed 1mA lsb, scaled by 1000000 to have result in uW */ 466 - power = div_u64(regval * 1000ULL * INA238_FIXED_SHUNT * data->gain * 467 - data->config->power_calculate_factor, 4 * 100 * data->rshunt); 464 + power = (long long)regval * data->power_lsb; 468 465 /* Clamp value to maximum value of long */ 469 466 *val = clamp_val(power, 0, LONG_MAX); 470 467 break; ··· 475 476 * Truncated 24-bit compare register, lower 8-bits are 476 477 * truncated. Same conversion to/from uW as POWER register. 477 478 */ 478 - power = div_u64((regval << 8) * 1000ULL * INA238_FIXED_SHUNT * data->gain * 479 - data->config->power_calculate_factor, 4 * 100 * data->rshunt); 479 + power = ((long long)regval << 8) * data->power_lsb; 480 480 /* Clamp value to maximum value of long */ 481 481 *val = clamp_val(power, 0, LONG_MAX); 482 482 break; ··· 496 498 static int ina238_write_power_max(struct device *dev, long val) 497 499 { 498 500 struct ina238_data *data = dev_get_drvdata(dev); 499 - long regval; 500 501 501 502 /* 502 503 * Unsigned postive values. Compared against the 24-bit power register, ··· 503 506 * register. 504 507 * The first clamp_val() is to establish a baseline to avoid overflows. 505 508 */ 506 - regval = clamp_val(val, 0, LONG_MAX / 2); 507 - regval = div_u64(regval * 4 * 100 * data->rshunt, data->config->power_calculate_factor * 508 - 1000ULL * INA238_FIXED_SHUNT * data->gain); 509 - regval = clamp_val(regval >> 8, 0, U16_MAX); 509 + val = clamp_val(val, 0, LONG_MAX / 2); 510 + val = DIV_ROUND_CLOSEST(val, data->power_lsb); 511 + val = clamp_val(val >> 8, 0, U16_MAX); 510 512 511 - return regmap_write(data->regmap, INA238_POWER_LIMIT, regval); 513 + return regmap_write(data->regmap, INA238_POWER_LIMIT, val); 512 514 } 513 515 514 516 static int ina238_temp_from_reg(s16 regval, u8 resolution) ··· 580 584 return ret; 581 585 582 586 /* result in uJ */ 583 - energy = div_u64(regval * INA238_FIXED_SHUNT * data->gain * 16 * 10 * 584 - data->config->power_calculate_factor, 4 * data->rshunt); 587 + energy = regval * data->energy_lsb; 585 588 586 589 return sysfs_emit(buf, "%llu\n", energy); 587 590 } ··· 811 816 dev_err(dev, "error configuring the device: %d\n", ret); 812 817 return -ENODEV; 813 818 } 819 + 820 + if (data->config->current_lsb) 821 + data->current_lsb = data->config->current_lsb; 822 + else 823 + data->current_lsb = DIV_U64_ROUND_CLOSEST(250ULL * INA238_FIXED_SHUNT * data->gain, 824 + data->rshunt); 825 + 826 + data->power_lsb = DIV_ROUND_CLOSEST(data->current_lsb * 827 + data->config->power_calculate_factor, 828 + 100); 829 + 830 + data->energy_lsb = data->power_lsb * 16; 814 831 815 832 hwmon_dev = devm_hwmon_device_register_with_info(dev, client->name, data, 816 833 &ina238_chip_info,