hwmon: (pwm-fan) Introduce start from stopped state handling

Delta AFC0612DB-F00 fan has to be set to at least 30% PWM duty cycle
to spin up from a stopped state, and can be afterward throttled down to
lower PWM duty cycle. Introduce support for operating such fans which
need to start at higher PWM duty cycle first and can slow down next.

Introduce two new DT properties, "fan-stop-to-start-percent" and
"fan-stop-to-start-us". The former describes the minimum percent
of fan RPM at which it will surely spin up from stopped state. This
value can be found in the fan datasheet and can be converted to PWM
duty cycle easily. The "fan-stop-to-start-us" describes the minimum
time in microseconds for which the fan has to be set to stopped state
start RPM for the fan to surely spin up.

Adjust the PWM setting code such that if the PWM duty cycle is below
the minimum duty cycle needed by the fan to spin up from stopped state,
then first set the PWM duty cycle to the minimum duty cycle needed
by the fan to spin up from stopped state, then wait the time necessary
for the fan to spin up from stopped state, and finally set the PWM duty
cycle to the one desired by user.

Signed-off-by: Marek Vasut <marex@denx.de>
Message-ID: <20241106185925.223736-2-marex@denx.de>
Signed-off-by: Guenter Roeck <linux@roeck-us.net>
This commit is contained in:
Marek Vasut 2024-11-06 19:59:05 +01:00 committed by Guenter Roeck
parent 80bc64201e
commit 255ab27a07

View File

@ -7,6 +7,7 @@
* Author: Kamil Debski <k.debski@samsung.com>
*/
#include <linux/delay.h>
#include <linux/hwmon.h>
#include <linux/interrupt.h>
#include <linux/mod_devicetable.h>
@ -60,6 +61,9 @@ struct pwm_fan_ctx {
struct hwmon_chip_info info;
struct hwmon_channel_info fan_channel;
u64 pwm_duty_cycle_from_stopped;
u32 pwm_usec_from_stopped;
};
/* This handler assumes self resetting edge triggered interrupt. */
@ -199,7 +203,9 @@ static int pwm_fan_power_off(struct pwm_fan_ctx *ctx, bool force_disable)
static int __set_pwm(struct pwm_fan_ctx *ctx, unsigned long pwm)
{
struct pwm_state *state = &ctx->pwm_state;
unsigned long final_pwm = pwm;
unsigned long period;
bool update = false;
int ret = 0;
if (pwm > 0) {
@ -208,11 +214,22 @@ static int __set_pwm(struct pwm_fan_ctx *ctx, unsigned long pwm)
return 0;
period = state->period;
state->duty_cycle = DIV_ROUND_UP(pwm * (period - 1), MAX_PWM);
update = state->duty_cycle < ctx->pwm_duty_cycle_from_stopped;
if (update)
state->duty_cycle = ctx->pwm_duty_cycle_from_stopped;
else
state->duty_cycle = DIV_ROUND_UP(pwm * (period - 1), MAX_PWM);
ret = pwm_apply_might_sleep(ctx->pwm, state);
if (ret)
return ret;
ret = pwm_fan_power_on(ctx);
if (!ret && update) {
pwm = final_pwm;
state->duty_cycle = DIV_ROUND_UP(pwm * (period - 1), MAX_PWM);
usleep_range(ctx->pwm_usec_from_stopped,
ctx->pwm_usec_from_stopped * 2);
ret = pwm_apply_might_sleep(ctx->pwm, state);
}
} else {
ret = pwm_fan_power_off(ctx, false);
}
@ -480,6 +497,7 @@ static int pwm_fan_probe(struct platform_device *pdev)
struct device *hwmon;
int ret;
const struct hwmon_channel_info **channels;
u32 pwm_min_from_stopped = 0;
u32 *fan_channel_config;
int channel_count = 1; /* We always have a PWM channel. */
int i;
@ -620,6 +638,19 @@ static int pwm_fan_probe(struct platform_device *pdev)
channels[1] = &ctx->fan_channel;
}
ret = of_property_read_u32(dev->of_node, "fan-stop-to-start-percent",
&pwm_min_from_stopped);
if (!ret && pwm_min_from_stopped) {
ctx->pwm_duty_cycle_from_stopped =
DIV_ROUND_UP_ULL(pwm_min_from_stopped *
(ctx->pwm_state.period - 1),
100);
}
ret = of_property_read_u32(dev->of_node, "fan-stop-to-start-us",
&ctx->pwm_usec_from_stopped);
if (ret)
ctx->pwm_usec_from_stopped = 250000;
ctx->info.ops = &pwm_fan_hwmon_ops;
ctx->info.info = channels;