hwmon: scmi: Scale values to target desired HWMON units

If the SCMI firmware implementation is reporting values in a scale that
is different from the HWMON units, we need to scale up or down the value
according to how far apart they are.

Reviewed-by: Guenter Roeck <linux@roeck-us.net>
Signed-off-by: Florian Fainelli <f.fainelli@gmail.com>
[sudeep.holla: added check of scale = 0 for early exit in scmi_hwmon_scale]
Signed-off-by: Sudeep Holla <sudeep.holla@arm.com>
This commit is contained in:
Florian Fainelli 2019-05-08 11:46:35 -07:00 committed by Sudeep Holla
parent 0b673b6486
commit ac778e6263

View File

@ -18,6 +18,50 @@ struct scmi_sensors {
const struct scmi_sensor_info **info[hwmon_max];
};
static inline u64 __pow10(u8 x)
{
u64 r = 1;
while (x--)
r *= 10;
return r;
}
static int scmi_hwmon_scale(const struct scmi_sensor_info *sensor, u64 *value)
{
s8 scale = sensor->scale;
u64 f;
switch (sensor->type) {
case TEMPERATURE_C:
case VOLTAGE:
case CURRENT:
scale += 3;
break;
case POWER:
case ENERGY:
scale += 6;
break;
default:
break;
}
if (scale == 0)
return 0;
if (abs(scale) > 19)
return -E2BIG;
f = __pow10(abs(scale));
if (scale > 0)
*value *= f;
else
*value = div64_u64(*value, f);
return 0;
}
static int scmi_hwmon_read(struct device *dev, enum hwmon_sensor_types type,
u32 attr, int channel, long *val)
{
@ -29,6 +73,10 @@ static int scmi_hwmon_read(struct device *dev, enum hwmon_sensor_types type,
sensor = *(scmi_sensors->info[type] + channel);
ret = h->sensor_ops->reading_get(h, sensor->id, false, &value);
if (ret)
return ret;
ret = scmi_hwmon_scale(sensor, &value);
if (!ret)
*val = value;