[PATCH v3 6/6] hwmon: pwm-fan: Add hwmon_pwm_enable attribute

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



This adds the enable attribute which is used to differentiate if PWM duty
means to switch off regulator and PWM or to keep them enabled but
at inactive PWM output level.

Signed-off-by: Alexander Stein <alexander.stein@xxxxxxxxxxxxxxx>
---
 Documentation/hwmon/pwm-fan.rst | 10 ++++
 drivers/hwmon/pwm-fan.c         | 95 +++++++++++++++++++++++++++++----
 2 files changed, 95 insertions(+), 10 deletions(-)

diff --git a/Documentation/hwmon/pwm-fan.rst b/Documentation/hwmon/pwm-fan.rst
index 82fe96742fee..0083480068d1 100644
--- a/Documentation/hwmon/pwm-fan.rst
+++ b/Documentation/hwmon/pwm-fan.rst
@@ -18,3 +18,13 @@ the hwmon's sysfs interface.
 
 The fan rotation speed returned via the optional 'fan1_input' is extrapolated
 from the sampled interrupts from the tachometer signal within 1 second.
+
+The driver provides the following sensor accesses in sysfs:
+
+=============== ======= =======================================================
+fan1_input	ro	fan tachometer speed in RPM
+pwm1_enable	rw	keep enable mode, defines behaviour when pwm1=0
+			0=switch off regulator and disable PWM
+			1=keep regulator enabled and set PWM to inactive level
+pwm1		rw	relative speed (0-255), 255=max. speed.
+=============== ======= =======================================================
diff --git a/drivers/hwmon/pwm-fan.c b/drivers/hwmon/pwm-fan.c
index 9ebe958cc908..cb29206ddcdc 100644
--- a/drivers/hwmon/pwm-fan.c
+++ b/drivers/hwmon/pwm-fan.c
@@ -35,6 +35,7 @@ struct pwm_fan_ctx {
 	struct pwm_device *pwm;
 	struct regulator *reg_en;
 	bool enabled;
+	bool keep_enabled;
 
 	int tach_count;
 	struct pwm_fan_tach *tachs;
@@ -129,7 +130,8 @@ static int pwm_fan_power_off(struct pwm_fan_ctx *ctx)
 		return 0;
 
 	pwm_get_state(ctx->pwm, &state);
-	state.enabled = false;
+	if (!ctx->keep_enabled)
+		state.enabled = false;
 	state.duty_cycle = 0;
 	pwm_apply_state(ctx->pwm, &state);
 
@@ -178,20 +180,87 @@ static void pwm_fan_update_state(struct pwm_fan_ctx *ctx, unsigned long pwm)
 	ctx->pwm_fan_state = i;
 }
 
+static int pwm_fan_update_enable(struct pwm_fan_ctx *ctx, long val)
+{
+	struct pwm_state old_state;
+	int ret;
+
+	pwm_get_state(ctx->pwm, &old_state);
+
+	if (val) {
+		/*
+		 * If PWM is already enabled, nothing will change
+		 * If PWM is disabled, it will enable with inactive level (duty == 0)
+		 */
+		ret = pwm_fan_enable_pwm(ctx);
+		if (ret) {
+			dev_err(ctx->dev, "failed to apply PWM state\n");
+			return ret;
+		}
+
+		/* Increase regulator reference counter to prevent switching off */
+		ret = regulator_enable(ctx->reg_en);
+		if (ret < 0) {
+			dev_err(ctx->dev, "failed to enable regulator\n");
+			/* Restore old state */
+			pwm_apply_state(ctx->pwm, &old_state);
+		}
+	} else {
+		/* Only disable PWM if currently on inactive state */
+		if (!ctx->pwm_value) {
+			struct pwm_state disable_state;
+
+			pwm_get_state(ctx->pwm, &disable_state);
+			disable_state.duty_cycle = 0;
+			disable_state.enabled = false;
+			ret = pwm_apply_state(ctx->pwm, &disable_state);
+			if (ret) {
+				dev_err(ctx->dev, "failed to apply PWM state\n");
+				return ret;
+			}
+		}
+		/* Decrease regulator reference counter */
+		ret = regulator_disable(ctx->reg_en);
+		if (ret < 0) {
+			dev_err(ctx->dev, "failed to decrease power supply usage\n");
+			/* Restore old state */
+			pwm_apply_state(ctx->pwm, &old_state);
+		}
+	}
+	if (ret == 0)
+		ctx->keep_enabled = val;
+
+	return ret;
+}
+
 static int pwm_fan_write(struct device *dev, enum hwmon_sensor_types type,
 			 u32 attr, int channel, long val)
 {
 	struct pwm_fan_ctx *ctx = dev_get_drvdata(dev);
 	int ret;
 
-	if (val < 0 || val > MAX_PWM)
-		return -EINVAL;
+	switch (attr) {
+	case hwmon_pwm_input:
+		if (val < 0 || val > MAX_PWM)
+			return -EINVAL;
+		ret = __set_pwm(ctx, val);
+		if (ret)
+			return ret;
+		pwm_fan_update_state(ctx, val);
+		break;
+	case hwmon_pwm_enable:
+		mutex_lock(&ctx->lock);
+		if (val < 0 || val > 1)
+			ret = -EINVAL;
+		else if (ctx->keep_enabled != val)
+			ret = pwm_fan_update_enable(ctx, val);
+		mutex_unlock(&ctx->lock);
 
-	ret = __set_pwm(ctx, val);
-	if (ret)
 		return ret;
+	default:
+		return -EOPNOTSUPP;
+	}
 
-	pwm_fan_update_state(ctx, val);
 	return 0;
 }
 
@@ -202,9 +271,15 @@ static int pwm_fan_read(struct device *dev, enum hwmon_sensor_types type,
 
 	switch (type) {
 	case hwmon_pwm:
-		*val = ctx->pwm_value;
-		return 0;
-
+		switch (attr) {
+		case hwmon_pwm_input:
+			*val = ctx->pwm_value;
+			return 0;
+		case hwmon_pwm_enable:
+			*val = ctx->keep_enabled;
+			return 0;
+		}
+		return -EOPNOTSUPP;
 	case hwmon_fan:
 		*val = ctx->tachs[channel].rpm;
 		return 0;
@@ -436,7 +511,7 @@ static int pwm_fan_probe(struct platform_device *pdev)
 	if (!channels)
 		return -ENOMEM;
 
-	channels[0] = HWMON_CHANNEL_INFO(pwm, HWMON_PWM_INPUT);
+	channels[0] = HWMON_CHANNEL_INFO(pwm, HWMON_PWM_INPUT | HWMON_PWM_ENABLE);
 
 	for (i = 0; i < ctx->tach_count; i++) {
 		struct pwm_fan_tach *tach = &ctx->tachs[i];
-- 
2.25.1




[Index of Archives]     [LM Sensors]     [Linux Sound]     [ALSA Users]     [ALSA Devel]     [Linux Audio Users]     [Linux Media]     [Kernel]     [Gimp]     [Yosemite News]     [Linux Media]

  Powered by Linux