Intel-XE Archive on lore.kernel.org
 help / color / mirror / Atom feed
From: "Poosa, Karthik" <karthik.poosa@intel.com>
To: "Purkait, Soham" <soham.purkait@intel.com>,
	<intel-xe@lists.freedesktop.org>
Cc: <rodrigo.vivi@intel.com>, <anshuman.gupta@intel.com>,
	<badal.nilawar@intel.com>, <raag.jadav@intel.com>,
	<riana.tauro@intel.com>, <sk.anirban@intel.com>,
	<mallesh.koujalagi@intel.com>,
	<ravi.kishore.koppuravuri@intel.com>
Subject: Re: [PATCH v3 1/9] drm/xe/hwmon: initialize fan-control backend and table cache
Date: Thu, 29 Oct 2026 17:04:31 +0530	[thread overview]
Message-ID: <c652ebce-4e73-483c-8b5d-82dcaf453d7a@intel.com> (raw)
In-Reply-To: <244e9d47-794d-4290-9bdf-d6c83a6f0fbe@intel.com>


On 29-10-2026 00:16, Purkait, Soham wrote:
>
> Hi Karthik,
>
> On 16-10-2026 00:37, Karthik Poosa wrote:
>> Initialize Xe hwmon fan-control support by detecting fan count,
>> reading stock fan control points and min PWM.
>>
>> v2:
>>   - Avoid user table initialization during probe. (Badal)
>>   - Move unused code to appropriate patches.
>>   - Use xe helpers for dmesg logs.
>>
>> v3:
>>   - Rebase.
>>   - Refine a few debug log messages and comments.
>>   - Update xe_hwmon_read_fan_control_info() to return on any stock table
>>     read failures.
>>   - Avoid returning when num_fans is 0 in
>>     xe_hwmon_get_preregistration_info(), ensuring thermal mailbox
>>     initialization is not skipped.
>>   - Remove XE_PCODE_FAN_CONTROL_TIMEOUT_MS which is unncessary as default
>>     timeout itself is now 10ms.
>>   - Use xe_pcode_read() instead of xe_pcode_read_timeout().
>>   - Rename fan_control_point_count to point_count in struct fan_table.
>>   - Resolve kgdoc warnings.
>>   - Add valid check for stock table fan point count.
>>   - Use the fan-enable bitmap reported by FSC_READ_NUM_FANS when enumerating
>>     fans. This avoids assuming contiguous fan indices and allows masks with
>>     gaps to be represented correctly in the hwmon interface.
>>
>> Signed-off-by: Karthik Poosa<karthik.poosa@intel.com>
>> Assisted-by: LLM
>> ---
>>   drivers/gpu/drm/xe/xe_hwmon.c     | 167 +++++++++++++++++++++++++++---
>>   drivers/gpu/drm/xe/xe_pcode_api.h |   5 +
>>   2 files changed, 157 insertions(+), 15 deletions(-)
>>
>> diff --git a/drivers/gpu/drm/xe/xe_hwmon.c b/drivers/gpu/drm/xe/xe_hwmon.c
>> index 42acfeb8818c..f2bd20f8b0a2 100644
>> --- a/drivers/gpu/drm/xe/xe_hwmon.c
>> +++ b/drivers/gpu/drm/xe/xe_hwmon.c
>> @@ -21,6 +21,7 @@
>>   #include "xe_pcode_api.h"
>>   #include "xe_sriov.h"
>>   #include "xe_pm.h"
>> +#include "xe_printk.h"
>>   #include "xe_vsec.h"
>>   #include "regs/xe_pmt.h"
>>   
>> @@ -137,14 +138,51 @@ struct xe_hwmon_energy_info {
>>   	long accum_energy;
>>   };
>>   
>> +enum fan_table_type {
>> +	/** @STOCK_FAN_TABLE: firmware-provided fan table */
>> +	STOCK_FAN_TABLE,
>> +	/** @USER_FAN_TABLE: user fan table written through sysfs */
>> +	USER_FAN_TABLE,
>> +	/** @FAN_TABLE_MAX: number of fan tables tracked per fan */
>> +	FAN_TABLE_MAX,
>> +};
>> +
>> +/* Check if a fan is enabled based on the fan_enable_mask got from FSC_READ_NUM_FANS. */
>> +#define IS_FAN_ENABLED(fan) (hwmon->fan_enable_mask & BIT(fan))
> If this macro is called from any function having no local variable 
> named hwmon it will break. Should it take hwmon as an argument ?
yes will change this
>> +
>> +/* Maximum number of fan control points supported by each fan. */
>> +#define MAX_FAN_CONTROL_POINTS	(10)
>> +
>> +/* Fan control point index: bit31 selects fan table type; bits30:0 control point index. */
>> +#define FCP_INDEX(FAN_TABLE_TYPE, POINT_NUM) (((FAN_TABLE_TYPE) << 31) | \
> Cast to u32 ? shifting the signed enum into bit 31 could lead to UB.
okay
>> +						((POINT_NUM) & REG_GENMASK(30, 0)))
>> +
>>   /**
>> - * struct xe_hwmon_fan_info - to cache previous fan reading
>> + * struct xe_hwmon_fan_info - cached fan telemetry and control state
>> + *
>> + * Each fan keeps the latest tachometer sampling state along with two fan
>> + * tables: the stock table discovered from firmware and the user table managed
>> + * by hwmon sysfs writes.
>>    */
>>   struct xe_hwmon_fan_info {
>>   	/** @reg_val_prev: previous fan reg val */
>>   	u32 reg_val_prev;
>>   	/** @time_prev: previous timestamp */
>>   	u64 time_prev;
>> +	/** @fan_table: fan control tables */
>> +	struct fan_table {
>> +		/** @point_count: number of fan control points */
>> +		u8 point_count;
>> +		/** @fcp: array of fan control points */
>> +		struct fan_control_point {
>> +			/** @temp: temperature in degree celsius */
>> +			u8 temp;
>> +			/** @speed: fan speed in percentage */
>> +			u8 speed;
>> +		} fcp[MAX_FAN_CONTROL_POINTS];
>> +	} fan_table[FAN_TABLE_MAX];
>> +	/** @min_pwm: minimum fan PWM */
>> +	u32 min_pwm;
>>   };
>>   
>>   /**
>> @@ -193,6 +231,8 @@ struct xe_hwmon {
>>   	int scl_shift_time;
>>   	/** @ei: Energy info for energyN_input */
>>   	struct xe_hwmon_energy_info ei[CHANNEL_MAX];
>> +	/** @fan_enable_mask: bitmask of available fans */
>> +	u32 fan_enable_mask;
>>   	/** @fi: Fan info for fanN_input */
>>   	struct xe_hwmon_fan_info fi[FAN_MAX];
>>   	/** @read_power_limit_from_pcode: pcode accepted power limits write for the channel */
>> @@ -1075,17 +1115,112 @@ 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)
>> +static int xe_hwmon_pcode_read_fan_control(const struct xe_hwmon *hwmon, u32 subcmd, u8 fan_num,
>> +					   u32 *uval)
>>   {
>>   	struct xe_tile *root_tile = xe_device_get_root_tile(hwmon->xe);
> Nit: blank line after variable declaration
ok
>> +	return xe_pcode_read(root_tile, PCODE_MBOX(FAN_SPEED_CONTROL, subcmd, fan_num),
>> +				     uval, NULL);
>> +}
>> +
>> +static int xe_hwmon_get_fan_enable_mask(struct xe_hwmon *hwmon)
>> +{
>> +	int ret;
>>   
>>   	/* Platforms that don't return correct value */
>> -	if (hwmon->xe->info.platform == XE_DG2 && subcmd == FSC_READ_NUM_FANS) {
>> -		*uval = 2;
>> +	if (hwmon->xe->info.platform == XE_DG2) {
>> +		hwmon->fan_enable_mask = GENMASK(1, 0);
>>   		return 0;
>>   	}
>>   
>> -	return xe_pcode_read(root_tile, PCODE_MBOX(FAN_SPEED_CONTROL, subcmd, 0), uval, NULL);
>> +	ret = xe_hwmon_pcode_read_fan_control(hwmon, FSC_READ_NUM_FANS, 0,
>> +					      &hwmon->fan_enable_mask);
>> +	if (ret) {
>> +		xe_warn(hwmon->xe, "failed to read number of fans, ret=%d\n", ret);
>> +		return ret;
>> +	}
>> +
>> +	/* Check if fan mask exceeds FAN_MAX and log a warning if it does */
>> +	if (hwmon->fan_enable_mask & ~GENMASK(FAN_MAX - 1, 0)) {
>> +		xe_warn(hwmon->xe, "fan enable mask exceeds maximum supported fans\n");
> Reset the mask to 0 so IS_FAN_ENABLED() stays false in case fan mask 
> exceeds FAN_MAX.
how about clamping to FAN_MAX ?
>> +		return -EINVAL;
>> +	}
>> +
>> +	xe_info(hwmon->xe, "Number of fans detected: %u\n",
>> +		hweight32(hwmon->fan_enable_mask));
>> +	return 0;
>> +}
>> +
>> +static int xe_hwmon_read_fan_control_info(struct xe_hwmon *hwmon)
>> +{
>> +	struct xe_tile *root_tile = xe_device_get_root_tile(hwmon->xe);
>> +	int point;
>> +	int fan;
>> +	int ret;
>> +
>> +	for (fan = 0; fan < FAN_MAX; fan++) {
>> +		u32 stock_fcp_count = 0;
>> +
>> +		struct xe_hwmon_fan_info *fi = &hwmon->fi[fan];
>> +
>> +		if (!IS_FAN_ENABLED(fan))
>> +			continue;
>> +
>> +		ret = xe_hwmon_pcode_read_fan_control(hwmon,
>> +						      FSC_READ_STOCK_FAN_CONTROL_POINTS,
>> +						      fan, &stock_fcp_count);
>> +		if (ret) {
>> +			xe_err(hwmon->xe,
>> +			       "failed to read fan %d stock table point count, ret=%d\n",
>> +			       fan, ret);
> Why not continue for the rest of the fans marking the current one ?
yes we can continue, you want to mark current fan invalid ?
>> +			return ret;
>> +		}
>> +
>> +		if (!stock_fcp_count || stock_fcp_count > MAX_FAN_CONTROL_POINTS) {
>> +			xe_warn(hwmon->xe,
>> +				"fan %d stock table point count %u is invalid\n",
>> +				fan, stock_fcp_count);
> Ditto.
okay
>> +			return -EINVAL;
>> +		}
>> +
>> +		fi->fan_table[STOCK_FAN_TABLE].point_count = stock_fcp_count;
>> +		xe_dbg(hwmon->xe, "fan %d stock table point count is %u\n", fan,
>> +		       fi->fan_table[STOCK_FAN_TABLE].point_count);
>> +
>> +		/* Cache the stock fan table for later use. */
>> +		for (point = 0; point < fi->fan_table[STOCK_FAN_TABLE].point_count;
>> +		     point++) {
>> +			u32 fcp = 0;
>> +
>> +			fcp = FCP_INDEX(STOCK_FAN_TABLE, point);
>
> nit: u32 fcp = FCP_INDEX(STOCK_FAN_TABLE, point);
>
> Regards,
> Soham
ok
>> +			ret = xe_pcode_read(root_tile, PCODE_MBOX(FAN_SPEED_CONTROL,
>> +								  FSC_READ_FAN_TABLE, fan),
>> +					    &fcp, NULL);
>> +			if (ret) {
>> +				xe_err(hwmon->xe, "failed to read fan %d stock point %d, ret=%d\n",
>> +				       fan, point, ret);
>> +				fi->fan_table[STOCK_FAN_TABLE].point_count = 0;
>> +				return ret;
>> +			}
>> +
>> +			fi->fan_table[STOCK_FAN_TABLE].fcp[point].temp =
>> +						REG_FIELD_GET(FAN_CONTROL_POINT_TEMP_MASK, fcp);
>> +			fi->fan_table[STOCK_FAN_TABLE].fcp[point].speed =
>> +						REG_FIELD_GET(FAN_CONTROL_POINT_SPEED_MASK, fcp);
>> +			xe_dbg(hwmon->xe, "fan %d stock point %d: temp %u C, speed %u\n",
>> +			       fan, point, fi->fan_table[STOCK_FAN_TABLE].fcp[point].temp,
>> +			       fi->fan_table[STOCK_FAN_TABLE].fcp[point].speed);
>> +		}
>> +
>> +		/* Read minimum fan PWM */
>> +		ret = xe_hwmon_pcode_read_fan_control(hwmon, FSC_READ_FAN_MIN_PWM, fan,
>> +						      &fi->min_pwm);
>> +		if (ret)
>> +			xe_warn(hwmon->xe, "fan %d min pwm unavailable, ret=%d\n", fan, ret);
>> +		else
>> +			xe_dbg(hwmon->xe, "fan %d min PWM %u\n", fan, fi->min_pwm);
>> +	}
>> +	return 0;
>>   }
>>   
>>   static int xe_hwmon_power_curr_crit_read(struct xe_hwmon *hwmon, int channel,
>> @@ -1615,17 +1750,12 @@ 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 = 0;
>> -
>>   	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;
>> +		return IS_FAN_ENABLED(channel) ? 0444 : 0;
>>   	default:
>>   		return 0;
>>   	}
>> @@ -1858,11 +1988,18 @@ xe_hwmon_get_preregistration_info(struct xe_hwmon *hwmon)
>>   		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);
>> +	if (hwmon->xe->info.has_fan_control) {
>> +		xe_hwmon_get_fan_enable_mask(hwmon);
>> +
>> +		/* Initialize 'struct xe_hwmon_fan_info' with initial fan register reading. */
>> +		for (channel = 0; channel < FAN_MAX; channel++)
>> +			if (IS_FAN_ENABLED(channel))
>> +				xe_hwmon_fan_input_read(hwmon, channel, &fan_speed);
>>   
>> +		/* Fan control tables initialization */
>> +		if (xe_hwmon_read_fan_control_info(hwmon))
>> +			xe_warn(hwmon->xe, "Fan control tables are not available\n");
>> +	}
>>   	return xe_hwmon_init_temp_info(hwmon);
>>   }
>>   
>> diff --git a/drivers/gpu/drm/xe/xe_pcode_api.h b/drivers/gpu/drm/xe/xe_pcode_api.h
>> index 00c4fc7d0124..0349b24a8259 100644
>> --- a/drivers/gpu/drm/xe/xe_pcode_api.h
>> +++ b/drivers/gpu/drm/xe/xe_pcode_api.h
>> @@ -83,6 +83,11 @@
>>   
>>   #define   FAN_SPEED_CONTROL			0x7D
>>   #define     FSC_READ_NUM_FANS			0x4
>> +#define     FSC_READ_STOCK_FAN_CONTROL_POINTS	0x5
>> +#define     FSC_READ_FAN_TABLE			0x7
>> +#define     FAN_CONTROL_POINT_TEMP_MASK		REG_GENMASK(7, 0)
>> +#define     FAN_CONTROL_POINT_SPEED_MASK	REG_GENMASK(15, 8)
>> +#define     FSC_READ_FAN_MIN_PWM		0x8
>>   
>>   #define PCODE_SCRATCH(x)		XE_REG(0x138320 + ((x) * 4))
>>   /* PCODE_SCRATCH0 */

  reply	other threads:[~2026-10-29 11:35 UTC|newest]

Thread overview: 26+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-10-15 19:07 [PATCH v3 0/9] Add fan control support Karthik Poosa
2026-10-15 19:07 ` [PATCH v3 1/9] drm/xe/hwmon: initialize fan-control backend and table cache Karthik Poosa
2026-10-28 18:46   ` Purkait, Soham
2026-10-29 11:34     ` Poosa, Karthik [this message]
2026-10-29 12:02       ` Purkait, Soham
2026-10-29 12:04       ` Purkait, Soham
2026-10-15 19:07 ` [PATCH v3 2/9] drm/xe/hwmon: expose fanN_max Karthik Poosa
2026-10-29  4:35   ` Purkait, Soham
2026-10-29 11:39     ` Poosa, Karthik
2026-10-15 19:07 ` [PATCH v3 3/9] drm/xe/pcode: add ordered write batch helper Karthik Poosa
2026-10-29  5:45   ` Purkait, Soham
2026-10-29 11:40     ` Poosa, Karthik
2026-10-15 19:07 ` [PATCH v3 4/9] drm/xe/hwmon: expose pwm[1-3] Karthik Poosa
2026-10-29 11:31   ` Purkait, Soham
2026-10-15 19:07 ` [PATCH v3 5/9] drm/xe/hwmon: expose pwm[1-3]_enable Karthik Poosa
2026-10-29 18:33   ` Purkait, Soham
2026-10-15 19:07 ` [PATCH v3 6/9] drm/xe/hwmon: Enable fan curve control support Karthik Poosa
2026-10-30  6:33   ` Purkait, Soham
2026-10-15 19:07 ` [PATCH v3 7/9] drm/xe/hwmon: Add kernel-doc for fan control Karthik Poosa
2026-10-15 19:07 ` [PATCH v3 8/9] drm/xe/hwmon: preserve fan user table across suspend resume Karthik Poosa
2026-10-15 19:07 ` [PATCH v3 9/9] drm/xe/hwmon: Update fan info after late binding Karthik Poosa
2026-10-15 19:46 ` ✗ CI.checkpatch: warning for Add fan control support Patchwork
2026-10-15 19:48 ` ✓ CI.KUnit: success " Patchwork
2026-10-15 21:01 ` ✗ Xe.CI.BAT: failure " Patchwork
2026-10-16  7:56 ` ✗ Xe.CI.FULL: " Patchwork
  -- strict thread matches above, loose matches on Subject: below --
2026-10-15 19:01 [PATCH v3 0/9] " Karthik Poosa
2026-10-15 19:01 ` [PATCH v3 1/9] drm/xe/hwmon: initialize fan-control backend and table cache Karthik Poosa

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=c652ebce-4e73-483c-8b5d-82dcaf453d7a@intel.com \
    --to=karthik.poosa@intel.com \
    --cc=anshuman.gupta@intel.com \
    --cc=badal.nilawar@intel.com \
    --cc=intel-xe@lists.freedesktop.org \
    --cc=mallesh.koujalagi@intel.com \
    --cc=raag.jadav@intel.com \
    --cc=ravi.kishore.koppuravuri@intel.com \
    --cc=riana.tauro@intel.com \
    --cc=rodrigo.vivi@intel.com \
    --cc=sk.anirban@intel.com \
    --cc=soham.purkait@intel.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox