Skip to content

Commit 2643187

Browse files
antheasij-intel
authored andcommitted
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>
1 parent 02c15e3 commit 2643187

1 file changed

Lines changed: 84 additions & 1 deletion

File tree

drivers/platform/x86/ayaneo-ec.c

Lines changed: 84 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
#include <linux/kernel.h>
1717
#include <linux/module.h>
1818
#include <linux/platform_device.h>
19+
#include <linux/pm.h>
1920
#include <linux/power_supply.h>
2021
#include <linux/sysfs.h>
2122
#include <acpi/battery.h>
@@ -52,6 +53,11 @@ struct ayaneo_ec_platform_data {
5253
struct platform_device *pdev;
5354
struct ayaneo_ec_quirk *quirks;
5455
struct acpi_battery_hook battery_hook;
56+
57+
// Protects access to restore_pwm
58+
struct mutex hwmon_lock;
59+
bool restore_charge_limit;
60+
bool restore_pwm;
5561
};
5662

5763
static const struct ayaneo_ec_quirk quirk_fan = {
@@ -208,10 +214,16 @@ static int ayaneo_ec_read(struct device *dev, enum hwmon_sensor_types type,
208214
static int ayaneo_ec_write(struct device *dev, enum hwmon_sensor_types type,
209215
u32 attr, int channel, long val)
210216
{
217+
struct ayaneo_ec_platform_data *data = dev_get_drvdata(dev);
218+
int ret;
219+
220+
guard(mutex)(&data->hwmon_lock);
221+
211222
switch (type) {
212223
case hwmon_pwm:
213224
switch (attr) {
214225
case hwmon_pwm_enable:
226+
data->restore_pwm = false;
215227
switch (val) {
216228
case 1:
217229
return ec_write(AYANEO_PWM_ENABLE_REG,
@@ -225,6 +237,17 @@ static int ayaneo_ec_write(struct device *dev, enum hwmon_sensor_types type,
225237
case hwmon_pwm_input:
226238
if (val < 0 || val > 255)
227239
return -EINVAL;
240+
if (data->restore_pwm) {
241+
/*
242+
* Defer restoring PWM control to after
243+
* userspace resumes successfully
244+
*/
245+
ret = ec_write(AYANEO_PWM_ENABLE_REG,
246+
AYANEO_PWM_MODE_MANUAL);
247+
if (ret)
248+
return ret;
249+
data->restore_pwm = false;
250+
}
228251
return ec_write(AYANEO_PWM_REG, (val * 100) / 255);
229252
default:
230253
break;
@@ -454,11 +477,14 @@ static int ayaneo_ec_probe(struct platform_device *pdev)
454477

455478
data->pdev = pdev;
456479
data->quirks = dmi_entry->driver_data;
480+
ret = devm_mutex_init(&pdev->dev, &data->hwmon_lock);
481+
if (ret)
482+
return ret;
457483
platform_set_drvdata(pdev, data);
458484

459485
if (data->quirks->has_fan_control) {
460486
hwdev = devm_hwmon_device_register_with_info(&pdev->dev,
461-
"ayaneo_ec", NULL, &ayaneo_ec_chip_info, NULL);
487+
"ayaneo_ec", data, &ayaneo_ec_chip_info, NULL);
462488
if (IS_ERR(hwdev))
463489
return PTR_ERR(hwdev);
464490
}
@@ -475,10 +501,67 @@ static int ayaneo_ec_probe(struct platform_device *pdev)
475501
return 0;
476502
}
477503

504+
static int ayaneo_freeze(struct device *dev)
505+
{
506+
struct platform_device *pdev = to_platform_device(dev);
507+
struct ayaneo_ec_platform_data *data = platform_get_drvdata(pdev);
508+
int ret;
509+
u8 tmp;
510+
511+
if (data->quirks->has_charge_control) {
512+
ret = ec_read(AYANEO_CHARGE_REG, &tmp);
513+
if (ret)
514+
return ret;
515+
516+
data->restore_charge_limit = tmp == AYANEO_CHARGE_VAL_INHIBIT;
517+
}
518+
519+
if (data->quirks->has_fan_control) {
520+
ret = ec_read(AYANEO_PWM_ENABLE_REG, &tmp);
521+
if (ret)
522+
return ret;
523+
524+
data->restore_pwm = tmp == AYANEO_PWM_MODE_MANUAL;
525+
526+
/*
527+
* Release the fan when entering hibernation to avoid
528+
* overheating if hibernation fails and hangs.
529+
*/
530+
if (data->restore_pwm) {
531+
ret = ec_write(AYANEO_PWM_ENABLE_REG, AYANEO_PWM_MODE_AUTO);
532+
if (ret)
533+
return ret;
534+
}
535+
}
536+
537+
return 0;
538+
}
539+
540+
static int ayaneo_restore(struct device *dev)
541+
{
542+
struct platform_device *pdev = to_platform_device(dev);
543+
struct ayaneo_ec_platform_data *data = platform_get_drvdata(pdev);
544+
int ret;
545+
546+
if (data->quirks->has_charge_control && data->restore_charge_limit) {
547+
ret = ec_write(AYANEO_CHARGE_REG, AYANEO_CHARGE_VAL_INHIBIT);
548+
if (ret)
549+
return ret;
550+
}
551+
552+
return 0;
553+
}
554+
555+
static const struct dev_pm_ops ayaneo_pm_ops = {
556+
.freeze = ayaneo_freeze,
557+
.restore = ayaneo_restore,
558+
};
559+
478560
static struct platform_driver ayaneo_platform_driver = {
479561
.driver = {
480562
.name = "ayaneo-ec",
481563
.dev_groups = ayaneo_ec_groups,
564+
.pm = pm_sleep_ptr(&ayaneo_pm_ops),
482565
},
483566
.probe = ayaneo_ec_probe,
484567
};

0 commit comments

Comments
 (0)