hwmon: (aquacomputer_d5next) Add support for Aquacomputer Aquastream Ultimate

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@gmail.com>
Link: https://lore.kernel.org/r/20230203120324.579808-1-savicaleksa83@gmail.com
Signed-off-by: Guenter Roeck <linux@roeck-us.net>
This commit is contained in:
Aleksa Savic 2023-02-03 13:03:24 +01:00 committed by Guenter Roeck
parent e0f6c370f0
commit 7505dab78f
2 changed files with 125 additions and 6 deletions

View File

@ -12,6 +12,7 @@ Supported devices:
* Aquacomputer Octo fan controller * Aquacomputer Octo fan controller
* Aquacomputer Quadro fan controller * Aquacomputer Quadro fan controller
* Aquacomputer High Flow Next sensor * Aquacomputer High Flow Next sensor
* Aquacomputer Aquastream Ultimate watercooling pump
* Aquacomputer Poweradjust 3 fan controller * Aquacomputer Poweradjust 3 fan controller
Author: Aleksa Savic 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 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. 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. The Poweradjust 3 controller exposes a single external temperature sensor.
Depending on the device, not all sysfs and debugfs entries will be available. Depending on the device, not all sysfs and debugfs entries will be available.

View File

@ -1,7 +1,7 @@
// SPDX-License-Identifier: GPL-2.0+ // SPDX-License-Identifier: GPL-2.0+
/* /*
* hwmon driver for Aquacomputer devices (D5 Next, Farbwerk, Farbwerk 360, Octo, * 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 * Aquacomputer devices send HID reports (with ID 0x01) every second to report
* sensor values, except for devices that communicate through the * sensor values, except for devices that communicate through the
@ -29,9 +29,13 @@
#define USB_PRODUCT_ID_FARBWERK360 0xf010 #define USB_PRODUCT_ID_FARBWERK360 0xf010
#define USB_PRODUCT_ID_OCTO 0xf011 #define USB_PRODUCT_ID_OCTO 0xf011
#define USB_PRODUCT_ID_HIGHFLOWNEXT 0xf012 #define USB_PRODUCT_ID_HIGHFLOWNEXT 0xf012
#define USB_PRODUCT_ID_AQUASTREAMULT 0xf00b
#define USB_PRODUCT_ID_POWERADJUST3 0xf0bd #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[] = { static const char *const aqc_device_names[] = {
[d5next] = "d5next", [d5next] = "d5next",
@ -41,6 +45,7 @@ static const char *const aqc_device_names[] = {
[quadro] = "quadro", [quadro] = "quadro",
[highflownext] = "highflownext", [highflownext] = "highflownext",
[aquaero] = "aquaero", [aquaero] = "aquaero",
[aquastreamult] = "aquastreamultimate",
[poweradjust3] = "poweradjust3" [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 */ #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%) */ 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 */ /* Spec and sensor report offset for the Farbwerk RGB controller */
#define FARBWERK_NUM_SENSORS 4 #define FARBWERK_NUM_SENSORS 4
#define FARBWERK_SENSOR_START 0x2f #define FARBWERK_SENSOR_START 0x2f
@ -339,6 +364,34 @@ static const char *const label_highflownext_voltage[] = {
"+5V USB 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 */ /* Labels for Poweradjust 3 */
static const char *const label_poweradjust3_temp_sensors[] = { static const char *const label_poweradjust3_temp_sensors[] = {
"External sensor" "External sensor"
@ -359,7 +412,15 @@ static struct aqc_fan_structure_offsets aqc_aquaero_fan_structure = {
.speed = AQUAERO_FAN_SPEED_OFFSET .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 = { static struct aqc_fan_structure_offsets aqc_general_fan_structure = {
.voltage = AQC_FAN_VOLTAGE_OFFSET, .voltage = AQC_FAN_VOLTAGE_OFFSET,
.curr = AQC_FAN_CURRENT_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_input:
case hwmon_fan_label: case hwmon_fan_label:
switch (priv->kind) { 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: case highflownext:
/* Special case to support flow sensor, water quality /* Special case to support flow sensor, water quality
* and conductivity * and conductivity
@ -595,6 +664,11 @@ static umode_t aqc_is_visible(const void *data, enum hwmon_sensor_types type, u3
break; break;
case hwmon_power: case hwmon_power:
switch (priv->kind) { switch (priv->kind) {
case aquastreamult:
/* Special case to support pump and fan power */
if (channel < 2)
return 0444;
break;
case highflownext: case highflownext:
/* Special case to support one power sensor */ /* Special case to support one power sensor */
if (channel == 0) if (channel == 0)
@ -607,8 +681,17 @@ static umode_t aqc_is_visible(const void *data, enum hwmon_sensor_types type, u3
} }
break; break;
case hwmon_curr: case hwmon_curr:
if (channel < priv->num_fans) switch (priv->kind) {
return 0444; 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; break;
case hwmon_in: case hwmon_in:
switch (priv->kind) { 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) if (channel < priv->num_fans + 2)
return 0444; return 0444;
break; break;
case aquastreamult:
case highflownext: case highflownext:
/* Special case to support two voltage sensors */ /* Special case to support two voltage sensors */
if (channel < 2) if (channel < 2)
@ -1001,6 +1085,17 @@ static int aqc_raw_event(struct hid_device *hdev, struct hid_report *report, u8
i++; i++;
} }
break; 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: case d5next:
priv->voltage_input[2] = get_unaligned_be16(data + D5NEXT_5V_VOLTAGE) * 10; priv->voltage_input[2] = get_unaligned_be16(data + D5NEXT_5V_VOLTAGE) * 10;
priv->voltage_input[3] = get_unaligned_be16(data + D5NEXT_12V_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->power_label = label_highflownext_power;
priv->voltage_label = label_highflownext_voltage; priv->voltage_label = label_highflownext_voltage;
break; 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: case USB_PRODUCT_ID_POWERADJUST3:
priv->kind = 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->serial_number_start_offset = AQC_SERIAL_START;
priv->firmware_version_offset = AQC_FIRMWARE_VERSION; 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; 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_OCTO) },
{ HID_USB_DEVICE(USB_VENDOR_ID_AQUACOMPUTER, USB_PRODUCT_ID_QUADRO) }, { 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_HIGHFLOWNEXT) },
{ HID_USB_DEVICE(USB_VENDOR_ID_AQUACOMPUTER, USB_PRODUCT_ID_AQUASTREAMULT) },
{ HID_USB_DEVICE(USB_VENDOR_ID_AQUACOMPUTER, USB_PRODUCT_ID_POWERADJUST3) }, { HID_USB_DEVICE(USB_VENDOR_ID_AQUACOMPUTER, USB_PRODUCT_ID_POWERADJUST3) },
{ } { }
}; };