hwmon: (nzxt-kraken3) Add support for NZXT Kraken 2023 (standard and Elite) models
authorAleksa Savic <savicaleksa83@gmail.com>
Sun, 28 Apr 2024 10:48:11 +0000 (12:48 +0200)
committerGuenter Roeck <linux@roeck-us.net>
Tue, 30 Apr 2024 17:32:14 +0000 (10:32 -0700)
Add support for NZXT Kraken 2023 (standard) and NZXT Kraken 2023 Elite
all-in-one CPU coolers. These models communicate identically to the NZXT
Kraken Z-series (Z53 code paths) in all cases except when writing the
fan curve, where setting additional bits in the report is needed.

Reviewed-by: Jonas Malaco <jonas@protocubo.io>
Signed-off-by: Aleksa Savic <savicaleksa83@gmail.com>
Link: https://lore.kernel.org/r/20240428104812.14037-3-savicaleksa83@gmail.com
Signed-off-by: Guenter Roeck <linux@roeck-us.net>
Documentation/hwmon/nzxt-kraken3.rst
drivers/hwmon/nzxt-kraken3.c

index 90fd9dec15ff2255acd13855ff9f83e7c5e9f33c..57fe99d233011ad4c624c3cdb56a55ce23335a77 100644 (file)
@@ -11,17 +11,20 @@ Supported devices:
 * NZXT Kraken Z53
 * NZXT Kraken Z63
 * NZXT Kraken Z73
+* NZXT Kraken 2023
+* NZXT Kraken 2023 Elite
 
 Author: Jonas Malaco, Aleksa Savic
 
 Description
 -----------
 
-This driver enables hardware monitoring support for NZXT Kraken X53/X63/X73 and
-Z53/Z63/Z73 all-in-one CPU liquid coolers. All models expose liquid temperature
-and pump speed (in RPM), as well as PWM control (either as a fixed value
-or through a temp-PWM curve). The Z-series models additionally expose the speed
-and duty of an optionally connected fan, with the same PWM control capabilities.
+This driver enables hardware monitoring support for NZXT Kraken X53/X63/X73,
+Z53/Z63/Z73 and Kraken 2023 (standard and Elite) all-in-one CPU liquid coolers.
+All models expose liquid temperature and pump speed (in RPM), as well as PWM
+control (either as a fixed value or through a temp-PWM curve). The Z-series and
+Kraken 2023 models additionally expose the speed and duty of an optionally connected
+fan, with the same PWM control capabilities.
 
 Pump and fan duty control mode can be set through pwm[1-2]_enable, where 1 is
 for the manual control mode and 2 is for the liquid temp to PWM curve mode.
@@ -39,9 +42,9 @@ The devices can report if they are faulty. The driver supports that situation
 and will issue a warning. This can also happen when the USB cable is connected,
 but SATA power is not.
 
-The addressable RGB LEDs and LCD screen (only on Z-series models) are not
-supported in this driver, but can be controlled through existing userspace tools,
-such as `liquidctl`_.
+The addressable RGB LEDs and LCD screen (only on Z-series and Kraken 2023 models)
+are not supported in this driver, but can be controlled through existing userspace
+tools, such as `liquidctl`_.
 
 .. _liquidctl: https://github.com/liquidctl/liquidctl
 
index 571087e3fd3eff4b68f0d5f03bf803d38f28a785..0b3f04c740b0d5002296d84f8141470bae2b4cd0 100644 (file)
@@ -1,8 +1,8 @@
 // SPDX-License-Identifier: GPL-2.0+
 /*
- * hwmon driver for NZXT Kraken X53/X63/X73 and Z53/Z63/Z73 all in one coolers.
- * X53 and Z53 in code refer to all models in their respective series (shortened
- * for brevity).
+ * hwmon driver for NZXT Kraken X53/X63/X73, Z53/Z63/Z73 and 2023/2023 Elite all in one coolers.
+ * X53 and Z53 in code refer to all models in their respective series (shortened for brevity).
+ * 2023 models use the Z53 code paths.
  *
  * Copyright 2021  Jonas Malaco <jonas@protocubo.io>
  * Copyright 2022  Aleksa Savic <savicaleksa83@gmail.com>
 #define USB_PRODUCT_ID_X53             0x2007
 #define USB_PRODUCT_ID_X53_SECOND      0x2014
 #define USB_PRODUCT_ID_Z53             0x3008
+#define USB_PRODUCT_ID_KRAKEN2023      0x300E
+#define USB_PRODUCT_ID_KRAKEN2023_ELITE        0x300C
 
-enum kinds { X53, Z53 } __packed;
+enum kinds { X53, Z53, KRAKEN2023 } __packed;
 enum pwm_enable { off, manual, curve } __packed;
 
 #define DRIVER_NAME            "nzxt_kraken3"
@@ -136,6 +138,7 @@ static umode_t kraken3_is_visible(const void *data, enum hwmon_sensor_types type
                                return 0444;
                        break;
                case Z53:
+               case KRAKEN2023:
                        /* Pump and fan */
                        if (channel < 2)
                                return 0444;
@@ -155,6 +158,7 @@ static umode_t kraken3_is_visible(const void *data, enum hwmon_sensor_types type
                                        return 0644;
                                break;
                        case Z53:
+                       case KRAKEN2023:
                                /* Pump and fan */
                                if (channel < 2)
                                        return 0644;
@@ -242,6 +246,7 @@ static int kraken3_read_x53(struct kraken3_data *priv)
        return 0;
 }
 
+/* Covers Z53 and KRAKEN2023 device kinds */
 static int kraken3_read_z53(struct kraken3_data *priv)
 {
        int ret = mutex_lock_interruptible(&priv->z53_status_request_lock);
@@ -355,6 +360,13 @@ static int kraken3_write_curve(struct kraken3_data *priv, u8 *curve_array, int c
        /* Set the correct ID for writing pump/fan duty (0x01 or 0x02, respectively) */
        fixed_duty_cmd[SET_DUTY_ID_OFFSET] = channel + 1;
 
+       if (priv->kind == KRAKEN2023) {
+               /* These require 1s in the next one or two slots after SET_DUTY_ID_OFFSET */
+               fixed_duty_cmd[SET_DUTY_ID_OFFSET + 1] = 1;
+               if (channel == 1) /* Fan */
+                       fixed_duty_cmd[SET_DUTY_ID_OFFSET + 2] = 1;
+       }
+
        /* Copy curve to command */
        memcpy(fixed_duty_cmd + SET_CURVE_DUTY_CMD_HEADER_LENGTH, curve_array, CUSTOM_CURVE_POINTS);
 
@@ -502,8 +514,8 @@ static umode_t kraken3_curve_props_are_visible(struct kobject *kobj, struct attr
        struct device *dev = kobj_to_dev(kobj);
        struct kraken3_data *priv = dev_get_drvdata(dev);
 
-       /* Only Z53 has the fan curve */
-       if (index >= CUSTOM_CURVE_POINTS && priv->kind != Z53)
+       /* X53 does not have a fan */
+       if (index >= CUSTOM_CURVE_POINTS && priv->kind == X53)
                return 0;
 
        return attr->mode;
@@ -769,8 +781,8 @@ static int kraken3_raw_event(struct hid_device *hdev, struct hid_report *report,
        if (priv->kind == X53 && !completion_done(&priv->status_report_processed)) {
                /* Mark first X-series device report as received */
                complete_all(&priv->status_report_processed);
-       } else if (priv->kind == Z53) {
-               /* Additional readings for Z53 */
+       } else if (priv->kind == Z53 || priv->kind == KRAKEN2023) {
+               /* Additional readings for Z53 and KRAKEN2023 */
                priv->fan_input[1] = get_unaligned_le16(data + Z53_FAN_SPEED_OFFSET);
                priv->channel_info[1].reported_duty =
                    kraken3_percent_to_pwm(data[Z53_FAN_DUTY_OFFSET]);
@@ -907,6 +919,14 @@ static int kraken3_probe(struct hid_device *hdev, const struct hid_device_id *id
                priv->kind = Z53;
                device_name = "z53";
                break;
+       case USB_PRODUCT_ID_KRAKEN2023:
+               priv->kind = KRAKEN2023;
+               device_name = "kraken2023";
+               break;
+       case USB_PRODUCT_ID_KRAKEN2023_ELITE:
+               priv->kind = KRAKEN2023;
+               device_name = "kraken2023elite";
+               break;
        default:
                break;
        }
@@ -969,6 +989,8 @@ static const struct hid_device_id kraken3_table[] = {
        { HID_USB_DEVICE(USB_VENDOR_ID_NZXT, USB_PRODUCT_ID_X53) },
        { HID_USB_DEVICE(USB_VENDOR_ID_NZXT, USB_PRODUCT_ID_X53_SECOND) },
        { HID_USB_DEVICE(USB_VENDOR_ID_NZXT, USB_PRODUCT_ID_Z53) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_NZXT, USB_PRODUCT_ID_KRAKEN2023) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_NZXT, USB_PRODUCT_ID_KRAKEN2023_ELITE) },
        { }
 };