xref: /linux/drivers/hwmon/asus_rog_ryujin.c (revision 4b660dbd9ee2059850fd30e0df420ca7a38a1856)
1 // SPDX-License-Identifier: GPL-2.0+
2 /*
3  * hwmon driver for Asus ROG Ryujin II 360 AIO cooler.
4  *
5  * Copyright 2024 Aleksa Savic <savicaleksa83@gmail.com>
6  */
7 
8 #include <linux/debugfs.h>
9 #include <linux/hid.h>
10 #include <linux/hwmon.h>
11 #include <linux/jiffies.h>
12 #include <linux/module.h>
13 #include <linux/spinlock.h>
14 #include <asm/unaligned.h>
15 
16 #define DRIVER_NAME	"asus_rog_ryujin"
17 
18 #define USB_VENDOR_ID_ASUS_ROG		0x0b05
19 #define USB_PRODUCT_ID_RYUJIN_AIO	0x1988	/* ASUS ROG RYUJIN II 360 */
20 
21 #define STATUS_VALIDITY		1500	/* ms */
22 #define MAX_REPORT_LENGTH	65
23 
24 /* Cooler status report offsets */
25 #define RYUJIN_TEMP_SENSOR_1		3
26 #define RYUJIN_TEMP_SENSOR_2		4
27 #define RYUJIN_PUMP_SPEED		5
28 #define RYUJIN_INTERNAL_FAN_SPEED	7
29 
30 /* Cooler duty report offsets */
31 #define RYUJIN_PUMP_DUTY		4
32 #define RYUJIN_INTERNAL_FAN_DUTY	5
33 
34 /* Controller status (speeds) report offsets */
35 #define RYUJIN_CONTROLLER_SPEED_1	5
36 #define RYUJIN_CONTROLLER_SPEED_2	7
37 #define RYUJIN_CONTROLLER_SPEED_3	9
38 #define RYUJIN_CONTROLLER_SPEED_4	3
39 
40 /* Controller duty report offsets */
41 #define RYUJIN_CONTROLLER_DUTY		4
42 
43 /* Control commands and their inner offsets */
44 #define RYUJIN_CMD_PREFIX	0xEC
45 
46 static const u8 get_cooler_status_cmd[] = { RYUJIN_CMD_PREFIX, 0x99 };
47 static const u8 get_cooler_duty_cmd[] = { RYUJIN_CMD_PREFIX, 0x9A };
48 static const u8 get_controller_speed_cmd[] = { RYUJIN_CMD_PREFIX, 0xA0 };
49 static const u8 get_controller_duty_cmd[] = { RYUJIN_CMD_PREFIX, 0xA1 };
50 
51 #define RYUJIN_SET_COOLER_PUMP_DUTY_OFFSET	3
52 #define RYUJIN_SET_COOLER_FAN_DUTY_OFFSET	4
53 static const u8 set_cooler_duty_cmd[] = { RYUJIN_CMD_PREFIX, 0x1A, 0x00, 0x00, 0x00 };
54 
55 #define RYUJIN_SET_CONTROLLER_FAN_DUTY_OFFSET	4
56 static const u8 set_controller_duty_cmd[] = { RYUJIN_CMD_PREFIX, 0x21, 0x00, 0x00, 0x00 };
57 
58 /* Command lengths */
59 #define GET_CMD_LENGTH	2	/* Same length for all get commands */
60 #define SET_CMD_LENGTH	5	/* Same length for all set commands */
61 
62 /* Command response headers */
63 #define RYUJIN_GET_COOLER_STATUS_CMD_RESPONSE		0x19
64 #define RYUJIN_GET_COOLER_DUTY_CMD_RESPONSE		0x1A
65 #define RYUJIN_GET_CONTROLLER_SPEED_CMD_RESPONSE	0x20
66 #define RYUJIN_GET_CONTROLLER_DUTY_CMD_RESPONSE		0x21
67 
68 static const char *const rog_ryujin_temp_label[] = {
69 	"Coolant temp"
70 };
71 
72 static const char *const rog_ryujin_speed_label[] = {
73 	"Pump speed",
74 	"Internal fan speed",
75 	"Controller fan 1 speed",
76 	"Controller fan 2 speed",
77 	"Controller fan 3 speed",
78 	"Controller fan 4 speed",
79 };
80 
81 struct rog_ryujin_data {
82 	struct hid_device *hdev;
83 	struct device *hwmon_dev;
84 	/* For locking access to buffer */
85 	struct mutex buffer_lock;
86 	/* For queueing multiple readers */
87 	struct mutex status_report_request_mutex;
88 	/* For reinitializing the completions below */
89 	spinlock_t status_report_request_lock;
90 	struct completion cooler_status_received;
91 	struct completion controller_status_received;
92 	struct completion cooler_duty_received;
93 	struct completion controller_duty_received;
94 	struct completion cooler_duty_set;
95 	struct completion controller_duty_set;
96 
97 	/* Sensor data */
98 	s32 temp_input[1];
99 	u16 speed_input[6];	/* Pump, internal fan and four controller fan speeds in RPM */
100 	u8 duty_input[3];	/* Pump, internal fan and controller fan duty in PWM */
101 
102 	u8 *buffer;
103 	unsigned long updated;	/* jiffies */
104 };
105 
106 static int rog_ryujin_percent_to_pwm(u16 val)
107 {
108 	return DIV_ROUND_CLOSEST(val * 255, 100);
109 }
110 
111 static int rog_ryujin_pwm_to_percent(long val)
112 {
113 	return DIV_ROUND_CLOSEST(val * 100, 255);
114 }
115 
116 static umode_t rog_ryujin_is_visible(const void *data,
117 				     enum hwmon_sensor_types type, u32 attr, int channel)
118 {
119 	switch (type) {
120 	case hwmon_temp:
121 		switch (attr) {
122 		case hwmon_temp_label:
123 		case hwmon_temp_input:
124 			return 0444;
125 		default:
126 			break;
127 		}
128 		break;
129 	case hwmon_fan:
130 		switch (attr) {
131 		case hwmon_fan_label:
132 		case hwmon_fan_input:
133 			return 0444;
134 		default:
135 			break;
136 		}
137 		break;
138 	case hwmon_pwm:
139 		switch (attr) {
140 		case hwmon_pwm_input:
141 			return 0644;
142 		default:
143 			break;
144 		}
145 		break;
146 	default:
147 		break;
148 	}
149 
150 	return 0;
151 }
152 
153 /* Writes the command to the device with the rest of the report filled with zeroes */
154 static int rog_ryujin_write_expanded(struct rog_ryujin_data *priv, const u8 *cmd, int cmd_length)
155 {
156 	int ret;
157 
158 	mutex_lock(&priv->buffer_lock);
159 
160 	memcpy_and_pad(priv->buffer, MAX_REPORT_LENGTH, cmd, cmd_length, 0x00);
161 	ret = hid_hw_output_report(priv->hdev, priv->buffer, MAX_REPORT_LENGTH);
162 
163 	mutex_unlock(&priv->buffer_lock);
164 	return ret;
165 }
166 
167 /* Assumes priv->status_report_request_mutex is locked */
168 static int rog_ryujin_execute_cmd(struct rog_ryujin_data *priv, const u8 *cmd, int cmd_length,
169 				  struct completion *status_completion)
170 {
171 	int ret;
172 
173 	/*
174 	 * Disable raw event parsing for a moment to safely reinitialize the
175 	 * completion. Reinit is done because hidraw could have triggered
176 	 * the raw event parsing and marked the passed in completion as done.
177 	 */
178 	spin_lock_bh(&priv->status_report_request_lock);
179 	reinit_completion(status_completion);
180 	spin_unlock_bh(&priv->status_report_request_lock);
181 
182 	/* Send command for getting data */
183 	ret = rog_ryujin_write_expanded(priv, cmd, cmd_length);
184 	if (ret < 0)
185 		return ret;
186 
187 	ret = wait_for_completion_interruptible_timeout(status_completion,
188 							msecs_to_jiffies(STATUS_VALIDITY));
189 	if (ret == 0)
190 		return -ETIMEDOUT;
191 	else if (ret < 0)
192 		return ret;
193 
194 	return 0;
195 }
196 
197 static int rog_ryujin_get_status(struct rog_ryujin_data *priv)
198 {
199 	int ret = mutex_lock_interruptible(&priv->status_report_request_mutex);
200 
201 	if (ret < 0)
202 		return ret;
203 
204 	if (!time_after(jiffies, priv->updated + msecs_to_jiffies(STATUS_VALIDITY))) {
205 		/* Data is up to date */
206 		goto unlock_and_return;
207 	}
208 
209 	/* Retrieve cooler status */
210 	ret =
211 	    rog_ryujin_execute_cmd(priv, get_cooler_status_cmd, GET_CMD_LENGTH,
212 				   &priv->cooler_status_received);
213 	if (ret < 0)
214 		goto unlock_and_return;
215 
216 	/* Retrieve controller status (speeds) */
217 	ret =
218 	    rog_ryujin_execute_cmd(priv, get_controller_speed_cmd, GET_CMD_LENGTH,
219 				   &priv->controller_status_received);
220 	if (ret < 0)
221 		goto unlock_and_return;
222 
223 	/* Retrieve cooler duty */
224 	ret =
225 	    rog_ryujin_execute_cmd(priv, get_cooler_duty_cmd, GET_CMD_LENGTH,
226 				   &priv->cooler_duty_received);
227 	if (ret < 0)
228 		goto unlock_and_return;
229 
230 	/* Retrieve controller duty */
231 	ret =
232 	    rog_ryujin_execute_cmd(priv, get_controller_duty_cmd, GET_CMD_LENGTH,
233 				   &priv->controller_duty_received);
234 	if (ret < 0)
235 		goto unlock_and_return;
236 
237 	priv->updated = jiffies;
238 
239 unlock_and_return:
240 	mutex_unlock(&priv->status_report_request_mutex);
241 	if (ret < 0)
242 		return ret;
243 
244 	return 0;
245 }
246 
247 static int rog_ryujin_read(struct device *dev, enum hwmon_sensor_types type,
248 			   u32 attr, int channel, long *val)
249 {
250 	struct rog_ryujin_data *priv = dev_get_drvdata(dev);
251 	int ret = rog_ryujin_get_status(priv);
252 
253 	if (ret < 0)
254 		return ret;
255 
256 	switch (type) {
257 	case hwmon_temp:
258 		*val = priv->temp_input[channel];
259 		break;
260 	case hwmon_fan:
261 		*val = priv->speed_input[channel];
262 		break;
263 	case hwmon_pwm:
264 		switch (attr) {
265 		case hwmon_pwm_input:
266 			*val = priv->duty_input[channel];
267 			break;
268 		default:
269 			return -EOPNOTSUPP;
270 		}
271 		break;
272 	default:
273 		return -EOPNOTSUPP;	/* unreachable */
274 	}
275 
276 	return 0;
277 }
278 
279 static int rog_ryujin_read_string(struct device *dev, enum hwmon_sensor_types type,
280 				  u32 attr, int channel, const char **str)
281 {
282 	switch (type) {
283 	case hwmon_temp:
284 		*str = rog_ryujin_temp_label[channel];
285 		break;
286 	case hwmon_fan:
287 		*str = rog_ryujin_speed_label[channel];
288 		break;
289 	default:
290 		return -EOPNOTSUPP;	/* unreachable */
291 	}
292 
293 	return 0;
294 }
295 
296 static int rog_ryujin_write_fixed_duty(struct rog_ryujin_data *priv, int channel, int val)
297 {
298 	u8 set_cmd[SET_CMD_LENGTH];
299 	int ret;
300 
301 	if (channel < 2) {
302 		/*
303 		 * Retrieve cooler duty since both pump and internal fan are set
304 		 * together, then write back with one of them modified.
305 		 */
306 		ret = mutex_lock_interruptible(&priv->status_report_request_mutex);
307 		if (ret < 0)
308 			return ret;
309 		ret =
310 		    rog_ryujin_execute_cmd(priv, get_cooler_duty_cmd, GET_CMD_LENGTH,
311 					   &priv->cooler_duty_received);
312 		if (ret < 0)
313 			goto unlock_and_return;
314 
315 		memcpy(set_cmd, set_cooler_duty_cmd, SET_CMD_LENGTH);
316 
317 		/* Cooler duties are set as 0-100% */
318 		val = rog_ryujin_pwm_to_percent(val);
319 
320 		if (channel == 0) {
321 			/* Cooler pump duty */
322 			set_cmd[RYUJIN_SET_COOLER_PUMP_DUTY_OFFSET] = val;
323 			set_cmd[RYUJIN_SET_COOLER_FAN_DUTY_OFFSET] =
324 			    rog_ryujin_pwm_to_percent(priv->duty_input[1]);
325 		} else if (channel == 1) {
326 			/* Cooler internal fan duty */
327 			set_cmd[RYUJIN_SET_COOLER_PUMP_DUTY_OFFSET] =
328 			    rog_ryujin_pwm_to_percent(priv->duty_input[0]);
329 			set_cmd[RYUJIN_SET_COOLER_FAN_DUTY_OFFSET] = val;
330 		}
331 
332 		ret = rog_ryujin_execute_cmd(priv, set_cmd, SET_CMD_LENGTH, &priv->cooler_duty_set);
333 unlock_and_return:
334 		mutex_unlock(&priv->status_report_request_mutex);
335 		if (ret < 0)
336 			return ret;
337 	} else {
338 		/*
339 		 * Controller fan duty (channel == 2). No need to retrieve current
340 		 * duty, so just send the command.
341 		 */
342 		memcpy(set_cmd, set_controller_duty_cmd, SET_CMD_LENGTH);
343 		set_cmd[RYUJIN_SET_CONTROLLER_FAN_DUTY_OFFSET] = val;
344 
345 		ret =
346 		    rog_ryujin_execute_cmd(priv, set_cmd, SET_CMD_LENGTH,
347 					   &priv->controller_duty_set);
348 		if (ret < 0)
349 			return ret;
350 	}
351 
352 	/* Lock onto this value until next refresh cycle */
353 	priv->duty_input[channel] = val;
354 
355 	return 0;
356 }
357 
358 static int rog_ryujin_write(struct device *dev, enum hwmon_sensor_types type, u32 attr, int channel,
359 			    long val)
360 {
361 	struct rog_ryujin_data *priv = dev_get_drvdata(dev);
362 	int ret;
363 
364 	switch (type) {
365 	case hwmon_pwm:
366 		switch (attr) {
367 		case hwmon_pwm_input:
368 			if (val < 0 || val > 255)
369 				return -EINVAL;
370 
371 			ret = rog_ryujin_write_fixed_duty(priv, channel, val);
372 			if (ret < 0)
373 				return ret;
374 			break;
375 		default:
376 			return -EOPNOTSUPP;
377 		}
378 		break;
379 	default:
380 		return -EOPNOTSUPP;
381 	}
382 
383 	return 0;
384 }
385 
386 static const struct hwmon_ops rog_ryujin_hwmon_ops = {
387 	.is_visible = rog_ryujin_is_visible,
388 	.read = rog_ryujin_read,
389 	.read_string = rog_ryujin_read_string,
390 	.write = rog_ryujin_write
391 };
392 
393 static const struct hwmon_channel_info *rog_ryujin_info[] = {
394 	HWMON_CHANNEL_INFO(temp,
395 			   HWMON_T_INPUT | HWMON_T_LABEL),
396 	HWMON_CHANNEL_INFO(fan,
397 			   HWMON_F_INPUT | HWMON_F_LABEL,
398 			   HWMON_F_INPUT | HWMON_F_LABEL,
399 			   HWMON_F_INPUT | HWMON_F_LABEL,
400 			   HWMON_F_INPUT | HWMON_F_LABEL,
401 			   HWMON_F_INPUT | HWMON_F_LABEL,
402 			   HWMON_F_INPUT | HWMON_F_LABEL),
403 	HWMON_CHANNEL_INFO(pwm,
404 			   HWMON_PWM_INPUT,
405 			   HWMON_PWM_INPUT,
406 			   HWMON_PWM_INPUT),
407 	NULL
408 };
409 
410 static const struct hwmon_chip_info rog_ryujin_chip_info = {
411 	.ops = &rog_ryujin_hwmon_ops,
412 	.info = rog_ryujin_info,
413 };
414 
415 static int rog_ryujin_raw_event(struct hid_device *hdev, struct hid_report *report, u8 *data,
416 				int size)
417 {
418 	struct rog_ryujin_data *priv = hid_get_drvdata(hdev);
419 
420 	if (data[0] != RYUJIN_CMD_PREFIX)
421 		return 0;
422 
423 	if (data[1] == RYUJIN_GET_COOLER_STATUS_CMD_RESPONSE) {
424 		/* Received coolant temp and speeds of pump and internal fan */
425 		priv->temp_input[0] =
426 		    data[RYUJIN_TEMP_SENSOR_1] * 1000 + data[RYUJIN_TEMP_SENSOR_2] * 100;
427 		priv->speed_input[0] = get_unaligned_le16(data + RYUJIN_PUMP_SPEED);
428 		priv->speed_input[1] = get_unaligned_le16(data + RYUJIN_INTERNAL_FAN_SPEED);
429 
430 		if (!completion_done(&priv->cooler_status_received))
431 			complete_all(&priv->cooler_status_received);
432 	} else if (data[1] == RYUJIN_GET_CONTROLLER_SPEED_CMD_RESPONSE) {
433 		/* Received speeds of four fans attached to the controller */
434 		priv->speed_input[2] = get_unaligned_le16(data + RYUJIN_CONTROLLER_SPEED_1);
435 		priv->speed_input[3] = get_unaligned_le16(data + RYUJIN_CONTROLLER_SPEED_2);
436 		priv->speed_input[4] = get_unaligned_le16(data + RYUJIN_CONTROLLER_SPEED_3);
437 		priv->speed_input[5] = get_unaligned_le16(data + RYUJIN_CONTROLLER_SPEED_4);
438 
439 		if (!completion_done(&priv->controller_status_received))
440 			complete_all(&priv->controller_status_received);
441 	} else if (data[1] == RYUJIN_GET_COOLER_DUTY_CMD_RESPONSE) {
442 		/* Received report for pump and internal fan duties (in %) */
443 		if (data[RYUJIN_PUMP_DUTY] == 0 && data[RYUJIN_INTERNAL_FAN_DUTY] == 0) {
444 			/*
445 			 * We received a report with zeroes for duty in both places.
446 			 * The device returns this as a confirmation that setting values
447 			 * is successful. If we initiated a write, mark it as complete.
448 			 */
449 			if (!completion_done(&priv->cooler_duty_set))
450 				complete_all(&priv->cooler_duty_set);
451 			else if (!completion_done(&priv->cooler_duty_received))
452 				/*
453 				 * We didn't initiate a write, but received both zeroes.
454 				 * This means that either both duties are actually zero,
455 				 * or that we received a success report caused by userspace.
456 				 * We're expecting a report, so parse it.
457 				 */
458 				goto read_cooler_duty;
459 			return 0;
460 		}
461 read_cooler_duty:
462 		priv->duty_input[0] = rog_ryujin_percent_to_pwm(data[RYUJIN_PUMP_DUTY]);
463 		priv->duty_input[1] = rog_ryujin_percent_to_pwm(data[RYUJIN_INTERNAL_FAN_DUTY]);
464 
465 		if (!completion_done(&priv->cooler_duty_received))
466 			complete_all(&priv->cooler_duty_received);
467 	} else if (data[1] == RYUJIN_GET_CONTROLLER_DUTY_CMD_RESPONSE) {
468 		/* Received report for controller duty for fans (in PWM) */
469 		if (data[RYUJIN_CONTROLLER_DUTY] == 0) {
470 			/*
471 			 * We received a report with a zero for duty. The device returns this as
472 			 * a confirmation that setting the controller duty value was successful.
473 			 * If we initiated a write, mark it as complete.
474 			 */
475 			if (!completion_done(&priv->controller_duty_set))
476 				complete_all(&priv->controller_duty_set);
477 			else if (!completion_done(&priv->controller_duty_received))
478 				/*
479 				 * We didn't initiate a write, but received a zero for duty.
480 				 * This means that either the duty is actually zero, or that
481 				 * we received a success report caused by userspace.
482 				 * We're expecting a report, so parse it.
483 				 */
484 				goto read_controller_duty;
485 			return 0;
486 		}
487 read_controller_duty:
488 		priv->duty_input[2] = data[RYUJIN_CONTROLLER_DUTY];
489 
490 		if (!completion_done(&priv->controller_duty_received))
491 			complete_all(&priv->controller_duty_received);
492 	}
493 
494 	return 0;
495 }
496 
497 static int rog_ryujin_probe(struct hid_device *hdev, const struct hid_device_id *id)
498 {
499 	struct rog_ryujin_data *priv;
500 	int ret;
501 
502 	priv = devm_kzalloc(&hdev->dev, sizeof(*priv), GFP_KERNEL);
503 	if (!priv)
504 		return -ENOMEM;
505 
506 	priv->hdev = hdev;
507 	hid_set_drvdata(hdev, priv);
508 
509 	/*
510 	 * Initialize priv->updated to STATUS_VALIDITY seconds in the past, making
511 	 * the initial empty data invalid for rog_ryujin_read() without the need for
512 	 * a special case there.
513 	 */
514 	priv->updated = jiffies - msecs_to_jiffies(STATUS_VALIDITY);
515 
516 	ret = hid_parse(hdev);
517 	if (ret) {
518 		hid_err(hdev, "hid parse failed with %d\n", ret);
519 		return ret;
520 	}
521 
522 	/* Enable hidraw so existing user-space tools can continue to work */
523 	ret = hid_hw_start(hdev, HID_CONNECT_HIDRAW);
524 	if (ret) {
525 		hid_err(hdev, "hid hw start failed with %d\n", ret);
526 		return ret;
527 	}
528 
529 	ret = hid_hw_open(hdev);
530 	if (ret) {
531 		hid_err(hdev, "hid hw open failed with %d\n", ret);
532 		goto fail_and_stop;
533 	}
534 
535 	priv->buffer = devm_kzalloc(&hdev->dev, MAX_REPORT_LENGTH, GFP_KERNEL);
536 	if (!priv->buffer) {
537 		ret = -ENOMEM;
538 		goto fail_and_close;
539 	}
540 
541 	mutex_init(&priv->status_report_request_mutex);
542 	mutex_init(&priv->buffer_lock);
543 	spin_lock_init(&priv->status_report_request_lock);
544 	init_completion(&priv->cooler_status_received);
545 	init_completion(&priv->controller_status_received);
546 	init_completion(&priv->cooler_duty_received);
547 	init_completion(&priv->controller_duty_received);
548 	init_completion(&priv->cooler_duty_set);
549 	init_completion(&priv->controller_duty_set);
550 
551 	priv->hwmon_dev = hwmon_device_register_with_info(&hdev->dev, "rog_ryujin",
552 							  priv, &rog_ryujin_chip_info, NULL);
553 	if (IS_ERR(priv->hwmon_dev)) {
554 		ret = PTR_ERR(priv->hwmon_dev);
555 		hid_err(hdev, "hwmon registration failed with %d\n", ret);
556 		goto fail_and_close;
557 	}
558 
559 	return 0;
560 
561 fail_and_close:
562 	hid_hw_close(hdev);
563 fail_and_stop:
564 	hid_hw_stop(hdev);
565 	return ret;
566 }
567 
568 static void rog_ryujin_remove(struct hid_device *hdev)
569 {
570 	struct rog_ryujin_data *priv = hid_get_drvdata(hdev);
571 
572 	hwmon_device_unregister(priv->hwmon_dev);
573 
574 	hid_hw_close(hdev);
575 	hid_hw_stop(hdev);
576 }
577 
578 static const struct hid_device_id rog_ryujin_table[] = {
579 	{ HID_USB_DEVICE(USB_VENDOR_ID_ASUS_ROG, USB_PRODUCT_ID_RYUJIN_AIO) },
580 	{ }
581 };
582 
583 MODULE_DEVICE_TABLE(hid, rog_ryujin_table);
584 
585 static struct hid_driver rog_ryujin_driver = {
586 	.name = "rog_ryujin",
587 	.id_table = rog_ryujin_table,
588 	.probe = rog_ryujin_probe,
589 	.remove = rog_ryujin_remove,
590 	.raw_event = rog_ryujin_raw_event,
591 };
592 
593 static int __init rog_ryujin_init(void)
594 {
595 	return hid_register_driver(&rog_ryujin_driver);
596 }
597 
598 static void __exit rog_ryujin_exit(void)
599 {
600 	hid_unregister_driver(&rog_ryujin_driver);
601 }
602 
603 /* When compiled into the kernel, initialize after the HID bus */
604 late_initcall(rog_ryujin_init);
605 module_exit(rog_ryujin_exit);
606 
607 MODULE_LICENSE("GPL");
608 MODULE_AUTHOR("Aleksa Savic <savicaleksa83@gmail.com>");
609 MODULE_DESCRIPTION("Hwmon driver for Asus ROG Ryujin II 360 AIO cooler");
610