.../ABI/testing/sysfs-driver-intel-i915-hwmon | 8 ++ drivers/gpu/drm/i915/gt/intel_gt_regs.h | 2 + drivers/gpu/drm/i915/i915_hwmon.c | 85 +++++++++++++++++++ 3 files changed, 95 insertions(+)
Add hwmon support for fan1_input attribute, which will expose fan speed
in RPM. With this in place we can monitor fan speed using lm-sensors tool.
$ sensors
i915-pci-0300
Adapter: PCI adapter
in0: 653.00 mV
fan1: 3833 RPM
power1: N/A (max = 43.00 W)
energy1: 32.02 kJ
v2: Handle overflow, add mutex protection and ABI documentation
Aesthetic adjustments (Riana)
v3: Declare rotations as "long", change ABI date and version
Add commenter name in changelog (Riana)
v4: Fix wakeref leak
Drop switch case and simplify hwm_fan_xx() (Andi)
v5: Rework time calculation, aesthetic adjustments (Andy)
Signed-off-by: Raag Jadav <raag.jadav@intel.com>
Reviewed-by: Riana Tauro <riana.tauro@intel.com>
---
.../ABI/testing/sysfs-driver-intel-i915-hwmon | 8 ++
drivers/gpu/drm/i915/gt/intel_gt_regs.h | 2 +
drivers/gpu/drm/i915/i915_hwmon.c | 85 +++++++++++++++++++
3 files changed, 95 insertions(+)
diff --git a/Documentation/ABI/testing/sysfs-driver-intel-i915-hwmon b/Documentation/ABI/testing/sysfs-driver-intel-i915-hwmon
index 92fe7c5c5ac1..be4141a7522f 100644
--- a/Documentation/ABI/testing/sysfs-driver-intel-i915-hwmon
+++ b/Documentation/ABI/testing/sysfs-driver-intel-i915-hwmon
@@ -75,3 +75,11 @@ Description: RO. Energy input of device or gt in microjoules.
for the gt.
Only supported for particular Intel i915 graphics platforms.
+
+What: /sys/bus/pci/drivers/i915/.../hwmon/hwmon<i>/fan1_input
+Date: November 2024
+KernelVersion: 6.12
+Contact: intel-gfx@lists.freedesktop.org
+Description: RO. Fan speed of device in RPM.
+
+ Only supported for particular Intel i915 graphics platforms.
diff --git a/drivers/gpu/drm/i915/gt/intel_gt_regs.h b/drivers/gpu/drm/i915/gt/intel_gt_regs.h
index e42b3a5d4e63..57a3c83d3655 100644
--- a/drivers/gpu/drm/i915/gt/intel_gt_regs.h
+++ b/drivers/gpu/drm/i915/gt/intel_gt_regs.h
@@ -1553,6 +1553,8 @@
#define VLV_RENDER_C0_COUNT _MMIO(0x138118)
#define VLV_MEDIA_C0_COUNT _MMIO(0x13811c)
+#define PCU_PWM_FAN_SPEED _MMIO(0x138140)
+
#define GEN12_RPSTAT1 _MMIO(0x1381b4)
#define GEN12_VOLTAGE_MASK REG_GENMASK(10, 0)
#define GEN12_CAGF_MASK REG_GENMASK(19, 11)
diff --git a/drivers/gpu/drm/i915/i915_hwmon.c b/drivers/gpu/drm/i915/i915_hwmon.c
index 49db3e09826c..a867b81a0900 100644
--- a/drivers/gpu/drm/i915/i915_hwmon.c
+++ b/drivers/gpu/drm/i915/i915_hwmon.c
@@ -5,6 +5,7 @@
#include <linux/hwmon.h>
#include <linux/hwmon-sysfs.h>
+#include <linux/jiffies.h>
#include <linux/types.h>
#include "i915_drv.h"
@@ -36,6 +37,7 @@ struct hwm_reg {
i915_reg_t pkg_rapl_limit;
i915_reg_t energy_status_all;
i915_reg_t energy_status_tile;
+ i915_reg_t fan_speed;
};
struct hwm_energy_info {
@@ -43,11 +45,17 @@ struct hwm_energy_info {
long accum_energy; /* Accumulated energy for energy1_input */
};
+struct hwm_fan_info {
+ u32 reg_val_prev;
+ u64 time_prev;
+};
+
struct hwm_drvdata {
struct i915_hwmon *hwmon;
struct intel_uncore *uncore;
struct device *hwmon_dev;
struct hwm_energy_info ei; /* Energy info for energy1_input */
+ struct hwm_fan_info fi; /* Fan info for fan1_input */
char name[12];
int gt_n;
bool reset_in_progress;
@@ -276,6 +284,7 @@ static const struct hwmon_channel_info * const hwm_info[] = {
HWMON_CHANNEL_INFO(power, HWMON_P_MAX | HWMON_P_RATED_MAX | HWMON_P_CRIT),
HWMON_CHANNEL_INFO(energy, HWMON_E_INPUT),
HWMON_CHANNEL_INFO(curr, HWMON_C_CRIT),
+ HWMON_CHANNEL_INFO(fan, HWMON_F_INPUT),
NULL
};
@@ -613,6 +622,66 @@ hwm_curr_write(struct hwm_drvdata *ddat, u32 attr, long val)
}
}
+static umode_t
+hwm_fan_is_visible(const struct hwm_drvdata *ddat, u32 attr)
+{
+ struct i915_hwmon *hwmon = ddat->hwmon;
+
+ return attr == hwmon_fan_input &&
+ i915_mmio_reg_valid(hwmon->rg.fan_speed) ? 0444 : 0;
+}
+
+static int
+hwm_fan_read(struct hwm_drvdata *ddat, u32 attr, long *val)
+{
+ struct i915_hwmon *hwmon = ddat->hwmon;
+ struct hwm_fan_info *fi = &ddat->fi;
+ u64 rotations, time_now, time;
+ intel_wakeref_t wakeref;
+ u32 reg_val, pulses;
+ int ret = 0;
+
+ if (attr != hwmon_fan_input)
+ return -EOPNOTSUPP;
+
+ wakeref = intel_runtime_pm_get(ddat->uncore->rpm);
+ mutex_lock(&hwmon->hwmon_lock);
+
+ reg_val = intel_uncore_read(ddat->uncore, hwmon->rg.fan_speed);
+ time_now = get_jiffies_64();
+
+ /* Handle HW register overflow */
+ if (reg_val >= fi->reg_val_prev)
+ pulses = reg_val - fi->reg_val_prev;
+ else
+ pulses = UINT_MAX - fi->reg_val_prev + reg_val;
+
+ /*
+ * HW register value is accumulated count of pulses from
+ * PWM fan with the scale of 2 pulses per rotation.
+ */
+ rotations = pulses / 2;
+
+ time = jiffies_delta_to_msecs(time_now - fi->time_prev);
+ if (unlikely(!time)) {
+ ret = -EAGAIN;
+ goto exit;
+ }
+
+ /*
+ * Convert to minutes for calculating RPM.
+ * RPM = number of rotations * msecs per minute / time in msecs
+ */
+ *val = DIV_ROUND_UP(rotations * (MSEC_PER_SEC * 60), time);
+
+ fi->reg_val_prev = reg_val;
+ fi->time_prev = time_now;
+exit:
+ mutex_unlock(&hwmon->hwmon_lock);
+ intel_runtime_pm_put(ddat->uncore->rpm, wakeref);
+ return ret;
+}
+
static umode_t
hwm_is_visible(const void *drvdata, enum hwmon_sensor_types type,
u32 attr, int channel)
@@ -628,6 +697,8 @@ hwm_is_visible(const void *drvdata, enum hwmon_sensor_types type,
return hwm_energy_is_visible(ddat, attr);
case hwmon_curr:
return hwm_curr_is_visible(ddat, attr);
+ case hwmon_fan:
+ return hwm_fan_is_visible(ddat, attr);
default:
return 0;
}
@@ -648,6 +719,8 @@ hwm_read(struct device *dev, enum hwmon_sensor_types type, u32 attr,
return hwm_energy_read(ddat, attr, val);
case hwmon_curr:
return hwm_curr_read(ddat, attr, val);
+ case hwmon_fan:
+ return hwm_fan_read(ddat, attr, val);
default:
return -EOPNOTSUPP;
}
@@ -739,12 +812,14 @@ hwm_get_preregistration_info(struct drm_i915_private *i915)
hwmon->rg.pkg_rapl_limit = PCU_PACKAGE_RAPL_LIMIT;
hwmon->rg.energy_status_all = PCU_PACKAGE_ENERGY_STATUS;
hwmon->rg.energy_status_tile = INVALID_MMIO_REG;
+ hwmon->rg.fan_speed = PCU_PWM_FAN_SPEED;
} else {
hwmon->rg.pkg_power_sku_unit = INVALID_MMIO_REG;
hwmon->rg.pkg_power_sku = INVALID_MMIO_REG;
hwmon->rg.pkg_rapl_limit = INVALID_MMIO_REG;
hwmon->rg.energy_status_all = INVALID_MMIO_REG;
hwmon->rg.energy_status_tile = INVALID_MMIO_REG;
+ hwmon->rg.fan_speed = INVALID_MMIO_REG;
}
with_intel_runtime_pm(uncore->rpm, wakeref) {
@@ -755,6 +830,16 @@ hwm_get_preregistration_info(struct drm_i915_private *i915)
if (i915_mmio_reg_valid(hwmon->rg.pkg_power_sku_unit))
val_sku_unit = intel_uncore_read(uncore,
hwmon->rg.pkg_power_sku_unit);
+
+ /*
+ * Store the initial fan register value, so that we can use it for
+ * initial fan speed calculation.
+ */
+ if (i915_mmio_reg_valid(hwmon->rg.fan_speed)) {
+ ddat->fi.reg_val_prev = intel_uncore_read(uncore,
+ hwmon->rg.fan_speed);
+ ddat->fi.time_prev = get_jiffies_64();
+ }
}
hwmon->scl_shift_power = REG_FIELD_GET(PKG_PWR_UNIT, val_sku_unit);
--
2.34.1
Hi Raag, kernel test robot noticed the following build errors: [auto build test ERROR on groeck-staging/hwmon-next] [also build test ERROR on linus/master v6.11-rc3 next-20240812] [If your patch is applied to the wrong git tree, kindly drop us a note. And when submitting patch, we suggest to use '--base' as documented in https://git-scm.com/docs/git-format-patch#_base_tree_information] url: https://github.com/intel-lab-lkp/linux/commits/Raag-Jadav/drm-i915-hwmon-expose-fan-speed/20240812-161645 base: https://git.kernel.org/pub/scm/linux/kernel/git/groeck/linux-staging.git hwmon-next patch link: https://lore.kernel.org/r/20240812081538.1457396-1-raag.jadav%40intel.com patch subject: [PATCH v5] drm/i915/hwmon: expose fan speed config: i386-allyesconfig (https://download.01.org/0day-ci/archive/20240813/202408130800.XtY6XxQ5-lkp@intel.com/config) compiler: gcc-12 (Debian 12.2.0-14) 12.2.0 reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20240813/202408130800.XtY6XxQ5-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/202408130800.XtY6XxQ5-lkp@intel.com/ All errors (new ones prefixed by >>): ld: drivers/gpu/drm/i915/i915_hwmon.o: in function `hwm_read': >> i915_hwmon.c:(.text+0xe60): undefined reference to `__udivdi3' -- 0-DAY CI Kernel Test Service https://github.com/intel/lkp-tests/wiki
Hi Raag,
kernel test robot noticed the following build errors:
[auto build test ERROR on groeck-staging/hwmon-next]
[also build test ERROR on linus/master v6.11-rc3 next-20240812]
[If your patch is applied to the wrong git tree, kindly drop us a note.
And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch#_base_tree_information]
url: https://github.com/intel-lab-lkp/linux/commits/Raag-Jadav/drm-i915-hwmon-expose-fan-speed/20240812-161645
base: https://git.kernel.org/pub/scm/linux/kernel/git/groeck/linux-staging.git hwmon-next
patch link: https://lore.kernel.org/r/20240812081538.1457396-1-raag.jadav%40intel.com
patch subject: [PATCH v5] drm/i915/hwmon: expose fan speed
config: i386-randconfig-012-20240813 (https://download.01.org/0day-ci/archive/20240813/202408130500.SgCVoR2D-lkp@intel.com/config)
compiler: gcc-12 (Debian 12.2.0-14) 12.2.0
reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20240813/202408130500.SgCVoR2D-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/202408130500.SgCVoR2D-lkp@intel.com/
All errors (new ones prefixed by >>):
ld: drivers/gpu/drm/i915/i915_hwmon.o: in function `hwm_fan_read':
>> drivers/gpu/drm/i915/i915_hwmon.c:675: undefined reference to `__udivdi3'
vim +675 drivers/gpu/drm/i915/i915_hwmon.c
633
634 static int
635 hwm_fan_read(struct hwm_drvdata *ddat, u32 attr, long *val)
636 {
637 struct i915_hwmon *hwmon = ddat->hwmon;
638 struct hwm_fan_info *fi = &ddat->fi;
639 u64 rotations, time_now, time;
640 intel_wakeref_t wakeref;
641 u32 reg_val, pulses;
642 int ret = 0;
643
644 if (attr != hwmon_fan_input)
645 return -EOPNOTSUPP;
646
647 wakeref = intel_runtime_pm_get(ddat->uncore->rpm);
648 mutex_lock(&hwmon->hwmon_lock);
649
650 reg_val = intel_uncore_read(ddat->uncore, hwmon->rg.fan_speed);
651 time_now = get_jiffies_64();
652
653 /* Handle HW register overflow */
654 if (reg_val >= fi->reg_val_prev)
655 pulses = reg_val - fi->reg_val_prev;
656 else
657 pulses = UINT_MAX - fi->reg_val_prev + reg_val;
658
659 /*
660 * HW register value is accumulated count of pulses from
661 * PWM fan with the scale of 2 pulses per rotation.
662 */
663 rotations = pulses / 2;
664
665 time = jiffies_delta_to_msecs(time_now - fi->time_prev);
666 if (unlikely(!time)) {
667 ret = -EAGAIN;
668 goto exit;
669 }
670
671 /*
672 * Convert to minutes for calculating RPM.
673 * RPM = number of rotations * msecs per minute / time in msecs
674 */
> 675 *val = DIV_ROUND_UP(rotations * (MSEC_PER_SEC * 60), time);
676
677 fi->reg_val_prev = reg_val;
678 fi->time_prev = time_now;
679 exit:
680 mutex_unlock(&hwmon->hwmon_lock);
681 intel_runtime_pm_put(ddat->uncore->rpm, wakeref);
682 return ret;
683 }
684
--
0-DAY CI Kernel Test Service
https://github.com/intel/lkp-tests/wiki
Hi Raag,
> +static int
> +hwm_fan_read(struct hwm_drvdata *ddat, u32 attr, long *val)
> +{
> + struct i915_hwmon *hwmon = ddat->hwmon;
> + struct hwm_fan_info *fi = &ddat->fi;
> + u64 rotations, time_now, time;
> + intel_wakeref_t wakeref;
> + u32 reg_val, pulses;
> + int ret = 0;
> +
> + if (attr != hwmon_fan_input)
> + return -EOPNOTSUPP;
> +
> + wakeref = intel_runtime_pm_get(ddat->uncore->rpm);
> + mutex_lock(&hwmon->hwmon_lock);
> +
> + reg_val = intel_uncore_read(ddat->uncore, hwmon->rg.fan_speed);
> + time_now = get_jiffies_64();
> +
> + /* Handle HW register overflow */
> + if (reg_val >= fi->reg_val_prev)
> + pulses = reg_val - fi->reg_val_prev;
> + else
> + pulses = UINT_MAX - fi->reg_val_prev + reg_val;
> +
> + /*
> + * HW register value is accumulated count of pulses from
> + * PWM fan with the scale of 2 pulses per rotation.
> + */
> + rotations = pulses / 2;
> +
> + time = jiffies_delta_to_msecs(time_now - fi->time_prev);
> + if (unlikely(!time)) {
> + ret = -EAGAIN;
> + goto exit;
> + }
Can you please add a comment describing how you obtain the speed
calculation?
Basically at every read you store the values. Is it possible that
we don't have reads for a long time and the register resets more
than once?
Thanks,
Andi
> + /*
> + * Convert to minutes for calculating RPM.
> + * RPM = number of rotations * msecs per minute / time in msecs
> + */
> + *val = DIV_ROUND_UP(rotations * (MSEC_PER_SEC * 60), time);
> +
> + fi->reg_val_prev = reg_val;
> + fi->time_prev = time_now;
> +exit:
> + mutex_unlock(&hwmon->hwmon_lock);
> + intel_runtime_pm_put(ddat->uncore->rpm, wakeref);
> + return ret;
> +}
On Mon, Aug 12, 2024 at 03:50:47PM +0200, Andi Shyti wrote:
> Hi Raag,
>
> > +static int
> > +hwm_fan_read(struct hwm_drvdata *ddat, u32 attr, long *val)
> > +{
> > + struct i915_hwmon *hwmon = ddat->hwmon;
> > + struct hwm_fan_info *fi = &ddat->fi;
> > + u64 rotations, time_now, time;
> > + intel_wakeref_t wakeref;
> > + u32 reg_val, pulses;
> > + int ret = 0;
> > +
> > + if (attr != hwmon_fan_input)
> > + return -EOPNOTSUPP;
> > +
> > + wakeref = intel_runtime_pm_get(ddat->uncore->rpm);
> > + mutex_lock(&hwmon->hwmon_lock);
> > +
> > + reg_val = intel_uncore_read(ddat->uncore, hwmon->rg.fan_speed);
> > + time_now = get_jiffies_64();
> > +
> > + /* Handle HW register overflow */
> > + if (reg_val >= fi->reg_val_prev)
> > + pulses = reg_val - fi->reg_val_prev;
> > + else
> > + pulses = UINT_MAX - fi->reg_val_prev + reg_val;
> > +
> > + /*
> > + * HW register value is accumulated count of pulses from
> > + * PWM fan with the scale of 2 pulses per rotation.
> > + */
> > + rotations = pulses / 2;
> > +
> > + time = jiffies_delta_to_msecs(time_now - fi->time_prev);
> > + if (unlikely(!time)) {
> > + ret = -EAGAIN;
> > + goto exit;
> > + }
>
> Can you please add a comment describing how you obtain the speed
> calculation?
That's what I initially tried but ended up dropping it in favour of RPM
formula below, which I found to be doing a better job of explaining than
a few lines of description.
> Basically at every read you store the values. Is it possible that
> we don't have reads for a long time and the register resets more
> than once?
Considering a fan continuously running at higher speeds (for example 4000 RPM
which is quite optimistic), with the scale of 2 pulses per rotation, a 32 bit
register will take around a year to overflow, which is more than most usecases
I could think of.
Raag
> > + /*
> > + * Convert to minutes for calculating RPM.
> > + * RPM = number of rotations * msecs per minute / time in msecs
> > + */
> > + *val = DIV_ROUND_UP(rotations * (MSEC_PER_SEC * 60), time);
> > +
> > + fi->reg_val_prev = reg_val;
> > + fi->time_prev = time_now;
> > +exit:
> > + mutex_unlock(&hwmon->hwmon_lock);
> > + intel_runtime_pm_put(ddat->uncore->rpm, wakeref);
> > + return ret;
> > +}
> > > + /*
> > > + * HW register value is accumulated count of pulses from
> > > + * PWM fan with the scale of 2 pulses per rotation.
> > > + */
> > > + rotations = pulses / 2;
> > > +
> > > + time = jiffies_delta_to_msecs(time_now - fi->time_prev);
> > > + if (unlikely(!time)) {
> > > + ret = -EAGAIN;
> > > + goto exit;
> > > + }
> >
> > Can you please add a comment describing how you obtain the speed
> > calculation?
>
> That's what I initially tried but ended up dropping it in favour of RPM
> formula below, which I found to be doing a better job of explaining than
> a few lines of description.
>
> > Basically at every read you store the values. Is it possible that
> > we don't have reads for a long time and the register resets more
> > than once?
>
> Considering a fan continuously running at higher speeds (for example 4000 RPM
> which is quite optimistic), with the scale of 2 pulses per rotation, a 32 bit
> register will take around a year to overflow, which is more than most usecases
> I could think of.
Which can be considered as a worse case scenario. I would have
preferred here a runtime calculation, which means read now, wait
a bit, read again and calculate. The read might be slow, but
efficient.
Anyway, your argument makes sense, so that I'm not going to push
on this, I already r-b'ed it.
Thanks,
Andi
On Mon, Aug 12, 2024 at 01:45:38PM +0530, Raag Jadav wrote:
> Add hwmon support for fan1_input attribute, which will expose fan speed
> in RPM. With this in place we can monitor fan speed using lm-sensors tool.
>
> $ sensors
> i915-pci-0300
> Adapter: PCI adapter
> in0: 653.00 mV
> fan1: 3833 RPM
> power1: N/A (max = 43.00 W)
> energy1: 32.02 kJ
...
> +static int
> +hwm_fan_read(struct hwm_drvdata *ddat, u32 attr, long *val)
> +{
> + struct i915_hwmon *hwmon = ddat->hwmon;
> + struct hwm_fan_info *fi = &ddat->fi;
> + u64 rotations, time_now, time;
> + intel_wakeref_t wakeref;
> + u32 reg_val, pulses;
> + int ret = 0;
> +
> + if (attr != hwmon_fan_input)
> + return -EOPNOTSUPP;
> +
> + wakeref = intel_runtime_pm_get(ddat->uncore->rpm);
> + mutex_lock(&hwmon->hwmon_lock);
> +
> + reg_val = intel_uncore_read(ddat->uncore, hwmon->rg.fan_speed);
> + time_now = get_jiffies_64();
> + /* Handle HW register overflow */
> + if (reg_val >= fi->reg_val_prev)
> + pulses = reg_val - fi->reg_val_prev;
> + else
> + pulses = UINT_MAX - fi->reg_val_prev + reg_val;
Isn't it the abs_diff() reimplementation?
> + /*
> + * HW register value is accumulated count of pulses from
> + * PWM fan with the scale of 2 pulses per rotation.
> + */
> + rotations = pulses / 2;
> +
> + time = jiffies_delta_to_msecs(time_now - fi->time_prev);
> + if (unlikely(!time)) {
> + ret = -EAGAIN;
> + goto exit;
> + }
> +
> + /*
> + * Convert to minutes for calculating RPM.
> + * RPM = number of rotations * msecs per minute / time in msecs
> + */
> + *val = DIV_ROUND_UP(rotations * (MSEC_PER_SEC * 60), time);
> +
> + fi->reg_val_prev = reg_val;
> + fi->time_prev = time_now;
> +exit:
> + mutex_unlock(&hwmon->hwmon_lock);
> + intel_runtime_pm_put(ddat->uncore->rpm, wakeref);
> + return ret;
> +}
--
With Best Regards,
Andy Shevchenko
On Mon, Aug 12, 2024 at 04:15:14PM +0300, Andy Shevchenko wrote:
> On Mon, Aug 12, 2024 at 01:45:38PM +0530, Raag Jadav wrote:
> > Add hwmon support for fan1_input attribute, which will expose fan speed
> > in RPM. With this in place we can monitor fan speed using lm-sensors tool.
> >
> > $ sensors
> > i915-pci-0300
> > Adapter: PCI adapter
> > in0: 653.00 mV
> > fan1: 3833 RPM
> > power1: N/A (max = 43.00 W)
> > energy1: 32.02 kJ
>
> ...
>
> > +static int
> > +hwm_fan_read(struct hwm_drvdata *ddat, u32 attr, long *val)
> > +{
> > + struct i915_hwmon *hwmon = ddat->hwmon;
> > + struct hwm_fan_info *fi = &ddat->fi;
> > + u64 rotations, time_now, time;
> > + intel_wakeref_t wakeref;
> > + u32 reg_val, pulses;
> > + int ret = 0;
> > +
> > + if (attr != hwmon_fan_input)
> > + return -EOPNOTSUPP;
> > +
> > + wakeref = intel_runtime_pm_get(ddat->uncore->rpm);
> > + mutex_lock(&hwmon->hwmon_lock);
> > +
> > + reg_val = intel_uncore_read(ddat->uncore, hwmon->rg.fan_speed);
> > + time_now = get_jiffies_64();
>
> > + /* Handle HW register overflow */
> > + if (reg_val >= fi->reg_val_prev)
> > + pulses = reg_val - fi->reg_val_prev;
> > + else
> > + pulses = UINT_MAX - fi->reg_val_prev + reg_val;
>
> Isn't it the abs_diff() reimplementation?
Not exactly. This is specific to 32 bit register overflow, so we count
from max value.
Raag
On Tue, Aug 13, 2024 at 08:45:19AM +0300, Raag Jadav wrote:
> On Mon, Aug 12, 2024 at 04:15:14PM +0300, Andy Shevchenko wrote:
> > On Mon, Aug 12, 2024 at 01:45:38PM +0530, Raag Jadav wrote:
...
> > > +static int
> > > +hwm_fan_read(struct hwm_drvdata *ddat, u32 attr, long *val)
> > > +{
> > > + struct i915_hwmon *hwmon = ddat->hwmon;
> > > + struct hwm_fan_info *fi = &ddat->fi;
> > > + u64 rotations, time_now, time;
> > > + intel_wakeref_t wakeref;
> > > + u32 reg_val, pulses;
> > > + int ret = 0;
> > > +
> > > + if (attr != hwmon_fan_input)
> > > + return -EOPNOTSUPP;
> > > +
> > > + wakeref = intel_runtime_pm_get(ddat->uncore->rpm);
> > > + mutex_lock(&hwmon->hwmon_lock);
> > > +
> > > + reg_val = intel_uncore_read(ddat->uncore, hwmon->rg.fan_speed);
> > > + time_now = get_jiffies_64();
> >
> > > + /* Handle HW register overflow */
> > > + if (reg_val >= fi->reg_val_prev)
> > > + pulses = reg_val - fi->reg_val_prev;
> > > + else
> > > + pulses = UINT_MAX - fi->reg_val_prev + reg_val;
> >
> > Isn't it the abs_diff() reimplementation?
>
> Not exactly. This is specific to 32 bit register overflow, so we count
> from max value.
I see. But since you have the both variables of u32, why:
1) UINT_MAX?
2) Not simply using
pulses = reg_val - fi->reg_val_prev;
which will wrap over correctly?
Note, in your case (in comparison to the wrap over variant) the off-by-one is
present. Is it on purpose?
--
With Best Regards,
Andy Shevchenko
On Tue, Aug 13, 2024 at 11:27:22AM +0300, Andy Shevchenko wrote:
> On Tue, Aug 13, 2024 at 08:45:19AM +0300, Raag Jadav wrote:
> > On Mon, Aug 12, 2024 at 04:15:14PM +0300, Andy Shevchenko wrote:
> > > On Mon, Aug 12, 2024 at 01:45:38PM +0530, Raag Jadav wrote:
>
> ...
>
> > > > +static int
> > > > +hwm_fan_read(struct hwm_drvdata *ddat, u32 attr, long *val)
> > > > +{
> > > > + struct i915_hwmon *hwmon = ddat->hwmon;
> > > > + struct hwm_fan_info *fi = &ddat->fi;
> > > > + u64 rotations, time_now, time;
> > > > + intel_wakeref_t wakeref;
> > > > + u32 reg_val, pulses;
> > > > + int ret = 0;
> > > > +
> > > > + if (attr != hwmon_fan_input)
> > > > + return -EOPNOTSUPP;
> > > > +
> > > > + wakeref = intel_runtime_pm_get(ddat->uncore->rpm);
> > > > + mutex_lock(&hwmon->hwmon_lock);
> > > > +
> > > > + reg_val = intel_uncore_read(ddat->uncore, hwmon->rg.fan_speed);
> > > > + time_now = get_jiffies_64();
> > >
> > > > + /* Handle HW register overflow */
> > > > + if (reg_val >= fi->reg_val_prev)
> > > > + pulses = reg_val - fi->reg_val_prev;
> > > > + else
> > > > + pulses = UINT_MAX - fi->reg_val_prev + reg_val;
> > >
> > > Isn't it the abs_diff() reimplementation?
> >
> > Not exactly. This is specific to 32 bit register overflow, so we count
> > from max value.
>
> I see. But since you have the both variables of u32, why:
> 1) UINT_MAX?
> 2) Not simply using
>
> pulses = reg_val - fi->reg_val_prev;
>
> which will wrap over correctly?
Agree. Will update.
Raag
© 2016 - 2026 Red Hat, Inc.