hwmon: (pmbus/bpa-rs600) Handle Vin readings >= 256V
authorChris Packham <chris.packham@alliedtelesis.co.nz>
Wed, 16 Jun 2021 03:42:18 +0000 (15:42 +1200)
committerGuenter Roeck <linux@roeck-us.net>
Thu, 17 Jun 2021 11:21:25 +0000 (04:21 -0700)
The BPA-RS600 doesn't follow the PMBus spec for linear data.
Specifically it treats the mantissa as an unsigned 11-bit value instead
of a two's complement 11-bit value. At this point it's unclear whether
this only affects Vin or if Pin/Pout1 are affected as well. Erring on
the side of caution only Vin is dealt with here.

Fixes: 15b2703e5e02 ("hwmon: (pmbus) Add driver for BluTek BPA-RS600")
Signed-off-by: Chris Packham <chris.packham@alliedtelesis.co.nz>
Link: https://lore.kernel.org/r/20210616034218.25821-1-chris.packham@alliedtelesis.co.nz
Signed-off-by: Guenter Roeck <linux@roeck-us.net>
drivers/hwmon/pmbus/bpa-rs600.c

index f6558ee..2be69fe 100644 (file)
@@ -46,6 +46,32 @@ static int bpa_rs600_read_byte_data(struct i2c_client *client, int page, int reg
        return ret;
 }
 
+/*
+ * The BPA-RS600 violates the PMBus spec. Specifically it treats the
+ * mantissa as unsigned. Deal with this here to allow the PMBus core
+ * to work with correctly encoded data.
+ */
+static int bpa_rs600_read_vin(struct i2c_client *client)
+{
+       int ret, exponent, mantissa;
+
+       ret = pmbus_read_word_data(client, 0, 0xff, PMBUS_READ_VIN);
+       if (ret < 0)
+               return ret;
+
+       if (ret & BIT(10)) {
+               exponent = ret >> 11;
+               mantissa = ret & 0x7ff;
+
+               exponent++;
+               mantissa >>= 1;
+
+               ret = (exponent << 11) | mantissa;
+       }
+
+       return ret;
+}
+
 static int bpa_rs600_read_word_data(struct i2c_client *client, int page, int phase, int reg)
 {
        int ret;
@@ -85,6 +111,9 @@ static int bpa_rs600_read_word_data(struct i2c_client *client, int page, int pha
                /* These commands return data but it is invalid/un-documented */
                ret = -ENXIO;
                break;
+       case PMBUS_READ_VIN:
+               ret = bpa_rs600_read_vin(client);
+               break;
        default:
                if (reg >= PMBUS_VIRT_BASE)
                        ret = -ENXIO;