Skip to content

Instantly share code, notes, and snippets.

@dominikpaulus
Created June 8, 2024 20:26
Show Gist options
  • Save dominikpaulus/278886a1955397972bf2ecdd80d69b42 to your computer and use it in GitHub Desktop.
Save dominikpaulus/278886a1955397972bf2ecdd80d69b42 to your computer and use it in GitHub Desktop.
Kernel patch to support (rudimentary) fan control in the FTS Teutates kernel driver
diff --git a/drivers/hwmon/ftsteutates.c b/drivers/hwmon/ftsteutates.c
index a3a0766..49a7cd5 100644
--- a/drivers/hwmon/ftsteutates.c
+++ b/drivers/hwmon/ftsteutates.c
@@ -79,6 +79,7 @@ struct fts_data {
u8 fan_present;
u8 fan_input[FTS_NO_FAN_SENSORS]; /* in rps */
u8 fan_source[FTS_NO_FAN_SENSORS];
+ u8 pwm_input[FTS_NO_FAN_SENSORS];
u8 fan_alarm;
};
@@ -86,6 +87,8 @@ struct fts_data {
#define FTS_REG_FAN_SOURCE(idx) ((idx) + 0x30)
#define FTS_REG_FAN_CONTROL(idx) (((idx) << 16) + 0x4881)
+#define FTS_REG_FAN_PWM(idx) (((idx) << 8) + 0x4886)
+
#define FTS_REG_TEMP_INPUT(idx) ((idx) + 0x40)
#define FTS_REG_TEMP_CONTROL(idx) (((idx) << 16) + 0x0681)
@@ -184,9 +187,15 @@ static int fts_update_device(struct fts_data *data)
if (err < 0)
goto exit;
data->fan_source[i] = err;
+
+ err = fts_read_byte(data->client, FTS_REG_FAN_PWM(i));
+ if (err < 0)
+ goto exit;
+ data->pwm_input[i] = err;
} else {
data->fan_input[i] = 0;
data->fan_source[i] = FTS_FAN_SOURCE_INVALID;
+ data->pwm_input[i] = 0;
}
}
@@ -365,6 +374,14 @@ static umode_t fts_is_visible(const void *devdata, enum hwmon_sensor_types type,
}
break;
case hwmon_pwm:
+ switch (attr) {
+ case hwmon_pwm_enable:
+ case hwmon_pwm_input:
+ return 0644;
+ default:
+ break;
+ }
+ break;
case hwmon_in:
return 0444;
default:
@@ -423,6 +440,12 @@ static int fts_read(struct device *dev, enum hwmon_sensor_types type, u32 attr,
break;
case hwmon_pwm:
switch (attr) {
+ case hwmon_pwm_input:
+ *val = data->pwm_input[channel];
+ return 0;
+ case hwmon_pwm_enable:
+ *val = 1;
+ return 0;
case hwmon_pwm_auto_channels_temp:
if (data->fan_source[channel] == FTS_FAN_SOURCE_INVALID)
*val = 0;
@@ -484,6 +507,29 @@ static int fts_write(struct device *dev, enum hwmon_sensor_types type, u32 attr,
break;
}
break;
+ case hwmon_pwm:
+ switch (attr) {
+ case hwmon_pwm_input:
+ if (val < 0 || val > 255)
+ return -EINVAL;
+
+ mutex_lock(&data->update_lock);
+ ret = fts_write_byte(data->client, FTS_REG_FAN_PWM(channel), val);
+ if (ret >= 0)
+ data->valid = false;
+
+ mutex_unlock(&data->update_lock);
+
+ if (ret < 0)
+ return ret;
+
+ return 0;
+ case hwmon_pwm_enable:
+ return 0;
+ default:
+ break;
+ }
+ break;
case hwmon_fan:
switch (attr) {
case hwmon_fan_alarm:
@@ -551,14 +597,14 @@ static const struct hwmon_channel_info * const fts_info[] = {
HWMON_F_INPUT | HWMON_F_ALARM | HWMON_F_FAULT
),
HWMON_CHANNEL_INFO(pwm,
- HWMON_PWM_AUTO_CHANNELS_TEMP,
- HWMON_PWM_AUTO_CHANNELS_TEMP,
- HWMON_PWM_AUTO_CHANNELS_TEMP,
- HWMON_PWM_AUTO_CHANNELS_TEMP,
- HWMON_PWM_AUTO_CHANNELS_TEMP,
- HWMON_PWM_AUTO_CHANNELS_TEMP,
- HWMON_PWM_AUTO_CHANNELS_TEMP,
- HWMON_PWM_AUTO_CHANNELS_TEMP
+ HWMON_PWM_INPUT | HWMON_PWM_ENABLE,
+ HWMON_PWM_INPUT | HWMON_PWM_ENABLE,
+ HWMON_PWM_INPUT | HWMON_PWM_ENABLE,
+ HWMON_PWM_INPUT | HWMON_PWM_ENABLE,
+ HWMON_PWM_INPUT | HWMON_PWM_ENABLE,
+ HWMON_PWM_INPUT | HWMON_PWM_ENABLE,
+ HWMON_PWM_INPUT | HWMON_PWM_ENABLE,
+ HWMON_PWM_INPUT | HWMON_PWM_ENABLE
),
HWMON_CHANNEL_INFO(in,
HWMON_I_INPUT,
@the2masters
Copy link

Did you ever try to send this upstream?

@dominikpaulus
Copy link
Author

I did not - primarily because the registers this patch is writing to are undocumented and don't exactly do what I would hope them to do (e.g.: if you set the fan speed too low, the controller will spin the fan quickly up to maximum speed (likely because there's some minimum fan speed threshold hardcoded somewhere) and then slowly reduce back to the minimum speed - forming some sawtooth pattern).

I tried to get Kontron to provide me with a complete datasheet for the controller (and offered them to implement the fan control functionality and send the corresponding patches upstream), but they declined that because they don't want to deal with private customers, unfortunately.

@the2masters
Copy link

Okay, thanks for at least posting the patch! I'll just buy quiet fans, that's probably easier ๐Ÿ˜„ The platform is 10-12 years old, so I don't think we should put any more work into it.
I got a couple of old servers with D3417 motherboards, Xeon 1275v5 and 32 GB of ECC RAM and converted them into storage servers with 4x 4TB NVME hard drives and 25 Gbit LAN, and they work perfectly. 12 W idle power, which is less than I can achieve on newer platforms ๐Ÿ‘

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment