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) Add support for current limits

Since the shunt voltage register and the current register now report the
same values, use the shunt voltage limit registers to report and adjust
current limits, using the same LSB as the LSB used for the actual current
register.

Handle current register accuracy differences in separate function to
improve code readability.

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>

+87 -22
+4
Documentation/hwmon/ina238.rst
··· 82 82 (SQ52206 only) 83 83 84 84 curr1_input Current measurement (mA) 85 + curr1_min Minimum current threshold (mA) 86 + curr1_min_alarm Minimum current alarm 87 + curr1_max Maximum current threshold (mA) 88 + curr1_max_alarm Maximum current alarm 85 89 86 90 energy1_input Energy measurement (uJ) 87 91 (SQ52206 and INA237 only)
+83 -22
drivers/hwmon/ina238.c
··· 335 335 } 336 336 } 337 337 338 - static int ina238_read_current(struct device *dev, u32 attr, long *val) 338 + static int __ina238_read_curr(struct ina238_data *data, long *val) 339 + { 340 + u32 lsb = data->current_lsb; 341 + int err, regval; 342 + 343 + if (data->config->has_20bit_voltage_current) { 344 + err = ina238_read_field_s20(data->client, INA238_CURRENT, &regval); 345 + if (err) 346 + return err; 347 + lsb /= 16; /* Adjust accuracy */ 348 + } else { 349 + err = regmap_read(data->regmap, INA238_CURRENT, &regval); 350 + if (err) 351 + return err; 352 + regval = (s16)regval; 353 + } 354 + 355 + *val = DIV_S64_ROUND_CLOSEST((s64)regval * lsb, 1000); 356 + return 0; 357 + } 358 + 359 + static int ina238_read_curr(struct device *dev, u32 attr, long *val) 339 360 { 340 361 struct ina238_data *data = dev_get_drvdata(dev); 362 + int reg, mask = 0; 341 363 int regval; 342 364 int err; 343 365 366 + if (attr == hwmon_curr_input) 367 + return __ina238_read_curr(data, val); 368 + 344 369 switch (attr) { 345 - case hwmon_curr_input: 346 - if (data->config->has_20bit_voltage_current) { 347 - err = ina238_read_field_s20(data->client, INA238_CURRENT, &regval); 348 - if (err) 349 - return err; 350 - } else { 351 - err = regmap_read(data->regmap, INA238_CURRENT, &regval); 352 - if (err < 0) 353 - return err; 354 - /* sign-extend */ 355 - regval = (s16)regval; 356 - } 357 - 358 - /* Signed register. Result in mA */ 359 - *val = DIV_S64_ROUND_CLOSEST((s64)regval * data->current_lsb, 1000); 360 - 361 - /* Account for 4 bit offset */ 362 - if (data->config->has_20bit_voltage_current) 363 - *val /= 16; 370 + case hwmon_curr_min: 371 + reg = INA238_SHUNT_UNDER_VOLTAGE; 372 + break; 373 + case hwmon_curr_min_alarm: 374 + reg = INA238_DIAG_ALERT; 375 + mask = INA238_DIAG_ALERT_SHNTUL; 376 + break; 377 + case hwmon_curr_max: 378 + reg = INA238_SHUNT_OVER_VOLTAGE; 379 + break; 380 + case hwmon_curr_max_alarm: 381 + reg = INA238_DIAG_ALERT; 382 + mask = INA238_DIAG_ALERT_SHNTOL; 364 383 break; 365 384 default: 366 385 return -EOPNOTSUPP; 367 386 } 368 387 388 + err = regmap_read(data->regmap, reg, &regval); 389 + if (err < 0) 390 + return err; 391 + 392 + if (mask) 393 + *val = !!(regval & mask); 394 + else 395 + *val = DIV_S64_ROUND_CLOSEST((s64)(s16)regval * data->current_lsb, 1000); 396 + 369 397 return 0; 398 + } 399 + 400 + static int ina238_write_curr(struct device *dev, u32 attr, long val) 401 + { 402 + struct ina238_data *data = dev_get_drvdata(dev); 403 + int regval; 404 + 405 + /* Set baseline range to avoid over/underflows */ 406 + val = clamp_val(val, -1000000, 1000000); 407 + /* Scale */ 408 + val = DIV_ROUND_CLOSEST(val * 1000, data->current_lsb); 409 + /* Clamp to register size */ 410 + regval = clamp_val(val, S16_MIN, S16_MAX) & 0xffff; 411 + 412 + switch (attr) { 413 + case hwmon_curr_min: 414 + return regmap_write(data->regmap, INA238_SHUNT_UNDER_VOLTAGE, 415 + regval); 416 + case hwmon_curr_max: 417 + return regmap_write(data->regmap, INA238_SHUNT_OVER_VOLTAGE, 418 + regval); 419 + default: 420 + return -EOPNOTSUPP; 421 + } 370 422 } 371 423 372 424 static int ina238_read_power(struct device *dev, u32 attr, long *val) ··· 573 521 case hwmon_in: 574 522 return ina238_read_in(dev, attr, channel, val); 575 523 case hwmon_curr: 576 - return ina238_read_current(dev, attr, val); 524 + return ina238_read_curr(dev, attr, val); 577 525 case hwmon_power: 578 526 return ina238_read_power(dev, attr, val); 579 527 case hwmon_temp: ··· 595 543 switch (type) { 596 544 case hwmon_in: 597 545 err = ina238_write_in(dev, attr, channel, val); 546 + break; 547 + case hwmon_curr: 548 + err = ina238_write_curr(dev, attr, val); 598 549 break; 599 550 case hwmon_power: 600 551 err = ina238_write_power_max(dev, val); ··· 637 582 case hwmon_curr: 638 583 switch (attr) { 639 584 case hwmon_curr_input: 585 + case hwmon_curr_max_alarm: 586 + case hwmon_curr_min_alarm: 640 587 return 0444; 588 + case hwmon_curr_max: 589 + case hwmon_curr_min: 590 + return 0644; 641 591 default: 642 592 return 0; 643 593 } ··· 687 627 INA238_HWMON_IN_CONFIG), 688 628 HWMON_CHANNEL_INFO(curr, 689 629 /* 0: current through shunt */ 690 - HWMON_C_INPUT), 630 + HWMON_C_INPUT | HWMON_C_MIN | HWMON_C_MIN_ALARM | 631 + HWMON_C_MAX | HWMON_C_MAX_ALARM), 691 632 HWMON_CHANNEL_INFO(power, 692 633 /* 0: power */ 693 634 HWMON_P_INPUT | HWMON_P_MAX |