From 0624236d85fc1c50ad9418d4ee2df4a89e5c31b6 Mon Sep 17 00:00:00 2001 From: Peter Marshall Date: Mon, 22 Jun 2026 11:26:33 -0400 Subject: [PATCH] platform/surface: platform_profile: add fan power state handling Add functionality that switches the fan off if a fan is present when entering a suspend transition and switches it back to normal operation afterward. --- .../surface/surface_platform_profile.c | 63 +++++++++++++++++++ 1 file changed, 63 insertions(+) diff --git a/drivers/platform/surface/surface_platform_profile.c b/drivers/platform/surface/surface_platform_profile.c index 0e479e35e66e..203d21aa715c 100644 --- a/drivers/platform/surface/surface_platform_profile.c +++ b/drivers/platform/surface/surface_platform_profile.c @@ -32,6 +32,11 @@ enum ssam_fan_profile { SSAM_FAN_PROFILE_BEST_PERFORMANCE = 4, }; +enum ssam_fan_state { + SSAM_FAN_STATE_NORMAL = 1, + SSAM_FAN_STATE_STANDBY = 2, +}; + struct ssam_tmp_profile_info { __le32 profile; __le16 unknown1; @@ -61,6 +66,13 @@ SSAM_DEFINE_SYNC_REQUEST_W(__ssam_fan_profile_set, u8, { .instance_id = 0x01, }); +SSAM_DEFINE_SYNC_REQUEST_W(__ssam_fan_state_set, u8, { + .target_category = SSAM_SSH_TC_FAN, + .target_id = SSAM_SSH_TID_SAM, + .command_id = 0x0f, + .instance_id = 0x01, +}); + static int ssam_tmp_profile_get(struct ssam_device *sdev, enum ssam_tmp_profile *p) { struct ssam_tmp_profile_info info; @@ -88,6 +100,13 @@ static int ssam_fan_profile_set(struct ssam_device *sdev, enum ssam_fan_profile return ssam_retry(__ssam_fan_profile_set, sdev->ctrl, &profile); } +static int ssam_fan_state_set(struct ssam_device *sdev, enum ssam_fan_state s) +{ + const u8 state = s; + + return ssam_retry(__ssam_fan_state_set, sdev->ctrl, &state); +} + static int convert_ssam_tmp_to_profile(struct ssam_device *sdev, enum ssam_tmp_profile p) { switch (p) { @@ -236,6 +255,49 @@ static int surface_platform_profile_probe(struct ssam_device *sdev) return PTR_ERR_OR_ZERO(tpd->ppdev); } +#ifdef CONFIG_PM_SLEEP + +static int surface_platform_profile_pm_prepare(struct device *dev) +{ + struct ssam_platform_profile_device *tpd; + int status; + + tpd = dev_get_drvdata(dev); + + if (tpd->has_fan) { + status = ssam_fan_state_set(tpd->sdev, SSAM_FAN_STATE_STANDBY); + if (status) + dev_err(&tpd->sdev->dev, "pm: fan standby notification failed"); + } + + return status; +} + +static void surface_platform_profile_pm_complete(struct device *dev) +{ + struct ssam_platform_profile_device *tpd; + int status; + + tpd = dev_get_drvdata(dev); + + if (tpd->has_fan) { + status = ssam_fan_state_set(tpd->sdev, SSAM_FAN_STATE_NORMAL); + if (status) + dev_err(&tpd->sdev->dev, "pm: fan resume notification failed"); + } +} + +static const struct dev_pm_ops surface_platform_profile_pm_ops = { + .prepare = surface_platform_profile_pm_prepare, + .complete = surface_platform_profile_pm_complete, +}; + +#else /* CONFIG_PM_SLEEP */ + +static const struct dev_pm_ops surface_platform_profile_pm_ops = { }; + +#endif /* CONFIG_PM_SLEEP */ + static const struct ssam_device_id ssam_platform_profile_match[] = { { SSAM_SDEV(TMP, SAM, 0x00, 0x01) }, { }, @@ -247,6 +309,7 @@ static struct ssam_device_driver surface_platform_profile = { .match_table = ssam_platform_profile_match, .driver = { .name = "surface_platform_profile", + .pm = &surface_platform_profile_pm_ops, .probe_type = PROBE_PREFER_ASYNCHRONOUS, }, };