Re: [PATCH] hwmon: (aquacomputer_d5next) Add support for Aquacomputer Aquastream Ultimate

From: Guenter Roeck
Date: Fri Feb 03 2023 - 10:27:38 EST


On Fri, Feb 03, 2023 at 01:03:24PM +0100, Aleksa Savic wrote:
> Extend aquacomputer_d5next driver to expose various hardware sensors of the
> Aquacomputer Aquastream Ultimate watercooling pump, which communicates
> through a proprietary USB HID protocol.
>
> Coolant temp and external temp sensor readings are available, along with
> speed, power, voltage and current of both the pump and optionally connected
> fan. It also exposes pressure and flow speed readings.
>
> Additionally, serial number and firmware version are exposed through
> debugfs.
>
> Tested by a user on Github [1].
>
> [1] https://github.com/aleksamagicka/aquacomputer_d5next-hwmon/issues/50
>
> Signed-off-by: Aleksa Savic <savicaleksa83@xxxxxxxxx>

Applied.

Thanks,
Guenter

> ---
> Documentation/hwmon/aquacomputer_d5next.rst | 5 +
> drivers/hwmon/aquacomputer_d5next.c | 126 +++++++++++++++++++-
> 2 files changed, 125 insertions(+), 6 deletions(-)
>
> diff --git a/Documentation/hwmon/aquacomputer_d5next.rst b/Documentation/hwmon/aquacomputer_d5next.rst
> index 527bcd3edda9..7d0d015b1a52 100644
> --- a/Documentation/hwmon/aquacomputer_d5next.rst
> +++ b/Documentation/hwmon/aquacomputer_d5next.rst
> @@ -12,6 +12,7 @@ Supported devices:
> * Aquacomputer Octo fan controller
> * Aquacomputer Quadro fan controller
> * Aquacomputer High Flow Next sensor
> +* Aquacomputer Aquastream Ultimate watercooling pump
> * Aquacomputer Poweradjust 3 fan controller
>
> Author: Aleksa Savic
> @@ -54,6 +55,10 @@ The High Flow Next exposes +5V voltages, water quality, conductivity and flow re
> A temperature sensor can be connected to it, in which case it provides its reading
> and an estimation of the dissipated/absorbed power in the liquid cooling loop.
>
> +The Aquastream Ultimate pump exposes coolant temp and an external temp sensor, along
> +with speed, power, voltage and current of both the pump and optionally connected fan.
> +It also exposes pressure and flow speed readings.
> +
> The Poweradjust 3 controller exposes a single external temperature sensor.
>
> Depending on the device, not all sysfs and debugfs entries will be available.
> diff --git a/drivers/hwmon/aquacomputer_d5next.c b/drivers/hwmon/aquacomputer_d5next.c
> index 2945b630b4a0..12682a610ce7 100644
> --- a/drivers/hwmon/aquacomputer_d5next.c
> +++ b/drivers/hwmon/aquacomputer_d5next.c
> @@ -1,7 +1,7 @@
> // SPDX-License-Identifier: GPL-2.0+
> /*
> * hwmon driver for Aquacomputer devices (D5 Next, Farbwerk, Farbwerk 360, Octo,
> - * Quadro, High Flow Next, Aquaero)
> + * Quadro, High Flow Next, Aquaero, Aquastream Ultimate)
> *
> * Aquacomputer devices send HID reports (with ID 0x01) every second to report
> * sensor values, except for devices that communicate through the
> @@ -29,9 +29,13 @@
> #define USB_PRODUCT_ID_FARBWERK360 0xf010
> #define USB_PRODUCT_ID_OCTO 0xf011
> #define USB_PRODUCT_ID_HIGHFLOWNEXT 0xf012
> +#define USB_PRODUCT_ID_AQUASTREAMULT 0xf00b
> #define USB_PRODUCT_ID_POWERADJUST3 0xf0bd
>
> -enum kinds { d5next, farbwerk, farbwerk360, octo, quadro, highflownext, aquaero, poweradjust3 };
> +enum kinds {
> + d5next, farbwerk, farbwerk360, octo, quadro,
> + highflownext, aquaero, poweradjust3, aquastreamult
> +};
>
> static const char *const aqc_device_names[] = {
> [d5next] = "d5next",
> @@ -41,6 +45,7 @@ static const char *const aqc_device_names[] = {
> [quadro] = "quadro",
> [highflownext] = "highflownext",
> [aquaero] = "aquaero",
> + [aquastreamult] = "aquastreamultimate",
> [poweradjust3] = "poweradjust3"
> };
>
> @@ -117,6 +122,26 @@ static u16 d5next_sensor_fan_offsets[] = { D5NEXT_PUMP_OFFSET, D5NEXT_FAN_OFFSET
> #define D5NEXT_TEMP_CTRL_OFFSET 0x2D /* Temperature sensor offsets location */
> static u16 d5next_ctrl_fan_offsets[] = { 0x97, 0x42 }; /* Pump and fan speed (from 0-100%) */
>
> +/* Specs of the Aquastream Ultimate pump */
> +/* Pump does not follow the standard structure, so only consider the fan */
> +#define AQUASTREAMULT_NUM_FANS 1
> +#define AQUASTREAMULT_NUM_SENSORS 2
> +
> +/* Sensor report offsets for the Aquastream Ultimate pump */
> +#define AQUASTREAMULT_SENSOR_START 0x2D
> +#define AQUASTREAMULT_PUMP_OFFSET 0x51
> +#define AQUASTREAMULT_PUMP_VOLTAGE 0x3D
> +#define AQUASTREAMULT_PUMP_CURRENT 0x53
> +#define AQUASTREAMULT_PUMP_POWER 0x55
> +#define AQUASTREAMULT_FAN_OFFSET 0x41
> +#define AQUASTREAMULT_PRESSURE_OFFSET 0x57
> +#define AQUASTREAMULT_FLOW_SENSOR_OFFSET 0x37
> +#define AQUASTREAMULT_FAN_VOLTAGE_OFFSET 0x02
> +#define AQUASTREAMULT_FAN_CURRENT_OFFSET 0x00
> +#define AQUASTREAMULT_FAN_POWER_OFFSET 0x04
> +#define AQUASTREAMULT_FAN_SPEED_OFFSET 0x06
> +static u16 aquastreamult_sensor_fan_offsets[] = { AQUASTREAMULT_FAN_OFFSET };
> +
> /* Spec and sensor report offset for the Farbwerk RGB controller */
> #define FARBWERK_NUM_SENSORS 4
> #define FARBWERK_SENSOR_START 0x2f
> @@ -339,6 +364,34 @@ static const char *const label_highflownext_voltage[] = {
> "+5V USB voltage"
> };
>
> +/* Labels for Aquastream Ultimate */
> +static const char *const label_aquastreamult_temp[] = {
> + "Coolant temp",
> + "External temp"
> +};
> +
> +static const char *const label_aquastreamult_speeds[] = {
> + "Fan speed",
> + "Pump speed",
> + "Pressure [mbar]",
> + "Flow speed [dL/h]"
> +};
> +
> +static const char *const label_aquastreamult_power[] = {
> + "Fan power",
> + "Pump power"
> +};
> +
> +static const char *const label_aquastreamult_voltages[] = {
> + "Fan voltage",
> + "Pump voltage"
> +};
> +
> +static const char *const label_aquastreamult_current[] = {
> + "Fan current",
> + "Pump current"
> +};
> +
> /* Labels for Poweradjust 3 */
> static const char *const label_poweradjust3_temp_sensors[] = {
> "External sensor"
> @@ -359,7 +412,15 @@ static struct aqc_fan_structure_offsets aqc_aquaero_fan_structure = {
> .speed = AQUAERO_FAN_SPEED_OFFSET
> };
>
> -/* Fan structure offsets for all devices except Aquaero */
> +/* Fan structure offsets for Aquastream Ultimate */
> +static struct aqc_fan_structure_offsets aqc_aquastreamult_fan_structure = {
> + .voltage = AQUASTREAMULT_FAN_VOLTAGE_OFFSET,
> + .curr = AQUASTREAMULT_FAN_CURRENT_OFFSET,
> + .power = AQUASTREAMULT_FAN_POWER_OFFSET,
> + .speed = AQUASTREAMULT_FAN_SPEED_OFFSET
> +};
> +
> +/* Fan structure offsets for all devices except those above */
> static struct aqc_fan_structure_offsets aqc_general_fan_structure = {
> .voltage = AQC_FAN_VOLTAGE_OFFSET,
> .curr = AQC_FAN_CURRENT_OFFSET,
> @@ -565,6 +626,14 @@ static umode_t aqc_is_visible(const void *data, enum hwmon_sensor_types type, u3
> case hwmon_fan_input:
> case hwmon_fan_label:
> switch (priv->kind) {
> + case aquastreamult:
> + /*
> + * Special case to support pump RPM, fan RPM,
> + * pressure and flow sensor
> + */
> + if (channel < 4)
> + return 0444;
> + break;
> case highflownext:
> /* Special case to support flow sensor, water quality
> * and conductivity
> @@ -595,6 +664,11 @@ static umode_t aqc_is_visible(const void *data, enum hwmon_sensor_types type, u3
> break;
> case hwmon_power:
> switch (priv->kind) {
> + case aquastreamult:
> + /* Special case to support pump and fan power */
> + if (channel < 2)
> + return 0444;
> + break;
> case highflownext:
> /* Special case to support one power sensor */
> if (channel == 0)
> @@ -607,8 +681,17 @@ static umode_t aqc_is_visible(const void *data, enum hwmon_sensor_types type, u3
> }
> break;
> case hwmon_curr:
> - if (channel < priv->num_fans)
> - return 0444;
> + switch (priv->kind) {
> + case aquastreamult:
> + /* Special case to support pump and fan current */
> + if (channel < 2)
> + return 0444;
> + break;
> + default:
> + if (channel < priv->num_fans)
> + return 0444;
> + break;
> + }
> break;
> case hwmon_in:
> switch (priv->kind) {
> @@ -617,6 +700,7 @@ static umode_t aqc_is_visible(const void *data, enum hwmon_sensor_types type, u3
> if (channel < priv->num_fans + 2)
> return 0444;
> break;
> + case aquastreamult:
> case highflownext:
> /* Special case to support two voltage sensors */
> if (channel < 2)
> @@ -1001,6 +1085,17 @@ static int aqc_raw_event(struct hid_device *hdev, struct hid_report *report, u8
> i++;
> }
> break;
> + case aquastreamult:
> + priv->speed_input[1] = get_unaligned_be16(data + AQUASTREAMULT_PUMP_OFFSET);
> + priv->speed_input[2] = get_unaligned_be16(data + AQUASTREAMULT_PRESSURE_OFFSET);
> + priv->speed_input[3] = get_unaligned_be16(data + AQUASTREAMULT_FLOW_SENSOR_OFFSET);
> +
> + priv->power_input[1] = get_unaligned_be16(data + AQUASTREAMULT_PUMP_POWER) * 10000;
> +
> + priv->voltage_input[1] = get_unaligned_be16(data + AQUASTREAMULT_PUMP_VOLTAGE) * 10;
> +
> + priv->current_input[1] = get_unaligned_be16(data + AQUASTREAMULT_PUMP_CURRENT);
> + break;
> case d5next:
> priv->voltage_input[2] = get_unaligned_be16(data + D5NEXT_5V_VOLTAGE) * 10;
> priv->voltage_input[3] = get_unaligned_be16(data + D5NEXT_12V_VOLTAGE) * 10;
> @@ -1273,6 +1368,21 @@ static int aqc_probe(struct hid_device *hdev, const struct hid_device_id *id)
> priv->power_label = label_highflownext_power;
> priv->voltage_label = label_highflownext_voltage;
> break;
> + case USB_PRODUCT_ID_AQUASTREAMULT:
> + priv->kind = aquastreamult;
> +
> + priv->num_fans = AQUASTREAMULT_NUM_FANS;
> + priv->fan_sensor_offsets = aquastreamult_sensor_fan_offsets;
> +
> + priv->num_temp_sensors = AQUASTREAMULT_NUM_SENSORS;
> + priv->temp_sensor_start_offset = AQUASTREAMULT_SENSOR_START;
> +
> + priv->temp_label = label_aquastreamult_temp;
> + priv->speed_label = label_aquastreamult_speeds;
> + priv->power_label = label_aquastreamult_power;
> + priv->voltage_label = label_aquastreamult_voltages;
> + priv->current_label = label_aquastreamult_current;
> + break;
> case USB_PRODUCT_ID_POWERADJUST3:
> priv->kind = poweradjust3;
>
> @@ -1302,7 +1412,10 @@ static int aqc_probe(struct hid_device *hdev, const struct hid_device_id *id)
> priv->serial_number_start_offset = AQC_SERIAL_START;
> priv->firmware_version_offset = AQC_FIRMWARE_VERSION;
>
> - priv->fan_structure = &aqc_general_fan_structure;
> + if (priv->kind == aquastreamult)
> + priv->fan_structure = &aqc_aquastreamult_fan_structure;
> + else
> + priv->fan_structure = &aqc_general_fan_structure;
> break;
> }
>
> @@ -1360,6 +1473,7 @@ static const struct hid_device_id aqc_table[] = {
> { HID_USB_DEVICE(USB_VENDOR_ID_AQUACOMPUTER, USB_PRODUCT_ID_OCTO) },
> { HID_USB_DEVICE(USB_VENDOR_ID_AQUACOMPUTER, USB_PRODUCT_ID_QUADRO) },
> { HID_USB_DEVICE(USB_VENDOR_ID_AQUACOMPUTER, USB_PRODUCT_ID_HIGHFLOWNEXT) },
> + { HID_USB_DEVICE(USB_VENDOR_ID_AQUACOMPUTER, USB_PRODUCT_ID_AQUASTREAMULT) },
> { HID_USB_DEVICE(USB_VENDOR_ID_AQUACOMPUTER, USB_PRODUCT_ID_POWERADJUST3) },
> { }
> };