static int jz4740_pwm_apply()

in pwm-jz4740.c [123:215]


static int jz4740_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm,
			    const struct pwm_state *state)
{
	struct jz4740_pwm_chip *jz4740 = to_jz4740(pwm->chip);
	unsigned long long tmp = 0xffffull * NSEC_PER_SEC;
	struct clk *clk = pwm_get_chip_data(pwm);
	unsigned long period, duty;
	long rate;
	int err;

	/*
	 * Limit the clock to a maximum rate that still gives us a period value
	 * which fits in 16 bits.
	 */
	do_div(tmp, state->period);

	/*
	 * /!\ IMPORTANT NOTE:
	 * -------------------
	 * This code relies on the fact that clk_round_rate() will always round
	 * down, which is not a valid assumption given by the clk API, but only
	 * happens to be true with the clk drivers used for Ingenic SoCs.
	 *
	 * Right now, there is no alternative as the clk API does not have a
	 * round-down function (and won't have one for a while), but if it ever
	 * comes to light, a round-down function should be used instead.
	 */
	rate = clk_round_rate(clk, tmp);
	if (rate < 0) {
		dev_err(chip->dev, "Unable to round rate: %ld", rate);
		return rate;
	}

	/* Calculate period value */
	tmp = (unsigned long long)rate * state->period;
	do_div(tmp, NSEC_PER_SEC);
	period = tmp;

	/* Calculate duty value */
	tmp = (unsigned long long)rate * state->duty_cycle;
	do_div(tmp, NSEC_PER_SEC);
	duty = tmp;

	if (duty >= period)
		duty = period - 1;

	jz4740_pwm_disable(chip, pwm);

	err = clk_set_rate(clk, rate);
	if (err) {
		dev_err(chip->dev, "Unable to set rate: %d", err);
		return err;
	}

	/* Reset counter to 0 */
	regmap_write(jz4740->map, TCU_REG_TCNTc(pwm->hwpwm), 0);

	/* Set duty */
	regmap_write(jz4740->map, TCU_REG_TDHRc(pwm->hwpwm), duty);

	/* Set period */
	regmap_write(jz4740->map, TCU_REG_TDFRc(pwm->hwpwm), period);

	/* Set abrupt shutdown */
	regmap_update_bits(jz4740->map, TCU_REG_TCSRc(pwm->hwpwm),
			   TCU_TCSR_PWM_SD, TCU_TCSR_PWM_SD);

	/*
	 * Set polarity.
	 *
	 * The PWM starts in inactive state until the internal timer reaches the
	 * duty value, then becomes active until the timer reaches the period
	 * value. In theory, we should then use (period - duty) as the real duty
	 * value, as a high duty value would otherwise result in the PWM pin
	 * being inactive most of the time.
	 *
	 * Here, we don't do that, and instead invert the polarity of the PWM
	 * when it is active. This trick makes the PWM start with its active
	 * state instead of its inactive state.
	 */
	if ((state->polarity == PWM_POLARITY_NORMAL) ^ state->enabled)
		regmap_update_bits(jz4740->map, TCU_REG_TCSRc(pwm->hwpwm),
				   TCU_TCSR_PWM_INITL_HIGH, 0);
	else
		regmap_update_bits(jz4740->map, TCU_REG_TCSRc(pwm->hwpwm),
				   TCU_TCSR_PWM_INITL_HIGH,
				   TCU_TCSR_PWM_INITL_HIGH);

	if (state->enabled)
		jz4740_pwm_enable(chip, pwm);

	return 0;
}