From: Jan-Henrik Bruhn Date: Mon, 25 May 2026 08:37:11 +0000 (+0200) Subject: kernel: backport lm63 enhancements X-Git-Url: http://git.ipfire.org/gitweb/index.cgi?a=commitdiff_plain;h=89ef8aaf3e75c35844feccd733146a37f7da3460;p=thirdparty%2Fopenwrt.git kernel: backport lm63 enhancements This was a pending patch before, it has been accepted by upstream. The patch exposes the PWM frequency and temperature hysteresis values for the LM63 fan controller. Signed-off-by: Jan-Henrik Bruhn Link: https://github.com/openwrt/openwrt/pull/23525 Signed-off-by: Jonas Jelonek --- diff --git a/target/linux/generic/pending-6.18/842-hwmon-lm63-make-pwm1_freq-and-lut-hyst-writable.patch b/target/linux/generic/backport-6.18/842-v7.2-hwmon-lm63-make-pwm1_freq-and-lut-hyst-writable.patch similarity index 70% rename from target/linux/generic/pending-6.18/842-hwmon-lm63-make-pwm1_freq-and-lut-hyst-writable.patch rename to target/linux/generic/backport-6.18/842-v7.2-hwmon-lm63-make-pwm1_freq-and-lut-hyst-writable.patch index a7b1353abfc..e68aefba816 100644 --- a/target/linux/generic/pending-6.18/842-hwmon-lm63-make-pwm1_freq-and-lut-hyst-writable.patch +++ b/target/linux/generic/backport-6.18/842-v7.2-hwmon-lm63-make-pwm1_freq-and-lut-hyst-writable.patch @@ -1,6 +1,7 @@ +From 70159b00aff8c26069f25db8638cbfca970e06a0 Mon Sep 17 00:00:00 2001 From: Jan-Henrik Bruhn -Date: Wed, 21 May 2026 00:00:00 +0200 -Subject: [PATCH] hwmon: lm63: expose PWM frequency and LUT hysteresis as writable +Date: Sat, 23 May 2026 15:36:17 +0200 +Subject: hwmon: (lm63) expose PWM frequency and LUT hysteresis as writable The driver caches the PWM frequency register and the CONFIG_FAN slow-clock select bit, but never lets userspace pick a different output frequency. @@ -17,10 +18,22 @@ holding the hysteresis amount in millidegrees; the per-point attributes stay RO and continue to show the resulting absolute trip-down temperature for each entry. +This was tested on a Linksys LGS328MPC switch hardware where the fan +would not spin with the default PWM Frequency, which is why this change +is required. + Signed-off-by: Jan-Henrik Bruhn +Link: https://lore.kernel.org/r/20260523133617.3439102-1-kernel@jhbruhn.de +Signed-off-by: Guenter Roeck +--- + drivers/hwmon/lm63.c | 133 +++++++++++++++++++++++++++++++++++++++++++++++++++ + 1 file changed, 133 insertions(+) + +diff --git a/drivers/hwmon/lm63.c b/drivers/hwmon/lm63.c +index a0d77a7386a9d..e2a429e579ac1 100644 --- a/drivers/hwmon/lm63.c +++ b/drivers/hwmon/lm63.c -@@ -92,6 +92,9 @@ static const unsigned short normal_i2c[] +@@ -92,6 +92,9 @@ static const unsigned short normal_i2c[] = { 0x18, 0x4c, 0x4e, I2C_CLIENT_END }; #define LM96163_REG_REMOTE_TEMP_U_LSB 0x32 #define LM96163_REG_CONFIG_ENHANCED 0x45 @@ -30,7 +43,7 @@ Signed-off-by: Jan-Henrik Bruhn #define LM63_MAX_CONVRATE 9 #define LM63_MAX_CONVRATE_HZ 32 -@@ -447,6 +450,75 @@ static ssize_t pwm1_enable_store(struct +@@ -455,6 +458,91 @@ static ssize_t pwm1_enable_store(struct device *dev, return count; } @@ -38,10 +51,15 @@ Signed-off-by: Jan-Henrik Bruhn + struct device_attribute *dummy, char *buf) +{ + struct lm63_data *data = lm63_update_device(dev); -+ unsigned int base = (data->config_fan & 0x08) ? -+ LM63_PWM_BASE_SLOW_HZ : LM63_PWM_BASE_FAST_HZ; ++ unsigned int base, freq; ++ ++ mutex_lock(&data->update_lock); ++ base = (data->config_fan & 0x08) ? ++ LM63_PWM_BASE_SLOW_HZ : LM63_PWM_BASE_FAST_HZ; ++ freq = data->pwm1_freq; ++ mutex_unlock(&data->update_lock); + -+ return sprintf(buf, "%u\n", base / data->pwm1_freq); ++ return sprintf(buf, "%u\n", base / freq); +} + +/* @@ -58,14 +76,11 @@ Signed-off-by: Jan-Henrik Bruhn + struct i2c_client *client = data->client; + unsigned long val, pfr_fast, pfr_slow, err_fast, err_slow, pfr; + bool slow_clock; -+ int err; -+ -+ if (!(data->config_fan & 0x20)) /* register is read-only */ -+ return -EPERM; ++ int ret; + -+ err = kstrtoul(buf, 10, &val); -+ if (err) -+ return err; ++ ret = kstrtoul(buf, 10, &val); ++ if (ret) ++ return ret; + if (val == 0) + return -EINVAL; + @@ -85,7 +100,27 @@ Signed-off-by: Jan-Henrik Bruhn + } + + mutex_lock(&data->update_lock); -+ data->config_fan = i2c_smbus_read_byte_data(client, LM63_REG_CONFIG_FAN); ++ ret = i2c_smbus_read_byte_data(client, LM63_REG_CONFIG_FAN); ++ if (ret < 0) { ++ mutex_unlock(&data->update_lock); ++ return ret; ++ } ++ data->config_fan = ret; ++ ++ if (!(data->config_fan & 0x20)) { /* register is read-only */ ++ mutex_unlock(&data->update_lock); ++ return -EPERM; ++ } ++ ++ if (data->kind == lm96163) { ++ ret = i2c_smbus_read_byte_data(client, LM96163_REG_CONFIG_ENHANCED); ++ if (ret < 0) { ++ mutex_unlock(&data->update_lock); ++ return ret; ++ } ++ data->pwm_highres = !slow_clock && pfr == 8 && (ret & 0x10); ++ } ++ + if (slow_clock) + data->config_fan |= 0x08; + else @@ -93,12 +128,6 @@ Signed-off-by: Jan-Henrik Bruhn + i2c_smbus_write_byte_data(client, LM63_REG_CONFIG_FAN, data->config_fan); + i2c_smbus_write_byte_data(client, LM63_REG_PWM_FREQ, pfr); + data->pwm1_freq = pfr; -+ -+ if (data->kind == lm96163) { -+ u8 enh = i2c_smbus_read_byte_data(client, -+ LM96163_REG_CONFIG_ENHANCED); -+ data->pwm_highres = !slow_clock && pfr == 8 && (enh & 0x10); -+ } + mutex_unlock(&data->update_lock); + return count; +} @@ -106,10 +135,11 @@ Signed-off-by: Jan-Henrik Bruhn /* * There are 8bit registers for both local(temp1) and remote(temp2) sensor. * For remote sensor registers temp2_offset has to be considered, -@@ -609,6 +681,42 @@ static ssize_t show_lut_temp_hyst(struct +@@ -629,6 +717,47 @@ static ssize_t show_lut_temp_hyst(struct device *dev, + return sprintf(buf, "%d\n", temp); } - /* ++/* + * The LM63 has a single hysteresis register shared by all LUT entries. + * Expose it as a chip-wide hysteresis amount in millidegrees; the + * per-point pwm1_auto_pointN_temp_hyst attributes remain read-only and @@ -120,8 +150,13 @@ Signed-off-by: Jan-Henrik Bruhn + char *buf) +{ + struct lm63_data *data = lm63_update_device(dev); ++ u8 hyst; + -+ return sprintf(buf, "%d\n", TEMP8_FROM_REG(data->lut_temp_hyst)); ++ mutex_lock(&data->update_lock); ++ hyst = data->lut_temp_hyst; ++ mutex_unlock(&data->update_lock); ++ ++ return sprintf(buf, "%d\n", TEMP8_FROM_REG(hyst)); +} + +static ssize_t pwm1_auto_point_temp_hyst_store(struct device *dev, @@ -145,11 +180,10 @@ Signed-off-by: Jan-Henrik Bruhn + return count; +} + -+/* + /* * And now the other way around, user-space provides an absolute * hysteresis value and we have to store a relative one - */ -@@ -743,6 +851,8 @@ static SENSOR_DEVICE_ATTR(fan1_min, S_IW +@@ -764,6 +893,8 @@ static SENSOR_DEVICE_ATTR(fan1_min, S_IWUSR | S_IRUGO, show_fan, static SENSOR_DEVICE_ATTR(pwm1, S_IWUSR | S_IRUGO, show_pwm1, set_pwm1, 0); static DEVICE_ATTR_RW(pwm1_enable); @@ -158,7 +192,7 @@ Signed-off-by: Jan-Henrik Bruhn static SENSOR_DEVICE_ATTR(pwm1_auto_point1_pwm, S_IWUSR | S_IRUGO, show_pwm1, set_pwm1, 1); static SENSOR_DEVICE_ATTR(pwm1_auto_point1_temp, S_IWUSR | S_IRUGO, -@@ -848,6 +958,8 @@ static DEVICE_ATTR_RW(update_interval); +@@ -869,6 +1000,8 @@ static DEVICE_ATTR_RW(update_interval); static struct attribute *lm63_attributes[] = { &sensor_dev_attr_pwm1.dev_attr.attr, &dev_attr_pwm1_enable.attr,