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