From: Tomasz Bojanowski <tomasz@bojanowski.me>
To: Guenter Roeck <linux@roeck-us.net>
Cc: Hamza Mahfooz <someguy@effective-light.com>,
linux-hwmon@vger.kernel.org, linux-kernel@vger.kernel.org
Subject: [PATCH] hwmon: (nct6683) Restore manual fan control after resume
Date: Mon, 9 Nov 2026 17:33:28 +0100 [thread overview]
Message-ID: <20261109163328.61787-1-tomasz@bojanowski.me> (raw)
On MSI boards with NCT6687D-R, the BIOS restores automatic fan control
on resume from S3. The manual mode and duty cycle set through
pwmN_enable and pwmN are lost, and pwmN_enable reports 2.
Remember the channels switched to manual mode through sysfs and their
duty cycle, and restore them on resume if the EC switched them back to
automatic mode. Other channels are not touched.
Tested on an MSI MAG B850M MORTAR WIFI (MS-7E61): with pwm16_enable set
to 1 and pwm16 to 153, pwm16_enable reads 2 after resume from S3 without
this patch. With this patch it stays 1 and the fan returns to the manual
duty cycle.
Fixes: 5410a1257e26 ("hwmon: (nct6683) Add fan control support for MSI boards with NCT6687D-R")
Signed-off-by: Tomasz Bojanowski <tomasz@bojanowski.me>
---
I used an AI assistant (Claude Code) during development. I reviewed all
of the changes, tested them on the hardware described above, and take
responsibility for them.
This addresses your comment on suspend/resume in [1]: on the MS-7E61,
the BIOS restores automatic fan control on resume from S3. The patch
does not depend on the fan curve patch; both apply in either order.
[1] https://lore.kernel.org/linux-hwmon/487b2f39-7e22-46db-9eaf-9b1dd9b31d31@roeck-us.net/
drivers/hwmon/nct6683.c | 39 +++++++++++++++++++++++++++++++++++----
1 file changed, 35 insertions(+), 4 deletions(-)
diff --git a/drivers/hwmon/nct6683.c b/drivers/hwmon/nct6683.c
index 65e374c64fb8..d88b981efb1f 100644
--- a/drivers/hwmon/nct6683.c
+++ b/drivers/hwmon/nct6683.c
@@ -365,6 +365,10 @@ struct nct6683_data {
/* Remember extra register values over suspend/resume */
u8 hwm_cfg;
#endif
+
+ /* Manual fan control set through sysfs, restored on resume */
+ u16 pwm_manual; /* channels set to manual mode */
+ u8 pwm_cmd[NCT6683_NUM_REG_FAN]; /* their duty cycle */
};
struct nct6683_sio_data {
@@ -1052,8 +1056,11 @@ static int nct6683_set_pwm(struct nct6683_data *data, int ch, u8 val)
err = nct6683_fan_ctrl_update(data, ch, true, val);
else
err = -EBUSY;
- if (!err)
+ if (!err) {
data->pwm[ch] = val;
+ data->pwm_manual |= BIT(ch);
+ data->pwm_cmd[ch] = val;
+ }
mutex_unlock(&data->update_lock);
return err;
@@ -1130,10 +1137,17 @@ store_pwm_enable(struct device *dev, struct device_attribute *attr,
manual = val == 1;
mutex_lock(&data->update_lock);
- if (nct6683_pwm_manual(data, index) != manual) {
- /* Keep the current duty cycle when switching to manual mode */
- cmd = manual ? nct6683_read(data, nct6683_pwm_reg(index)) : -1;
+ /* Keep the current duty cycle when switching to manual mode */
+ cmd = manual ? nct6683_read(data, nct6683_pwm_reg(index)) : -1;
+ if (nct6683_pwm_manual(data, index) != manual)
err = nct6683_fan_ctrl_update(data, index, manual, cmd);
+ if (!err) {
+ if (manual) {
+ data->pwm_manual |= BIT(index);
+ data->pwm_cmd[index] = cmd;
+ } else {
+ data->pwm_manual &= ~BIT(index);
+ }
}
mutex_unlock(&data->update_lock);
@@ -1549,11 +1563,28 @@ static int nct6683_suspend(struct device *dev)
static int nct6683_resume(struct device *dev)
{
struct nct6683_data *data = dev_get_drvdata(dev);
+ unsigned long manual;
+ int ch, err;
mutex_lock(&data->update_lock);
nct6683_write(data, NCT6683_HWM_CFG, data->hwm_cfg);
+ /* The BIOS may switch the fans back to automatic mode */
+ manual = data->pwm_manual;
+ for_each_set_bit(ch, &manual, NCT6683_NUM_REG_FAN) {
+ if (nct6683_pwm_manual(data, ch))
+ continue;
+ err = nct6683_fan_ctrl_update(data, ch, true, data->pwm_cmd[ch]);
+ if (err) {
+ dev_warn(dev, "Failed to restore manual mode of pwm%d: %d\n",
+ ch + 1, err);
+ /* The EC does not respond, skip the remaining channels */
+ if (err == -ETIMEDOUT)
+ break;
+ }
+ }
+
/* Force re-reading all values */
data->valid = false;
mutex_unlock(&data->update_lock);
base-commit: ec921ef8d81acf0cc41b3f3b6db54698e9a2917f
--
2.56.0
next reply other threads:[~2026-11-09 16:33 UTC|newest]
Thread overview: 3+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-11-09 16:33 Tomasz Bojanowski [this message]
2026-11-09 17:13 ` Guenter Roeck
2026-11-09 17:13 ` Guenter Roeck
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20261109163328.61787-1-tomasz@bojanowski.me \
--to=tomasz@bojanowski.me \
--cc=linux-hwmon@vger.kernel.org \
--cc=linux-kernel@vger.kernel.org \
--cc=linux@roeck-us.net \
--cc=someguy@effective-light.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox
all inboxes | Powered by JetHome®