]> git.ipfire.org Git - thirdparty/linux.git/commitdiff
drm/xe/hwmon: expose fan speed
authorRaag Jadav <raag.jadav@intel.com>
Wed, 12 Mar 2025 08:59:09 +0000 (14:29 +0530)
committerRodrigo Vivi <rodrigo.vivi@intel.com>
Fri, 14 Mar 2025 18:08:44 +0000 (14:08 -0400)
Add hwmon support for fan1_input, fan2_input and fan3_input attributes,
which will expose fan speed of respective channels in RPM when supported
by hardware. With this in place we can monitor fan speed using lm-sensors
tool.

v2: Rely on platform checks instead of mailbox error (Aravind, Rodrigo)
v3: Introduce has_fan_control flag (Rodrigo)

Signed-off-by: Raag Jadav <raag.jadav@intel.com>
Reviewed-by: Andi Shyti <andi.shyti@linux.intel.com>
Link: https://patchwork.freedesktop.org/patch/msgid/20250312085909.755073-1-raag.jadav@intel.com
Signed-off-by: Rodrigo Vivi <rodrigo.vivi@intel.com>
Documentation/ABI/testing/sysfs-driver-intel-xe-hwmon
drivers/gpu/drm/xe/regs/xe_pcode_regs.h
drivers/gpu/drm/xe/xe_device_types.h
drivers/gpu/drm/xe/xe_hwmon.c
drivers/gpu/drm/xe/xe_pci.c
drivers/gpu/drm/xe/xe_pcode_api.h

index 9bce281314dfde5188d6e7d06933871182b3a216..adbb9bce15a5d6fefc52edd7da17d1cdc1961fbc 100644 (file)
@@ -124,3 +124,27 @@ Contact:   intel-xe@lists.freedesktop.org
 Description:   RO. VRAM temperature in millidegree Celsius.
 
                Only supported for particular Intel Xe graphics platforms.
+
+What:          /sys/bus/pci/drivers/xe/.../hwmon/hwmon<i>/fan1_input
+Date:          March 2025
+KernelVersion: 6.14
+Contact:       intel-xe@lists.freedesktop.org
+Description:   RO. Fan 1 speed in RPM.
+
+               Only supported for particular Intel Xe graphics platforms.
+
+What:          /sys/bus/pci/drivers/xe/.../hwmon/hwmon<i>/fan2_input
+Date:          March 2025
+KernelVersion: 6.14
+Contact:       intel-xe@lists.freedesktop.org
+Description:   RO. Fan 2 speed in RPM.
+
+               Only supported for particular Intel Xe graphics platforms.
+
+What:          /sys/bus/pci/drivers/xe/.../hwmon/hwmon<i>/fan3_input
+Date:          March 2025
+KernelVersion: 6.14
+Contact:       intel-xe@lists.freedesktop.org
+Description:   RO. Fan 3 speed in RPM.
+
+               Only supported for particular Intel Xe graphics platforms.
index 8846eb9ce2a40b36dc83e532c2f4e22057ddb3ab..c7d5d782e3f95c5f461d4091d5942b57bf1d4849 100644 (file)
@@ -21,6 +21,9 @@
 #define BMG_PACKAGE_POWER_SKU                  XE_REG(0x138098)
 #define BMG_PACKAGE_POWER_SKU_UNIT             XE_REG(0x1380dc)
 #define BMG_PACKAGE_ENERGY_STATUS              XE_REG(0x138120)
+#define BMG_FAN_1_SPEED                                XE_REG(0x138140)
+#define BMG_FAN_2_SPEED                                XE_REG(0x138170)
+#define BMG_FAN_3_SPEED                                XE_REG(0x1381a0)
 #define BMG_VRAM_TEMPERATURE                   XE_REG(0x1382c0)
 #define BMG_PACKAGE_TEMPERATURE                        XE_REG(0x138434)
 #define BMG_PACKAGE_RAPL_LIMIT                 XE_REG(0x138440)
index fac488942316dc59f2f91991aa5ade822f3f7465..a2c0e791b199b8d95d729c93705ced1e1d95632d 100644 (file)
@@ -314,6 +314,8 @@ struct xe_device {
                u8 has_atomic_enable_pte_bit:1;
                /** @info.has_device_atomics_on_smem: Supports device atomics on SMEM */
                u8 has_device_atomics_on_smem:1;
+               /** @info.has_fan_control: Device supports fan control */
+               u8 has_fan_control:1;
                /** @info.has_flat_ccs: Whether flat CCS metadata is used */
                u8 has_flat_ccs:1;
                /** @info.has_heci_cscfi: device has heci cscfi */
index 48d80ffdf7bb9cd84d97a8670d20c6b79d620554..eb293aec36a0fb9ee96cf752c9da85fd5491c943 100644 (file)
@@ -5,6 +5,7 @@
 
 #include <linux/hwmon-sysfs.h>
 #include <linux/hwmon.h>
+#include <linux/jiffies.h>
 #include <linux/types.h>
 #include <linux/units.h>
 
@@ -27,6 +28,7 @@ enum xe_hwmon_reg {
        REG_PKG_POWER_SKU_UNIT,
        REG_GT_PERF_STATUS,
        REG_PKG_ENERGY_STATUS,
+       REG_FAN_SPEED,
 };
 
 enum xe_hwmon_reg_operation {
@@ -42,6 +44,13 @@ enum xe_hwmon_channel {
        CHANNEL_MAX,
 };
 
+enum xe_fan_channel {
+       FAN_1,
+       FAN_2,
+       FAN_3,
+       FAN_MAX,
+};
+
 /*
  * SF_* - scale factors for particular quantities according to hwmon spec.
  */
@@ -61,6 +70,16 @@ struct xe_hwmon_energy_info {
        long accum_energy;
 };
 
+/**
+ * struct xe_hwmon_fan_info - to cache previous fan reading
+ */
+struct xe_hwmon_fan_info {
+       /** @reg_val_prev: previous fan reg val */
+       u32 reg_val_prev;
+       /** @time_prev: previous timestamp */
+       u64 time_prev;
+};
+
 /**
  * struct xe_hwmon - xe hwmon data structure
  */
@@ -79,6 +98,8 @@ struct xe_hwmon {
        int scl_shift_time;
        /** @ei: Energy info for energyN_input */
        struct xe_hwmon_energy_info ei[CHANNEL_MAX];
+       /** @fi: Fan info for fanN_input */
+       struct xe_hwmon_fan_info fi[FAN_MAX];
 };
 
 static struct xe_reg xe_hwmon_get_reg(struct xe_hwmon *hwmon, enum xe_hwmon_reg hwmon_reg,
@@ -144,6 +165,14 @@ static struct xe_reg xe_hwmon_get_reg(struct xe_hwmon *hwmon, enum xe_hwmon_reg
                        return PCU_CR_PACKAGE_ENERGY_STATUS;
                }
                break;
+       case REG_FAN_SPEED:
+               if (channel == FAN_1)
+                       return BMG_FAN_1_SPEED;
+               else if (channel == FAN_2)
+                       return BMG_FAN_2_SPEED;
+               else if (channel == FAN_3)
+                       return BMG_FAN_3_SPEED;
+               break;
        default:
                drm_warn(&xe->drm, "Unknown xe hwmon reg id: %d\n", hwmon_reg);
                break;
@@ -454,6 +483,7 @@ static const struct hwmon_channel_info * const hwmon_info[] = {
        HWMON_CHANNEL_INFO(curr, HWMON_C_LABEL, HWMON_C_CRIT | HWMON_C_LABEL),
        HWMON_CHANNEL_INFO(in, HWMON_I_INPUT | HWMON_I_LABEL, HWMON_I_INPUT | HWMON_I_LABEL),
        HWMON_CHANNEL_INFO(energy, HWMON_E_INPUT | HWMON_E_LABEL, HWMON_E_INPUT | HWMON_E_LABEL),
+       HWMON_CHANNEL_INFO(fan, HWMON_F_INPUT, HWMON_F_INPUT, HWMON_F_INPUT),
        NULL
 };
 
@@ -480,6 +510,19 @@ static int xe_hwmon_pcode_write_i1(const struct xe_hwmon *hwmon, u32 uval)
                              (uval & POWER_SETUP_I1_DATA_MASK));
 }
 
+static int xe_hwmon_pcode_read_fan_control(const struct xe_hwmon *hwmon, u32 subcmd, u32 *uval)
+{
+       struct xe_tile *root_tile = xe_device_get_root_tile(hwmon->xe);
+
+       /* Platforms that don't return correct value */
+       if (hwmon->xe->info.platform == XE_DG2 && subcmd == FSC_READ_NUM_FANS) {
+               *uval = 2;
+               return 0;
+       }
+
+       return xe_pcode_read(root_tile, PCODE_MBOX(FAN_SPEED_CONTROL, subcmd, 0), uval, NULL);
+}
+
 static int xe_hwmon_power_curr_crit_read(struct xe_hwmon *hwmon, int channel,
                                         long *value, u32 scale_factor)
 {
@@ -705,6 +748,75 @@ xe_hwmon_energy_read(struct xe_hwmon *hwmon, u32 attr, int channel, long *val)
        }
 }
 
+static umode_t
+xe_hwmon_fan_is_visible(struct xe_hwmon *hwmon, u32 attr, int channel)
+{
+       u32 uval;
+
+       if (!hwmon->xe->info.has_fan_control)
+               return 0;
+
+       switch (attr) {
+       case hwmon_fan_input:
+               if (xe_hwmon_pcode_read_fan_control(hwmon, FSC_READ_NUM_FANS, &uval))
+                       return 0;
+
+               return channel < uval ? 0444 : 0;
+       default:
+               return 0;
+       }
+}
+
+static int
+xe_hwmon_fan_input_read(struct xe_hwmon *hwmon, int channel, long *val)
+{
+       struct xe_mmio *mmio = xe_root_tile_mmio(hwmon->xe);
+       struct xe_hwmon_fan_info *fi = &hwmon->fi[channel];
+       u64 rotations, time_now, time;
+       u32 reg_val;
+       int ret = 0;
+
+       mutex_lock(&hwmon->hwmon_lock);
+
+       reg_val = xe_mmio_read32(mmio, xe_hwmon_get_reg(hwmon, REG_FAN_SPEED, channel));
+       time_now = get_jiffies_64();
+
+       /*
+        * HW register value is accumulated count of pulses from PWM fan with the scale
+        * of 2 pulses per rotation.
+        */
+       rotations = (reg_val - fi->reg_val_prev) / 2;
+
+       time = jiffies_delta_to_msecs(time_now - fi->time_prev);
+       if (unlikely(!time)) {
+               ret = -EAGAIN;
+               goto unlock;
+       }
+
+       /*
+        * Calculate fan speed in RPM by time averaging two subsequent readings in minutes.
+        * RPM = number of rotations * msecs per minute / time in msecs
+        */
+       *val = DIV_ROUND_UP_ULL(rotations * (MSEC_PER_SEC * 60), time);
+
+       fi->reg_val_prev = reg_val;
+       fi->time_prev = time_now;
+unlock:
+       mutex_unlock(&hwmon->hwmon_lock);
+       return ret;
+}
+
+static int
+xe_hwmon_fan_read(struct xe_hwmon *hwmon, u32 attr, int channel, long *val)
+{
+       switch (attr) {
+       case hwmon_fan_input:
+               return xe_hwmon_fan_input_read(hwmon, channel, val);
+       default:
+               return -EOPNOTSUPP;
+       }
+}
+
 static umode_t
 xe_hwmon_is_visible(const void *drvdata, enum hwmon_sensor_types type,
                    u32 attr, int channel)
@@ -730,6 +842,9 @@ xe_hwmon_is_visible(const void *drvdata, enum hwmon_sensor_types type,
        case hwmon_energy:
                ret = xe_hwmon_energy_is_visible(hwmon, attr, channel);
                break;
+       case hwmon_fan:
+               ret = xe_hwmon_fan_is_visible(hwmon, attr, channel);
+               break;
        default:
                ret = 0;
                break;
@@ -765,6 +880,9 @@ xe_hwmon_read(struct device *dev, enum hwmon_sensor_types type, u32 attr,
        case hwmon_energy:
                ret = xe_hwmon_energy_read(hwmon, attr, channel, val);
                break;
+       case hwmon_fan:
+               ret = xe_hwmon_fan_read(hwmon, attr, channel, val);
+               break;
        default:
                ret = -EOPNOTSUPP;
                break;
@@ -842,7 +960,7 @@ static void
 xe_hwmon_get_preregistration_info(struct xe_hwmon *hwmon)
 {
        struct xe_mmio *mmio = xe_root_tile_mmio(hwmon->xe);
-       long energy;
+       long energy, fan_speed;
        u64 val_sku_unit = 0;
        int channel;
        struct xe_reg pkg_power_sku_unit;
@@ -866,6 +984,11 @@ xe_hwmon_get_preregistration_info(struct xe_hwmon *hwmon)
        for (channel = 0; channel < CHANNEL_MAX; channel++)
                if (xe_hwmon_is_visible(hwmon, hwmon_energy, hwmon_energy_input, channel))
                        xe_hwmon_energy_get(hwmon, channel, &energy);
+
+       /* Initialize 'struct xe_hwmon_fan_info' with initial fan register reading. */
+       for (channel = 0; channel < FAN_MAX; channel++)
+               if (xe_hwmon_is_visible(hwmon, hwmon_fan, hwmon_fan_input, channel))
+                       xe_hwmon_fan_input_read(hwmon, channel, &fan_speed);
 }
 
 static void xe_hwmon_mutex_destroy(void *arg)
index da9679c8cf26100210e9910e818084a171d6e647..fc89d744978a6d4443cc574722532bdfbb60e810 100644 (file)
@@ -62,6 +62,7 @@ struct xe_device_desc {
        u8 is_dgfx:1;
 
        u8 has_display:1;
+       u8 has_fan_control:1;
        u8 has_heci_gscfi:1;
        u8 has_heci_cscfi:1;
        u8 has_llc:1;
@@ -302,6 +303,7 @@ static const struct xe_device_desc dg2_desc = {
 
        DG2_FEATURES,
        .has_display = true,
+       .has_fan_control = true,
 };
 
 static const __maybe_unused struct xe_device_desc pvc_desc = {
@@ -336,6 +338,7 @@ static const struct xe_device_desc bmg_desc = {
        PLATFORM(BATTLEMAGE),
        .dma_mask_size = 46,
        .has_display = true,
+       .has_fan_control = true,
        .has_heci_cscfi = 1,
 };
 
@@ -575,6 +578,7 @@ static int xe_info_init_early(struct xe_device *xe,
 
        xe->info.dma_mask_size = desc->dma_mask_size;
        xe->info.is_dgfx = desc->is_dgfx;
+       xe->info.has_fan_control = desc->has_fan_control;
        xe->info.has_heci_gscfi = desc->has_heci_gscfi;
        xe->info.has_heci_cscfi = desc->has_heci_cscfi;
        xe->info.has_llc = desc->has_llc;
index 2bae9afdbd352a0ac8a3246060f755c23f543cc9..e622ae17f08dd94b4997f3ad6d5e080e6beab890 100644 (file)
@@ -49,6 +49,9 @@
 /* Domain IDs (param2) */
 #define     PCODE_MBOX_DOMAIN_HBM              0x2
 
+#define   FAN_SPEED_CONTROL                    0x7D
+#define     FSC_READ_NUM_FANS                  0x4
+
 #define PCODE_SCRATCH(x)               XE_REG(0x138320 + ((x) * 4))
 /* PCODE_SCRATCH0 */
 #define   AUXINFO_REG_OFFSET           REG_GENMASK(17, 15)