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: ayaneo-ec: Add suspend hook

The Ayaneo EC resets after hibernation, losing the charge control state.
Add a small PM hook to restore this state on hibernation resume.

The fan speed is also lost during hibernation, but since hibernation
failures are common with this class of devices, setting a low fan speed
when the userspace program controlling the fan will potentially not
take over could cause the device to overheat, so it is not restored.

Reviewed-by: Armin Wolf <W_Armin@gmx.de>
Signed-off-by: Antheas Kapenekakis <lkml@antheas.dev>
Link: https://patch.msgid.link/20251119174505.597218-7-lkml@antheas.dev
Reviewed-by: Ilpo Järvinen <ilpo.jarvinen@linux.intel.com>
Signed-off-by: Ilpo Järvinen <ilpo.jarvinen@linux.intel.com>

authored by

Antheas Kapenekakis and committed by
Ilpo Järvinen
2643187c 02c15e3d

+84 -1
+84 -1
drivers/platform/x86/ayaneo-ec.c
··· 16 16 #include <linux/kernel.h> 17 17 #include <linux/module.h> 18 18 #include <linux/platform_device.h> 19 + #include <linux/pm.h> 19 20 #include <linux/power_supply.h> 20 21 #include <linux/sysfs.h> 21 22 #include <acpi/battery.h> ··· 53 52 struct platform_device *pdev; 54 53 struct ayaneo_ec_quirk *quirks; 55 54 struct acpi_battery_hook battery_hook; 55 + 56 + // Protects access to restore_pwm 57 + struct mutex hwmon_lock; 58 + bool restore_charge_limit; 59 + bool restore_pwm; 56 60 }; 57 61 58 62 static const struct ayaneo_ec_quirk quirk_fan = { ··· 214 208 static int ayaneo_ec_write(struct device *dev, enum hwmon_sensor_types type, 215 209 u32 attr, int channel, long val) 216 210 { 211 + struct ayaneo_ec_platform_data *data = dev_get_drvdata(dev); 212 + int ret; 213 + 214 + guard(mutex)(&data->hwmon_lock); 215 + 217 216 switch (type) { 218 217 case hwmon_pwm: 219 218 switch (attr) { 220 219 case hwmon_pwm_enable: 220 + data->restore_pwm = false; 221 221 switch (val) { 222 222 case 1: 223 223 return ec_write(AYANEO_PWM_ENABLE_REG, ··· 237 225 case hwmon_pwm_input: 238 226 if (val < 0 || val > 255) 239 227 return -EINVAL; 228 + if (data->restore_pwm) { 229 + /* 230 + * Defer restoring PWM control to after 231 + * userspace resumes successfully 232 + */ 233 + ret = ec_write(AYANEO_PWM_ENABLE_REG, 234 + AYANEO_PWM_MODE_MANUAL); 235 + if (ret) 236 + return ret; 237 + data->restore_pwm = false; 238 + } 240 239 return ec_write(AYANEO_PWM_REG, (val * 100) / 255); 241 240 default: 242 241 break; ··· 477 454 478 455 data->pdev = pdev; 479 456 data->quirks = dmi_entry->driver_data; 457 + ret = devm_mutex_init(&pdev->dev, &data->hwmon_lock); 458 + if (ret) 459 + return ret; 480 460 platform_set_drvdata(pdev, data); 481 461 482 462 if (data->quirks->has_fan_control) { 483 463 hwdev = devm_hwmon_device_register_with_info(&pdev->dev, 484 - "ayaneo_ec", NULL, &ayaneo_ec_chip_info, NULL); 464 + "ayaneo_ec", data, &ayaneo_ec_chip_info, NULL); 485 465 if (IS_ERR(hwdev)) 486 466 return PTR_ERR(hwdev); 487 467 } ··· 501 475 return 0; 502 476 } 503 477 478 + static int ayaneo_freeze(struct device *dev) 479 + { 480 + struct platform_device *pdev = to_platform_device(dev); 481 + struct ayaneo_ec_platform_data *data = platform_get_drvdata(pdev); 482 + int ret; 483 + u8 tmp; 484 + 485 + if (data->quirks->has_charge_control) { 486 + ret = ec_read(AYANEO_CHARGE_REG, &tmp); 487 + if (ret) 488 + return ret; 489 + 490 + data->restore_charge_limit = tmp == AYANEO_CHARGE_VAL_INHIBIT; 491 + } 492 + 493 + if (data->quirks->has_fan_control) { 494 + ret = ec_read(AYANEO_PWM_ENABLE_REG, &tmp); 495 + if (ret) 496 + return ret; 497 + 498 + data->restore_pwm = tmp == AYANEO_PWM_MODE_MANUAL; 499 + 500 + /* 501 + * Release the fan when entering hibernation to avoid 502 + * overheating if hibernation fails and hangs. 503 + */ 504 + if (data->restore_pwm) { 505 + ret = ec_write(AYANEO_PWM_ENABLE_REG, AYANEO_PWM_MODE_AUTO); 506 + if (ret) 507 + return ret; 508 + } 509 + } 510 + 511 + return 0; 512 + } 513 + 514 + static int ayaneo_restore(struct device *dev) 515 + { 516 + struct platform_device *pdev = to_platform_device(dev); 517 + struct ayaneo_ec_platform_data *data = platform_get_drvdata(pdev); 518 + int ret; 519 + 520 + if (data->quirks->has_charge_control && data->restore_charge_limit) { 521 + ret = ec_write(AYANEO_CHARGE_REG, AYANEO_CHARGE_VAL_INHIBIT); 522 + if (ret) 523 + return ret; 524 + } 525 + 526 + return 0; 527 + } 528 + 529 + static const struct dev_pm_ops ayaneo_pm_ops = { 530 + .freeze = ayaneo_freeze, 531 + .restore = ayaneo_restore, 532 + }; 533 + 504 534 static struct platform_driver ayaneo_platform_driver = { 505 535 .driver = { 506 536 .name = "ayaneo-ec", 507 537 .dev_groups = ayaneo_ec_groups, 538 + .pm = pm_sleep_ptr(&ayaneo_pm_ops), 508 539 }, 509 540 .probe = ayaneo_ec_probe, 510 541 };