static int pwm_sifive_apply()

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;
}