ANDROID: GKI: pwm: core: Add option to config PWM duty/period with u64 data length
Currently, PWM core driver provides interfaces for configuring PWM
period and duty length in nanoseconds with an integer data type, so
the max period can be only set to ~2.147 seconds. Add interfaces which
can set PWM period and duty with u64 data type to remove this
limitation.
Signed-off-by: Fenglin Wu <fenglinw@codeaurora.org>
Bug: 152542675
Test: build and boot
(cherry picked from commit a691c36aef
)
[surenb: removed sysfs API changes, replaced 32-bit divisions with 64-bit
ones in the following drivers to fix allmodconfig build:
drivers/clk/clk-pwm.c
drivers/hwmon/pwm-fan.c
drivers/pwm/pwm-clps711x.c
drivers/pwm/pwm-sti.c
drivers/pwm/pwm-sun4i.c
]
Signed-off-by: Suren Baghdasaryan <surenb@google.com>
Change-Id: I149c14b2d59b181344e7bb77393c64bcd9998de5
Merged-In: I149c14b2d59b181344e7bb77393c64bcd9998de5
This commit is contained in:
parent
1f1eb8c282
commit
d892c9f357
8 changed files with 99 additions and 21 deletions
|
@ -78,7 +78,7 @@ static int clk_pwm_probe(struct platform_device *pdev)
|
|||
}
|
||||
|
||||
if (of_property_read_u32(node, "clock-frequency", &clk_pwm->fixed_rate))
|
||||
clk_pwm->fixed_rate = NSEC_PER_SEC / pargs.period;
|
||||
clk_pwm->fixed_rate = div64_u64(NSEC_PER_SEC, pargs.period);
|
||||
|
||||
if (pargs.period != NSEC_PER_SEC / clk_pwm->fixed_rate &&
|
||||
pargs.period != DIV_ROUND_UP(NSEC_PER_SEC, clk_pwm->fixed_rate)) {
|
||||
|
|
|
@ -321,7 +321,7 @@ static int pwm_fan_resume(struct device *dev)
|
|||
return 0;
|
||||
|
||||
pwm_get_args(ctx->pwm, &pargs);
|
||||
duty = DIV_ROUND_UP(ctx->pwm_value * (pargs.period - 1), MAX_PWM);
|
||||
duty = DIV_ROUND_UP_ULL(ctx->pwm_value * (pargs.period - 1), MAX_PWM);
|
||||
ret = pwm_config(ctx->pwm, duty, pargs.period);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
|
|
@ -535,9 +535,19 @@ int pwm_apply_state(struct pwm_device *pwm, struct pwm_state *state)
|
|||
|
||||
if (state->period != pwm->state.period ||
|
||||
state->duty_cycle != pwm->state.duty_cycle) {
|
||||
err = pwm->chip->ops->config(pwm->chip, pwm,
|
||||
state->duty_cycle,
|
||||
state->period);
|
||||
if (pwm->chip->ops->config_extend) {
|
||||
err = pwm->chip->ops->config_extend(pwm->chip,
|
||||
pwm, state->duty_cycle,
|
||||
state->period);
|
||||
} else {
|
||||
if (state->period > UINT_MAX)
|
||||
pr_warn("period %llu duty_cycle %llu will be truncated\n",
|
||||
state->period,
|
||||
state->duty_cycle);
|
||||
err = pwm->chip->ops->config(pwm->chip, pwm,
|
||||
state->duty_cycle,
|
||||
state->period);
|
||||
}
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
|
@ -1022,8 +1032,8 @@ static void pwm_dbg_show(struct pwm_chip *chip, struct seq_file *s)
|
|||
if (state.enabled)
|
||||
seq_puts(s, " enabled");
|
||||
|
||||
seq_printf(s, " period: %u ns", state.period);
|
||||
seq_printf(s, " duty: %u ns", state.duty_cycle);
|
||||
seq_printf(s, " period: %llu ns", state.period);
|
||||
seq_printf(s, " duty: %llu ns", state.duty_cycle);
|
||||
seq_printf(s, " polarity: %s",
|
||||
state.polarity ? "inverse" : "normal");
|
||||
|
||||
|
|
|
@ -48,7 +48,9 @@ static void clps711x_pwm_update_val(struct clps711x_chip *priv, u32 n, u32 v)
|
|||
static unsigned int clps711x_get_duty(struct pwm_device *pwm, unsigned int v)
|
||||
{
|
||||
/* Duty cycle 0..15 max */
|
||||
return DIV_ROUND_CLOSEST(v * 0xf, pwm->args.period);
|
||||
/* DIV_ROUND_CLOSEST(v * 0xf, pwm->args.period) with u64 divisor */
|
||||
unsigned long long tmp = (v * 0xf) + do_div(pwm->args.period, 2);
|
||||
return div64_u64(tmp, pwm->args.period);
|
||||
}
|
||||
|
||||
static int clps711x_pwm_request(struct pwm_chip *chip, struct pwm_device *pwm)
|
||||
|
|
|
@ -375,10 +375,11 @@ static int sti_pwm_capture(struct pwm_chip *chip, struct pwm_device *pwm,
|
|||
effective_ticks = clk_get_rate(pc->cpt_clk);
|
||||
|
||||
result->period = (high + low) * NSEC_PER_SEC;
|
||||
result->period /= effective_ticks;
|
||||
result->period = do_div(result->period, effective_ticks);
|
||||
|
||||
result->duty_cycle = high * NSEC_PER_SEC;
|
||||
result->duty_cycle /= effective_ticks;
|
||||
result->duty_cycle = do_div(result->duty_cycle,
|
||||
effective_ticks);
|
||||
|
||||
break;
|
||||
|
||||
|
|
|
@ -252,7 +252,7 @@ static int sun4i_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm,
|
|||
val = (duty & PWM_DTY_MASK) | PWM_PRD(period);
|
||||
sun4i_pwm_writel(sun4i_pwm, val, PWM_CH_PRD(pwm->hwpwm));
|
||||
sun4i_pwm->next_period[pwm->hwpwm] = jiffies +
|
||||
usecs_to_jiffies(cstate.period / 1000 + 1);
|
||||
usecs_to_jiffies(do_div(cstate.period, 1000) + 1);
|
||||
sun4i_pwm->needs_delay[pwm->hwpwm] = true;
|
||||
}
|
||||
|
||||
|
|
|
@ -50,7 +50,7 @@ static ssize_t period_show(struct device *child,
|
|||
|
||||
pwm_get_state(pwm, &state);
|
||||
|
||||
return sprintf(buf, "%u\n", state.period);
|
||||
return sprintf(buf, "%llu\n", state.period);
|
||||
}
|
||||
|
||||
static ssize_t period_store(struct device *child,
|
||||
|
@ -85,7 +85,7 @@ static ssize_t duty_cycle_show(struct device *child,
|
|||
|
||||
pwm_get_state(pwm, &state);
|
||||
|
||||
return sprintf(buf, "%u\n", state.duty_cycle);
|
||||
return sprintf(buf, "%llu\n", state.duty_cycle);
|
||||
}
|
||||
|
||||
static ssize_t duty_cycle_store(struct device *child,
|
||||
|
@ -220,7 +220,7 @@ static ssize_t capture_show(struct device *child,
|
|||
if (ret)
|
||||
return ret;
|
||||
|
||||
return sprintf(buf, "%u %u\n", result.period, result.duty_cycle);
|
||||
return sprintf(buf, "%llu %llu\n", result.period, result.duty_cycle);
|
||||
}
|
||||
|
||||
static DEVICE_ATTR_RW(period);
|
||||
|
|
|
@ -39,7 +39,7 @@ enum pwm_polarity {
|
|||
* current PWM hardware state.
|
||||
*/
|
||||
struct pwm_args {
|
||||
unsigned int period;
|
||||
u64 period;
|
||||
enum pwm_polarity polarity;
|
||||
};
|
||||
|
||||
|
@ -66,9 +66,9 @@ enum pwm_output_type {
|
|||
* @cycles_per_duty: number of PWM period cycles an entry stays at
|
||||
*/
|
||||
struct pwm_output_pattern {
|
||||
unsigned int *duty_pattern;
|
||||
u64 *duty_pattern;
|
||||
unsigned int num_entries;
|
||||
unsigned int cycles_per_duty;
|
||||
u64 cycles_per_duty;
|
||||
};
|
||||
|
||||
/*
|
||||
|
@ -79,8 +79,8 @@ struct pwm_output_pattern {
|
|||
* @enabled: PWM enabled status
|
||||
*/
|
||||
struct pwm_state {
|
||||
unsigned int period;
|
||||
unsigned int duty_cycle;
|
||||
u64 period;
|
||||
u64 duty_cycle;
|
||||
enum pwm_polarity polarity;
|
||||
enum pwm_output_type output_type;
|
||||
struct pwm_output_pattern *output_pattern;
|
||||
|
@ -136,12 +136,30 @@ static inline void pwm_set_period(struct pwm_device *pwm, unsigned int period)
|
|||
pwm->state.period = period;
|
||||
}
|
||||
|
||||
static inline void pwm_set_period_extend(struct pwm_device *pwm, u64 period)
|
||||
{
|
||||
if (pwm)
|
||||
pwm->state.period = period;
|
||||
}
|
||||
|
||||
static inline unsigned int pwm_get_period(const struct pwm_device *pwm)
|
||||
{
|
||||
struct pwm_state state;
|
||||
|
||||
pwm_get_state(pwm, &state);
|
||||
|
||||
if (state.period > UINT_MAX)
|
||||
pr_warn("PWM period %llu is truncated\n", state.period);
|
||||
|
||||
return (unsigned int)state.period;
|
||||
}
|
||||
|
||||
static inline u64 pwm_get_period_extend(const struct pwm_device *pwm)
|
||||
{
|
||||
struct pwm_state state;
|
||||
|
||||
pwm_get_state(pwm, &state);
|
||||
|
||||
return state.period;
|
||||
}
|
||||
|
||||
|
@ -151,12 +169,30 @@ static inline void pwm_set_duty_cycle(struct pwm_device *pwm, unsigned int duty)
|
|||
pwm->state.duty_cycle = duty;
|
||||
}
|
||||
|
||||
static inline void pwm_set_duty_cycle_extend(struct pwm_device *pwm, u64 duty)
|
||||
{
|
||||
if (pwm)
|
||||
pwm->state.duty_cycle = duty;
|
||||
}
|
||||
|
||||
static inline unsigned int pwm_get_duty_cycle(const struct pwm_device *pwm)
|
||||
{
|
||||
struct pwm_state state;
|
||||
|
||||
pwm_get_state(pwm, &state);
|
||||
|
||||
if (state.duty_cycle > UINT_MAX)
|
||||
pr_warn("PWM duty cycle %llu is truncated\n", state.duty_cycle);
|
||||
|
||||
return (unsigned int)state.duty_cycle;
|
||||
}
|
||||
|
||||
static inline u64 pwm_get_duty_cycle_extend(const struct pwm_device *pwm)
|
||||
{
|
||||
struct pwm_state state;
|
||||
|
||||
pwm_get_state(pwm, &state);
|
||||
|
||||
return state.duty_cycle;
|
||||
}
|
||||
|
||||
|
@ -288,6 +324,8 @@ pwm_set_relative_duty_cycle(struct pwm_state *state, unsigned int duty_cycle,
|
|||
* @request: optional hook for requesting a PWM
|
||||
* @free: optional hook for freeing a PWM
|
||||
* @config: configure duty cycles and period length for this PWM
|
||||
* @config_extend: configure duty cycles and period length for this
|
||||
* PWM with u64 data type
|
||||
* @set_polarity: configure the polarity of this PWM
|
||||
* @capture: capture and report PWM signal
|
||||
* @enable: enable PWM output toggling
|
||||
|
@ -310,6 +348,8 @@ struct pwm_ops {
|
|||
void (*free)(struct pwm_chip *chip, struct pwm_device *pwm);
|
||||
int (*config)(struct pwm_chip *chip, struct pwm_device *pwm,
|
||||
int duty_ns, int period_ns);
|
||||
int (*config_extend)(struct pwm_chip *chip, struct pwm_device *pwm,
|
||||
u64 duty_ns, u64 period_ns);
|
||||
int (*set_polarity)(struct pwm_chip *chip, struct pwm_device *pwm,
|
||||
enum pwm_polarity polarity);
|
||||
int (*capture)(struct pwm_chip *chip, struct pwm_device *pwm,
|
||||
|
@ -364,8 +404,8 @@ struct pwm_chip {
|
|||
* @duty_cycle: duty cycle of the PWM signal (in nanoseconds)
|
||||
*/
|
||||
struct pwm_capture {
|
||||
unsigned int period;
|
||||
unsigned int duty_cycle;
|
||||
u64 period;
|
||||
u64 duty_cycle;
|
||||
};
|
||||
|
||||
#if IS_ENABLED(CONFIG_PWM)
|
||||
|
@ -418,6 +458,31 @@ static inline int pwm_config(struct pwm_device *pwm, int duty_ns,
|
|||
return pwm_apply_state(pwm, &state);
|
||||
}
|
||||
|
||||
/**
|
||||
* pwm_config_extend() - change PWM period and duty length with u64 data type
|
||||
* @pwm: PWM device
|
||||
* @duty_ns: "on" time (in nanoseconds)
|
||||
* @period_ns: duration (in nanoseconds) of one cycle
|
||||
*
|
||||
* Returns: 0 on success or a negative error code on failure.
|
||||
*/
|
||||
static inline int pwm_config_extend(struct pwm_device *pwm, u64 duty_ns,
|
||||
u64 period_ns)
|
||||
{
|
||||
struct pwm_state state;
|
||||
|
||||
if (!pwm)
|
||||
return -EINVAL;
|
||||
|
||||
pwm_get_state(pwm, &state);
|
||||
if (state.duty_cycle == duty_ns && state.period == period_ns)
|
||||
return 0;
|
||||
|
||||
state.duty_cycle = duty_ns;
|
||||
state.period = period_ns;
|
||||
return pwm_apply_state(pwm, &state);
|
||||
}
|
||||
|
||||
/**
|
||||
* pwm_set_polarity() - configure the polarity of a PWM signal
|
||||
* @pwm: PWM device
|
||||
|
|
Loading…
Reference in a new issue