linux-hwmon.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
From: "Ilpo Järvinen" <ilpo.jarvinen@linux.intel.com>
To: Rong Zhang <i@rong.moe>
Cc: Mark Pearson <mpearson-lenovo@squebb.ca>,
	 "Derek J. Clark" <derekjohn.clark@gmail.com>,
	Armin Wolf <W_Armin@gmx.de>,  Hans de Goede <hansg@kernel.org>,
	Guenter Roeck <linux@roeck-us.net>,
	 platform-driver-x86@vger.kernel.org,
	LKML <linux-kernel@vger.kernel.org>,
	 linux-hwmon@vger.kernel.org
Subject: Re: [PATCH v6 5/7] platform/x86: lenovo-wmi-capdata: Add support for Fan Test Data
Date: Mon, 24 Nov 2025 18:45:05 +0200 (EET)	[thread overview]
Message-ID: <1efe99d4-95ae-d76c-71f5-0a1f98292dd4@linux.intel.com> (raw)
In-Reply-To: <20251122184522.18677-6-i@rong.moe>

On Sun, 23 Nov 2025, Rong Zhang wrote:

> Add support for LENOVO_FAN_TEST_DATA WMI data block. Provides an
> interface for querying the min/max fan speed RPM (reference data) of a
> given fan ID.
> 
> This interface is optional. Hence, it does not bind to lenovo-wmi-other
> and is not registered as a component for the moment. Appropriate binding
> will be implemented in the subsequent patch.
> 
> Signed-off-by: Rong Zhang <i@rong.moe>
> Reviewed-by: Derek J. Clark <derekjohn.clark@gmail.com>
> Tested-by: Derek J. Clark <derekjohn.clark@gmail.com>
> ---
> Changes in v4:
> - Rebase on top of changes made to [PATCH v4 3/7]
> - Do not register it as a component until [PATCH v4 6/7]
> 
> Changes in v2:
> - Reword documentation
> ---
>  .../wmi/devices/lenovo-wmi-other.rst          |  17 +++
>  drivers/platform/x86/lenovo/wmi-capdata.c     | 102 ++++++++++++++++++
>  drivers/platform/x86/lenovo/wmi-capdata.h     |   7 ++
>  3 files changed, 126 insertions(+)
> 
> diff --git a/Documentation/wmi/devices/lenovo-wmi-other.rst b/Documentation/wmi/devices/lenovo-wmi-other.rst
> index fcad595d49af..821282e07d93 100644
> --- a/Documentation/wmi/devices/lenovo-wmi-other.rst
> +++ b/Documentation/wmi/devices/lenovo-wmi-other.rst
> @@ -62,6 +62,13 @@ The following firmware-attributes are implemented:
>   - ppt_pl2_sppt: Platform Profile Tracking Slow Package Power Tracking
>   - ppt_pl3_fppt: Platform Profile Tracking Fast Package Power Tracking
>  
> +LENOVO_FAN_TEST_DATA
> +-------------------------
> +
> +WMI GUID ``B642801B-3D21-45DE-90AE-6E86F164FB21``
> +
> +The LENOVO_FAN_TEST_DATA interface provides reference data for self-test of
> +cooling fans.
>  
>  WMI interface description
>  =========================
> @@ -115,3 +122,13 @@ data using the `bmfdec <https://github.com/pali/bmfdec>`_ utility:
>      [WmiDataId(3), read, Description("Data Size.")] uint32 DataSize;
>      [WmiDataId(4), read, Description("Default Value"), WmiSizeIs("DataSize")] uint8 DefaultValue[];
>    };
> +
> +  [WMI, Dynamic, Provider("WmiProv"), Locale("MS\\0x409"), Description("Definition of Fan Test Data"), guid("{B642801B-3D21-45DE-90AE-6E86F164FB21}")]
> +  class LENOVO_FAN_TEST_DATA {
> +    [key, read] string InstanceName;
> +    [read] boolean Active;
> +    [WmiDataId(1), read, Description("Mode.")] uint32 NumOfFans;
> +    [WmiDataId(2), read, Description("Fan ID."), WmiSizeIs("NumOfFans")] uint32 FanId[];
> +    [WmiDataId(3), read, Description("Maximum Fan Speed."), WmiSizeIs("NumOfFans")] uint32 FanMaxSpeed[];
> +    [WmiDataId(4), read, Description("Minumum Fan Speed."), WmiSizeIs("NumOfFans")] uint32 FanMinSpeed[];
> +  };
> diff --git a/drivers/platform/x86/lenovo/wmi-capdata.c b/drivers/platform/x86/lenovo/wmi-capdata.c
> index 29267c373ab3..e6392357395c 100644
> --- a/drivers/platform/x86/lenovo/wmi-capdata.c
> +++ b/drivers/platform/x86/lenovo/wmi-capdata.c
> @@ -13,6 +13,10 @@
>   * attribute has multiple pages, one for each of the thermal modes managed by
>   * the Gamezone interface.
>   *
> + * Fan Test Data includes the max/min fan speed RPM for each fan. This is
> + * reference data for self-test. If the fan is in good condition, it is capable
> + * to spin faster than max RPM or slower than min RPM.
> + *
>   * Copyright (C) 2025 Derek J. Clark <derekjohn.clark@gmail.com>
>   *   - Initial implementation (formerly named lenovo-wmi-capdata01)
>   *
> @@ -41,6 +45,7 @@
>  
>  #define LENOVO_CAPABILITY_DATA_00_GUID "362A3AFE-3D96-4665-8530-96DAD5BB300E"
>  #define LENOVO_CAPABILITY_DATA_01_GUID "7A8F5407-CB67-4D6E-B547-39B3BE018154"
> +#define LENOVO_FAN_TEST_DATA_GUID "B642801B-3D21-45DE-90AE-6E86F164FB21"
>  
>  #define ACPI_AC_CLASS "ac_adapter"
>  #define ACPI_AC_NOTIFY_STATUS 0x80
> @@ -48,6 +53,7 @@
>  enum lwmi_cd_type {
>  	LENOVO_CAPABILITY_DATA_00,
>  	LENOVO_CAPABILITY_DATA_01,
> +	LENOVO_FAN_TEST_DATA,
>  };
>  
>  #define LWMI_CD_TABLE_ITEM(_type)		\
> @@ -62,6 +68,7 @@ static const struct lwmi_cd_info {
>  } lwmi_cd_table[] = {
>  	LWMI_CD_TABLE_ITEM(LENOVO_CAPABILITY_DATA_00),
>  	LWMI_CD_TABLE_ITEM(LENOVO_CAPABILITY_DATA_01),
> +	LWMI_CD_TABLE_ITEM(LENOVO_FAN_TEST_DATA),
>  };
>  
>  struct lwmi_cd_priv {
> @@ -78,6 +85,7 @@ struct cd_list {
>  	union {
>  		DECLARE_FLEX_ARRAY(struct capdata00, cd00);
>  		DECLARE_FLEX_ARRAY(struct capdata01, cd01);
> +		DECLARE_FLEX_ARRAY(struct capdata_fan, cd_fan);
>  	};
>  };
>  
> @@ -117,6 +125,10 @@ void lwmi_cd_match_add_all(struct device *master, struct component_match **match
>  		return;
>  
>  	for (i = 0; i < ARRAY_SIZE(lwmi_cd_table); i++) {
> +		/* Skip optional interfaces. */
> +		if (lwmi_cd_table[i].type == LENOVO_FAN_TEST_DATA)
> +			continue;
> +
>  		component_match_add(master, matchptr, lwmi_cd_match,
>  				    (void *)&lwmi_cd_table[i].type);
>  		if (IS_ERR(*matchptr))
> @@ -194,6 +206,9 @@ EXPORT_SYMBOL_NS_GPL(lwmi_cd00_get_data, "LENOVO_WMI_CD");
>  DEF_LWMI_CDXX_GET_DATA(cd01, LENOVO_CAPABILITY_DATA_01, struct capdata01);
>  EXPORT_SYMBOL_NS_GPL(lwmi_cd01_get_data, "LENOVO_WMI_CD");
>  
> +DEF_LWMI_CDXX_GET_DATA(cd_fan, LENOVO_FAN_TEST_DATA, struct capdata_fan);
> +EXPORT_SYMBOL_NS_GPL(lwmi_cd_fan_get_data, "LENOVO_WMI_CD");
> +
>  /**
>   * lwmi_cd_cache() - Cache all WMI data block information
>   * @priv: lenovo-wmi-capdata driver data.
> @@ -217,6 +232,9 @@ static int lwmi_cd_cache(struct lwmi_cd_priv *priv)
>  		p = &priv->list->cd01[0];
>  		size = sizeof(priv->list->cd01[0]);
>  		break;
> +	case LENOVO_FAN_TEST_DATA:
> +		/* Done by lwmi_cd_alloc() => lwmi_cd_fan_list_alloc_cache(). */
> +		return 0;
>  	default:
>  		return -EINVAL;
>  	}
> @@ -239,6 +257,78 @@ static int lwmi_cd_cache(struct lwmi_cd_priv *priv)
>  	return 0;
>  }
>  
> +/**
> + * lwmi_cd_fan_list_alloc_cache() - Alloc and cache Fan Test Data list
> + * @priv: lenovo-wmi-capdata driver data.
> + * @listptr: Pointer to returned cd_list pointer.
> + *
> + * Return: count of fans found, or an error.
> + */
> +static int lwmi_cd_fan_list_alloc_cache(struct lwmi_cd_priv *priv, struct cd_list **listptr)
> +{
> +	u32 count, *fan_ids, *fan_min_rpms, *fan_max_rpms;
> +	union acpi_object *ret_obj __free(kfree) = NULL;

Since you're using __free(), please move this to where you assign the 
value. This is to create a pattern with cleanup helpers. The cleanup 
order depends on the order the variables are introduced which in some 
other cases may be significant.

> +	struct block { u32 nr; u32 data[]; } *block;

This is the first time I see this style anywhere in the kernel's context, 
has there been some general discussion about this style somewhere?

At least it seems immediately obvious to me that this style will have a 
negative impact on documentability due to (too) concise use of space.

> +	struct cd_list *list;
> +	size_t size;
> +	int idx;
> +
> +	ret_obj = wmidev_block_query(priv->wdev, 0);
> +	if (!ret_obj)
> +		return -ENODEV;
> +
> +	/*
> +	 * This is usually caused by a dummy ACPI method. Do not return an error
> +	 * as failing to probe this device will result in master driver being
> +	 * unbound - this behavior aligns with lwmi_cd_cache().
> +	 */
> +	if (ret_obj->type != ACPI_TYPE_BUFFER) {
> +		count = 0;
> +		goto alloc;
> +	}
> +
> +	size = ret_obj->buffer.length;
> +	block = (struct block *)ret_obj->buffer.pointer;

void * can be cast implicitly.

> +
> +	count = size >= sizeof(*block) ? block->nr : 0;
> +	if (size < struct_size(block, data, count * 3)) {
> +		dev_warn(&priv->wdev->dev,
> +			 "incomplete fan test data block: %zu < %zu, ignoring\n",
> +			 size, struct_size(block, data, count * 3));
> +		count = 0;
> +	}
> +
> +	if (count == 0)
> +		goto alloc;
> +
> +	if (count > U8_MAX) {
> +		dev_warn(&priv->wdev->dev,
> +			 "too many fans reported: %u > %u, truncating\n",
> +			 count, U8_MAX);
> +		count = U8_MAX;
> +	}
> +
> +	fan_ids = &block->data[0];
> +	fan_max_rpms = &block->data[count];
> +	fan_min_rpms = &block->data[count * 2];

Please rearrange so you can remove the goto:

	1. limit count
	if (count) {
		...
	}


> +
> +alloc:
> +	list = devm_kzalloc(&priv->wdev->dev, struct_size(list, cd_fan, count), GFP_KERNEL);
> +	if (!list)
> +		return -ENOMEM;
> +
> +	for (idx = 0; idx < count; idx++) {
> +		list->cd_fan[idx] = (struct capdata_fan) {
> +			.id = fan_ids[idx],
> +			.min_rpm = fan_min_rpms[idx],
> +			.max_rpm = fan_max_rpms[idx],
> +		};
> +	}
> +
> +	*listptr = list;
> +	return count;
> +}
> +
>  /**
>   * lwmi_cd_alloc() - Allocate a cd_list struct in drvdata
>   * @priv: lenovo-wmi-capdata driver data.
> @@ -264,6 +354,12 @@ static int lwmi_cd_alloc(struct lwmi_cd_priv *priv, enum lwmi_cd_type type)
>  	case LENOVO_CAPABILITY_DATA_01:
>  		list_size = struct_size(list, cd01, count);
>  		break;
> +	case LENOVO_FAN_TEST_DATA:
> +		count = lwmi_cd_fan_list_alloc_cache(priv, &list);
> +		if (count < 0)
> +			return count;
> +
> +		goto got_list;
>  	default:
>  		return -EINVAL;
>  	}
> @@ -272,6 +368,7 @@ static int lwmi_cd_alloc(struct lwmi_cd_priv *priv, enum lwmi_cd_type type)
>  	if (!list)
>  		return -ENOMEM;
>  
> +got_list:
>  	ret = devm_mutex_init(&priv->wdev->dev, &list->list_mutex);
>  	if (ret)
>  		return ret;
> @@ -390,6 +487,8 @@ static int lwmi_cd_probe(struct wmi_device *wdev, const void *context)
>  
>  		ret = component_add(&wdev->dev, &lwmi_cd_component_ops);
>  		goto out;
> +	case LENOVO_FAN_TEST_DATA:
> +		goto out;
>  	default:
>  		return -EINVAL;
>  	}
> @@ -413,6 +512,8 @@ static void lwmi_cd_remove(struct wmi_device *wdev)
>  	case LENOVO_CAPABILITY_DATA_01:
>  		component_del(&wdev->dev, &lwmi_cd_component_ops);
>  		break;
> +	case LENOVO_FAN_TEST_DATA:
> +		break;
>  	default:
>  		WARN_ON(1);
>  	}
> @@ -425,6 +526,7 @@ static void lwmi_cd_remove(struct wmi_device *wdev)
>  static const struct wmi_device_id lwmi_cd_id_table[] = {
>  	{ LWMI_CD_WDEV_ID(LENOVO_CAPABILITY_DATA_00) },
>  	{ LWMI_CD_WDEV_ID(LENOVO_CAPABILITY_DATA_01) },
> +	{ LWMI_CD_WDEV_ID(LENOVO_FAN_TEST_DATA) },
>  	{}
>  };
>  
> diff --git a/drivers/platform/x86/lenovo/wmi-capdata.h b/drivers/platform/x86/lenovo/wmi-capdata.h
> index a6d006ef458f..38af4c4e4ef4 100644
> --- a/drivers/platform/x86/lenovo/wmi-capdata.h
> +++ b/drivers/platform/x86/lenovo/wmi-capdata.h
> @@ -26,6 +26,12 @@ struct capdata01 {
>  	u32 max_value;
>  };
>  
> +struct capdata_fan {
> +	u32 id;
> +	u32 min_rpm;
> +	u32 max_rpm;
> +};
> +
>  struct lwmi_cd_binder {
>  	struct cd_list *cd00_list;
>  	struct cd_list *cd01_list;
> @@ -34,5 +40,6 @@ struct lwmi_cd_binder {
>  void lwmi_cd_match_add_all(struct device *master, struct component_match **matchptr);
>  int lwmi_cd00_get_data(struct cd_list *list, u32 attribute_id, struct capdata00 *output);
>  int lwmi_cd01_get_data(struct cd_list *list, u32 attribute_id, struct capdata01 *output);
> +int lwmi_cd_fan_get_data(struct cd_list *list, u32 attribute_id, struct capdata_fan *output);
>  
>  #endif /* !_LENOVO_WMI_CAPDATA_H_ */
> 

-- 
 i.


  reply	other threads:[~2025-11-24 16:45 UTC|newest]

Thread overview: 22+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2025-11-22 18:44 [PATCH v6 0/7] platform/x86: lenovo-wmi-{capdata,other}: Add HWMON for fan speed Rong Zhang
2025-11-22 18:44 ` [PATCH v6 1/7] platform/x86: lenovo-wmi-helpers: Convert returned buffer into u32 Rong Zhang
2025-11-22 18:44 ` [PATCH v6 2/7] platform/x86: Rename lenovo-wmi-capdata01 to lenovo-wmi-capdata Rong Zhang
2025-11-22 18:44 ` [PATCH v6 3/7] platform/x86: lenovo-wmi-{capdata,other}: Support multiple Capability Data Rong Zhang
2025-11-24 16:30   ` Ilpo Järvinen
2025-11-24 20:03     ` Rong Zhang
2025-11-22 18:44 ` [PATCH v6 4/7] platform/x86: lenovo-wmi-capdata: Add support for Capability Data 00 Rong Zhang
2025-11-22 18:44 ` [PATCH v6 5/7] platform/x86: lenovo-wmi-capdata: Add support for Fan Test Data Rong Zhang
2025-11-24 16:45   ` Ilpo Järvinen [this message]
2025-11-24 20:07     ` Rong Zhang
2025-11-26  7:44       ` Ilpo Järvinen
2025-11-26 13:57         ` Rong Zhang
2025-11-22 18:44 ` [PATCH v6 6/7] platform/x86: lenovo-wmi-capdata: Wire up " Rong Zhang
2025-11-23  3:36   ` Armin Wolf
2025-11-23 15:02     ` Rong Zhang
2025-11-25  2:08       ` Armin Wolf
2025-11-24 16:49   ` Ilpo Järvinen
2025-11-24 20:10     ` Rong Zhang
2025-11-22 18:44 ` [PATCH v6 7/7] platform/x86: lenovo-wmi-other: Add HWMON for fan reporting/tuning Rong Zhang
2025-11-24 16:58   ` Ilpo Järvinen
2025-11-24 20:12     ` Rong Zhang
2025-11-26  8:04       ` Ilpo Järvinen

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=1efe99d4-95ae-d76c-71f5-0a1f98292dd4@linux.intel.com \
    --to=ilpo.jarvinen@linux.intel.com \
    --cc=W_Armin@gmx.de \
    --cc=derekjohn.clark@gmail.com \
    --cc=hansg@kernel.org \
    --cc=i@rong.moe \
    --cc=linux-hwmon@vger.kernel.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=linux@roeck-us.net \
    --cc=mpearson-lenovo@squebb.ca \
    --cc=platform-driver-x86@vger.kernel.org \
    /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;
as well as URLs for NNTP newsgroup(s).