in pwm-sifive.c [149:207]
static int pwm_sifive_apply(struct pwm_chip *chip, struct pwm_device *pwm,
const struct pwm_state *state)
{
struct pwm_sifive_ddata *ddata = pwm_sifive_chip_to_ddata(chip);
struct pwm_state cur_state;
unsigned int duty_cycle;
unsigned long long num;
bool enabled;
int ret = 0;
u32 frac;
if (state->polarity != PWM_POLARITY_INVERSED)
return -EINVAL;
ret = clk_enable(ddata->clk);
if (ret) {
dev_err(ddata->chip.dev, "Enable clk failed\n");
return ret;
}
mutex_lock(&ddata->lock);
cur_state = pwm->state;
enabled = cur_state.enabled;
duty_cycle = state->duty_cycle;
if (!state->enabled)
duty_cycle = 0;
/*
* The problem of output producing mixed setting as mentioned at top,
* occurs here. To minimize the window for this problem, we are
* calculating the register values first and then writing them
* consecutively
*/
num = (u64)duty_cycle * (1U << PWM_SIFIVE_CMPWIDTH);
frac = DIV64_U64_ROUND_CLOSEST(num, state->period);
/* The hardware cannot generate a 100% duty cycle */
frac = min(frac, (1U << PWM_SIFIVE_CMPWIDTH) - 1);
if (state->period != ddata->approx_period) {
if (ddata->user_count != 1) {
ret = -EBUSY;
goto exit;
}
ddata->approx_period = state->period;
pwm_sifive_update_clock(ddata, clk_get_rate(ddata->clk));
}
writel(frac, ddata->regs + PWM_SIFIVE_PWMCMP0 +
pwm->hwpwm * PWM_SIFIVE_SIZE_PWMCMP);
if (state->enabled != enabled)
pwm_sifive_enable(chip, state->enabled);
exit:
clk_disable(ddata->clk);
mutex_unlock(&ddata->lock);
return ret;
}