* [PATCH 0/2] hwmon: (asus_rog_ryujin) Ryujin III pump control source
@ 2026-09-27 12:09 Zac Lanzon
2026-09-27 12:09 ` [PATCH 1/2] hwmon: (asus_rog_ryujin) Fix pump control source on the Ryujin III Zac Lanzon
2026-09-27 12:09 ` [PATCH 2/2] hwmon: (asus_rog_ryujin) Add pwm1_enable for the Ryujin III pump Zac Lanzon
0 siblings, 2 replies; 3+ messages in thread
From: Zac Lanzon @ 2026-09-27 12:09 UTC (permalink / raw)
To: linux-hwmon
Cc: Guenter Roeck, Aleksa Savic, Edward Blair, Jonathan Corbet,
Shuah Khan, Randy Dunlap, linux-doc, linux-kernel, Zac Lanzon
The RYUJIN III pump either runs at the duty set over USB or is handed
to its internal controller, which follows the PWM of the motherboard
header its 4-pin cable is plugged into. Byte 2 of the set cooler duty
command selects which. The driver sends 1 there with every duty write,
so a pwm2 write takes the pump away from its internal controller, and
after power-on a pwm1 write of the reported 40% leaves the pump at full
speed. Patch 1 fixes both. Patch 2 adds pwm1_enable to switch between
the two and to report which one is in control.
Patch 1 fixes a user-visible bug and applies to v7.3-rc4 as well as to
hwmon-next. On v7.3-rc4 the driver does not bind the base RYUJIN III
360, whose ID is only in hwmon-next, so there patch 1 changes behavior
for the EXTREME, EVA and WHITE EDITION, none of which I could test.
Edward Blair's analysis of the WHITE EDITION firmware shows the same
pump control source. Patch 2 is a new feature for hwmon-next.
Tested on a RYUJIN III 360, firmware AURJ2-S750-0108, with out-of-tree
builds of drivers/hwmon/asus_rog_ryujin.c on 7.0.0-34-generic:
hwmon-next 9edd5ab2656 as is, with patch 1, and with both patches. The
pump cable is on AIO_PUMP (nct6799 pwm7, set by hand for the tests); it
was on CPU_FAN at the time of the first comments in the linked issue.
Pump rpm at header 50% / 80%, then the reported source:
before patch 1 patches 1-2
a on header, pwm2 = 40% 1710/1710 1 2100/3060 0 2145/3090 0
b on header, pwm1 = 40% 1725/1740 1 1740/1740 1 1740/1740 1
c pwm1 = 60%, then 40% 2460, 1740 2460, 1740 2475, 1740
d then pwm2 = 40% 1740/1740 1 1740/1740 1 1740/1740 1
e1 reload with the pump on USB 1740/1740 1 1740/1740 1 1740/1740 1
then pwm2, pwm1 = 40% 1740, 1740 1755, 1740 1740, 1755
e2 reload with the pump on header 2160/3090 0 2160/3090 0 2160/3105 0
then pwm2 = 40% 1740/1740 1 2130/3090 0 2130/3090 0
then pwm1 = 60% 2475 1 2460 1 2475 1
f pwm1_enable = 2 2130/3090 0
pwm1_enable = 1 1740/1740 1
pwm1_enable = 0, 3 -EINVAL
"On header" is the state after EC 1A 00 28 1E, which leaves the pump on
its internal controller. The internal fan followed pwm2 in every column
(40%: 1545 to 1590 rpm).
Written back to back, 2 runs each, final state:
pwm1 = 60%, pwm2 = 40% (patch 1) source 1, pump 60%, fan 40%
pwm1 = 40%, pwm1 = 60% (patch 1) source 1, pump 60%
pwm1_enable = 2, pwm1 = 60% source 1, pump 60%
pwm1_enable = 1, 2, 1 source 1
pwm1 = 60%, pwm1_enable = 2 source 0
The failure in the linked issue needs the power-on state, which a USB
reset does not bring back, so it was measured from power-on (PSU
switched off), once per column, with the header at 100%. Source,
byte 9 of the status report (the duty the pump runs at) and pump rpm:
before patch 1 patches 1-2
no write yet 0 100 3570 0 100 3570 0 100 3570
pwm2 = 40% 1 100 3570 0 100 3570 0 100 3570
pwm1 = 40% (reported) 1 100 3570 1 40 1710 1 40 1710
pwm1 = 60% 1 60 2430
pwm1 = 40% 1 40 1725
pwm1_enable = 2 0 100 3585
The two writes that patch 1 sends for a pwm1 write of the reported duty
reached the requested duty in all 19 trials, 9 with raw commands and 10
through the driver.
The commit in the Fixes: tag, cd258aca0dc4, first appeared in v7.3-rc1.
Zac Lanzon (2):
hwmon: (asus_rog_ryujin) Fix pump control source on the Ryujin III
hwmon: (asus_rog_ryujin) Add pwm1_enable for the Ryujin III pump
Documentation/hwmon/asus_rog_ryujin.rst | 10 ++
drivers/hwmon/asus_rog_ryujin.c | 164 +++++++++++++++++++++++-
2 files changed, 171 insertions(+), 3 deletions(-)
base-commit: 9edd5ab2656f040d726b5388f49ceac10bb2944f
--
2.53.0
^ permalink raw reply [flat|nested] 3+ messages in thread
* [PATCH 1/2] hwmon: (asus_rog_ryujin) Fix pump control source on the Ryujin III
2026-09-27 12:09 [PATCH 0/2] hwmon: (asus_rog_ryujin) Ryujin III pump control source Zac Lanzon
@ 2026-09-27 12:09 ` Zac Lanzon
2026-09-27 12:09 ` [PATCH 2/2] hwmon: (asus_rog_ryujin) Add pwm1_enable for the Ryujin III pump Zac Lanzon
1 sibling, 0 replies; 3+ messages in thread
From: Zac Lanzon @ 2026-09-27 12:09 UTC (permalink / raw)
To: linux-hwmon
Cc: Guenter Roeck, Aleksa Savic, Edward Blair, Jonathan Corbet,
Shuah Khan, Randy Dunlap, linux-doc, linux-kernel, Zac Lanzon,
stable
The RYUJIN III pump has a 4-pin cable for a motherboard fan header in
addition to USB. Byte 2 of the set cooler duty command selects the pump
control source: 0 hands the pump to its internal controller, which
follows the PWM of the header its 4-pin cable is plugged into, and 1
makes it follow the pump duty in byte 3. Byte 3 of the cooler duty
report returns the current source.
The driver fills byte 2 with duty_channel, which is 1 for the RYUJIN
III, and a pwm2 write re-sends the reported pump duty with the new fan
duty. So writing only the internal fan duty takes the pump away from
its internal controller.
Measured on a RYUJIN III 360 (firmware AURJ2-S750-0108) with the pump
cable on AIO_PUMP and the header PWM set through nct6775, after loading
the driver with the pump on its internal controller (pump rpm):
header 50% header 80% source
no write yet 2160 3090 0
pwm2 = 40%, before this patch 1740 1740 1
pwm2 = 40%, with this patch 2130 3090 0
Send the reported source with pwm2 writes. The fan duty applies with
either source (40%: 1545 to 1590 rpm, 30%: 870 rpm). A pwm2 write sent
right after a pwm1 write that took the pump over, before the report
could show the change, left the pump on USB and applied its fan duty
(2 of 2 trials, and 7 of 7 with raw commands up to 0.9 s after).
pwm1 writes keep selecting source 1. The cooler reports the pump duty of
the last write, whichever source it selected, and 40% after power-on.
Byte 9 of the status report follows the duty the pump runs at. Measured
from power-on (PSU switched off) once per driver, with the header at
100%:
before this patch with this patch
source duty rpm source duty rpm
no write yet 0 100 3570 0 100 3570
pwm2 = 40% 1 100 3570 0 100 3570
pwm1 = 40%, the reported duty 1 100 3570 1 40 1710
pwm1 = 60% 1 60 2430
pwm1 = 40% 1 40 1725
So a pwm1 write of the reported duty did not reach the pump, while
other duties applied in one write, also when taking the pump from its
internal controller (60%: 2460 rpm). When a pwm1 write requests the
reported duty, write a duty 1% away first (below at 100%), then the
requested one. Sent back to back, the two writes left the pump at the
requested duty in all 19 trials, 9 with raw commands and 10 through
this driver.
Tested on the RYUJIN III 360 only. The EXTREME, EVA and WHITE EDITION
use the same rog_ryujin_iii_info and send the same command, and Edward
Blair's analysis of the WHITE EDITION firmware shows the same pump
control source. The RYUJIN II, whose duty_channel is 0, is unchanged.
Fixes: cd258aca0dc4 ("hwmon: (asus_rog_ryujin) Add ROG Ryujin III support")
Closes: https://github.com/aleksamagicka/asus_rog_ryujin-hwmon/issues/12
Suggested-by: Edward Blair <edward.blair@gmail.com>
Cc: stable@vger.kernel.org
Assisted-by: Claude:claude-opus-5-5
Signed-off-by: Zac Lanzon <lanzonza@gmail.com>
---
Documentation/hwmon/asus_rog_ryujin.rst | 6 +++
drivers/hwmon/asus_rog_ryujin.c | 54 ++++++++++++++++++++++++-
2 files changed, 59 insertions(+), 1 deletion(-)
diff --git a/Documentation/hwmon/asus_rog_ryujin.rst b/Documentation/hwmon/asus_rog_ryujin.rst
index 3ebab230e8c..fe072e94d87 100644
--- a/Documentation/hwmon/asus_rog_ryujin.rst
+++ b/Documentation/hwmon/asus_rog_ryujin.rst
@@ -34,6 +34,12 @@ Usage notes
As these are USB HIDs, the driver can be loaded automatically by the kernel and
supports hot swapping.
+The pump of the RYUJIN III also has a 4-pin cable for a motherboard fan header.
+The cooler either hands the pump to its internal controller, which follows the
+PWM of the header its 4-pin cable is plugged into, or runs it at the pump duty
+set over USB. Writing pwm1 selects the duty set over USB. Writing pwm2 sets
+only the internal fan duty and leaves the pump control as it is.
+
Sysfs entries
-------------
diff --git a/drivers/hwmon/asus_rog_ryujin.c b/drivers/hwmon/asus_rog_ryujin.c
index 5ced69e94ea..ba188e86793 100644
--- a/drivers/hwmon/asus_rog_ryujin.c
+++ b/drivers/hwmon/asus_rog_ryujin.c
@@ -50,6 +50,7 @@ static const struct rog_ryujin_device_info rog_ryujin_iii_info = {
#define MAX_REPORT_LENGTH 65
/* Cooler duty report offsets */
+#define RYUJIN_PUMP_SOURCE 3
#define RYUJIN_PUMP_DUTY 4
#define RYUJIN_INTERNAL_FAN_DUTY 5
@@ -70,10 +71,15 @@ static const u8 get_cooler_duty_cmd[] = { RYUJIN_CMD_PREFIX, 0x9A };
static const u8 get_controller_speed_cmd[] = { RYUJIN_CMD_PREFIX, 0xA0 };
static const u8 get_controller_duty_cmd[] = { RYUJIN_CMD_PREFIX, 0xA1 };
+#define RYUJIN_SET_COOLER_CHANNEL_OFFSET 2
#define RYUJIN_SET_COOLER_PUMP_DUTY_OFFSET 3
#define RYUJIN_SET_COOLER_FAN_DUTY_OFFSET 4
static const u8 set_cooler_duty_cmd[] = { RYUJIN_CMD_PREFIX, 0x1A, 0x00, 0x00, 0x00 };
+/* Pump control sources in the set cooler duty command and the cooler duty report */
+#define RYUJIN_SOURCE_HEADER 0
+#define RYUJIN_SOURCE_USB 1
+
#define RYUJIN_SET_CONTROLLER_FAN_DUTY_OFFSET 4
static const u8 set_controller_duty_cmd[] = { RYUJIN_CMD_PREFIX, 0x21, 0x00, 0x00, 0x00 };
@@ -117,6 +123,7 @@ struct rog_ryujin_data {
s32 temp_input[1];
u16 speed_input[6]; /* Pump, internal fan and four controller fan speeds in RPM */
u8 duty_input[3]; /* Pump, internal fan and controller fan duty in PWM */
+ u8 pump_source; /* Pump control source from the cooler duty report */
u8 *buffer;
unsigned long updated; /* jiffies */
@@ -306,6 +313,44 @@ static int rog_ryujin_read_string(struct device *dev, enum hwmon_sensor_types ty
return 0;
}
+/*
+ * On devices with a non-zero duty_channel (the RYUJIN III), the channel byte of
+ * the set cooler duty command selects the pump control source.
+ * RYUJIN_SOURCE_HEADER hands the pump to its internal controller, which follows
+ * the PWM of the header its 4-pin cable is plugged into. RYUJIN_SOURCE_USB
+ * makes the pump follow the pump duty in the command. The cooler duty report
+ * returns the current source.
+ *
+ * A fan duty write sends the reported source back, so that it leaves the pump
+ * control source as it is. A pump duty write selects USB. The cooler does not
+ * pass on a pump duty equal to the one it reports, and after power-on it
+ * reports 40% while a pump handed over at that duty runs at full speed. So
+ * when a pump duty write asks for the reported duty, write a duty 1% away
+ * first.
+ */
+static int rog_ryujin_select_pump_source(struct rog_ryujin_data *priv, u8 *set_cmd,
+ int channel)
+{
+ u8 duty = set_cmd[RYUJIN_SET_COOLER_PUMP_DUTY_OFFSET];
+ int ret;
+
+ if (channel == 1) {
+ set_cmd[RYUJIN_SET_COOLER_CHANNEL_OFFSET] = priv->pump_source;
+ return 0;
+ }
+
+ set_cmd[RYUJIN_SET_COOLER_CHANNEL_OFFSET] = RYUJIN_SOURCE_USB;
+
+ if (duty != rog_ryujin_pwm_to_percent(priv->duty_input[0]))
+ return 0;
+
+ set_cmd[RYUJIN_SET_COOLER_PUMP_DUTY_OFFSET] = duty < 100 ? duty + 1 : duty - 1;
+ ret = rog_ryujin_execute_cmd(priv, set_cmd, SET_CMD_LENGTH, &priv->cooler_duty_set);
+ set_cmd[RYUJIN_SET_COOLER_PUMP_DUTY_OFFSET] = duty;
+
+ return ret;
+}
+
static int rog_ryujin_write_fixed_duty(struct rog_ryujin_data *priv, int channel, int val)
{
u8 set_cmd[SET_CMD_LENGTH];
@@ -323,7 +368,7 @@ static int rog_ryujin_write_fixed_duty(struct rog_ryujin_data *priv, int channel
return ret;
memcpy(set_cmd, set_cooler_duty_cmd, SET_CMD_LENGTH);
- set_cmd[2] = priv->info->duty_channel;
+ set_cmd[RYUJIN_SET_COOLER_CHANNEL_OFFSET] = priv->info->duty_channel;
/* Cooler duties are set as 0-100% */
val = rog_ryujin_pwm_to_percent(val);
@@ -340,6 +385,12 @@ static int rog_ryujin_write_fixed_duty(struct rog_ryujin_data *priv, int channel
set_cmd[RYUJIN_SET_COOLER_FAN_DUTY_OFFSET] = val;
}
+ if (priv->info->duty_channel) {
+ ret = rog_ryujin_select_pump_source(priv, set_cmd, channel);
+ if (ret < 0)
+ return ret;
+ }
+
return rog_ryujin_execute_cmd(priv, set_cmd, SET_CMD_LENGTH, &priv->cooler_duty_set);
} else {
/*
@@ -482,6 +533,7 @@ static int rog_ryujin_raw_event(struct hid_device *hdev, struct hid_report *repo
goto unlock;
}
read_cooler_duty:
+ priv->pump_source = data[RYUJIN_PUMP_SOURCE];
priv->duty_input[0] = rog_ryujin_percent_to_pwm(data[RYUJIN_PUMP_DUTY]);
priv->duty_input[1] = rog_ryujin_percent_to_pwm(data[RYUJIN_INTERNAL_FAN_DUTY]);
--
2.53.0
^ permalink raw reply [flat|nested] 3+ messages in thread
* [PATCH 2/2] hwmon: (asus_rog_ryujin) Add pwm1_enable for the Ryujin III pump
2026-09-27 12:09 [PATCH 0/2] hwmon: (asus_rog_ryujin) Ryujin III pump control source Zac Lanzon
2026-09-27 12:09 ` [PATCH 1/2] hwmon: (asus_rog_ryujin) Fix pump control source on the Ryujin III Zac Lanzon
@ 2026-09-27 12:09 ` Zac Lanzon
1 sibling, 0 replies; 3+ messages in thread
From: Zac Lanzon @ 2026-09-27 12:09 UTC (permalink / raw)
To: linux-hwmon
Cc: Guenter Roeck, Aleksa Savic, Edward Blair, Jonathan Corbet,
Shuah Khan, Randy Dunlap, linux-doc, linux-kernel, Zac Lanzon
Once pwm1 has been written, nothing in the driver hands the RYUJIN III
pump back to its internal controller, and user space cannot tell which
source controls the pump.
Expose the pump control source as pwm1_enable, read from the cooler
duty report: 1 when the pump follows pwm1, 2 when it is handed to its
internal controller, which follows the PWM of the header its 4-pin
cable is plugged into. Writing 2 sends the reported duties with source
0, and writing 1 sends them with source 1, with the same neighbouring
duty write as pwm1. Other values return -EINVAL. Writing pwm1 still
selects source 1. The attribute is not created for the RYUJIN II.
With changes in both directions, source changes can follow each other
closely. The cooler ignored a source change sent 500 ms or less after
the previous one (7 of 7 trials) and applied one sent 1 s or more after
it (5 of 5). Its duty report showed a change within 1.06 s of the
command in every trial. So a write that changes the source now waits
until 1 s has passed since the previous change, then polls the duty
report every 100 ms until it shows the new source, and fails with
-ETIMEDOUT after 2 s. pump_source is only taken from the report. Other
writes are unaffected.
Measured on a RYUJIN III 360 (firmware AURJ2-S750-0108) with the pump
cable on AIO_PUMP and the header PWM set through nct6775 (pump rpm):
header 50% header 80% pwm1_enable
driver loaded, internal controller 2160 3105 2
pwm1 = 40% 1740 1740 1
pwm1_enable = 2 2130 3090 2
pwm1_enable = 1 1740 1740 1
pwm1_enable = 2, then pwm1 = 60% 2475 1
driver reloaded, pump on USB 1740 1740 1
Written back to back, each source change took 0.94 to 1.11 s, and the
sequences ended as requested in 2 of 2 runs each: pwm1_enable = 2 then
pwm1 = 60% with the pump at 60% on USB, pwm1_enable = 1, 2, 1 on USB,
and pwm1 = 60% then pwm1_enable = 2 on the internal controller.
From power-on, with the header at 100%, pwm1_enable read 2, read 1
after pwm1 = 40% (pump at 1710 rpm), and writing 2 handed the pump back
to its internal controller (3585 rpm).
Assisted-by: Claude:claude-opus-5-5
Signed-off-by: Zac Lanzon <lanzonza@gmail.com>
---
Documentation/hwmon/asus_rog_ryujin.rst | 8 +-
drivers/hwmon/asus_rog_ryujin.c | 112 +++++++++++++++++++++++-
2 files changed, 115 insertions(+), 5 deletions(-)
diff --git a/Documentation/hwmon/asus_rog_ryujin.rst b/Documentation/hwmon/asus_rog_ryujin.rst
index fe072e94d87..1fdf6eb9c84 100644
--- a/Documentation/hwmon/asus_rog_ryujin.rst
+++ b/Documentation/hwmon/asus_rog_ryujin.rst
@@ -37,8 +37,10 @@ supports hot swapping.
The pump of the RYUJIN III also has a 4-pin cable for a motherboard fan header.
The cooler either hands the pump to its internal controller, which follows the
PWM of the header its 4-pin cable is plugged into, or runs it at the pump duty
-set over USB. Writing pwm1 selects the duty set over USB. Writing pwm2 sets
-only the internal fan duty and leaves the pump control as it is.
+set over USB, as reported by pwm1_enable. Writing pwm1 selects the duty set
+over USB, and writing 2 to pwm1_enable hands the pump back to its internal
+controller. Writing pwm2 sets only the internal fan duty and leaves the pump
+control as it is.
Sysfs entries
-------------
@@ -52,6 +54,8 @@ fan5_input External (controller) fan 3 speed (in rpm, RYUJIN II only)
fan6_input External (controller) fan 4 speed (in rpm, RYUJIN II only)
temp1_input Coolant temperature (in millidegrees Celsius)
pwm1 Pump duty
+pwm1_enable Pump control: 1 = pwm1, 2 = internal controller following
+ the fan header PWM (RYUJIN III only)
pwm2 Internal fan duty
pwm3 External (controller) fan duty (RYUJIN II only)
=========== ==========================================================
diff --git a/drivers/hwmon/asus_rog_ryujin.c b/drivers/hwmon/asus_rog_ryujin.c
index ba188e86793..c89d854b2ae 100644
--- a/drivers/hwmon/asus_rog_ryujin.c
+++ b/drivers/hwmon/asus_rog_ryujin.c
@@ -6,6 +6,7 @@
*/
#include <linux/debugfs.h>
+#include <linux/delay.h>
#include <linux/hid.h>
#include <linux/hwmon.h>
#include <linux/jiffies.h>
@@ -80,6 +81,14 @@ static const u8 set_cooler_duty_cmd[] = { RYUJIN_CMD_PREFIX, 0x1A, 0x00, 0x00, 0
#define RYUJIN_SOURCE_HEADER 0
#define RYUJIN_SOURCE_USB 1
+/*
+ * The cooler ignores a pump source change sent 500 ms or less after the
+ * previous one and applies one sent 1 s or more after it. The cooler duty
+ * report shows a change within 1.1 s.
+ */
+#define RYUJIN_SOURCE_CHANGE_INTERVAL 1000 /* ms */
+#define RYUJIN_SOURCE_CHANGE_TIMEOUT 2000 /* ms */
+
#define RYUJIN_SET_CONTROLLER_FAN_DUTY_OFFSET 4
static const u8 set_controller_duty_cmd[] = { RYUJIN_CMD_PREFIX, 0x21, 0x00, 0x00, 0x00 };
@@ -124,6 +133,7 @@ struct rog_ryujin_data {
u16 speed_input[6]; /* Pump, internal fan and four controller fan speeds in RPM */
u8 duty_input[3]; /* Pump, internal fan and controller fan duty in PWM */
u8 pump_source; /* Pump control source from the cooler duty report */
+ unsigned long source_changed; /* jiffies */
u8 *buffer;
unsigned long updated; /* jiffies */
@@ -171,6 +181,10 @@ static umode_t rog_ryujin_is_visible(const void *data,
switch (attr) {
case hwmon_pwm_input:
return 0644;
+ case hwmon_pwm_enable:
+ if (channel == 0 && priv->info->duty_channel)
+ return 0644;
+ break;
default:
break;
}
@@ -285,6 +299,9 @@ static int rog_ryujin_read(struct device *dev, enum hwmon_sensor_types type,
case hwmon_pwm_input:
*val = priv->duty_input[channel];
break;
+ case hwmon_pwm_enable:
+ *val = priv->pump_source == RYUJIN_SOURCE_USB ? 1 : 2;
+ break;
default:
return -EOPNOTSUPP;
}
@@ -313,6 +330,46 @@ static int rog_ryujin_read_string(struct device *dev, enum hwmon_sensor_types ty
return 0;
}
+/*
+ * Send a set cooler duty command. If it changes the pump control source, first
+ * wait until RYUJIN_SOURCE_CHANGE_INTERVAL has passed since the last change,
+ * then wait for the cooler duty report to show the new source, so that success
+ * means the change took effect.
+ */
+static int rog_ryujin_write_cooler_duty(struct rog_ryujin_data *priv, const u8 *set_cmd)
+{
+ u8 source = set_cmd[RYUJIN_SET_COOLER_CHANNEL_OFFSET];
+ unsigned long now, next, timeout;
+ int ret;
+
+ if (!priv->info->duty_channel || source == priv->pump_source)
+ return rog_ryujin_execute_cmd(priv, set_cmd, SET_CMD_LENGTH,
+ &priv->cooler_duty_set);
+
+ now = jiffies;
+ next = priv->source_changed + msecs_to_jiffies(RYUJIN_SOURCE_CHANGE_INTERVAL);
+ if (time_before(now, next))
+ msleep(jiffies_to_msecs(next - now));
+
+ ret = rog_ryujin_execute_cmd(priv, set_cmd, SET_CMD_LENGTH, &priv->cooler_duty_set);
+ if (ret < 0)
+ return ret;
+
+ priv->source_changed = jiffies;
+ timeout = jiffies + msecs_to_jiffies(RYUJIN_SOURCE_CHANGE_TIMEOUT);
+ do {
+ msleep(100);
+ ret = rog_ryujin_execute_cmd(priv, get_cooler_duty_cmd, GET_CMD_LENGTH,
+ &priv->cooler_duty_received);
+ if (ret < 0)
+ return ret;
+ if (priv->pump_source == source)
+ return 0;
+ } while (time_before(jiffies, timeout));
+
+ return -ETIMEDOUT;
+}
+
/*
* On devices with a non-zero duty_channel (the RYUJIN III), the channel byte of
* the set cooler duty command selects the pump control source.
@@ -345,7 +402,7 @@ static int rog_ryujin_select_pump_source(struct rog_ryujin_data *priv, u8 *set_c
return 0;
set_cmd[RYUJIN_SET_COOLER_PUMP_DUTY_OFFSET] = duty < 100 ? duty + 1 : duty - 1;
- ret = rog_ryujin_execute_cmd(priv, set_cmd, SET_CMD_LENGTH, &priv->cooler_duty_set);
+ ret = rog_ryujin_write_cooler_duty(priv, set_cmd);
set_cmd[RYUJIN_SET_COOLER_PUMP_DUTY_OFFSET] = duty;
return ret;
@@ -391,7 +448,7 @@ static int rog_ryujin_write_fixed_duty(struct rog_ryujin_data *priv, int channel
return ret;
}
- return rog_ryujin_execute_cmd(priv, set_cmd, SET_CMD_LENGTH, &priv->cooler_duty_set);
+ return rog_ryujin_write_cooler_duty(priv, set_cmd);
} else {
/*
* Controller fan duty (channel == 2). No need to retrieve current
@@ -413,6 +470,52 @@ static int rog_ryujin_write_fixed_duty(struct rog_ryujin_data *priv, int channel
return 0;
}
+/*
+ * pwm1_enable: 1 hands the pump to pwm1, 2 hands it back to its internal
+ * controller. Both keep the reported duties.
+ */
+static int rog_ryujin_write_pwm_enable(struct rog_ryujin_data *priv, long val)
+{
+ u8 set_cmd[SET_CMD_LENGTH];
+ u8 source;
+ int ret;
+
+ switch (val) {
+ case 1:
+ source = RYUJIN_SOURCE_USB;
+ break;
+ case 2:
+ source = RYUJIN_SOURCE_HEADER;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ ret = rog_ryujin_execute_cmd(priv, get_cooler_duty_cmd, GET_CMD_LENGTH,
+ &priv->cooler_duty_received);
+ if (ret < 0)
+ return ret;
+
+ if (priv->pump_source == source)
+ return 0;
+
+ memcpy(set_cmd, set_cooler_duty_cmd, SET_CMD_LENGTH);
+ set_cmd[RYUJIN_SET_COOLER_CHANNEL_OFFSET] = source;
+ set_cmd[RYUJIN_SET_COOLER_PUMP_DUTY_OFFSET] =
+ rog_ryujin_pwm_to_percent(priv->duty_input[0]);
+ set_cmd[RYUJIN_SET_COOLER_FAN_DUTY_OFFSET] =
+ rog_ryujin_pwm_to_percent(priv->duty_input[1]);
+
+ if (source == RYUJIN_SOURCE_USB) {
+ /* As for a pwm1 write of the reported duty */
+ ret = rog_ryujin_select_pump_source(priv, set_cmd, 0);
+ if (ret < 0)
+ return ret;
+ }
+
+ return rog_ryujin_write_cooler_duty(priv, set_cmd);
+}
+
static int rog_ryujin_write(struct device *dev, enum hwmon_sensor_types type, u32 attr, int channel,
long val)
{
@@ -430,6 +533,8 @@ static int rog_ryujin_write(struct device *dev, enum hwmon_sensor_types type, u3
if (ret < 0)
return ret;
break;
+ case hwmon_pwm_enable:
+ return rog_ryujin_write_pwm_enable(priv, val);
default:
return -EOPNOTSUPP;
}
@@ -459,7 +564,7 @@ static const struct hwmon_channel_info *rog_ryujin_info[] = {
HWMON_F_INPUT | HWMON_F_LABEL,
HWMON_F_INPUT | HWMON_F_LABEL),
HWMON_CHANNEL_INFO(pwm,
- HWMON_PWM_INPUT,
+ HWMON_PWM_INPUT | HWMON_PWM_ENABLE,
HWMON_PWM_INPUT,
HWMON_PWM_INPUT),
NULL
@@ -596,6 +701,7 @@ static int rog_ryujin_probe(struct hid_device *hdev, const struct hid_device_id
* a special case there.
*/
priv->updated = jiffies - msecs_to_jiffies(STATUS_VALIDITY);
+ priv->source_changed = jiffies - msecs_to_jiffies(RYUJIN_SOURCE_CHANGE_INTERVAL);
ret = hid_parse(hdev);
if (ret) {
--
2.53.0
^ permalink raw reply [flat|nested] 3+ messages in thread
end of thread, other threads:[~2026-09-27 12:09 UTC | newest]
Thread overview: 3+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2026-09-27 12:09 [PATCH 0/2] hwmon: (asus_rog_ryujin) Ryujin III pump control source Zac Lanzon
2026-09-27 12:09 ` [PATCH 1/2] hwmon: (asus_rog_ryujin) Fix pump control source on the Ryujin III Zac Lanzon
2026-09-27 12:09 ` [PATCH 2/2] hwmon: (asus_rog_ryujin) Add pwm1_enable for the Ryujin III pump Zac Lanzon
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®