Skip to content

Commit

Permalink
hwmon: (aquacomputer_d5next) Add fan PWM control for Aquaero
Browse files Browse the repository at this point in the history
Add the option to control fan PWM on Aquacomputer Aquaero. The Aquaero is
the most complex Aquacomputer device, control is therefore more complicated
then on already supported devices.
Setting PWM requires multiple steps. First, an internal static PWM
controller is set to the desired PWM value. Second, the fan is set to use
that PWM controller. Last, the minimum and maximum accepted PWM values
of the fan are set to allow all possible PWM values.

Signed-off-by: Leonard Anderweit <leonard.anderweit@gmail.com>
Link: https://lore.kernel.org/r/20230214220221.15003-7-leonard.anderweit@gmail.com
Signed-off-by: Guenter Roeck <linux@roeck-us.net>
  • Loading branch information
leoratte authored and groeck committed Apr 19, 2023
1 parent 866e630 commit bd1e92f
Show file tree
Hide file tree
Showing 2 changed files with 61 additions and 6 deletions.
3 changes: 2 additions & 1 deletion Documentation/hwmon/aquacomputer_d5next.rst
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,8 @@ communicate through proprietary USB HID protocols.

The Aquaero devices expose eight physical, eight virtual and four calculated
virtual temperature sensors, as well as two flow sensors. The fans expose their
speed (in RPM), power, voltage and current. Temperature offsets can be controlled.
speed (in RPM), power, voltage and current. Temperature offsets and fan speeds
can be controlled.

For the D5 Next pump, available sensors are pump and fan speed, power, voltage
and current, as well as coolant temperature and eight virtual temp sensors. Also
Expand Down
64 changes: 59 additions & 5 deletions drivers/hwmon/aquacomputer_d5next.c
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,9 @@ static u8 aquaero_secondary_ctrl_report[] = {
#define AQUAERO_NUM_CALC_VIRTUAL_SENSORS 4
#define AQUAERO_NUM_FLOW_SENSORS 2
#define AQUAERO_CTRL_REPORT_SIZE 0xa93
#define AQUAERO_CTRL_PRESET_ID 0x5c
#define AQUAERO_CTRL_PRESET_SIZE 0x02
#define AQUAERO_CTRL_PRESET_START 0x55c

/* Sensor report offsets for Aquaero fan controllers */
#define AQUAERO_SENSOR_START 0x65
Expand All @@ -118,6 +121,10 @@ static u16 aquaero_sensor_fan_offsets[] = { 0x167, 0x173, 0x17f, 0x18B };

/* Control report offsets for the Aquaero fan controllers */
#define AQUAERO_TEMP_CTRL_OFFSET 0xdb
#define AQUAERO_FAN_CTRL_MIN_PWR_OFFSET 0x04
#define AQUAERO_FAN_CTRL_MAX_PWR_OFFSET 0x06
#define AQUAERO_FAN_CTRL_SRC_OFFSET 0x10
static u16 aquaero_ctrl_fan_offsets[] = { 0x20c, 0x220, 0x234, 0x248 };

/* Specs of the D5 Next pump */
#define D5NEXT_NUM_FANS 2
Expand Down Expand Up @@ -857,13 +864,23 @@ static int aqc_read(struct device *dev, enum hwmon_sensor_types type, u32 attr,
*val = priv->power_input[channel];
break;
case hwmon_pwm:
if (priv->fan_ctrl_offsets) {
switch (priv->kind) {
case aquaero:
ret = aqc_get_ctrl_val(priv,
AQUAERO_CTRL_PRESET_START + channel * AQUAERO_CTRL_PRESET_SIZE,
val, AQC_BE16);
if (ret < 0)
return ret;
*val = aqc_percent_to_pwm(*val);
break;
default:
ret = aqc_get_ctrl_val(priv, priv->fan_ctrl_offsets[channel],
val, AQC_BE16);
if (ret < 0)
return ret;

*val = aqc_percent_to_pwm(ret);
break;
}
break;
case hwmon_in:
Expand Down Expand Up @@ -922,6 +939,10 @@ static int aqc_write(struct device *dev, enum hwmon_sensor_types type, u32 attr,
long val)
{
int ret, pwm_value;
/* Arrays for setting multiple values at once in the control report */
int ctrl_values_offsets[4];
long ctrl_values[4];
int ctrl_values_types[4];
struct aqc_data *priv = dev_get_drvdata(dev);

switch (type) {
Expand Down Expand Up @@ -956,15 +977,47 @@ static int aqc_write(struct device *dev, enum hwmon_sensor_types type, u32 attr,
case hwmon_pwm:
switch (attr) {
case hwmon_pwm_input:
if (priv->fan_ctrl_offsets) {
pwm_value = aqc_pwm_to_percent(val);
if (pwm_value < 0)
return pwm_value;
pwm_value = aqc_pwm_to_percent(val);
if (pwm_value < 0)
return pwm_value;

switch (priv->kind) {
case aquaero:
/* Write pwm value to preset corresponding to the channel */
ctrl_values_offsets[0] = AQUAERO_CTRL_PRESET_START +
channel * AQUAERO_CTRL_PRESET_SIZE;
ctrl_values[0] = pwm_value;
ctrl_values_types[0] = AQC_BE16;

/* Write preset number in fan control source */
ctrl_values_offsets[1] = priv->fan_ctrl_offsets[channel] +
AQUAERO_FAN_CTRL_SRC_OFFSET;
ctrl_values[1] = AQUAERO_CTRL_PRESET_ID + channel;
ctrl_values_types[1] = AQC_BE16;

/* Set minimum power to 0 to allow the fan to turn off */
ctrl_values_offsets[2] = priv->fan_ctrl_offsets[channel] +
AQUAERO_FAN_CTRL_MIN_PWR_OFFSET;
ctrl_values[2] = 0;
ctrl_values_types[2] = AQC_BE16;

/* Set maximum power to 255 to allow the fan to reach max speed */
ctrl_values_offsets[3] = priv->fan_ctrl_offsets[channel] +
AQUAERO_FAN_CTRL_MAX_PWR_OFFSET;
ctrl_values[3] = aqc_pwm_to_percent(255);
ctrl_values_types[3] = AQC_BE16;

ret = aqc_set_ctrl_vals(priv, ctrl_values_offsets, ctrl_values,
ctrl_values_types, 4);
if (ret < 0)
return ret;
break;
default:
ret = aqc_set_ctrl_val(priv, priv->fan_ctrl_offsets[channel],
pwm_value, AQC_BE16);
if (ret < 0)
return ret;
break;
}
break;
default:
Expand Down Expand Up @@ -1287,6 +1340,7 @@ static int aqc_probe(struct hid_device *hdev, const struct hid_device_id *id)

priv->num_fans = AQUAERO_NUM_FANS;
priv->fan_sensor_offsets = aquaero_sensor_fan_offsets;
priv->fan_ctrl_offsets = aquaero_ctrl_fan_offsets;

priv->num_temp_sensors = AQUAERO_NUM_SENSORS;
priv->temp_sensor_start_offset = AQUAERO_SENSOR_START;
Expand Down

0 comments on commit bd1e92f

Please sign in to comment.