Skip to content

Commit 28f79ac

Browse files
raagjadavrodrigovivi
authored andcommitted
drm/xe/hwmon: expose fan speed
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>
1 parent 278469f commit 28f79ac

File tree

6 files changed

+160
-1
lines changed

6 files changed

+160
-1
lines changed

Documentation/ABI/testing/sysfs-driver-intel-xe-hwmon

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -124,3 +124,27 @@ Contact: intel-xe@lists.freedesktop.org
124124
Description: RO. VRAM temperature in millidegree Celsius.
125125

126126
Only supported for particular Intel Xe graphics platforms.
127+
128+
What: /sys/bus/pci/drivers/xe/.../hwmon/hwmon<i>/fan1_input
129+
Date: March 2025
130+
KernelVersion: 6.14
131+
Contact: intel-xe@lists.freedesktop.org
132+
Description: RO. Fan 1 speed in RPM.
133+
134+
Only supported for particular Intel Xe graphics platforms.
135+
136+
What: /sys/bus/pci/drivers/xe/.../hwmon/hwmon<i>/fan2_input
137+
Date: March 2025
138+
KernelVersion: 6.14
139+
Contact: intel-xe@lists.freedesktop.org
140+
Description: RO. Fan 2 speed in RPM.
141+
142+
Only supported for particular Intel Xe graphics platforms.
143+
144+
What: /sys/bus/pci/drivers/xe/.../hwmon/hwmon<i>/fan3_input
145+
Date: March 2025
146+
KernelVersion: 6.14
147+
Contact: intel-xe@lists.freedesktop.org
148+
Description: RO. Fan 3 speed in RPM.
149+
150+
Only supported for particular Intel Xe graphics platforms.

drivers/gpu/drm/xe/regs/xe_pcode_regs.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,9 @@
2121
#define BMG_PACKAGE_POWER_SKU XE_REG(0x138098)
2222
#define BMG_PACKAGE_POWER_SKU_UNIT XE_REG(0x1380dc)
2323
#define BMG_PACKAGE_ENERGY_STATUS XE_REG(0x138120)
24+
#define BMG_FAN_1_SPEED XE_REG(0x138140)
25+
#define BMG_FAN_2_SPEED XE_REG(0x138170)
26+
#define BMG_FAN_3_SPEED XE_REG(0x1381a0)
2427
#define BMG_VRAM_TEMPERATURE XE_REG(0x1382c0)
2528
#define BMG_PACKAGE_TEMPERATURE XE_REG(0x138434)
2629
#define BMG_PACKAGE_RAPL_LIMIT XE_REG(0x138440)

drivers/gpu/drm/xe/xe_device_types.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -314,6 +314,8 @@ struct xe_device {
314314
u8 has_atomic_enable_pte_bit:1;
315315
/** @info.has_device_atomics_on_smem: Supports device atomics on SMEM */
316316
u8 has_device_atomics_on_smem:1;
317+
/** @info.has_fan_control: Device supports fan control */
318+
u8 has_fan_control:1;
317319
/** @info.has_flat_ccs: Whether flat CCS metadata is used */
318320
u8 has_flat_ccs:1;
319321
/** @info.has_heci_cscfi: device has heci cscfi */

drivers/gpu/drm/xe/xe_hwmon.c

Lines changed: 124 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55

66
#include <linux/hwmon-sysfs.h>
77
#include <linux/hwmon.h>
8+
#include <linux/jiffies.h>
89
#include <linux/types.h>
910
#include <linux/units.h>
1011

@@ -27,6 +28,7 @@ enum xe_hwmon_reg {
2728
REG_PKG_POWER_SKU_UNIT,
2829
REG_GT_PERF_STATUS,
2930
REG_PKG_ENERGY_STATUS,
31+
REG_FAN_SPEED,
3032
};
3133

3234
enum xe_hwmon_reg_operation {
@@ -42,6 +44,13 @@ enum xe_hwmon_channel {
4244
CHANNEL_MAX,
4345
};
4446

47+
enum xe_fan_channel {
48+
FAN_1,
49+
FAN_2,
50+
FAN_3,
51+
FAN_MAX,
52+
};
53+
4554
/*
4655
* SF_* - scale factors for particular quantities according to hwmon spec.
4756
*/
@@ -61,6 +70,16 @@ struct xe_hwmon_energy_info {
6170
long accum_energy;
6271
};
6372

73+
/**
74+
* struct xe_hwmon_fan_info - to cache previous fan reading
75+
*/
76+
struct xe_hwmon_fan_info {
77+
/** @reg_val_prev: previous fan reg val */
78+
u32 reg_val_prev;
79+
/** @time_prev: previous timestamp */
80+
u64 time_prev;
81+
};
82+
6483
/**
6584
* struct xe_hwmon - xe hwmon data structure
6685
*/
@@ -79,6 +98,8 @@ struct xe_hwmon {
7998
int scl_shift_time;
8099
/** @ei: Energy info for energyN_input */
81100
struct xe_hwmon_energy_info ei[CHANNEL_MAX];
101+
/** @fi: Fan info for fanN_input */
102+
struct xe_hwmon_fan_info fi[FAN_MAX];
82103
};
83104

84105
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
144165
return PCU_CR_PACKAGE_ENERGY_STATUS;
145166
}
146167
break;
168+
case REG_FAN_SPEED:
169+
if (channel == FAN_1)
170+
return BMG_FAN_1_SPEED;
171+
else if (channel == FAN_2)
172+
return BMG_FAN_2_SPEED;
173+
else if (channel == FAN_3)
174+
return BMG_FAN_3_SPEED;
175+
break;
147176
default:
148177
drm_warn(&xe->drm, "Unknown xe hwmon reg id: %d\n", hwmon_reg);
149178
break;
@@ -454,6 +483,7 @@ static const struct hwmon_channel_info * const hwmon_info[] = {
454483
HWMON_CHANNEL_INFO(curr, HWMON_C_LABEL, HWMON_C_CRIT | HWMON_C_LABEL),
455484
HWMON_CHANNEL_INFO(in, HWMON_I_INPUT | HWMON_I_LABEL, HWMON_I_INPUT | HWMON_I_LABEL),
456485
HWMON_CHANNEL_INFO(energy, HWMON_E_INPUT | HWMON_E_LABEL, HWMON_E_INPUT | HWMON_E_LABEL),
486+
HWMON_CHANNEL_INFO(fan, HWMON_F_INPUT, HWMON_F_INPUT, HWMON_F_INPUT),
457487
NULL
458488
};
459489

@@ -480,6 +510,19 @@ static int xe_hwmon_pcode_write_i1(const struct xe_hwmon *hwmon, u32 uval)
480510
(uval & POWER_SETUP_I1_DATA_MASK));
481511
}
482512

513+
static int xe_hwmon_pcode_read_fan_control(const struct xe_hwmon *hwmon, u32 subcmd, u32 *uval)
514+
{
515+
struct xe_tile *root_tile = xe_device_get_root_tile(hwmon->xe);
516+
517+
/* Platforms that don't return correct value */
518+
if (hwmon->xe->info.platform == XE_DG2 && subcmd == FSC_READ_NUM_FANS) {
519+
*uval = 2;
520+
return 0;
521+
}
522+
523+
return xe_pcode_read(root_tile, PCODE_MBOX(FAN_SPEED_CONTROL, subcmd, 0), uval, NULL);
524+
}
525+
483526
static int xe_hwmon_power_curr_crit_read(struct xe_hwmon *hwmon, int channel,
484527
long *value, u32 scale_factor)
485528
{
@@ -705,6 +748,75 @@ xe_hwmon_energy_read(struct xe_hwmon *hwmon, u32 attr, int channel, long *val)
705748
}
706749
}
707750

751+
static umode_t
752+
xe_hwmon_fan_is_visible(struct xe_hwmon *hwmon, u32 attr, int channel)
753+
{
754+
u32 uval;
755+
756+
if (!hwmon->xe->info.has_fan_control)
757+
return 0;
758+
759+
switch (attr) {
760+
case hwmon_fan_input:
761+
if (xe_hwmon_pcode_read_fan_control(hwmon, FSC_READ_NUM_FANS, &uval))
762+
return 0;
763+
764+
return channel < uval ? 0444 : 0;
765+
default:
766+
return 0;
767+
}
768+
}
769+
770+
static int
771+
xe_hwmon_fan_input_read(struct xe_hwmon *hwmon, int channel, long *val)
772+
{
773+
struct xe_mmio *mmio = xe_root_tile_mmio(hwmon->xe);
774+
struct xe_hwmon_fan_info *fi = &hwmon->fi[channel];
775+
u64 rotations, time_now, time;
776+
u32 reg_val;
777+
int ret = 0;
778+
779+
mutex_lock(&hwmon->hwmon_lock);
780+
781+
reg_val = xe_mmio_read32(mmio, xe_hwmon_get_reg(hwmon, REG_FAN_SPEED, channel));
782+
time_now = get_jiffies_64();
783+
784+
/*
785+
* HW register value is accumulated count of pulses from PWM fan with the scale
786+
* of 2 pulses per rotation.
787+
*/
788+
rotations = (reg_val - fi->reg_val_prev) / 2;
789+
790+
time = jiffies_delta_to_msecs(time_now - fi->time_prev);
791+
if (unlikely(!time)) {
792+
ret = -EAGAIN;
793+
goto unlock;
794+
}
795+
796+
/*
797+
* Calculate fan speed in RPM by time averaging two subsequent readings in minutes.
798+
* RPM = number of rotations * msecs per minute / time in msecs
799+
*/
800+
*val = DIV_ROUND_UP_ULL(rotations * (MSEC_PER_SEC * 60), time);
801+
802+
fi->reg_val_prev = reg_val;
803+
fi->time_prev = time_now;
804+
unlock:
805+
mutex_unlock(&hwmon->hwmon_lock);
806+
return ret;
807+
}
808+
809+
static int
810+
xe_hwmon_fan_read(struct xe_hwmon *hwmon, u32 attr, int channel, long *val)
811+
{
812+
switch (attr) {
813+
case hwmon_fan_input:
814+
return xe_hwmon_fan_input_read(hwmon, channel, val);
815+
default:
816+
return -EOPNOTSUPP;
817+
}
818+
}
819+
708820
static umode_t
709821
xe_hwmon_is_visible(const void *drvdata, enum hwmon_sensor_types type,
710822
u32 attr, int channel)
@@ -730,6 +842,9 @@ xe_hwmon_is_visible(const void *drvdata, enum hwmon_sensor_types type,
730842
case hwmon_energy:
731843
ret = xe_hwmon_energy_is_visible(hwmon, attr, channel);
732844
break;
845+
case hwmon_fan:
846+
ret = xe_hwmon_fan_is_visible(hwmon, attr, channel);
847+
break;
733848
default:
734849
ret = 0;
735850
break;
@@ -765,6 +880,9 @@ xe_hwmon_read(struct device *dev, enum hwmon_sensor_types type, u32 attr,
765880
case hwmon_energy:
766881
ret = xe_hwmon_energy_read(hwmon, attr, channel, val);
767882
break;
883+
case hwmon_fan:
884+
ret = xe_hwmon_fan_read(hwmon, attr, channel, val);
885+
break;
768886
default:
769887
ret = -EOPNOTSUPP;
770888
break;
@@ -842,7 +960,7 @@ static void
842960
xe_hwmon_get_preregistration_info(struct xe_hwmon *hwmon)
843961
{
844962
struct xe_mmio *mmio = xe_root_tile_mmio(hwmon->xe);
845-
long energy;
963+
long energy, fan_speed;
846964
u64 val_sku_unit = 0;
847965
int channel;
848966
struct xe_reg pkg_power_sku_unit;
@@ -866,6 +984,11 @@ xe_hwmon_get_preregistration_info(struct xe_hwmon *hwmon)
866984
for (channel = 0; channel < CHANNEL_MAX; channel++)
867985
if (xe_hwmon_is_visible(hwmon, hwmon_energy, hwmon_energy_input, channel))
868986
xe_hwmon_energy_get(hwmon, channel, &energy);
987+
988+
/* Initialize 'struct xe_hwmon_fan_info' with initial fan register reading. */
989+
for (channel = 0; channel < FAN_MAX; channel++)
990+
if (xe_hwmon_is_visible(hwmon, hwmon_fan, hwmon_fan_input, channel))
991+
xe_hwmon_fan_input_read(hwmon, channel, &fan_speed);
869992
}
870993

871994
static void xe_hwmon_mutex_destroy(void *arg)

drivers/gpu/drm/xe/xe_pci.c

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,7 @@ struct xe_device_desc {
6262
u8 is_dgfx:1;
6363

6464
u8 has_display:1;
65+
u8 has_fan_control:1;
6566
u8 has_heci_gscfi:1;
6667
u8 has_heci_cscfi:1;
6768
u8 has_llc:1;
@@ -302,6 +303,7 @@ static const struct xe_device_desc dg2_desc = {
302303

303304
DG2_FEATURES,
304305
.has_display = true,
306+
.has_fan_control = true,
305307
};
306308

307309
static const __maybe_unused struct xe_device_desc pvc_desc = {
@@ -336,6 +338,7 @@ static const struct xe_device_desc bmg_desc = {
336338
PLATFORM(BATTLEMAGE),
337339
.dma_mask_size = 46,
338340
.has_display = true,
341+
.has_fan_control = true,
339342
.has_heci_cscfi = 1,
340343
};
341344

@@ -575,6 +578,7 @@ static int xe_info_init_early(struct xe_device *xe,
575578

576579
xe->info.dma_mask_size = desc->dma_mask_size;
577580
xe->info.is_dgfx = desc->is_dgfx;
581+
xe->info.has_fan_control = desc->has_fan_control;
578582
xe->info.has_heci_gscfi = desc->has_heci_gscfi;
579583
xe->info.has_heci_cscfi = desc->has_heci_cscfi;
580584
xe->info.has_llc = desc->has_llc;

drivers/gpu/drm/xe/xe_pcode_api.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,9 @@
4949
/* Domain IDs (param2) */
5050
#define PCODE_MBOX_DOMAIN_HBM 0x2
5151

52+
#define FAN_SPEED_CONTROL 0x7D
53+
#define FSC_READ_NUM_FANS 0x4
54+
5255
#define PCODE_SCRATCH(x) XE_REG(0x138320 + ((x) * 4))
5356
/* PCODE_SCRATCH0 */
5457
#define AUXINFO_REG_OFFSET REG_GENMASK(17, 15)

0 commit comments

Comments
 (0)