mirror of
https://github.com/torvalds/linux.git
synced 2026-05-13 08:39:31 +02:00
platform/x86: hp-wmi: implement fan keep-alive
The firmware on some HP laptops automatically reverts the fan speed control to "Auto" mode after a 120 second timeout window. To ensure that the user-selected fan profile (Max/Manual) persists, implement a keep-alive mechanism that periodically refreshes the fan mode trigger before the timeout occurs. - Introduce a delayed workqueue to trigger the fan mode refresh every 90 seconds, ensuring the system maintains the correct fan mode setting. - Integrate the refresh mechanism into hp_wmi_apply_fan_settings() to start, update or cancel the keep-alive process based on the current fan mode. This ensures that the driver stays in sync with the hardware. Tested on: HP Omen 16-wf1xxx (board ID 8C78) Signed-off-by: Krishna Chomal <krishna.chomal108@gmail.com> Link: https://patch.msgid.link/20260113123738.222244-4-krishna.chomal108@gmail.com Reviewed-by: Ilpo Järvinen <ilpo.jarvinen@linux.intel.com> Signed-off-by: Ilpo Järvinen <ilpo.jarvinen@linux.intel.com>
This commit is contained in:
parent
46be1453e6
commit
c203c59fb5
|
|
@ -34,6 +34,7 @@
|
|||
#include <linux/slab.h>
|
||||
#include <linux/string.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/workqueue.h>
|
||||
|
||||
MODULE_AUTHOR("Matthew Garrett <mjg59@srcf.ucam.org>");
|
||||
MODULE_DESCRIPTION("HP laptop WMI driver");
|
||||
|
|
@ -368,6 +369,7 @@ struct hp_wmi_hwmon_priv {
|
|||
u8 gpu_delta;
|
||||
u8 mode;
|
||||
u8 pwm;
|
||||
struct delayed_work keep_alive_dwork;
|
||||
};
|
||||
|
||||
struct victus_s_fan_table_header {
|
||||
|
|
@ -386,6 +388,12 @@ struct victus_s_fan_table {
|
|||
struct victus_s_fan_table_entry entries[];
|
||||
} __packed;
|
||||
|
||||
/*
|
||||
* 90s delay to prevent the firmware from resetting fan mode after fixed
|
||||
* 120s timeout
|
||||
*/
|
||||
#define KEEP_ALIVE_DELAY_SECS 90
|
||||
|
||||
static inline u8 rpm_to_pwm(u8 rpm, struct hp_wmi_hwmon_priv *priv)
|
||||
{
|
||||
return fixp_linear_interpolate(0, 0, priv->max_rpm, U8_MAX,
|
||||
|
|
@ -2093,6 +2101,7 @@ static int __init hp_wmi_bios_setup(struct platform_device *device)
|
|||
static void __exit hp_wmi_bios_remove(struct platform_device *device)
|
||||
{
|
||||
int i;
|
||||
struct hp_wmi_hwmon_priv *priv;
|
||||
|
||||
for (i = 0; i < rfkill2_count; i++) {
|
||||
rfkill_unregister(rfkill2[i].rfkill);
|
||||
|
|
@ -2111,6 +2120,10 @@ static void __exit hp_wmi_bios_remove(struct platform_device *device)
|
|||
rfkill_unregister(wwan_rfkill);
|
||||
rfkill_destroy(wwan_rfkill);
|
||||
}
|
||||
|
||||
priv = platform_get_drvdata(device);
|
||||
if (priv)
|
||||
cancel_delayed_work_sync(&priv->keep_alive_dwork);
|
||||
}
|
||||
|
||||
static int hp_wmi_resume_handler(struct device *device)
|
||||
|
|
@ -2179,12 +2192,20 @@ static int hp_wmi_apply_fan_settings(struct hp_wmi_hwmon_priv *priv)
|
|||
if (is_victus_s_thermal_profile())
|
||||
hp_wmi_get_fan_count_userdefine_trigger();
|
||||
ret = hp_wmi_fan_speed_max_set(1);
|
||||
return ret;
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
schedule_delayed_work(&priv->keep_alive_dwork,
|
||||
secs_to_jiffies(KEEP_ALIVE_DELAY_SECS));
|
||||
return 0;
|
||||
case PWM_MODE_MANUAL:
|
||||
if (!is_victus_s_thermal_profile())
|
||||
return -EOPNOTSUPP;
|
||||
ret = hp_wmi_fan_speed_set(priv, pwm_to_rpm(priv->pwm, priv));
|
||||
return ret;
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
schedule_delayed_work(&priv->keep_alive_dwork,
|
||||
secs_to_jiffies(KEEP_ALIVE_DELAY_SECS));
|
||||
return 0;
|
||||
case PWM_MODE_AUTO:
|
||||
if (is_victus_s_thermal_profile()) {
|
||||
hp_wmi_get_fan_count_userdefine_trigger();
|
||||
|
|
@ -2192,7 +2213,10 @@ static int hp_wmi_apply_fan_settings(struct hp_wmi_hwmon_priv *priv)
|
|||
} else {
|
||||
ret = hp_wmi_fan_speed_max_set(0);
|
||||
}
|
||||
return ret;
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
cancel_delayed_work_sync(&priv->keep_alive_dwork);
|
||||
return 0;
|
||||
default:
|
||||
/* shouldn't happen */
|
||||
return -EINVAL;
|
||||
|
|
@ -2336,6 +2360,20 @@ static const struct hwmon_chip_info chip_info = {
|
|||
.info = info,
|
||||
};
|
||||
|
||||
static void hp_wmi_hwmon_keep_alive_handler(struct work_struct *work)
|
||||
{
|
||||
struct delayed_work *dwork;
|
||||
struct hp_wmi_hwmon_priv *priv;
|
||||
|
||||
dwork = to_delayed_work(work);
|
||||
priv = container_of(dwork, struct hp_wmi_hwmon_priv, keep_alive_dwork);
|
||||
/*
|
||||
* Re-apply the current hwmon context settings.
|
||||
* NOTE: hp_wmi_apply_fan_settings will handle the re-scheduling.
|
||||
*/
|
||||
hp_wmi_apply_fan_settings(priv);
|
||||
}
|
||||
|
||||
static int hp_wmi_setup_fan_settings(struct hp_wmi_hwmon_priv *priv)
|
||||
{
|
||||
u8 fan_data[128] = { 0 };
|
||||
|
|
@ -2393,6 +2431,8 @@ static int hp_wmi_hwmon_init(void)
|
|||
return PTR_ERR(hwmon);
|
||||
}
|
||||
|
||||
INIT_DELAYED_WORK(&priv->keep_alive_dwork, hp_wmi_hwmon_keep_alive_handler);
|
||||
platform_set_drvdata(hp_wmi_platform_dev, priv);
|
||||
hp_wmi_apply_fan_settings(priv);
|
||||
|
||||
return 0;
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user