Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

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>

authored by

Krishna Chomal and committed by
Ilpo Järvinen
c203c59f 46be1453

+43 -3
+43 -3
drivers/platform/x86/hp/hp-wmi.c
··· 34 34 #include <linux/slab.h> 35 35 #include <linux/string.h> 36 36 #include <linux/types.h> 37 + #include <linux/workqueue.h> 37 38 38 39 MODULE_AUTHOR("Matthew Garrett <mjg59@srcf.ucam.org>"); 39 40 MODULE_DESCRIPTION("HP laptop WMI driver"); ··· 369 368 u8 gpu_delta; 370 369 u8 mode; 371 370 u8 pwm; 371 + struct delayed_work keep_alive_dwork; 372 372 }; 373 373 374 374 struct victus_s_fan_table_header { ··· 387 385 struct victus_s_fan_table_header header; 388 386 struct victus_s_fan_table_entry entries[]; 389 387 } __packed; 388 + 389 + /* 390 + * 90s delay to prevent the firmware from resetting fan mode after fixed 391 + * 120s timeout 392 + */ 393 + #define KEEP_ALIVE_DELAY_SECS 90 390 394 391 395 static inline u8 rpm_to_pwm(u8 rpm, struct hp_wmi_hwmon_priv *priv) 392 396 { ··· 2101 2093 static void __exit hp_wmi_bios_remove(struct platform_device *device) 2102 2094 { 2103 2095 int i; 2096 + struct hp_wmi_hwmon_priv *priv; 2104 2097 2105 2098 for (i = 0; i < rfkill2_count; i++) { 2106 2099 rfkill_unregister(rfkill2[i].rfkill); ··· 2120 2111 rfkill_unregister(wwan_rfkill); 2121 2112 rfkill_destroy(wwan_rfkill); 2122 2113 } 2114 + 2115 + priv = platform_get_drvdata(device); 2116 + if (priv) 2117 + cancel_delayed_work_sync(&priv->keep_alive_dwork); 2123 2118 } 2124 2119 2125 2120 static int hp_wmi_resume_handler(struct device *device) ··· 2192 2179 if (is_victus_s_thermal_profile()) 2193 2180 hp_wmi_get_fan_count_userdefine_trigger(); 2194 2181 ret = hp_wmi_fan_speed_max_set(1); 2195 - return ret; 2182 + if (ret < 0) 2183 + return ret; 2184 + schedule_delayed_work(&priv->keep_alive_dwork, 2185 + secs_to_jiffies(KEEP_ALIVE_DELAY_SECS)); 2186 + return 0; 2196 2187 case PWM_MODE_MANUAL: 2197 2188 if (!is_victus_s_thermal_profile()) 2198 2189 return -EOPNOTSUPP; 2199 2190 ret = hp_wmi_fan_speed_set(priv, pwm_to_rpm(priv->pwm, priv)); 2200 - return ret; 2191 + if (ret < 0) 2192 + return ret; 2193 + schedule_delayed_work(&priv->keep_alive_dwork, 2194 + secs_to_jiffies(KEEP_ALIVE_DELAY_SECS)); 2195 + return 0; 2201 2196 case PWM_MODE_AUTO: 2202 2197 if (is_victus_s_thermal_profile()) { 2203 2198 hp_wmi_get_fan_count_userdefine_trigger(); ··· 2213 2192 } else { 2214 2193 ret = hp_wmi_fan_speed_max_set(0); 2215 2194 } 2216 - return ret; 2195 + if (ret < 0) 2196 + return ret; 2197 + cancel_delayed_work_sync(&priv->keep_alive_dwork); 2198 + return 0; 2217 2199 default: 2218 2200 /* shouldn't happen */ 2219 2201 return -EINVAL; ··· 2360 2336 .info = info, 2361 2337 }; 2362 2338 2339 + static void hp_wmi_hwmon_keep_alive_handler(struct work_struct *work) 2340 + { 2341 + struct delayed_work *dwork; 2342 + struct hp_wmi_hwmon_priv *priv; 2343 + 2344 + dwork = to_delayed_work(work); 2345 + priv = container_of(dwork, struct hp_wmi_hwmon_priv, keep_alive_dwork); 2346 + /* 2347 + * Re-apply the current hwmon context settings. 2348 + * NOTE: hp_wmi_apply_fan_settings will handle the re-scheduling. 2349 + */ 2350 + hp_wmi_apply_fan_settings(priv); 2351 + } 2352 + 2363 2353 static int hp_wmi_setup_fan_settings(struct hp_wmi_hwmon_priv *priv) 2364 2354 { 2365 2355 u8 fan_data[128] = { 0 }; ··· 2431 2393 return PTR_ERR(hwmon); 2432 2394 } 2433 2395 2396 + INIT_DELAYED_WORK(&priv->keep_alive_dwork, hp_wmi_hwmon_keep_alive_handler); 2397 + platform_set_drvdata(hp_wmi_platform_dev, priv); 2434 2398 hp_wmi_apply_fan_settings(priv); 2435 2399 2436 2400 return 0;