diff mbox series

hwmon: Conditionally clear individual status bits for pmbus rev >= 1.2

Message ID 20240909-pmbus-status-reg-clearing-v1-1-f1c0d68c6408@gmail.com (mailing list archive)
State Accepted
Headers show
Series hwmon: Conditionally clear individual status bits for pmbus rev >= 1.2 | expand

Commit Message

Patryk Sept. 9, 2024, 9:30 a.m. UTC
This change adds fetching PMBus revision and using it to conditionally
clear individual status bits while calling pmbus_show_boolean, only if
the device is compliant with PMBus specs >= 1.2.

Signed-off-by: Patryk Biel <pbiel7@gmail.com>
---
Current implementation of pmbus_show_boolean assumes that all devices
support write-back operation of status register so as to clear pending
warning or faults. Since clearing individual bits in the status registers
was introduced in PMBus specification 1.2, this operation may not be 
supported by some older devices, thus resulting in error while reading 
boolean attributes like e.g. temp1_max_alarm. 

This change adds fetching PMBus revision supported by device and
modifies pmbus_show_boolean so that it only tries to clear individual
status bits if the device is compilant with PMBus specs >= 1.2.

Tested on: LTC2971, LTC2971-1, LTC2974, LTC2977.
---
 drivers/hwmon/pmbus/pmbus.h      |  6 ++++++
 drivers/hwmon/pmbus/pmbus_core.c | 12 +++++++++++-
 2 files changed, 17 insertions(+), 1 deletion(-)


---
base-commit: c763c43396883456ef57e5e78b64d3c259c4babc
change-id: 20240905-pmbus-status-reg-clearing-abc9c0184c3b

Best regards,

Comments

Guenter Roeck Sept. 9, 2024, 4:54 p.m. UTC | #1
On Mon, Sep 09, 2024 at 11:30:28AM +0200, Patryk Biel wrote:
> This change adds fetching PMBus revision and using it to conditionally
> clear individual status bits while calling pmbus_show_boolean, only if
> the device is compliant with PMBus specs >= 1.2.
> 
> Signed-off-by: Patryk Biel <pbiel7@gmail.com>
> ---
> Current implementation of pmbus_show_boolean assumes that all devices
> support write-back operation of status register so as to clear pending
> warning or faults. Since clearing individual bits in the status registers
> was introduced in PMBus specification 1.2, this operation may not be 
> supported by some older devices, thus resulting in error while reading 
> boolean attributes like e.g. temp1_max_alarm. 
> 
> This change adds fetching PMBus revision supported by device and
> modifies pmbus_show_boolean so that it only tries to clear individual
> status bits if the device is compilant with PMBus specs >= 1.2.
> 
Most of the above should have been in the description, and "This change
adds" should be "Add ...". See the "submitting Patches" document for
rationale.

> +	ret = i2c_smbus_read_byte_data(client, PMBUS_REVISION);
> +	if (ret > 0)

	>= 0 for consistency

> +		data->revision = ret;
> +

The code needs to be be further up, ahead of clearing faults, to have the
faults cleared if the command failed.

Never mind, though, I made those changes and applied the patch (I want to
make sure this patch is upstream in v6.11 final).

Thanks,
Guenter
Guenter Roeck Sept. 9, 2024, 5:59 p.m. UTC | #2
On Mon, Sep 09, 2024 at 11:30:28AM +0200, Patryk Biel wrote:
> This change adds fetching PMBus revision and using it to conditionally
> clear individual status bits while calling pmbus_show_boolean, only if
> the device is compliant with PMBus specs >= 1.2.
> 
> Signed-off-by: Patryk Biel <pbiel7@gmail.com>
> ---
> Current implementation of pmbus_show_boolean assumes that all devices
> support write-back operation of status register so as to clear pending
> warning or faults. Since clearing individual bits in the status registers
> was introduced in PMBus specification 1.2, this operation may not be 
> supported by some older devices, thus resulting in error while reading 
> boolean attributes like e.g. temp1_max_alarm. 
> 
> This change adds fetching PMBus revision supported by device and
> modifies pmbus_show_boolean so that it only tries to clear individual
> status bits if the device is compilant with PMBus specs >= 1.2.
> 
> Tested on: LTC2971, LTC2971-1, LTC2974, LTC2977.
> ---
>  drivers/hwmon/pmbus/pmbus.h      |  6 ++++++
>  drivers/hwmon/pmbus/pmbus_core.c | 12 +++++++++++-
>  2 files changed, 17 insertions(+), 1 deletion(-)
> 
> 
> ---
> base-commit: c763c43396883456ef57e5e78b64d3c259c4babc
> change-id: 20240905-pmbus-status-reg-clearing-abc9c0184c3b
> 
> Best regards,
> 
> diff --git a/drivers/hwmon/pmbus/pmbus.h b/drivers/hwmon/pmbus/pmbus.h
> index fb442fae7b3e..0bea603994e7 100644
> --- a/drivers/hwmon/pmbus/pmbus.h
> +++ b/drivers/hwmon/pmbus/pmbus.h
> @@ -418,6 +418,12 @@ enum pmbus_sensor_classes {
>  enum pmbus_data_format { linear = 0, ieee754, direct, vid };
>  enum vrm_version { vr11 = 0, vr12, vr13, imvp9, amd625mv };
>  
> +/* PMBus revision identifiers */
> +#define PMBUS_REV_10 0x00	/* PMBus revision 1.0 */
> +#define PMBUS_REV_11 0x11	/* PMBus revision 1.1 */
> +#define PMBUS_REV_12 0x22	/* PMBus revision 1.2 */
> +#define PMBUS_REV_13 0x33	/* PMBus revision 1.3 */
> +
>  struct pmbus_driver_info {
>  	int pages;		/* Total number of pages */
>  	u8 phases[PMBUS_PAGES];	/* Number of phases per page */
> diff --git a/drivers/hwmon/pmbus/pmbus_core.c b/drivers/hwmon/pmbus/pmbus_core.c
> index cb4c65a7f288..50ba093a38e8 100644
> --- a/drivers/hwmon/pmbus/pmbus_core.c
> +++ b/drivers/hwmon/pmbus/pmbus_core.c
> @@ -108,6 +108,8 @@ struct pmbus_data {
>  
>  	int vout_low[PMBUS_PAGES];	/* voltage low margin */
>  	int vout_high[PMBUS_PAGES];	/* voltage high margin */
> +
> +	u8 revision;	/* The PMBus revision the device is compliant with */
>  };
>  
>  struct pmbus_debugfs_entry {
> @@ -1095,7 +1097,11 @@ static int pmbus_get_boolean(struct i2c_client *client, struct pmbus_boolean *b,
>  
>  	regval = status & mask;
>  	if (regval) {
> -		ret = _pmbus_write_byte_data(client, page, reg, regval);
> +		if (data->revision >= PMBUS_REV_12)
> +			ret = _pmbus_write_byte_data(client, page, reg, regval);
> +		else
> +			pmbus_clear_fault_page(client, page);
> +
>  		if (ret)
>  			goto unlock;

That check needs to be part of the if() statement above. Never mind, though,
I fixed that up.

Guenter
kernel test robot Sept. 12, 2024, 4:36 p.m. UTC | #3
Hi Patryk,

kernel test robot noticed the following build warnings:

[auto build test WARNING on c763c43396883456ef57e5e78b64d3c259c4babc]

url:    https://github.com/intel-lab-lkp/linux/commits/Patryk-Biel/hwmon-Conditionally-clear-individual-status-bits-for-pmbus-rev-1-2/20240909-173838
base:   c763c43396883456ef57e5e78b64d3c259c4babc
patch link:    https://lore.kernel.org/r/20240909-pmbus-status-reg-clearing-v1-1-f1c0d68c6408%40gmail.com
patch subject: [PATCH] hwmon: Conditionally clear individual status bits for pmbus rev >= 1.2
config: x86_64-rhel-8.3-rust (https://download.01.org/0day-ci/archive/20240912/202409122210.audc5WGS-lkp@intel.com/config)
compiler: clang version 18.1.8 (https://github.com/llvm/llvm-project 3b5b5c1ec4a3095ab096dd780e84d7ab81f3d7ff)
reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20240912/202409122210.audc5WGS-lkp@intel.com/reproduce)

If you fix the issue in a separate patch/commit (i.e. not just a new version of
the same patch/commit), kindly add following tags
| Reported-by: kernel test robot <lkp@intel.com>
| Closes: https://lore.kernel.org/oe-kbuild-all/202409122210.audc5WGS-lkp@intel.com/

All warnings (new ones prefixed by >>):

>> drivers/hwmon/pmbus/pmbus_core.c:1100:7: warning: variable 'ret' is used uninitialized whenever 'if' condition is false [-Wsometimes-uninitialized]
    1100 |                 if (data->revision >= PMBUS_REV_12)
         |                     ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
   drivers/hwmon/pmbus/pmbus_core.c:1105:7: note: uninitialized use occurs here
    1105 |                 if (ret)
         |                     ^~~
   drivers/hwmon/pmbus/pmbus_core.c:1100:3: note: remove the 'if' if its condition is always true
    1100 |                 if (data->revision >= PMBUS_REV_12)
         |                 ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    1101 |                         ret = _pmbus_write_byte_data(client, page, reg, regval);
         |                                                                                ~
    1102 |                 else
         |                 ~~~~
    1103 |                         pmbus_clear_fault_page(client, page);
         |                         ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
   drivers/hwmon/pmbus/pmbus_core.c:1083:9: note: initialize the variable 'ret' to silence this warning
    1083 |         int ret, status;
         |                ^
         |                 = 0
   1 warning generated.


vim +1100 drivers/hwmon/pmbus/pmbus_core.c

  1050	
  1051	/*
  1052	 * Return boolean calculated from converted data.
  1053	 * <index> defines a status register index and mask.
  1054	 * The mask is in the lower 8 bits, the register index is in bits 8..23.
  1055	 *
  1056	 * The associated pmbus_boolean structure contains optional pointers to two
  1057	 * sensor attributes. If specified, those attributes are compared against each
  1058	 * other to determine if a limit has been exceeded.
  1059	 *
  1060	 * If the sensor attribute pointers are NULL, the function returns true if
  1061	 * (status[reg] & mask) is true.
  1062	 *
  1063	 * If sensor attribute pointers are provided, a comparison against a specified
  1064	 * limit has to be performed to determine the boolean result.
  1065	 * In this case, the function returns true if v1 >= v2 (where v1 and v2 are
  1066	 * sensor values referenced by sensor attribute pointers s1 and s2).
  1067	 *
  1068	 * To determine if an object exceeds upper limits, specify <s1,s2> = <v,limit>.
  1069	 * To determine if an object exceeds lower limits, specify <s1,s2> = <limit,v>.
  1070	 *
  1071	 * If a negative value is stored in any of the referenced registers, this value
  1072	 * reflects an error code which will be returned.
  1073	 */
  1074	static int pmbus_get_boolean(struct i2c_client *client, struct pmbus_boolean *b,
  1075				     int index)
  1076	{
  1077		struct pmbus_data *data = i2c_get_clientdata(client);
  1078		struct pmbus_sensor *s1 = b->s1;
  1079		struct pmbus_sensor *s2 = b->s2;
  1080		u16 mask = pb_index_to_mask(index);
  1081		u8 page = pb_index_to_page(index);
  1082		u16 reg = pb_index_to_reg(index);
  1083		int ret, status;
  1084		u16 regval;
  1085	
  1086		mutex_lock(&data->update_lock);
  1087		status = pmbus_get_status(client, page, reg);
  1088		if (status < 0) {
  1089			ret = status;
  1090			goto unlock;
  1091		}
  1092	
  1093		if (s1)
  1094			pmbus_update_sensor_data(client, s1);
  1095		if (s2)
  1096			pmbus_update_sensor_data(client, s2);
  1097	
  1098		regval = status & mask;
  1099		if (regval) {
> 1100			if (data->revision >= PMBUS_REV_12)
  1101				ret = _pmbus_write_byte_data(client, page, reg, regval);
  1102			else
  1103				pmbus_clear_fault_page(client, page);
  1104	
  1105			if (ret)
  1106				goto unlock;
  1107		}
  1108		if (s1 && s2) {
  1109			s64 v1, v2;
  1110	
  1111			if (s1->data < 0) {
  1112				ret = s1->data;
  1113				goto unlock;
  1114			}
  1115			if (s2->data < 0) {
  1116				ret = s2->data;
  1117				goto unlock;
  1118			}
  1119	
  1120			v1 = pmbus_reg2data(data, s1);
  1121			v2 = pmbus_reg2data(data, s2);
  1122			ret = !!(regval && v1 >= v2);
  1123		} else {
  1124			ret = !!regval;
  1125		}
  1126	unlock:
  1127		mutex_unlock(&data->update_lock);
  1128		return ret;
  1129	}
  1130
diff mbox series

Patch

diff --git a/drivers/hwmon/pmbus/pmbus.h b/drivers/hwmon/pmbus/pmbus.h
index fb442fae7b3e..0bea603994e7 100644
--- a/drivers/hwmon/pmbus/pmbus.h
+++ b/drivers/hwmon/pmbus/pmbus.h
@@ -418,6 +418,12 @@  enum pmbus_sensor_classes {
 enum pmbus_data_format { linear = 0, ieee754, direct, vid };
 enum vrm_version { vr11 = 0, vr12, vr13, imvp9, amd625mv };
 
+/* PMBus revision identifiers */
+#define PMBUS_REV_10 0x00	/* PMBus revision 1.0 */
+#define PMBUS_REV_11 0x11	/* PMBus revision 1.1 */
+#define PMBUS_REV_12 0x22	/* PMBus revision 1.2 */
+#define PMBUS_REV_13 0x33	/* PMBus revision 1.3 */
+
 struct pmbus_driver_info {
 	int pages;		/* Total number of pages */
 	u8 phases[PMBUS_PAGES];	/* Number of phases per page */
diff --git a/drivers/hwmon/pmbus/pmbus_core.c b/drivers/hwmon/pmbus/pmbus_core.c
index cb4c65a7f288..50ba093a38e8 100644
--- a/drivers/hwmon/pmbus/pmbus_core.c
+++ b/drivers/hwmon/pmbus/pmbus_core.c
@@ -108,6 +108,8 @@  struct pmbus_data {
 
 	int vout_low[PMBUS_PAGES];	/* voltage low margin */
 	int vout_high[PMBUS_PAGES];	/* voltage high margin */
+
+	u8 revision;	/* The PMBus revision the device is compliant with */
 };
 
 struct pmbus_debugfs_entry {
@@ -1095,7 +1097,11 @@  static int pmbus_get_boolean(struct i2c_client *client, struct pmbus_boolean *b,
 
 	regval = status & mask;
 	if (regval) {
-		ret = _pmbus_write_byte_data(client, page, reg, regval);
+		if (data->revision >= PMBUS_REV_12)
+			ret = _pmbus_write_byte_data(client, page, reg, regval);
+		else
+			pmbus_clear_fault_page(client, page);
+
 		if (ret)
 			goto unlock;
 	}
@@ -2653,6 +2659,10 @@  static int pmbus_init_common(struct i2c_client *client, struct pmbus_data *data,
 		}
 	}
 
+	ret = i2c_smbus_read_byte_data(client, PMBUS_REVISION);
+	if (ret > 0)
+		data->revision = ret;
+
 	if (info->pages <= 0 || info->pages > PMBUS_PAGES) {
 		dev_err(dev, "Bad number of PMBus pages: %d\n", info->pages);
 		return -ENODEV;