diff options
author | Guenter Roeck <linux@roeck-us.net> | 2014-01-30 22:51:14 -0500 |
---|---|---|
committer | Guenter Roeck <linux@roeck-us.net> | 2014-02-03 00:21:40 -0500 |
commit | daa436e67cd2dcac7cbb505bb4425fdfdafaa5a7 (patch) | |
tree | e61017bf92b7a4a021d362379ab3504df46b15bd /drivers | |
parent | 38dbfb59d1175ef458d006556061adeaa8751b72 (diff) |
hwmon: (pmbus) Support per-page exponent in linear mode
Some chips use different exponents for sensors on different pages
or rails. Detect and store exponent per page to support this situation.
This fixes a problem with wrong voltages seen on UCD90120.
Reported-by: Soren Brinkmann <soren.brinkmann@xilinx.com>
Tested-by: Soren Brinkmann <soren.brinkmann@xilinx.com>
Signed-off-by: Guenter Roeck <linux@roeck-us.net>
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/hwmon/pmbus/pmbus_core.c | 68 |
1 files changed, 36 insertions, 32 deletions
diff --git a/drivers/hwmon/pmbus/pmbus_core.c b/drivers/hwmon/pmbus/pmbus_core.c index 3cbf66e9d861..291d11fe93e7 100644 --- a/drivers/hwmon/pmbus/pmbus_core.c +++ b/drivers/hwmon/pmbus/pmbus_core.c | |||
@@ -90,7 +90,8 @@ struct pmbus_data { | |||
90 | 90 | ||
91 | u32 flags; /* from platform data */ | 91 | u32 flags; /* from platform data */ |
92 | 92 | ||
93 | int exponent; /* linear mode: exponent for output voltages */ | 93 | int exponent[PMBUS_PAGES]; |
94 | /* linear mode: exponent for output voltages */ | ||
94 | 95 | ||
95 | const struct pmbus_driver_info *info; | 96 | const struct pmbus_driver_info *info; |
96 | 97 | ||
@@ -410,7 +411,7 @@ static long pmbus_reg2data_linear(struct pmbus_data *data, | |||
410 | long val; | 411 | long val; |
411 | 412 | ||
412 | if (sensor->class == PSC_VOLTAGE_OUT) { /* LINEAR16 */ | 413 | if (sensor->class == PSC_VOLTAGE_OUT) { /* LINEAR16 */ |
413 | exponent = data->exponent; | 414 | exponent = data->exponent[sensor->page]; |
414 | mantissa = (u16) sensor->data; | 415 | mantissa = (u16) sensor->data; |
415 | } else { /* LINEAR11 */ | 416 | } else { /* LINEAR11 */ |
416 | exponent = ((s16)sensor->data) >> 11; | 417 | exponent = ((s16)sensor->data) >> 11; |
@@ -516,7 +517,7 @@ static long pmbus_reg2data(struct pmbus_data *data, struct pmbus_sensor *sensor) | |||
516 | #define MIN_MANTISSA (511 * 1000) | 517 | #define MIN_MANTISSA (511 * 1000) |
517 | 518 | ||
518 | static u16 pmbus_data2reg_linear(struct pmbus_data *data, | 519 | static u16 pmbus_data2reg_linear(struct pmbus_data *data, |
519 | enum pmbus_sensor_classes class, long val) | 520 | struct pmbus_sensor *sensor, long val) |
520 | { | 521 | { |
521 | s16 exponent = 0, mantissa; | 522 | s16 exponent = 0, mantissa; |
522 | bool negative = false; | 523 | bool negative = false; |
@@ -525,7 +526,7 @@ static u16 pmbus_data2reg_linear(struct pmbus_data *data, | |||
525 | if (val == 0) | 526 | if (val == 0) |
526 | return 0; | 527 | return 0; |
527 | 528 | ||
528 | if (class == PSC_VOLTAGE_OUT) { | 529 | if (sensor->class == PSC_VOLTAGE_OUT) { |
529 | /* LINEAR16 does not support negative voltages */ | 530 | /* LINEAR16 does not support negative voltages */ |
530 | if (val < 0) | 531 | if (val < 0) |
531 | return 0; | 532 | return 0; |
@@ -534,10 +535,10 @@ static u16 pmbus_data2reg_linear(struct pmbus_data *data, | |||
534 | * For a static exponents, we don't have a choice | 535 | * For a static exponents, we don't have a choice |
535 | * but to adjust the value to it. | 536 | * but to adjust the value to it. |
536 | */ | 537 | */ |
537 | if (data->exponent < 0) | 538 | if (data->exponent[sensor->page] < 0) |
538 | val <<= -data->exponent; | 539 | val <<= -data->exponent[sensor->page]; |
539 | else | 540 | else |
540 | val >>= data->exponent; | 541 | val >>= data->exponent[sensor->page]; |
541 | val = DIV_ROUND_CLOSEST(val, 1000); | 542 | val = DIV_ROUND_CLOSEST(val, 1000); |
542 | return val & 0xffff; | 543 | return val & 0xffff; |
543 | } | 544 | } |
@@ -548,14 +549,14 @@ static u16 pmbus_data2reg_linear(struct pmbus_data *data, | |||
548 | } | 549 | } |
549 | 550 | ||
550 | /* Power is in uW. Convert to mW before converting. */ | 551 | /* Power is in uW. Convert to mW before converting. */ |
551 | if (class == PSC_POWER) | 552 | if (sensor->class == PSC_POWER) |
552 | val = DIV_ROUND_CLOSEST(val, 1000L); | 553 | val = DIV_ROUND_CLOSEST(val, 1000L); |
553 | 554 | ||
554 | /* | 555 | /* |
555 | * For simplicity, convert fan data to milli-units | 556 | * For simplicity, convert fan data to milli-units |
556 | * before calculating the exponent. | 557 | * before calculating the exponent. |
557 | */ | 558 | */ |
558 | if (class == PSC_FAN) | 559 | if (sensor->class == PSC_FAN) |
559 | val = val * 1000; | 560 | val = val * 1000; |
560 | 561 | ||
561 | /* Reduce large mantissa until it fits into 10 bit */ | 562 | /* Reduce large mantissa until it fits into 10 bit */ |
@@ -585,22 +586,22 @@ static u16 pmbus_data2reg_linear(struct pmbus_data *data, | |||
585 | } | 586 | } |
586 | 587 | ||
587 | static u16 pmbus_data2reg_direct(struct pmbus_data *data, | 588 | static u16 pmbus_data2reg_direct(struct pmbus_data *data, |
588 | enum pmbus_sensor_classes class, long val) | 589 | struct pmbus_sensor *sensor, long val) |
589 | { | 590 | { |
590 | long m, b, R; | 591 | long m, b, R; |
591 | 592 | ||
592 | m = data->info->m[class]; | 593 | m = data->info->m[sensor->class]; |
593 | b = data->info->b[class]; | 594 | b = data->info->b[sensor->class]; |
594 | R = data->info->R[class]; | 595 | R = data->info->R[sensor->class]; |
595 | 596 | ||
596 | /* Power is in uW. Adjust R and b. */ | 597 | /* Power is in uW. Adjust R and b. */ |
597 | if (class == PSC_POWER) { | 598 | if (sensor->class == PSC_POWER) { |
598 | R -= 3; | 599 | R -= 3; |
599 | b *= 1000; | 600 | b *= 1000; |
600 | } | 601 | } |
601 | 602 | ||
602 | /* Calculate Y = (m * X + b) * 10^R */ | 603 | /* Calculate Y = (m * X + b) * 10^R */ |
603 | if (class != PSC_FAN) { | 604 | if (sensor->class != PSC_FAN) { |
604 | R -= 3; /* Adjust R and b for data in milli-units */ | 605 | R -= 3; /* Adjust R and b for data in milli-units */ |
605 | b *= 1000; | 606 | b *= 1000; |
606 | } | 607 | } |
@@ -619,7 +620,7 @@ static u16 pmbus_data2reg_direct(struct pmbus_data *data, | |||
619 | } | 620 | } |
620 | 621 | ||
621 | static u16 pmbus_data2reg_vid(struct pmbus_data *data, | 622 | static u16 pmbus_data2reg_vid(struct pmbus_data *data, |
622 | enum pmbus_sensor_classes class, long val) | 623 | struct pmbus_sensor *sensor, long val) |
623 | { | 624 | { |
624 | val = clamp_val(val, 500, 1600); | 625 | val = clamp_val(val, 500, 1600); |
625 | 626 | ||
@@ -627,20 +628,20 @@ static u16 pmbus_data2reg_vid(struct pmbus_data *data, | |||
627 | } | 628 | } |
628 | 629 | ||
629 | static u16 pmbus_data2reg(struct pmbus_data *data, | 630 | static u16 pmbus_data2reg(struct pmbus_data *data, |
630 | enum pmbus_sensor_classes class, long val) | 631 | struct pmbus_sensor *sensor, long val) |
631 | { | 632 | { |
632 | u16 regval; | 633 | u16 regval; |
633 | 634 | ||
634 | switch (data->info->format[class]) { | 635 | switch (data->info->format[sensor->class]) { |
635 | case direct: | 636 | case direct: |
636 | regval = pmbus_data2reg_direct(data, class, val); | 637 | regval = pmbus_data2reg_direct(data, sensor, val); |
637 | break; | 638 | break; |
638 | case vid: | 639 | case vid: |
639 | regval = pmbus_data2reg_vid(data, class, val); | 640 | regval = pmbus_data2reg_vid(data, sensor, val); |
640 | break; | 641 | break; |
641 | case linear: | 642 | case linear: |
642 | default: | 643 | default: |
643 | regval = pmbus_data2reg_linear(data, class, val); | 644 | regval = pmbus_data2reg_linear(data, sensor, val); |
644 | break; | 645 | break; |
645 | } | 646 | } |
646 | return regval; | 647 | return regval; |
@@ -746,7 +747,7 @@ static ssize_t pmbus_set_sensor(struct device *dev, | |||
746 | return -EINVAL; | 747 | return -EINVAL; |
747 | 748 | ||
748 | mutex_lock(&data->update_lock); | 749 | mutex_lock(&data->update_lock); |
749 | regval = pmbus_data2reg(data, sensor->class, val); | 750 | regval = pmbus_data2reg(data, sensor, val); |
750 | ret = _pmbus_write_word_data(client, sensor->page, sensor->reg, regval); | 751 | ret = _pmbus_write_word_data(client, sensor->page, sensor->reg, regval); |
751 | if (ret < 0) | 752 | if (ret < 0) |
752 | rv = ret; | 753 | rv = ret; |
@@ -1643,12 +1644,13 @@ static int pmbus_find_attributes(struct i2c_client *client, | |||
1643 | * This function is called for all chips. | 1644 | * This function is called for all chips. |
1644 | */ | 1645 | */ |
1645 | static int pmbus_identify_common(struct i2c_client *client, | 1646 | static int pmbus_identify_common(struct i2c_client *client, |
1646 | struct pmbus_data *data) | 1647 | struct pmbus_data *data, int page) |
1647 | { | 1648 | { |
1648 | int vout_mode = -1; | 1649 | int vout_mode = -1; |
1649 | 1650 | ||
1650 | if (pmbus_check_byte_register(client, 0, PMBUS_VOUT_MODE)) | 1651 | if (pmbus_check_byte_register(client, page, PMBUS_VOUT_MODE)) |
1651 | vout_mode = _pmbus_read_byte_data(client, 0, PMBUS_VOUT_MODE); | 1652 | vout_mode = _pmbus_read_byte_data(client, page, |
1653 | PMBUS_VOUT_MODE); | ||
1652 | if (vout_mode >= 0 && vout_mode != 0xff) { | 1654 | if (vout_mode >= 0 && vout_mode != 0xff) { |
1653 | /* | 1655 | /* |
1654 | * Not all chips support the VOUT_MODE command, | 1656 | * Not all chips support the VOUT_MODE command, |
@@ -1659,7 +1661,7 @@ static int pmbus_identify_common(struct i2c_client *client, | |||
1659 | if (data->info->format[PSC_VOLTAGE_OUT] != linear) | 1661 | if (data->info->format[PSC_VOLTAGE_OUT] != linear) |
1660 | return -ENODEV; | 1662 | return -ENODEV; |
1661 | 1663 | ||
1662 | data->exponent = ((s8)(vout_mode << 3)) >> 3; | 1664 | data->exponent[page] = ((s8)(vout_mode << 3)) >> 3; |
1663 | break; | 1665 | break; |
1664 | case 1: /* VID mode */ | 1666 | case 1: /* VID mode */ |
1665 | if (data->info->format[PSC_VOLTAGE_OUT] != vid) | 1667 | if (data->info->format[PSC_VOLTAGE_OUT] != vid) |
@@ -1674,7 +1676,7 @@ static int pmbus_identify_common(struct i2c_client *client, | |||
1674 | } | 1676 | } |
1675 | } | 1677 | } |
1676 | 1678 | ||
1677 | pmbus_clear_fault_page(client, 0); | 1679 | pmbus_clear_fault_page(client, page); |
1678 | return 0; | 1680 | return 0; |
1679 | } | 1681 | } |
1680 | 1682 | ||
@@ -1682,7 +1684,7 @@ static int pmbus_init_common(struct i2c_client *client, struct pmbus_data *data, | |||
1682 | struct pmbus_driver_info *info) | 1684 | struct pmbus_driver_info *info) |
1683 | { | 1685 | { |
1684 | struct device *dev = &client->dev; | 1686 | struct device *dev = &client->dev; |
1685 | int ret; | 1687 | int page, ret; |
1686 | 1688 | ||
1687 | /* | 1689 | /* |
1688 | * Some PMBus chips don't support PMBUS_STATUS_BYTE, so try | 1690 | * Some PMBus chips don't support PMBUS_STATUS_BYTE, so try |
@@ -1715,10 +1717,12 @@ static int pmbus_init_common(struct i2c_client *client, struct pmbus_data *data, | |||
1715 | return -ENODEV; | 1717 | return -ENODEV; |
1716 | } | 1718 | } |
1717 | 1719 | ||
1718 | ret = pmbus_identify_common(client, data); | 1720 | for (page = 0; page < info->pages; page++) { |
1719 | if (ret < 0) { | 1721 | ret = pmbus_identify_common(client, data, page); |
1720 | dev_err(dev, "Failed to identify chip capabilities\n"); | 1722 | if (ret < 0) { |
1721 | return ret; | 1723 | dev_err(dev, "Failed to identify chip capabilities\n"); |
1724 | return ret; | ||
1725 | } | ||
1722 | } | 1726 | } |
1723 | return 0; | 1727 | return 0; |
1724 | } | 1728 | } |