All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH v5 0/5] hwmon: pwm-fan: switch regulator dynamically
@ 2022-09-14 15:31 Alexander Stein
  2022-09-14 15:31 ` [PATCH v5 1/5] hwmon: pwm-fan: Refactor fan power on/off Alexander Stein
                   ` (4 more replies)
  0 siblings, 5 replies; 11+ messages in thread
From: Alexander Stein @ 2022-09-14 15:31 UTC (permalink / raw)
  To: Jean Delvare, Guenter Roeck, Jonathan Corbet, Thierry Reding,
	Uwe Kleine-König, Lee Jones
  Cc: Alexander Stein, linux-hwmon, linux-pwm, Markus Niebel

Hello everyone,

this is the next version for supporting switching off the pwm-fan regulator.
Review points from Guenter are included.

Changes in v5:
* Added Guenter's R-b to Patch 1 & 4
* Removed useless goto (Patch 2)
* Added error checking when switching off PWM (Patch 2)
* Removed useless branches when switching power (Patch 3)
* Removed useless (temporary) variable (Patch 3)
* Updated Patch 5 to changes of previous patches
* Dropped Patch 6 completly for lack of benefit

Changes in v4:
* Reordered commits so current behavior is the default all the time
* Fixed pwm state bug in Patch 1, this affects the patch context in Patch 2
* Refactor even more code in Patch 3 & 4 for smaller further patches
* Squashed the dynamic regulator switch patch and the sysfs attribute patch
  into one, keeping default behavior.
  Overhaul the patch to support different modes altogether
* Fixed bugs in module removal in pwm1_enable=1
* Moved internal PWM state removal to the end to keep the patch smaller

Best regards,
Alexander

Alexander Stein (5):
  hwmon: pwm-fan: Refactor fan power on/off
  hwmon: pwm-fan: Simplify enable/disable check
  hwmon: pwm-fan: Add dedicated power switch function
  hwmon: pwm-fan: split __set_pwm into locked/unlocked functions
  hwmon: pwm-fan: Switch regulator dynamically

 Documentation/hwmon/pwm-fan.rst |  12 ++
 drivers/hwmon/pwm-fan.c         | 312 +++++++++++++++++++++++---------
 2 files changed, 241 insertions(+), 83 deletions(-)

-- 
2.25.1


^ permalink raw reply	[flat|nested] 11+ messages in thread

* [PATCH v5 1/5] hwmon: pwm-fan: Refactor fan power on/off
  2022-09-14 15:31 [PATCH v5 0/5] hwmon: pwm-fan: switch regulator dynamically Alexander Stein
@ 2022-09-14 15:31 ` Alexander Stein
  2022-09-20 14:15   ` Guenter Roeck
  2022-09-14 15:31 ` [PATCH v5 2/5] hwmon: pwm-fan: Simplify enable/disable check Alexander Stein
                   ` (3 subsequent siblings)
  4 siblings, 1 reply; 11+ messages in thread
From: Alexander Stein @ 2022-09-14 15:31 UTC (permalink / raw)
  To: Jean Delvare, Guenter Roeck, Jonathan Corbet, Thierry Reding,
	Uwe Kleine-König, Lee Jones
  Cc: Alexander Stein, linux-hwmon, linux-pwm, Markus Niebel

In preparation for dynamically switching regulator, split the power on
and power off sequence into separate functions.

Signed-off-by: Alexander Stein <alexander.stein@ew.tq-group.com>
Reviewed-by: Guenter Roeck <linux@roeck-us.net>
---
 drivers/hwmon/pwm-fan.c | 36 ++++++++++++++++++++++++++++++------
 1 file changed, 30 insertions(+), 6 deletions(-)

diff --git a/drivers/hwmon/pwm-fan.c b/drivers/hwmon/pwm-fan.c
index 06fd1d75101d..c8a7926d39e7 100644
--- a/drivers/hwmon/pwm-fan.c
+++ b/drivers/hwmon/pwm-fan.c
@@ -82,23 +82,47 @@ static void sample_timer(struct timer_list *t)
 	mod_timer(&ctx->rpm_timer, jiffies + HZ);
 }
 
-static int  __set_pwm(struct pwm_fan_ctx *ctx, unsigned long pwm)
+static int pwm_fan_power_on(struct pwm_fan_ctx *ctx)
 {
+	struct pwm_state *state = &ctx->pwm_state;
 	unsigned long period;
-	int ret = 0;
+	int ret;
+
+	period = state->period;
+	state->duty_cycle = DIV_ROUND_UP(ctx->pwm_value * (period - 1), MAX_PWM);
+	state->enabled = true;
+	ret = pwm_apply_state(ctx->pwm, state);
+
+	return ret;
+}
+
+static int pwm_fan_power_off(struct pwm_fan_ctx *ctx)
+{
 	struct pwm_state *state = &ctx->pwm_state;
 
+	state->enabled = false;
+	state->duty_cycle = 0;
+	pwm_apply_state(ctx->pwm, state);
+
+	return 0;
+}
+
+static int  __set_pwm(struct pwm_fan_ctx *ctx, unsigned long pwm)
+{
+	int ret = 0;
+
 	mutex_lock(&ctx->lock);
 	if (ctx->pwm_value == pwm)
 		goto exit_set_pwm_err;
 
-	period = state->period;
-	state->duty_cycle = DIV_ROUND_UP(pwm * (period - 1), MAX_PWM);
-	state->enabled = pwm ? true : false;
+	if (pwm > 0)
+		ret = pwm_fan_power_on(ctx);
+	else
+		ret = pwm_fan_power_off(ctx);
 
-	ret = pwm_apply_state(ctx->pwm, state);
 	if (!ret)
 		ctx->pwm_value = pwm;
+
 exit_set_pwm_err:
 	mutex_unlock(&ctx->lock);
 	return ret;
-- 
2.25.1


^ permalink raw reply related	[flat|nested] 11+ messages in thread

* [PATCH v5 2/5] hwmon: pwm-fan: Simplify enable/disable check
  2022-09-14 15:31 [PATCH v5 0/5] hwmon: pwm-fan: switch regulator dynamically Alexander Stein
  2022-09-14 15:31 ` [PATCH v5 1/5] hwmon: pwm-fan: Refactor fan power on/off Alexander Stein
@ 2022-09-14 15:31 ` Alexander Stein
  2022-09-20 14:17   ` Guenter Roeck
  2022-09-14 15:31 ` [PATCH v5 3/5] hwmon: pwm-fan: Add dedicated power switch function Alexander Stein
                   ` (2 subsequent siblings)
  4 siblings, 1 reply; 11+ messages in thread
From: Alexander Stein @ 2022-09-14 15:31 UTC (permalink / raw)
  To: Jean Delvare, Guenter Roeck, Jonathan Corbet, Thierry Reding,
	Uwe Kleine-König, Lee Jones
  Cc: Alexander Stein, linux-hwmon, linux-pwm, Markus Niebel

Instead of comparing the current to the new pwm duty to decide whether to
enable the PWM, use a dedicated flag. Also apply the new PWM duty in any
case. This is a preparation to enable/disable the regulator dynamically.

Signed-off-by: Alexander Stein <alexander.stein@ew.tq-group.com>
---
 drivers/hwmon/pwm-fan.c | 43 ++++++++++++++++++++++++++++++++---------
 1 file changed, 34 insertions(+), 9 deletions(-)

diff --git a/drivers/hwmon/pwm-fan.c b/drivers/hwmon/pwm-fan.c
index c8a7926d39e7..01412c71deb3 100644
--- a/drivers/hwmon/pwm-fan.c
+++ b/drivers/hwmon/pwm-fan.c
@@ -29,10 +29,13 @@ struct pwm_fan_tach {
 };
 
 struct pwm_fan_ctx {
+	struct device *dev;
+
 	struct mutex lock;
 	struct pwm_device *pwm;
 	struct pwm_state pwm_state;
 	struct regulator *reg_en;
+	bool enabled;
 
 	int tach_count;
 	struct pwm_fan_tach *tachs;
@@ -85,13 +88,19 @@ static void sample_timer(struct timer_list *t)
 static int pwm_fan_power_on(struct pwm_fan_ctx *ctx)
 {
 	struct pwm_state *state = &ctx->pwm_state;
-	unsigned long period;
 	int ret;
 
-	period = state->period;
-	state->duty_cycle = DIV_ROUND_UP(ctx->pwm_value * (period - 1), MAX_PWM);
+	if (ctx->enabled)
+		return 0;
+
 	state->enabled = true;
 	ret = pwm_apply_state(ctx->pwm, state);
+	if (ret) {
+		dev_err(ctx->dev, "failed to enable PWM\n");
+		return ret;
+	}
+
+	ctx->enabled = true;
 
 	return ret;
 }
@@ -99,27 +108,42 @@ static int pwm_fan_power_on(struct pwm_fan_ctx *ctx)
 static int pwm_fan_power_off(struct pwm_fan_ctx *ctx)
 {
 	struct pwm_state *state = &ctx->pwm_state;
+	int ret;
+
+	if (!ctx->enabled)
+		return 0;
 
 	state->enabled = false;
 	state->duty_cycle = 0;
-	pwm_apply_state(ctx->pwm, state);
+	ret = pwm_apply_state(ctx->pwm, state);
+	if (ret) {
+		dev_err(ctx->dev, "failed to disable PWM\n");
+		return ret;
+	}
+
+	ctx->enabled = false;
 
 	return 0;
 }
 
 static int  __set_pwm(struct pwm_fan_ctx *ctx, unsigned long pwm)
 {
+	struct pwm_state *state = &ctx->pwm_state;
+	unsigned long period;
 	int ret = 0;
 
 	mutex_lock(&ctx->lock);
-	if (ctx->pwm_value == pwm)
-		goto exit_set_pwm_err;
 
-	if (pwm > 0)
+	if (pwm > 0) {
+		period = state->period;
+		state->duty_cycle = DIV_ROUND_UP(pwm * (period - 1), MAX_PWM);
+		ret = pwm_apply_state(ctx->pwm, state);
+		if (ret)
+			goto exit_set_pwm_err;
 		ret = pwm_fan_power_on(ctx);
-	else
+	} else {
 		ret = pwm_fan_power_off(ctx);
-
+	}
 	if (!ret)
 		ctx->pwm_value = pwm;
 
@@ -326,6 +350,7 @@ static int pwm_fan_probe(struct platform_device *pdev)
 
 	mutex_init(&ctx->lock);
 
+	ctx->dev = &pdev->dev;
 	ctx->pwm = devm_pwm_get(dev, NULL);
 	if (IS_ERR(ctx->pwm))
 		return dev_err_probe(dev, PTR_ERR(ctx->pwm), "Could not get PWM\n");
-- 
2.25.1


^ permalink raw reply related	[flat|nested] 11+ messages in thread

* [PATCH v5 3/5] hwmon: pwm-fan: Add dedicated power switch function
  2022-09-14 15:31 [PATCH v5 0/5] hwmon: pwm-fan: switch regulator dynamically Alexander Stein
  2022-09-14 15:31 ` [PATCH v5 1/5] hwmon: pwm-fan: Refactor fan power on/off Alexander Stein
  2022-09-14 15:31 ` [PATCH v5 2/5] hwmon: pwm-fan: Simplify enable/disable check Alexander Stein
@ 2022-09-14 15:31 ` Alexander Stein
  2022-09-20 14:18   ` Guenter Roeck
  2022-09-14 15:31 ` [PATCH v5 4/5] hwmon: pwm-fan: split __set_pwm into locked/unlocked functions Alexander Stein
  2022-09-14 15:31 ` [PATCH v5 5/5] hwmon: pwm-fan: Switch regulator dynamically Alexander Stein
  4 siblings, 1 reply; 11+ messages in thread
From: Alexander Stein @ 2022-09-14 15:31 UTC (permalink / raw)
  To: Jean Delvare, Guenter Roeck, Jonathan Corbet, Thierry Reding,
	Uwe Kleine-König, Lee Jones
  Cc: Alexander Stein, linux-hwmon, linux-pwm, Markus Niebel

This handles enabling/disabling the regulator in a single function, while
keeping the enables/disabled balanced. This is a preparation when
regulator is switched from different code paths.

Signed-off-by: Alexander Stein <alexander.stein@ew.tq-group.com>
---
 drivers/hwmon/pwm-fan.c | 46 +++++++++++++++++++++++++++--------------
 1 file changed, 31 insertions(+), 15 deletions(-)

diff --git a/drivers/hwmon/pwm-fan.c b/drivers/hwmon/pwm-fan.c
index 01412c71deb3..92c5b7f5ddd6 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 pwm_state pwm_state;
 	struct regulator *reg_en;
+	bool regulator_enabled;
 	bool enabled;
 
 	int tach_count;
@@ -85,6 +86,25 @@ static void sample_timer(struct timer_list *t)
 	mod_timer(&ctx->rpm_timer, jiffies + HZ);
 }
 
+static int pwm_fan_switch_power(struct pwm_fan_ctx *ctx, bool on)
+{
+	int ret = 0;
+
+	if (!ctx->reg_en)
+		return ret;
+
+	if (!ctx->regulator_enabled && on) {
+		ret = regulator_enable(ctx->reg_en);
+		if (ret == 0)
+			ctx->regulator_enabled = true;
+	} else if (ctx->regulator_enabled && !on) {
+		ret = regulator_disable(ctx->reg_en);
+		if (ret == 0)
+			ctx->regulator_enabled = false;
+	}
+	return ret;
+}
+
 static int pwm_fan_power_on(struct pwm_fan_ctx *ctx)
 {
 	struct pwm_state *state = &ctx->pwm_state;
@@ -320,7 +340,7 @@ static int pwm_fan_of_get_cooling_data(struct device *dev,
 
 static void pwm_fan_regulator_disable(void *data)
 {
-	regulator_disable(data);
+	pwm_fan_switch_power(data, false);
 }
 
 static void pwm_fan_pwm_disable(void *__ctx)
@@ -364,13 +384,13 @@ static int pwm_fan_probe(struct platform_device *pdev)
 
 		ctx->reg_en = NULL;
 	} else {
-		ret = regulator_enable(ctx->reg_en);
+		ret = pwm_fan_switch_power(ctx, true);
 		if (ret) {
 			dev_err(dev, "Failed to enable fan supply: %d\n", ret);
 			return ret;
 		}
 		ret = devm_add_action_or_reset(dev, pwm_fan_regulator_disable,
-					       ctx->reg_en);
+					       ctx);
 		if (ret)
 			return ret;
 	}
@@ -516,12 +536,10 @@ static int pwm_fan_disable(struct device *dev)
 			return ret;
 	}
 
-	if (ctx->reg_en) {
-		ret = regulator_disable(ctx->reg_en);
-		if (ret) {
-			dev_err(dev, "Failed to disable fan supply: %d\n", ret);
-			return ret;
-		}
+	ret = pwm_fan_switch_power(ctx, false);
+	if (ret) {
+		dev_err(dev, "Failed to disable fan supply: %d\n", ret);
+		return ret;
 	}
 
 	return 0;
@@ -543,12 +561,10 @@ static int pwm_fan_resume(struct device *dev)
 	struct pwm_fan_ctx *ctx = dev_get_drvdata(dev);
 	int ret;
 
-	if (ctx->reg_en) {
-		ret = regulator_enable(ctx->reg_en);
-		if (ret) {
-			dev_err(dev, "Failed to enable fan supply: %d\n", ret);
-			return ret;
-		}
+	ret = pwm_fan_switch_power(ctx, true);
+	if (ret) {
+		dev_err(dev, "Failed to enable fan supply: %d\n", ret);
+		return ret;
 	}
 
 	if (ctx->pwm_value == 0)
-- 
2.25.1


^ permalink raw reply related	[flat|nested] 11+ messages in thread

* [PATCH v5 4/5] hwmon: pwm-fan: split __set_pwm into locked/unlocked functions
  2022-09-14 15:31 [PATCH v5 0/5] hwmon: pwm-fan: switch regulator dynamically Alexander Stein
                   ` (2 preceding siblings ...)
  2022-09-14 15:31 ` [PATCH v5 3/5] hwmon: pwm-fan: Add dedicated power switch function Alexander Stein
@ 2022-09-14 15:31 ` Alexander Stein
  2022-09-20 14:19   ` Guenter Roeck
  2022-09-14 15:31 ` [PATCH v5 5/5] hwmon: pwm-fan: Switch regulator dynamically Alexander Stein
  4 siblings, 1 reply; 11+ messages in thread
From: Alexander Stein @ 2022-09-14 15:31 UTC (permalink / raw)
  To: Jean Delvare, Guenter Roeck, Jonathan Corbet, Thierry Reding,
	Uwe Kleine-König, Lee Jones
  Cc: Alexander Stein, linux-hwmon, linux-pwm, Markus Niebel

Regular calls to set_pwm don't hold the mutex, but the upcoming
update_enable support needs to call set_pwm with the mutex being held.
So provide the previous behavior in set_pwm (handling the lock), while
adding __set_pwm which assumes the lock being held.

Signed-off-by: Alexander Stein <alexander.stein@ew.tq-group.com>
Reviewed-by: Guenter Roeck <linux@roeck-us.net>
---
 drivers/hwmon/pwm-fan.c | 23 +++++++++++++++--------
 1 file changed, 15 insertions(+), 8 deletions(-)

diff --git a/drivers/hwmon/pwm-fan.c b/drivers/hwmon/pwm-fan.c
index 92c5b7f5ddd6..12ef3b3dbe22 100644
--- a/drivers/hwmon/pwm-fan.c
+++ b/drivers/hwmon/pwm-fan.c
@@ -152,14 +152,12 @@ static int  __set_pwm(struct pwm_fan_ctx *ctx, unsigned long pwm)
 	unsigned long period;
 	int ret = 0;
 
-	mutex_lock(&ctx->lock);
-
 	if (pwm > 0) {
 		period = state->period;
 		state->duty_cycle = DIV_ROUND_UP(pwm * (period - 1), MAX_PWM);
 		ret = pwm_apply_state(ctx->pwm, state);
 		if (ret)
-			goto exit_set_pwm_err;
+			return ret;
 		ret = pwm_fan_power_on(ctx);
 	} else {
 		ret = pwm_fan_power_off(ctx);
@@ -167,8 +165,17 @@ static int  __set_pwm(struct pwm_fan_ctx *ctx, unsigned long pwm)
 	if (!ret)
 		ctx->pwm_value = pwm;
 
-exit_set_pwm_err:
+	return ret;
+}
+
+static int set_pwm(struct pwm_fan_ctx *ctx, unsigned long pwm)
+{
+	int ret;
+
+	mutex_lock(&ctx->lock);
+	ret = __set_pwm(ctx, pwm);
 	mutex_unlock(&ctx->lock);
+
 	return ret;
 }
 
@@ -192,7 +199,7 @@ static int pwm_fan_write(struct device *dev, enum hwmon_sensor_types type,
 	if (val < 0 || val > MAX_PWM)
 		return -EINVAL;
 
-	ret = __set_pwm(ctx, val);
+	ret = set_pwm(ctx, val);
 	if (ret)
 		return ret;
 
@@ -280,7 +287,7 @@ pwm_fan_set_cur_state(struct thermal_cooling_device *cdev, unsigned long state)
 	if (state == ctx->pwm_fan_state)
 		return 0;
 
-	ret = __set_pwm(ctx, ctx->pwm_fan_cooling_levels[state]);
+	ret = set_pwm(ctx, ctx->pwm_fan_cooling_levels[state]);
 	if (ret) {
 		dev_err(&cdev->device, "Cannot set pwm!\n");
 		return ret;
@@ -398,7 +405,7 @@ static int pwm_fan_probe(struct platform_device *pdev)
 	pwm_init_state(ctx->pwm, &ctx->pwm_state);
 
 	/*
-	 * __set_pwm assumes that MAX_PWM * (period - 1) fits into an unsigned
+	 * set_pwm assumes that MAX_PWM * (period - 1) fits into an unsigned
 	 * long. Check this here to prevent the fan running at a too low
 	 * frequency.
 	 */
@@ -408,7 +415,7 @@ static int pwm_fan_probe(struct platform_device *pdev)
 	}
 
 	/* Set duty cycle to maximum allowed and enable PWM output */
-	ret = __set_pwm(ctx, MAX_PWM);
+	ret = set_pwm(ctx, MAX_PWM);
 	if (ret) {
 		dev_err(dev, "Failed to configure PWM: %d\n", ret);
 		return ret;
-- 
2.25.1


^ permalink raw reply related	[flat|nested] 11+ messages in thread

* [PATCH v5 5/5] hwmon: pwm-fan: Switch regulator dynamically
  2022-09-14 15:31 [PATCH v5 0/5] hwmon: pwm-fan: switch regulator dynamically Alexander Stein
                   ` (3 preceding siblings ...)
  2022-09-14 15:31 ` [PATCH v5 4/5] hwmon: pwm-fan: split __set_pwm into locked/unlocked functions Alexander Stein
@ 2022-09-14 15:31 ` Alexander Stein
  2022-09-20 14:20   ` Guenter Roeck
  4 siblings, 1 reply; 11+ messages in thread
From: Alexander Stein @ 2022-09-14 15:31 UTC (permalink / raw)
  To: Jean Delvare, Guenter Roeck, Jonathan Corbet, Thierry Reding,
	Uwe Kleine-König, Lee Jones
  Cc: Alexander Stein, linux-hwmon, linux-pwm, Markus Niebel

This adds the enable attribute which is used to select if zero PWM duty
means to switch off regulator and PWM or to keep them enabled but
at inactive PWM output level.
Depending on the select enable mode, turn off the regulator and PWM if
the PWM duty is zero, or keep them enabled.
This is especially important for fan using inverted PWM signal polarity.
Having regulator supplied and PWM disabled, some PWM controllers provide
the active, rather than inactive signal.

With this change the shutdown as well as suspend/resume paths require
modifcations as well.

Signed-off-by: Alexander Stein <alexander.stein@ew.tq-group.com>
---
 Documentation/hwmon/pwm-fan.rst |  12 ++
 drivers/hwmon/pwm-fan.c         | 210 +++++++++++++++++++++-----------
 2 files changed, 154 insertions(+), 68 deletions(-)

diff --git a/Documentation/hwmon/pwm-fan.rst b/Documentation/hwmon/pwm-fan.rst
index 82fe96742fee..f77998b204ef 100644
--- a/Documentation/hwmon/pwm-fan.rst
+++ b/Documentation/hwmon/pwm-fan.rst
@@ -18,3 +18,15 @@ 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 -> disable pwm and regulator
+			1 -> enable pwm; if pwm==0, disable pwm, keep regulator enabled
+			2 -> enable pwm; if pwm==0, keep pwm and regulator enabled
+			3 -> enable pwm; if pwm==0, disable pwm and regulator
+pwm1		rw	relative speed (0-255), 255=max. speed.
+=============== ======= =======================================================
diff --git a/drivers/hwmon/pwm-fan.c b/drivers/hwmon/pwm-fan.c
index 12ef3b3dbe22..498128eb81f1 100644
--- a/drivers/hwmon/pwm-fan.c
+++ b/drivers/hwmon/pwm-fan.c
@@ -28,6 +28,13 @@ struct pwm_fan_tach {
 	u8 pulses_per_revolution;
 };
 
+enum pwm_fan_enable_mode {
+	pwm_off_reg_off,
+	pwm_disable_reg_enable,
+	pwm_enable_reg_enable,
+	pwm_disable_reg_disable,
+};
+
 struct pwm_fan_ctx {
 	struct device *dev;
 
@@ -35,6 +42,7 @@ struct pwm_fan_ctx {
 	struct pwm_device *pwm;
 	struct pwm_state pwm_state;
 	struct regulator *reg_en;
+	enum pwm_fan_enable_mode enable_mode;
 	bool regulator_enabled;
 	bool enabled;
 
@@ -86,6 +94,29 @@ static void sample_timer(struct timer_list *t)
 	mod_timer(&ctx->rpm_timer, jiffies + HZ);
 }
 
+static void pwm_fan_enable_mode_2_state(int enable_mode,
+					struct pwm_state *state,
+					bool *enable_regulator)
+{
+	switch (enable_mode) {
+	case pwm_disable_reg_enable:
+		/* disable pwm, keep regulator enabled */
+		state->enabled = false;
+		*enable_regulator = true;
+		break;
+	case pwm_enable_reg_enable:
+		/* keep pwm and regulator enabled */
+		state->enabled = true;
+		*enable_regulator = true;
+		break;
+	case pwm_off_reg_off:
+	case pwm_disable_reg_disable:
+		/* disable pwm and regulator */
+		state->enabled = false;
+		*enable_regulator = false;
+	}
+}
+
 static int pwm_fan_switch_power(struct pwm_fan_ctx *ctx, bool on)
 {
 	int ret = 0;
@@ -113,26 +144,41 @@ static int pwm_fan_power_on(struct pwm_fan_ctx *ctx)
 	if (ctx->enabled)
 		return 0;
 
+	ret = pwm_fan_switch_power(ctx, true);
+	if (ret < 0) {
+		dev_err(ctx->dev, "failed to enable power supply\n");
+		return ret;
+	}
+
 	state->enabled = true;
 	ret = pwm_apply_state(ctx->pwm, state);
 	if (ret) {
 		dev_err(ctx->dev, "failed to enable PWM\n");
-		return ret;
+		goto disable_regulator;
 	}
 
 	ctx->enabled = true;
 
+	return 0;
+
+disable_regulator:
+	pwm_fan_switch_power(ctx, false);
 	return ret;
 }
 
 static int pwm_fan_power_off(struct pwm_fan_ctx *ctx)
 {
 	struct pwm_state *state = &ctx->pwm_state;
+	bool enable_regulator = false;
 	int ret;
 
 	if (!ctx->enabled)
 		return 0;
 
+	pwm_fan_enable_mode_2_state(ctx->enable_mode,
+				    state,
+				    &enable_regulator);
+
 	state->enabled = false;
 	state->duty_cycle = 0;
 	ret = pwm_apply_state(ctx->pwm, state);
@@ -141,6 +187,8 @@ static int pwm_fan_power_off(struct pwm_fan_ctx *ctx)
 		return ret;
 	}
 
+	pwm_fan_switch_power(ctx, enable_regulator);
+
 	ctx->enabled = false;
 
 	return 0;
@@ -153,6 +201,10 @@ static int  __set_pwm(struct pwm_fan_ctx *ctx, unsigned long pwm)
 	int ret = 0;
 
 	if (pwm > 0) {
+		if (ctx->enable_mode == pwm_off_reg_off)
+			/* pwm-fan hard disabled */
+			return 0;
+
 		period = state->period;
 		state->duty_cycle = DIV_ROUND_UP(pwm * (period - 1), MAX_PWM);
 		ret = pwm_apply_state(ctx->pwm, state);
@@ -190,20 +242,76 @@ 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)
+{
+	int ret = 0;
+	int old_val;
+
+	mutex_lock(&ctx->lock);
+
+	if (ctx->enable_mode == val)
+		goto out;
+
+	old_val = ctx->enable_mode;
+	ctx->enable_mode = val;
+
+	if (val == 0) {
+		/* Disable pwm-fan unconditionally */
+		ret = __set_pwm(ctx, 0);
+		if (ret)
+			ctx->enable_mode = old_val;
+		pwm_fan_update_state(ctx, 0);
+	} else {
+		/*
+		 * Change PWM and/or regulator state if currently disabled
+		 * Nothing to do if currently enabled
+		 */
+		if (!ctx->enabled) {
+			struct pwm_state *state = &ctx->pwm_state;
+			bool enable_regulator = false;
+
+			state->duty_cycle = 0;
+			pwm_fan_enable_mode_2_state(val,
+						    state,
+						    &enable_regulator);
+
+			pwm_apply_state(ctx->pwm, state);
+			pwm_fan_switch_power(ctx, enable_regulator);
+			pwm_fan_update_state(ctx, 0);
+		}
+	}
+out:
+	mutex_unlock(&ctx->lock);
+
+	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:
+		if (val < 0 || val > 3)
+			ret = -EINVAL;
+		else
+			ret = pwm_fan_update_enable(ctx, val);
 
-	ret = set_pwm(ctx, val);
-	if (ret)
 		return ret;
+	default:
+		return -EOPNOTSUPP;
+	}
 
-	pwm_fan_update_state(ctx, val);
 	return 0;
 }
 
@@ -214,9 +322,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->enable_mode;
+			return 0;
+		}
+		return -EOPNOTSUPP;
 	case hwmon_fan:
 		*val = ctx->tachs[channel].rpm;
 		return 0;
@@ -345,18 +459,14 @@ static int pwm_fan_of_get_cooling_data(struct device *dev,
 	return 0;
 }
 
-static void pwm_fan_regulator_disable(void *data)
-{
-	pwm_fan_switch_power(data, false);
-}
-
-static void pwm_fan_pwm_disable(void *__ctx)
+static void pwm_fan_cleanup(void *__ctx)
 {
 	struct pwm_fan_ctx *ctx = __ctx;
 
-	ctx->pwm_state.enabled = false;
-	pwm_apply_state(ctx->pwm, &ctx->pwm_state);
 	del_timer_sync(&ctx->rpm_timer);
+	/* Switch off everything */
+	ctx->enable_mode = pwm_disable_reg_disable;
+	pwm_fan_power_off(ctx);
 }
 
 static int pwm_fan_probe(struct platform_device *pdev)
@@ -390,16 +500,6 @@ static int pwm_fan_probe(struct platform_device *pdev)
 			return PTR_ERR(ctx->reg_en);
 
 		ctx->reg_en = NULL;
-	} else {
-		ret = pwm_fan_switch_power(ctx, true);
-		if (ret) {
-			dev_err(dev, "Failed to enable fan supply: %d\n", ret);
-			return ret;
-		}
-		ret = devm_add_action_or_reset(dev, pwm_fan_regulator_disable,
-					       ctx);
-		if (ret)
-			return ret;
 	}
 
 	pwm_init_state(ctx->pwm, &ctx->pwm_state);
@@ -414,14 +514,19 @@ static int pwm_fan_probe(struct platform_device *pdev)
 		return -EINVAL;
 	}
 
-	/* Set duty cycle to maximum allowed and enable PWM output */
+	ctx->enable_mode = pwm_disable_reg_enable;
+
+	/*
+	 * Set duty cycle to maximum allowed and enable PWM output as well as
+	 * the regulator. In case of error nothing is changed
+	 */
 	ret = set_pwm(ctx, MAX_PWM);
 	if (ret) {
 		dev_err(dev, "Failed to configure PWM: %d\n", ret);
 		return ret;
 	}
 	timer_setup(&ctx->rpm_timer, sample_timer, 0);
-	ret = devm_add_action_or_reset(dev, pwm_fan_pwm_disable, ctx);
+	ret = devm_add_action_or_reset(dev, pwm_fan_cleanup, ctx);
 	if (ret)
 		return ret;
 
@@ -453,7 +558,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];
@@ -527,57 +632,26 @@ static int pwm_fan_probe(struct platform_device *pdev)
 	return 0;
 }
 
-static int pwm_fan_disable(struct device *dev)
-{
-	struct pwm_fan_ctx *ctx = dev_get_drvdata(dev);
-	int ret;
-
-	if (ctx->pwm_value) {
-		/* keep ctx->pwm_state unmodified for pwm_fan_resume() */
-		struct pwm_state state = ctx->pwm_state;
-
-		state.duty_cycle = 0;
-		state.enabled = false;
-		ret = pwm_apply_state(ctx->pwm, &state);
-		if (ret < 0)
-			return ret;
-	}
-
-	ret = pwm_fan_switch_power(ctx, false);
-	if (ret) {
-		dev_err(dev, "Failed to disable fan supply: %d\n", ret);
-		return ret;
-	}
-
-	return 0;
-}
-
 static void pwm_fan_shutdown(struct platform_device *pdev)
 {
-	pwm_fan_disable(&pdev->dev);
+	struct pwm_fan_ctx *ctx = platform_get_drvdata(pdev);
+
+	pwm_fan_cleanup(ctx);
 }
 
 #ifdef CONFIG_PM_SLEEP
 static int pwm_fan_suspend(struct device *dev)
 {
-	return pwm_fan_disable(dev);
+	struct pwm_fan_ctx *ctx = dev_get_drvdata(dev);
+
+	return pwm_fan_power_off(ctx);
 }
 
 static int pwm_fan_resume(struct device *dev)
 {
 	struct pwm_fan_ctx *ctx = dev_get_drvdata(dev);
-	int ret;
-
-	ret = pwm_fan_switch_power(ctx, true);
-	if (ret) {
-		dev_err(dev, "Failed to enable fan supply: %d\n", ret);
-		return ret;
-	}
-
-	if (ctx->pwm_value == 0)
-		return 0;
 
-	return pwm_apply_state(ctx->pwm, &ctx->pwm_state);
+	return set_pwm(ctx, ctx->pwm_value);
 }
 #endif
 
-- 
2.25.1


^ permalink raw reply related	[flat|nested] 11+ messages in thread

* Re: [PATCH v5 1/5] hwmon: pwm-fan: Refactor fan power on/off
  2022-09-14 15:31 ` [PATCH v5 1/5] hwmon: pwm-fan: Refactor fan power on/off Alexander Stein
@ 2022-09-20 14:15   ` Guenter Roeck
  0 siblings, 0 replies; 11+ messages in thread
From: Guenter Roeck @ 2022-09-20 14:15 UTC (permalink / raw)
  To: Alexander Stein
  Cc: Jean Delvare, Jonathan Corbet, Thierry Reding,
	Uwe Kleine-König, Lee Jones, linux-hwmon, linux-pwm,
	Markus Niebel

On Wed, Sep 14, 2022 at 05:31:33PM +0200, Alexander Stein wrote:
> In preparation for dynamically switching regulator, split the power on
> and power off sequence into separate functions.
> 
> Signed-off-by: Alexander Stein <alexander.stein@ew.tq-group.com>
> Reviewed-by: Guenter Roeck <linux@roeck-us.net>

Applied to hwmon-next.

Thanks,
Guenter

^ permalink raw reply	[flat|nested] 11+ messages in thread

* Re: [PATCH v5 2/5] hwmon: pwm-fan: Simplify enable/disable check
  2022-09-14 15:31 ` [PATCH v5 2/5] hwmon: pwm-fan: Simplify enable/disable check Alexander Stein
@ 2022-09-20 14:17   ` Guenter Roeck
  0 siblings, 0 replies; 11+ messages in thread
From: Guenter Roeck @ 2022-09-20 14:17 UTC (permalink / raw)
  To: Alexander Stein
  Cc: Jean Delvare, Jonathan Corbet, Thierry Reding,
	Uwe Kleine-König, Lee Jones, linux-hwmon, linux-pwm,
	Markus Niebel

On Wed, Sep 14, 2022 at 05:31:34PM +0200, Alexander Stein wrote:
> Instead of comparing the current to the new pwm duty to decide whether to
> enable the PWM, use a dedicated flag. Also apply the new PWM duty in any
> case. This is a preparation to enable/disable the regulator dynamically.
> 
> Signed-off-by: Alexander Stein <alexander.stein@ew.tq-group.com>

Applied to hwmon-next

Thanks,
Guenter

^ permalink raw reply	[flat|nested] 11+ messages in thread

* Re: [PATCH v5 3/5] hwmon: pwm-fan: Add dedicated power switch function
  2022-09-14 15:31 ` [PATCH v5 3/5] hwmon: pwm-fan: Add dedicated power switch function Alexander Stein
@ 2022-09-20 14:18   ` Guenter Roeck
  0 siblings, 0 replies; 11+ messages in thread
From: Guenter Roeck @ 2022-09-20 14:18 UTC (permalink / raw)
  To: Alexander Stein
  Cc: Jean Delvare, Jonathan Corbet, Thierry Reding,
	Uwe Kleine-König, Lee Jones, linux-hwmon, linux-pwm,
	Markus Niebel

On Wed, Sep 14, 2022 at 05:31:35PM +0200, Alexander Stein wrote:
> This handles enabling/disabling the regulator in a single function, while
> keeping the enables/disabled balanced. This is a preparation when
> regulator is switched from different code paths.
> 
> Signed-off-by: Alexander Stein <alexander.stein@ew.tq-group.com>

Applied to hwmon-next.

Thanks,
Guenter

^ permalink raw reply	[flat|nested] 11+ messages in thread

* Re: [PATCH v5 4/5] hwmon: pwm-fan: split __set_pwm into locked/unlocked functions
  2022-09-14 15:31 ` [PATCH v5 4/5] hwmon: pwm-fan: split __set_pwm into locked/unlocked functions Alexander Stein
@ 2022-09-20 14:19   ` Guenter Roeck
  0 siblings, 0 replies; 11+ messages in thread
From: Guenter Roeck @ 2022-09-20 14:19 UTC (permalink / raw)
  To: Alexander Stein
  Cc: Jean Delvare, Jonathan Corbet, Thierry Reding,
	Uwe Kleine-König, Lee Jones, linux-hwmon, linux-pwm,
	Markus Niebel

On Wed, Sep 14, 2022 at 05:31:36PM +0200, Alexander Stein wrote:
> Regular calls to set_pwm don't hold the mutex, but the upcoming
> update_enable support needs to call set_pwm with the mutex being held.
> So provide the previous behavior in set_pwm (handling the lock), while
> adding __set_pwm which assumes the lock being held.
> 
> Signed-off-by: Alexander Stein <alexander.stein@ew.tq-group.com>
> Reviewed-by: Guenter Roeck <linux@roeck-us.net>

Applied to hwmon-next.

Thanks,
Guenter

^ permalink raw reply	[flat|nested] 11+ messages in thread

* Re: [PATCH v5 5/5] hwmon: pwm-fan: Switch regulator dynamically
  2022-09-14 15:31 ` [PATCH v5 5/5] hwmon: pwm-fan: Switch regulator dynamically Alexander Stein
@ 2022-09-20 14:20   ` Guenter Roeck
  0 siblings, 0 replies; 11+ messages in thread
From: Guenter Roeck @ 2022-09-20 14:20 UTC (permalink / raw)
  To: Alexander Stein
  Cc: Jean Delvare, Jonathan Corbet, Thierry Reding,
	Uwe Kleine-König, Lee Jones, linux-hwmon, linux-pwm,
	Markus Niebel

On Wed, Sep 14, 2022 at 05:31:37PM +0200, Alexander Stein wrote:
> This adds the enable attribute which is used to select if zero PWM duty
> means to switch off regulator and PWM or to keep them enabled but
> at inactive PWM output level.
> Depending on the select enable mode, turn off the regulator and PWM if
> the PWM duty is zero, or keep them enabled.
> This is especially important for fan using inverted PWM signal polarity.
> Having regulator supplied and PWM disabled, some PWM controllers provide
> the active, rather than inactive signal.
> 
> With this change the shutdown as well as suspend/resume paths require
> modifcations as well.
> 
> Signed-off-by: Alexander Stein <alexander.stein@ew.tq-group.com>

Applied to hwmon-next.

Thanks,
Guenter

^ permalink raw reply	[flat|nested] 11+ messages in thread

end of thread, other threads:[~2022-09-20 14:22 UTC | newest]

Thread overview: 11+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2022-09-14 15:31 [PATCH v5 0/5] hwmon: pwm-fan: switch regulator dynamically Alexander Stein
2022-09-14 15:31 ` [PATCH v5 1/5] hwmon: pwm-fan: Refactor fan power on/off Alexander Stein
2022-09-20 14:15   ` Guenter Roeck
2022-09-14 15:31 ` [PATCH v5 2/5] hwmon: pwm-fan: Simplify enable/disable check Alexander Stein
2022-09-20 14:17   ` Guenter Roeck
2022-09-14 15:31 ` [PATCH v5 3/5] hwmon: pwm-fan: Add dedicated power switch function Alexander Stein
2022-09-20 14:18   ` Guenter Roeck
2022-09-14 15:31 ` [PATCH v5 4/5] hwmon: pwm-fan: split __set_pwm into locked/unlocked functions Alexander Stein
2022-09-20 14:19   ` Guenter Roeck
2022-09-14 15:31 ` [PATCH v5 5/5] hwmon: pwm-fan: Switch regulator dynamically Alexander Stein
2022-09-20 14:20   ` Guenter Roeck

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.