diff mbox series

[v2,2/5] hwmon: (max31790) Fix and split pwm*_enable

Message ID 20210413025948.901867-2-kubernat@cesnet.cz (mailing list archive)
State Superseded
Headers show
Series [v2,1/5] hwmon: (max31790) Rework to use regmap | expand

Commit Message

Václav Kubernát April 13, 2021, 2:59 a.m. UTC
In the old code, pwm*_enable does two things. Firstly, it sets whether
the chip should run in PWM or RPM mode. Secondly, it tells the chip
whether it should monitor fan RPM. However, these two settings aren't
tied together, so they shouldn't be set with a single value. In the new
code, fan*_enable now controls fan RPM monitoring (pwm*_enable no longer
controls that).

According to the sysfs hwmon documentation, pwm*_enable has three
possible values, 0 for "no control / full-speed", 1 for manual mode, and
2+ for automatic. The old code works fine for 1 and 2, but 0 only
differs from 1 in that it just turns off fan speed monitoring. The chip
actually does have a way to turn off fan controls (and only monitor),
but what that does is that it sets PWM to 0% duty cycle (which is the
opposite to full-speed) AND it also turns off fan speed monitoring.
Because of this, I implemented the 0 value by setting PWM mode to 100%.
This method does come with a problem: it is impossible to differentiate
between full-speed and PWM mode just from the values on the chip. The
new code solves this by saving a value indicating whether we're in
full-speed mode. This value is initialized to 0, so full-speed mode
won't persist across reboots.

These two changes are closely connected together, mainly because the
detection of the pwm*_enable value depended on whether fan speed
monitoring is enabled (which is now controlled as written in the first
paragraph).

Signed-off-by: Václav Kubernát <kubernat@cesnet.cz>
---
 Documentation/hwmon/max31790.rst |  8 +--
 drivers/hwmon/max31790.c         | 87 ++++++++++++++++++++++----------
 2 files changed, 66 insertions(+), 29 deletions(-)

Comments

Václav Kubernát April 22, 2021, 12:26 a.m. UTC | #1
Okay, no problem, I'll get rid of the full-speed mode (and update the
documentation) in the next version of the patch. Do you think I should
disable the value "0" in pwm*_enable?

Václav

út 20. 4. 2021 v 0:33 odesílatel Guenter Roeck <linux@roeck-us.net> napsal:
>
> On Tue, Apr 13, 2021 at 04:59:45AM +0200, Václav Kubernát wrote:
> > In the old code, pwm*_enable does two things. Firstly, it sets whether
> > the chip should run in PWM or RPM mode. Secondly, it tells the chip
> > whether it should monitor fan RPM. However, these two settings aren't
> > tied together, so they shouldn't be set with a single value. In the new
> > code, fan*_enable now controls fan RPM monitoring (pwm*_enable no longer
> > controls that).
> >
> > According to the sysfs hwmon documentation, pwm*_enable has three
> > possible values, 0 for "no control / full-speed", 1 for manual mode, and
> > 2+ for automatic. The old code works fine for 1 and 2, but 0 only
> > differs from 1 in that it just turns off fan speed monitoring. The chip
> > actually does have a way to turn off fan controls (and only monitor),
> > but what that does is that it sets PWM to 0% duty cycle (which is the
> > opposite to full-speed) AND it also turns off fan speed monitoring.
> > Because of this, I implemented the 0 value by setting PWM mode to 100%.
> > This method does come with a problem: it is impossible to differentiate
> > between full-speed and PWM mode just from the values on the chip. The
> > new code solves this by saving a value indicating whether we're in
> > full-speed mode. This value is initialized to 0, so full-speed mode
> > won't persist across reboots.
> >
> I don't think this is a good idea, sorry. It is not just a problem across
> reboots, but also when unloading and reloading the driver. I think we
> should stick with chip capabilities and adjust the documentation
> accordingly.
>
> Guenter
>
> > These two changes are closely connected together, mainly because the
> > detection of the pwm*_enable value depended on whether fan speed
> > monitoring is enabled (which is now controlled as written in the first
> > paragraph).
> >
> > Signed-off-by: Václav Kubernát <kubernat@cesnet.cz>
> > ---
> >  Documentation/hwmon/max31790.rst |  8 +--
> >  drivers/hwmon/max31790.c         | 87 ++++++++++++++++++++++----------
> >  2 files changed, 66 insertions(+), 29 deletions(-)
> >
> > diff --git a/Documentation/hwmon/max31790.rst b/Documentation/hwmon/max31790.rst
> > index f301385d8cef..8979c8a02cd1 100644
> > --- a/Documentation/hwmon/max31790.rst
> > +++ b/Documentation/hwmon/max31790.rst
> > @@ -34,10 +34,12 @@ also be configured to serve as tachometer inputs.
> >  Sysfs entries
> >  -------------
> >
> > -================== === =======================================================
> > +================== === =============================================================
> > +fan[1-12]_enable   RW  enable fan speed monitoring
> >  fan[1-12]_input    RO  fan tachometer speed in RPM
> >  fan[1-12]_fault    RO  fan experienced fault
> >  fan[1-6]_target    RW  desired fan speed in RPM
> > -pwm[1-6]_enable    RW  regulator mode, 0=disabled, 1=manual mode, 2=rpm mode
> > +pwm[1-6]_enable    RW  regulator mode, 0=full speed, 1=manual (pwm) mode, 2=rpm mode
> > +                       setting rpm mode sets fan*_enable to 1
> >  pwm[1-6]           RW  fan target duty cycle (0-255)
> > -================== === =======================================================
> > +================== === =============================================================
> > diff --git a/drivers/hwmon/max31790.c b/drivers/hwmon/max31790.c
> > index e3765ce4444a..ecdd55e12ffe 100644
> > --- a/drivers/hwmon/max31790.c
> > +++ b/drivers/hwmon/max31790.c
> > @@ -39,6 +39,7 @@
> >
> >  #define FAN_RPM_MIN                  120
> >  #define FAN_RPM_MAX                  7864320
> > +#define MAX_PWM                              0XFF80
> >
> >  #define RPM_FROM_REG(reg, sr)                (((reg) >> 4) ? \
> >                                        ((60 * (sr) * 8192) / ((reg) >> 4)) : \
> > @@ -90,6 +91,7 @@ struct max31790_data {
> >       struct regmap *regmap;
> >
> >       struct mutex update_lock;
> > +     bool full_speed[NR_CHANNEL];
> >       u8 fan_config[NR_CHANNEL];
> >       u8 fan_dynamics[NR_CHANNEL];
> >  };
> > @@ -191,6 +193,9 @@ static int max31790_read_fan(struct device *dev, u32 attr, int channel,
> >               else
> >                       *val = !!(fault & (1 << channel));
> >               return 0;
> > +     case hwmon_fan_enable:
> > +             *val = !!(data->fan_config[channel] & MAX31790_FAN_CFG_TACH_INPUT_EN);
> > +             return 0;
> >       default:
> >               return -EOPNOTSUPP;
> >       }
> > @@ -233,6 +238,15 @@ static int max31790_write_fan(struct device *dev, u32 attr, int channel,
> >                                    MAX31790_REG_TARGET_COUNT(channel),
> >                                    target_count);
> >               break;
> > +     case hwmon_fan_enable:
> > +             if (val == 0)
> > +                     data->fan_config[channel] &= ~MAX31790_FAN_CFG_TACH_INPUT_EN;
> > +             else
> > +                     data->fan_config[channel] |= MAX31790_FAN_CFG_TACH_INPUT_EN;
> > +             err = regmap_write(regmap,
> > +                                MAX31790_REG_FAN_CONFIG(channel),
> > +                                data->fan_config[channel]);
> > +             break;
> >       default:
> >               err = -EOPNOTSUPP;
> >               break;
> > @@ -260,6 +274,11 @@ static umode_t max31790_fan_is_visible(const void *_data, u32 attr, int channel)
> >                   !(fan_config & MAX31790_FAN_CFG_TACH_INPUT))
> >                       return 0644;
> >               return 0;
> > +     case hwmon_fan_enable:
> > +             if (channel < NR_CHANNEL ||
> > +                 (fan_config & MAX31790_FAN_CFG_TACH_INPUT))
> > +                     return 0644;
> > +             return 0;
> >       default:
> >               return 0;
> >       }
> > @@ -281,12 +300,12 @@ static int max31790_read_pwm(struct device *dev, u32 attr, int channel,
> >               *val = read >> 8;
> >               return 0;
> >       case hwmon_pwm_enable:
> > -             if (data->fan_config[channel] & MAX31790_FAN_CFG_RPM_MODE)
> > +             if (data->full_speed[channel])
> > +                     *val = 0;
> > +             else if (data->fan_config[channel] & MAX31790_FAN_CFG_RPM_MODE)
> >                       *val = 2;
> > -             else if (data->fan_config[channel] & MAX31790_FAN_CFG_TACH_INPUT_EN)
> > +             else
> >                       *val = 1;
> > -             else
> > -                     *val = 0;
> >               return 0;
> >       default:
> >               return -EOPNOTSUPP;
> > @@ -305,28 +324,42 @@ static int max31790_write_pwm(struct device *dev, u32 attr, int channel,
> >
> >       switch (attr) {
> >       case hwmon_pwm_input:
> > -             if (val < 0 || val > 255) {
> > +             if (data->full_speed[channel] || val < 0 || val > 255) {
> >                       err = -EINVAL;
> >                       break;
> >               }
> > +
> >               err = write_reg_word(regmap, MAX31790_REG_PWMOUT(channel), val << 8);
> >               break;
> >       case hwmon_pwm_enable:
> >               fan_config = data->fan_config[channel];
> > -             if (val == 0) {
> > -                     fan_config &= ~(MAX31790_FAN_CFG_TACH_INPUT_EN |
> > -                                     MAX31790_FAN_CFG_RPM_MODE);
> > -             } else if (val == 1) {
> > -                     fan_config = (fan_config |
> > -                                   MAX31790_FAN_CFG_TACH_INPUT_EN) &
> > -                                  ~MAX31790_FAN_CFG_RPM_MODE;
> > +             if (val == 0 || val == 1) {
> > +                     fan_config &= ~MAX31790_FAN_CFG_RPM_MODE;
> >               } else if (val == 2) {
> > -                     fan_config |= MAX31790_FAN_CFG_TACH_INPUT_EN |
> > -                                   MAX31790_FAN_CFG_RPM_MODE;
> > +                     fan_config |= MAX31790_FAN_CFG_RPM_MODE;
> >               } else {
> >                       err = -EINVAL;
> >                       break;
> >               }
> > +
> > +             /*
> > +              * The chip sets PWM to zero when using its "monitor only" mode
> > +              * and 0 means full speed.
> > +              */
> > +             if (val == 0) {
> > +                     data->full_speed[channel] = true;
> > +                     err = write_reg_word(regmap, MAX31790_REG_PWMOUT(channel), MAX_PWM);
> > +             } else {
> > +                     data->full_speed[channel] = false;
> > +             }
> > +
> > +             /*
> > +              * RPM mode implies enabled TACH input, so enable it in RPM
> > +              * mode.
> > +              */
> > +             if (val == 2)
> > +                     fan_config |= MAX31790_FAN_CFG_TACH_INPUT_EN;
> > +
> >               data->fan_config[channel] = fan_config;
> >               err = regmap_write(regmap,
> >                                  MAX31790_REG_FAN_CONFIG(channel),
> > @@ -400,18 +433,18 @@ static umode_t max31790_is_visible(const void *data,
> >
> >  static const struct hwmon_channel_info *max31790_info[] = {
> >       HWMON_CHANNEL_INFO(fan,
> > -                        HWMON_F_INPUT | HWMON_F_TARGET | HWMON_F_FAULT,
> > -                        HWMON_F_INPUT | HWMON_F_TARGET | HWMON_F_FAULT,
> > -                        HWMON_F_INPUT | HWMON_F_TARGET | HWMON_F_FAULT,
> > -                        HWMON_F_INPUT | HWMON_F_TARGET | HWMON_F_FAULT,
> > -                        HWMON_F_INPUT | HWMON_F_TARGET | HWMON_F_FAULT,
> > -                        HWMON_F_INPUT | HWMON_F_TARGET | HWMON_F_FAULT,
> > -                        HWMON_F_INPUT | HWMON_F_FAULT,
> > -                        HWMON_F_INPUT | HWMON_F_FAULT,
> > -                        HWMON_F_INPUT | HWMON_F_FAULT,
> > -                        HWMON_F_INPUT | HWMON_F_FAULT,
> > -                        HWMON_F_INPUT | HWMON_F_FAULT,
> > -                        HWMON_F_INPUT | HWMON_F_FAULT),
> > +                        HWMON_F_ENABLE | HWMON_F_INPUT | HWMON_F_TARGET | HWMON_F_FAULT,
> > +                        HWMON_F_ENABLE | HWMON_F_INPUT | HWMON_F_TARGET | HWMON_F_FAULT,
> > +                        HWMON_F_ENABLE | HWMON_F_INPUT | HWMON_F_TARGET | HWMON_F_FAULT,
> > +                        HWMON_F_ENABLE | HWMON_F_INPUT | HWMON_F_TARGET | HWMON_F_FAULT,
> > +                        HWMON_F_ENABLE | HWMON_F_INPUT | HWMON_F_TARGET | HWMON_F_FAULT,
> > +                        HWMON_F_ENABLE | HWMON_F_INPUT | HWMON_F_TARGET | HWMON_F_FAULT,
> > +                        HWMON_F_ENABLE | HWMON_F_INPUT | HWMON_F_FAULT,
> > +                        HWMON_F_ENABLE | HWMON_F_INPUT | HWMON_F_FAULT,
> > +                        HWMON_F_ENABLE | HWMON_F_INPUT | HWMON_F_FAULT,
> > +                        HWMON_F_ENABLE | HWMON_F_INPUT | HWMON_F_FAULT,
> > +                        HWMON_F_ENABLE | HWMON_F_INPUT | HWMON_F_FAULT,
> > +                        HWMON_F_ENABLE | HWMON_F_INPUT | HWMON_F_FAULT),
> >       HWMON_CHANNEL_INFO(pwm,
> >                          HWMON_PWM_INPUT | HWMON_PWM_ENABLE,
> >                          HWMON_PWM_INPUT | HWMON_PWM_ENABLE,
> > @@ -448,6 +481,8 @@ static int max31790_init_client(struct regmap *regmap,
> >               if (rv < 0)
> >                       return rv;
> >               data->fan_dynamics[i] = rv;
> > +
> > +             data->full_speed[i] = false;
> >       }
> >
> >       return 0;
diff mbox series

Patch

diff --git a/Documentation/hwmon/max31790.rst b/Documentation/hwmon/max31790.rst
index f301385d8cef..8979c8a02cd1 100644
--- a/Documentation/hwmon/max31790.rst
+++ b/Documentation/hwmon/max31790.rst
@@ -34,10 +34,12 @@  also be configured to serve as tachometer inputs.
 Sysfs entries
 -------------
 
-================== === =======================================================
+================== === =============================================================
+fan[1-12]_enable   RW  enable fan speed monitoring
 fan[1-12]_input    RO  fan tachometer speed in RPM
 fan[1-12]_fault    RO  fan experienced fault
 fan[1-6]_target    RW  desired fan speed in RPM
-pwm[1-6]_enable    RW  regulator mode, 0=disabled, 1=manual mode, 2=rpm mode
+pwm[1-6]_enable    RW  regulator mode, 0=full speed, 1=manual (pwm) mode, 2=rpm mode
+                       setting rpm mode sets fan*_enable to 1
 pwm[1-6]           RW  fan target duty cycle (0-255)
-================== === =======================================================
+================== === =============================================================
diff --git a/drivers/hwmon/max31790.c b/drivers/hwmon/max31790.c
index e3765ce4444a..ecdd55e12ffe 100644
--- a/drivers/hwmon/max31790.c
+++ b/drivers/hwmon/max31790.c
@@ -39,6 +39,7 @@ 
 
 #define FAN_RPM_MIN			120
 #define FAN_RPM_MAX			7864320
+#define MAX_PWM				0XFF80
 
 #define RPM_FROM_REG(reg, sr)		(((reg) >> 4) ? \
 					 ((60 * (sr) * 8192) / ((reg) >> 4)) : \
@@ -90,6 +91,7 @@  struct max31790_data {
 	struct regmap *regmap;
 
 	struct mutex update_lock;
+	bool full_speed[NR_CHANNEL];
 	u8 fan_config[NR_CHANNEL];
 	u8 fan_dynamics[NR_CHANNEL];
 };
@@ -191,6 +193,9 @@  static int max31790_read_fan(struct device *dev, u32 attr, int channel,
 		else
 			*val = !!(fault & (1 << channel));
 		return 0;
+	case hwmon_fan_enable:
+		*val = !!(data->fan_config[channel] & MAX31790_FAN_CFG_TACH_INPUT_EN);
+		return 0;
 	default:
 		return -EOPNOTSUPP;
 	}
@@ -233,6 +238,15 @@  static int max31790_write_fan(struct device *dev, u32 attr, int channel,
 				     MAX31790_REG_TARGET_COUNT(channel),
 				     target_count);
 		break;
+	case hwmon_fan_enable:
+		if (val == 0)
+			data->fan_config[channel] &= ~MAX31790_FAN_CFG_TACH_INPUT_EN;
+		else
+			data->fan_config[channel] |= MAX31790_FAN_CFG_TACH_INPUT_EN;
+		err = regmap_write(regmap,
+				   MAX31790_REG_FAN_CONFIG(channel),
+				   data->fan_config[channel]);
+		break;
 	default:
 		err = -EOPNOTSUPP;
 		break;
@@ -260,6 +274,11 @@  static umode_t max31790_fan_is_visible(const void *_data, u32 attr, int channel)
 		    !(fan_config & MAX31790_FAN_CFG_TACH_INPUT))
 			return 0644;
 		return 0;
+	case hwmon_fan_enable:
+		if (channel < NR_CHANNEL ||
+		    (fan_config & MAX31790_FAN_CFG_TACH_INPUT))
+			return 0644;
+		return 0;
 	default:
 		return 0;
 	}
@@ -281,12 +300,12 @@  static int max31790_read_pwm(struct device *dev, u32 attr, int channel,
 		*val = read >> 8;
 		return 0;
 	case hwmon_pwm_enable:
-		if (data->fan_config[channel] & MAX31790_FAN_CFG_RPM_MODE)
+		if (data->full_speed[channel])
+			*val = 0;
+		else if (data->fan_config[channel] & MAX31790_FAN_CFG_RPM_MODE)
 			*val = 2;
-		else if (data->fan_config[channel] & MAX31790_FAN_CFG_TACH_INPUT_EN)
+		else
 			*val = 1;
-		else
-			*val = 0;
 		return 0;
 	default:
 		return -EOPNOTSUPP;
@@ -305,28 +324,42 @@  static int max31790_write_pwm(struct device *dev, u32 attr, int channel,
 
 	switch (attr) {
 	case hwmon_pwm_input:
-		if (val < 0 || val > 255) {
+		if (data->full_speed[channel] || val < 0 || val > 255) {
 			err = -EINVAL;
 			break;
 		}
+
 		err = write_reg_word(regmap, MAX31790_REG_PWMOUT(channel), val << 8);
 		break;
 	case hwmon_pwm_enable:
 		fan_config = data->fan_config[channel];
-		if (val == 0) {
-			fan_config &= ~(MAX31790_FAN_CFG_TACH_INPUT_EN |
-					MAX31790_FAN_CFG_RPM_MODE);
-		} else if (val == 1) {
-			fan_config = (fan_config |
-				      MAX31790_FAN_CFG_TACH_INPUT_EN) &
-				     ~MAX31790_FAN_CFG_RPM_MODE;
+		if (val == 0 || val == 1) {
+			fan_config &= ~MAX31790_FAN_CFG_RPM_MODE;
 		} else if (val == 2) {
-			fan_config |= MAX31790_FAN_CFG_TACH_INPUT_EN |
-				      MAX31790_FAN_CFG_RPM_MODE;
+			fan_config |= MAX31790_FAN_CFG_RPM_MODE;
 		} else {
 			err = -EINVAL;
 			break;
 		}
+
+		/*
+		 * The chip sets PWM to zero when using its "monitor only" mode
+		 * and 0 means full speed.
+		 */
+		if (val == 0) {
+			data->full_speed[channel] = true;
+			err = write_reg_word(regmap, MAX31790_REG_PWMOUT(channel), MAX_PWM);
+		} else {
+			data->full_speed[channel] = false;
+		}
+
+		/*
+		 * RPM mode implies enabled TACH input, so enable it in RPM
+		 * mode.
+		 */
+		if (val == 2)
+			fan_config |= MAX31790_FAN_CFG_TACH_INPUT_EN;
+
 		data->fan_config[channel] = fan_config;
 		err = regmap_write(regmap,
 				   MAX31790_REG_FAN_CONFIG(channel),
@@ -400,18 +433,18 @@  static umode_t max31790_is_visible(const void *data,
 
 static const struct hwmon_channel_info *max31790_info[] = {
 	HWMON_CHANNEL_INFO(fan,
-			   HWMON_F_INPUT | HWMON_F_TARGET | HWMON_F_FAULT,
-			   HWMON_F_INPUT | HWMON_F_TARGET | HWMON_F_FAULT,
-			   HWMON_F_INPUT | HWMON_F_TARGET | HWMON_F_FAULT,
-			   HWMON_F_INPUT | HWMON_F_TARGET | HWMON_F_FAULT,
-			   HWMON_F_INPUT | HWMON_F_TARGET | HWMON_F_FAULT,
-			   HWMON_F_INPUT | HWMON_F_TARGET | HWMON_F_FAULT,
-			   HWMON_F_INPUT | HWMON_F_FAULT,
-			   HWMON_F_INPUT | HWMON_F_FAULT,
-			   HWMON_F_INPUT | HWMON_F_FAULT,
-			   HWMON_F_INPUT | HWMON_F_FAULT,
-			   HWMON_F_INPUT | HWMON_F_FAULT,
-			   HWMON_F_INPUT | HWMON_F_FAULT),
+			   HWMON_F_ENABLE | HWMON_F_INPUT | HWMON_F_TARGET | HWMON_F_FAULT,
+			   HWMON_F_ENABLE | HWMON_F_INPUT | HWMON_F_TARGET | HWMON_F_FAULT,
+			   HWMON_F_ENABLE | HWMON_F_INPUT | HWMON_F_TARGET | HWMON_F_FAULT,
+			   HWMON_F_ENABLE | HWMON_F_INPUT | HWMON_F_TARGET | HWMON_F_FAULT,
+			   HWMON_F_ENABLE | HWMON_F_INPUT | HWMON_F_TARGET | HWMON_F_FAULT,
+			   HWMON_F_ENABLE | HWMON_F_INPUT | HWMON_F_TARGET | HWMON_F_FAULT,
+			   HWMON_F_ENABLE | HWMON_F_INPUT | HWMON_F_FAULT,
+			   HWMON_F_ENABLE | HWMON_F_INPUT | HWMON_F_FAULT,
+			   HWMON_F_ENABLE | HWMON_F_INPUT | HWMON_F_FAULT,
+			   HWMON_F_ENABLE | HWMON_F_INPUT | HWMON_F_FAULT,
+			   HWMON_F_ENABLE | HWMON_F_INPUT | HWMON_F_FAULT,
+			   HWMON_F_ENABLE | HWMON_F_INPUT | HWMON_F_FAULT),
 	HWMON_CHANNEL_INFO(pwm,
 			   HWMON_PWM_INPUT | HWMON_PWM_ENABLE,
 			   HWMON_PWM_INPUT | HWMON_PWM_ENABLE,
@@ -448,6 +481,8 @@  static int max31790_init_client(struct regmap *regmap,
 		if (rv < 0)
 			return rv;
 		data->fan_dynamics[i] = rv;
+
+		data->full_speed[i] = false;
 	}
 
 	return 0;