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 <git@jhbruhn.de> Link: https://github.com/openwrt/openwrt/pull/23525 Signed-off-by: Jonas Jelonek <jelonek.jonas@gmail.com>
This commit is contained in:
parent
96b9e8130f
commit
89ef8aaf3e
@ -1,6 +1,7 @@
|
|||||||
|
From 70159b00aff8c26069f25db8638cbfca970e06a0 Mon Sep 17 00:00:00 2001
|
||||||
From: Jan-Henrik Bruhn <kernel@jhbruhn.de>
|
From: Jan-Henrik Bruhn <kernel@jhbruhn.de>
|
||||||
Date: Wed, 21 May 2026 00:00:00 +0200
|
Date: Sat, 23 May 2026 15:36:17 +0200
|
||||||
Subject: [PATCH] hwmon: lm63: expose PWM frequency and LUT hysteresis as writable
|
Subject: hwmon: (lm63) expose PWM frequency and LUT hysteresis as writable
|
||||||
|
|
||||||
The driver caches the PWM frequency register and the CONFIG_FAN slow-clock
|
The driver caches the PWM frequency register and the CONFIG_FAN slow-clock
|
||||||
select bit, but never lets userspace pick a different output frequency.
|
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
|
stay RO and continue to show the resulting absolute trip-down
|
||||||
temperature for each entry.
|
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 <kernel@jhbruhn.de>
|
Signed-off-by: Jan-Henrik Bruhn <kernel@jhbruhn.de>
|
||||||
|
Link: https://lore.kernel.org/r/20260523133617.3439102-1-kernel@jhbruhn.de
|
||||||
|
Signed-off-by: Guenter Roeck <linux@roeck-us.net>
|
||||||
|
---
|
||||||
|
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
|
--- a/drivers/hwmon/lm63.c
|
||||||
+++ b/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_REMOTE_TEMP_U_LSB 0x32
|
||||||
#define LM96163_REG_CONFIG_ENHANCED 0x45
|
#define LM96163_REG_CONFIG_ENHANCED 0x45
|
||||||
|
|
||||||
@ -30,7 +43,7 @@ Signed-off-by: Jan-Henrik Bruhn <kernel@jhbruhn.de>
|
|||||||
#define LM63_MAX_CONVRATE 9
|
#define LM63_MAX_CONVRATE 9
|
||||||
|
|
||||||
#define LM63_MAX_CONVRATE_HZ 32
|
#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;
|
return count;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -38,10 +51,15 @@ Signed-off-by: Jan-Henrik Bruhn <kernel@jhbruhn.de>
|
|||||||
+ struct device_attribute *dummy, char *buf)
|
+ struct device_attribute *dummy, char *buf)
|
||||||
+{
|
+{
|
||||||
+ struct lm63_data *data = lm63_update_device(dev);
|
+ struct lm63_data *data = lm63_update_device(dev);
|
||||||
+ unsigned int base = (data->config_fan & 0x08) ?
|
+ unsigned int base, freq;
|
||||||
+ LM63_PWM_BASE_SLOW_HZ : LM63_PWM_BASE_FAST_HZ;
|
|
||||||
+
|
+
|
||||||
+ return sprintf(buf, "%u\n", base / data->pwm1_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 / freq);
|
||||||
+}
|
+}
|
||||||
+
|
+
|
||||||
+/*
|
+/*
|
||||||
@ -58,14 +76,11 @@ Signed-off-by: Jan-Henrik Bruhn <kernel@jhbruhn.de>
|
|||||||
+ struct i2c_client *client = data->client;
|
+ struct i2c_client *client = data->client;
|
||||||
+ unsigned long val, pfr_fast, pfr_slow, err_fast, err_slow, pfr;
|
+ unsigned long val, pfr_fast, pfr_slow, err_fast, err_slow, pfr;
|
||||||
+ bool slow_clock;
|
+ bool slow_clock;
|
||||||
+ int err;
|
+ int ret;
|
||||||
+
|
+
|
||||||
+ if (!(data->config_fan & 0x20)) /* register is read-only */
|
+ ret = kstrtoul(buf, 10, &val);
|
||||||
+ return -EPERM;
|
+ if (ret)
|
||||||
+
|
+ return ret;
|
||||||
+ err = kstrtoul(buf, 10, &val);
|
|
||||||
+ if (err)
|
|
||||||
+ return err;
|
|
||||||
+ if (val == 0)
|
+ if (val == 0)
|
||||||
+ return -EINVAL;
|
+ return -EINVAL;
|
||||||
+
|
+
|
||||||
@ -85,7 +100,27 @@ Signed-off-by: Jan-Henrik Bruhn <kernel@jhbruhn.de>
|
|||||||
+ }
|
+ }
|
||||||
+
|
+
|
||||||
+ mutex_lock(&data->update_lock);
|
+ 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)
|
+ if (slow_clock)
|
||||||
+ data->config_fan |= 0x08;
|
+ data->config_fan |= 0x08;
|
||||||
+ else
|
+ else
|
||||||
@ -93,12 +128,6 @@ Signed-off-by: Jan-Henrik Bruhn <kernel@jhbruhn.de>
|
|||||||
+ i2c_smbus_write_byte_data(client, LM63_REG_CONFIG_FAN, data->config_fan);
|
+ i2c_smbus_write_byte_data(client, LM63_REG_CONFIG_FAN, data->config_fan);
|
||||||
+ i2c_smbus_write_byte_data(client, LM63_REG_PWM_FREQ, pfr);
|
+ i2c_smbus_write_byte_data(client, LM63_REG_PWM_FREQ, pfr);
|
||||||
+ data->pwm1_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);
|
+ mutex_unlock(&data->update_lock);
|
||||||
+ return count;
|
+ return count;
|
||||||
+}
|
+}
|
||||||
@ -106,10 +135,11 @@ Signed-off-by: Jan-Henrik Bruhn <kernel@jhbruhn.de>
|
|||||||
/*
|
/*
|
||||||
* There are 8bit registers for both local(temp1) and remote(temp2) sensor.
|
* There are 8bit registers for both local(temp1) and remote(temp2) sensor.
|
||||||
* For remote sensor registers temp2_offset has to be considered,
|
* 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.
|
+ * The LM63 has a single hysteresis register shared by all LUT entries.
|
||||||
+ * Expose it as a chip-wide hysteresis amount in millidegrees; the
|
+ * Expose it as a chip-wide hysteresis amount in millidegrees; the
|
||||||
+ * per-point pwm1_auto_pointN_temp_hyst attributes remain read-only and
|
+ * per-point pwm1_auto_pointN_temp_hyst attributes remain read-only and
|
||||||
@ -120,8 +150,13 @@ Signed-off-by: Jan-Henrik Bruhn <kernel@jhbruhn.de>
|
|||||||
+ char *buf)
|
+ char *buf)
|
||||||
+{
|
+{
|
||||||
+ struct lm63_data *data = lm63_update_device(dev);
|
+ 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,
|
+static ssize_t pwm1_auto_point_temp_hyst_store(struct device *dev,
|
||||||
@ -145,11 +180,10 @@ Signed-off-by: Jan-Henrik Bruhn <kernel@jhbruhn.de>
|
|||||||
+ return count;
|
+ return count;
|
||||||
+}
|
+}
|
||||||
+
|
+
|
||||||
+/*
|
/*
|
||||||
* And now the other way around, user-space provides an absolute
|
* And now the other way around, user-space provides an absolute
|
||||||
* hysteresis value and we have to store a relative one
|
* hysteresis value and we have to store a relative one
|
||||||
*/
|
@@ -764,6 +893,8 @@ static SENSOR_DEVICE_ATTR(fan1_min, S_IWUSR | S_IRUGO, show_fan,
|
||||||
@@ -743,6 +851,8 @@ static SENSOR_DEVICE_ATTR(fan1_min, S_IW
|
|
||||||
|
|
||||||
static SENSOR_DEVICE_ATTR(pwm1, S_IWUSR | S_IRUGO, show_pwm1, set_pwm1, 0);
|
static SENSOR_DEVICE_ATTR(pwm1, S_IWUSR | S_IRUGO, show_pwm1, set_pwm1, 0);
|
||||||
static DEVICE_ATTR_RW(pwm1_enable);
|
static DEVICE_ATTR_RW(pwm1_enable);
|
||||||
@ -158,7 +192,7 @@ Signed-off-by: Jan-Henrik Bruhn <kernel@jhbruhn.de>
|
|||||||
static SENSOR_DEVICE_ATTR(pwm1_auto_point1_pwm, S_IWUSR | S_IRUGO,
|
static SENSOR_DEVICE_ATTR(pwm1_auto_point1_pwm, S_IWUSR | S_IRUGO,
|
||||||
show_pwm1, set_pwm1, 1);
|
show_pwm1, set_pwm1, 1);
|
||||||
static SENSOR_DEVICE_ATTR(pwm1_auto_point1_temp, S_IWUSR | S_IRUGO,
|
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[] = {
|
static struct attribute *lm63_attributes[] = {
|
||||||
&sensor_dev_attr_pwm1.dev_attr.attr,
|
&sensor_dev_attr_pwm1.dev_attr.attr,
|
||||||
&dev_attr_pwm1_enable.attr,
|
&dev_attr_pwm1_enable.attr,
|
||||||
Loading…
Reference in New Issue
Block a user