[PATCH v2] drm/xe: Expose fan control and voltage regulator information

Rodrigo Vivi rodrigo.vivi at intel.com
Tue Jul 8 19:10:38 UTC 2025


On Fri, Jul 04, 2025 at 04:53:05PM +0530, Raag Jadav wrote:
> Expose sysfs attributes for late binding features which provide information
> about their support, provisioning and bound version to the user.
> 
> v2: s/late_bind_fan_info/lb_fan_control_info (Badal)
>     s/late_bind_vr_info/lb_voltage_regulator_info (Badal)
>     s/LATE_BINDING/PCODE_LATE_BINDING (Badal)
>     Add VERSION_MASK macros (Badal)
> 
> Signed-off-by: Raag Jadav <raag.jadav at intel.com>
> ---
>  drivers/gpu/drm/xe/xe_device_sysfs.c | 121 ++++++++++++++++++++++++++-
>  drivers/gpu/drm/xe/xe_pcode_api.h    |  15 ++++
>  2 files changed, 135 insertions(+), 1 deletion(-)
> 
> diff --git a/drivers/gpu/drm/xe/xe_device_sysfs.c b/drivers/gpu/drm/xe/xe_device_sysfs.c
> index b9440f8c781e..8a1bb2b7c951 100644
> --- a/drivers/gpu/drm/xe/xe_device_sysfs.c
> +++ b/drivers/gpu/drm/xe/xe_device_sysfs.c
> @@ -24,6 +24,15 @@
>   *
>   * vram_d3cold_threshold - Report/change vram used threshold(in MB) below
>   * which vram save/restore is permissible during runtime D3cold entry/exit.
> + *
> + * lb_fan_control_info - Fan control information of the device in comma
> + * separated format as ``<supported>,<provisioned>,<version>`` with optional
> + * ``<version>`` field which is available only if provisioned by late binding.
> + *
> + * lb_voltage_regulator_info - Voltage regulator information of the device in
> + * comma separated format as ``<supported>,<provisioned>,<version>`` with
> + * optional ``<version>`` field which is available only if provisioned by late

Ouch. I have to apologize here. I had overseen this patch, but only with the
version in mind. Only now, while I was going to merge this patch that I noticed
it has fancy format against the written rules:

https://docs.kernel.org/6.15/filesystems/sysfs.html

"Mixing types, expressing multiple lines of data, and doing fancy formatting
of data is heavily frowned upon. Doing these things may get you publicly
humiliated and your code rewritten without notice."

For simplicity we should only do lb_fan_control_version and expose that only
if supported and provisioned.

> + * binding.
>   */
>  
>  static ssize_t
> @@ -65,6 +74,110 @@ vram_d3cold_threshold_store(struct device *dev, struct device_attribute *attr,
>  
>  static DEVICE_ATTR_RW(vram_d3cold_threshold);
>  
> +static ssize_t
> +lb_fan_control_info_show(struct device *dev, struct device_attribute *attr, char *buf)
> +{
> +	struct xe_device *xe = pdev_to_xe_device(to_pci_dev(dev));
> +	struct xe_tile *root = xe_device_get_root_tile(xe);
> +	u32 cap, ver_low = FAN_TABLE, ver_high = FAN_TABLE;
> +	bool supported, provisioned;
> +	int ret, len = 0;
> +
> +	xe_pm_runtime_get(xe);
> +
> +	ret = xe_pcode_read(root, PCODE_MBOX(PCODE_LATE_BINDING, GET_CAPABILITY_STATUS, 0),
> +			    &cap, NULL);
> +	if (ret)
> +		goto out;
> +
> +	supported = REG_FIELD_GET(V1_FAN_SUPPORTED, cap);
> +	provisioned = REG_FIELD_GET(V1_FAN_PROVISIONED, cap);
> +
> +	if (provisioned) {
> +		ret = xe_pcode_read(root, PCODE_MBOX(PCODE_LATE_BINDING, GET_VERSION_LOW, 0),
> +				    &ver_low, NULL);
> +		if (ret)
> +			goto out;
> +
> +		ret = xe_pcode_read(root, PCODE_MBOX(PCODE_LATE_BINDING, GET_VERSION_HIGH, 0),
> +				    &ver_high, NULL);
> +		if (ret)
> +			goto out;
> +	}
> +
> +	len += sysfs_emit_at(buf, len, "%u", supported);
> +	len += sysfs_emit_at(buf, len, ",%u", provisioned);
> +
> +	if (provisioned)
> +		len += sysfs_emit_at(buf, len, ",%u.%u.%u.%u",
> +				     REG_FIELD_GET(MAJOR_VERSION_MASK, ver_low),
> +				     REG_FIELD_GET(MINOR_VERSION_MASK, ver_low),
> +				     REG_FIELD_GET(HOTFIX_VERSION_MASK, ver_high),
> +				     REG_FIELD_GET(BUILD_VERSION_MASK, ver_high));
> +
> +	len += sysfs_emit_at(buf, len, "\n");
> +out:
> +	xe_pm_runtime_put(xe);
> +
> +	return ret ?: len;
> +}
> +static DEVICE_ATTR_ADMIN_RO(lb_fan_control_info);
> +
> +static ssize_t
> +lb_voltage_regulator_info_show(struct device *dev, struct device_attribute *attr, char *buf)
> +{
> +	struct xe_device *xe = pdev_to_xe_device(to_pci_dev(dev));
> +	struct xe_tile *root = xe_device_get_root_tile(xe);
> +	u32 cap, ver_low = VR_CONFIG, ver_high = VR_CONFIG;
> +	bool supported, provisioned;
> +	int ret, len = 0;
> +
> +	xe_pm_runtime_get(xe);
> +
> +	ret = xe_pcode_read(root, PCODE_MBOX(PCODE_LATE_BINDING, GET_CAPABILITY_STATUS, 0),
> +			    &cap, NULL);
> +	if (ret)
> +		goto out;
> +
> +	supported = REG_FIELD_GET(VR_PARAMS_SUPPORTED, cap);
> +	provisioned = REG_FIELD_GET(VR_PARAMS_PROVISIONED, cap);
> +
> +	if (provisioned) {
> +		ret = xe_pcode_read(root, PCODE_MBOX(PCODE_LATE_BINDING, GET_VERSION_LOW, 0),
> +				    &ver_low, NULL);
> +		if (ret)
> +			goto out;
> +
> +		ret = xe_pcode_read(root, PCODE_MBOX(PCODE_LATE_BINDING, GET_VERSION_HIGH, 0),
> +				    &ver_high, NULL);
> +		if (ret)
> +			goto out;
> +	}
> +
> +	len += sysfs_emit_at(buf, len, "%u", supported);
> +	len += sysfs_emit_at(buf, len, ",%u", provisioned);
> +
> +	if (provisioned)
> +		len += sysfs_emit_at(buf, len, ",%u.%u.%u.%u",
> +				     REG_FIELD_GET(MAJOR_VERSION_MASK, ver_low),
> +				     REG_FIELD_GET(MINOR_VERSION_MASK, ver_low),
> +				     REG_FIELD_GET(HOTFIX_VERSION_MASK, ver_high),
> +				     REG_FIELD_GET(BUILD_VERSION_MASK, ver_high));
> +
> +	len += sysfs_emit_at(buf, len, "\n");
> +out:
> +	xe_pm_runtime_put(xe);
> +
> +	return ret ?: len;
> +}
> +static DEVICE_ATTR_ADMIN_RO(lb_voltage_regulator_info);
> +
> +static const struct attribute *late_bind_attrs[] = {
> +	&dev_attr_lb_fan_control_info.attr,
> +	&dev_attr_lb_voltage_regulator_info.attr,
> +	NULL
> +};
> +
>  /**
>   * DOC: PCIe Gen5 Limitations
>   *
> @@ -151,8 +264,10 @@ static void xe_device_sysfs_fini(void *arg)
>  	if (xe->d3cold.capable)
>  		sysfs_remove_file(&xe->drm.dev->kobj, &dev_attr_vram_d3cold_threshold.attr);
>  
> -	if (xe->info.platform == XE_BATTLEMAGE)
> +	if (xe->info.platform == XE_BATTLEMAGE) {
>  		sysfs_remove_files(&xe->drm.dev->kobj, auto_link_downgrade_attrs);
> +		sysfs_remove_files(&xe->drm.dev->kobj, late_bind_attrs);
> +	}
>  }
>  
>  int xe_device_sysfs_init(struct xe_device *xe)
> @@ -170,6 +285,10 @@ int xe_device_sysfs_init(struct xe_device *xe)
>  		ret = sysfs_create_files(&dev->kobj, auto_link_downgrade_attrs);
>  		if (ret)
>  			return ret;
> +
> +		ret = sysfs_create_files(&dev->kobj, late_bind_attrs);
> +		if (ret)
> +			return ret;
>  	}
>  
>  	return devm_add_action_or_reset(dev, xe_device_sysfs_fini, xe);
> diff --git a/drivers/gpu/drm/xe/xe_pcode_api.h b/drivers/gpu/drm/xe/xe_pcode_api.h
> index 0befdea77db1..92bfcba51e19 100644
> --- a/drivers/gpu/drm/xe/xe_pcode_api.h
> +++ b/drivers/gpu/drm/xe/xe_pcode_api.h
> @@ -50,6 +50,21 @@
>  #define	READ_PL_FROM_FW				0x1
>  #define	READ_PL_FROM_PCODE			0x0
>  
> +#define   PCODE_LATE_BINDING			0x5C
> +#define     GET_CAPABILITY_STATUS		0x0
> +#define       V1_FAN_SUPPORTED			REG_BIT(0)
> +#define       VR_PARAMS_SUPPORTED		REG_BIT(3)
> +#define       V1_FAN_PROVISIONED		REG_BIT(16)
> +#define       VR_PARAMS_PROVISIONED		REG_BIT(19)
> +#define     GET_VERSION_LOW			0x1
> +#define     GET_VERSION_HIGH			0x2
> +#define       MAJOR_VERSION_MASK		REG_GENMASK(31, 16)
> +#define       MINOR_VERSION_MASK		REG_GENMASK(15, 0)
> +#define       HOTFIX_VERSION_MASK		REG_GENMASK(31, 16)
> +#define       BUILD_VERSION_MASK		REG_GENMASK(15, 0)
> +#define       FAN_TABLE				1
> +#define       VR_CONFIG				2
> +
>  #define   PCODE_FREQUENCY_CONFIG		0x6e
>  /* Frequency Config Sub Commands (param1) */
>  #define     PCODE_MBOX_FC_SC_READ_FUSED_P0	0x0
> -- 
> 2.34.1
> 


More information about the Intel-xe mailing list