staticint tpu_pwm_timer_start(struct tpu_pwm_device *tpd)
{ int ret;
if (!tpd->timer_on) { /* Wake up device and enable clock. */
pm_runtime_get_sync(&tpd->tpu->pdev->dev);
ret = clk_prepare_enable(tpd->tpu->clk); if (ret) {
dev_err(&tpd->tpu->pdev->dev, "cannot enable clock\n"); return ret;
}
tpd->timer_on = true;
}
/* * Make sure the channel is stopped, as we need to reconfigure it * completely. First drive the pin to the inactive state to avoid * glitches.
*/
tpu_pwm_set_pin(tpd, TPU_PIN_INACTIVE);
tpu_pwm_start_stop(tpd, false);
/* * - Clear TCNT on TGRB match * - Count on rising edge * - Set prescaler * - Output 0 until TGRA, output 1 until TGRB (active low polarity) * - Output 1 until TGRA, output 0 until TGRB (active high polarity * - PWM mode
*/
tpu_pwm_write(tpd, TPU_TCRn, TPU_TCR_CCLR_TGRB | TPU_TCR_CKEG_RISING |
tpd->prescaler);
tpu_pwm_write(tpd, TPU_TMDRn, TPU_TMDR_MD_PWM);
tpu_pwm_set_pin(tpd, TPU_PIN_PWM);
tpu_pwm_write(tpd, TPU_TGRAn, tpd->duty);
tpu_pwm_write(tpd, TPU_TGRBn, tpd->period);
clk_rate = clk_get_rate(tpu->clk); if (unlikely(clk_rate > NSEC_PER_SEC)) { /* * This won't happen in the nearer future, so this is only a * safeguard to prevent the following calculation from * overflowing. With this clk_rate * period_ns / NSEC_PER_SEC is * not greater than period_ns and so fits into an u64.
*/ return -EINVAL;
}
period = mul_u64_u64_div_u64(clk_rate, period_ns, NSEC_PER_SEC);
/* * Find the minimal prescaler in [0..3] such that * * period >> (2 * prescaler) < 0x10000 * * This could be calculated using something like: * * prescaler = max(ilog2(period) / 2, 7) - 7; * * but given there are only four allowed results and that ilog2 isn't * cheap on all platforms using a switch statement is more effective.
*/ switch (period) { case 1 ... 0xffff:
prescaler = 0; break;
/* If the channel is disabled we're done. */ if (!enabled) return 0;
if (duty_only && tpd->timer_on) { /* * If only the duty cycle changed and the timer is already * running, there's no need to reconfigure it completely, Just * modify the duty cycle.
*/
tpu_pwm_write(tpd, TPU_TGRAn, tpd->duty);
dev_dbg(&tpu->pdev->dev, "%u: TGRA 0x%04x\n", tpd->channel,
tpd->duty);
} else { /* Otherwise perform a full reconfiguration. */
ret = tpu_pwm_timer_start(tpd); if (ret < 0) return ret;
}
if (duty == 0 || duty == period) { /* * To avoid running the timer when not strictly required, handle * 0% and 100% duty cycles as fixed levels and stop the timer.
*/
tpu_pwm_set_pin(tpd, duty ? TPU_PIN_ACTIVE : TPU_PIN_INACTIVE);
tpu_pwm_timer_stop(tpd);
}
ret = tpu_pwm_timer_start(tpd); if (ret < 0) return ret;
/* * To avoid running the timer when not strictly required, handle 0% and * 100% duty cycles as fixed levels and stop the timer.
*/ if (tpd->duty == 0 || tpd->duty == tpd->period) {
tpu_pwm_set_pin(tpd, tpd->duty ?
TPU_PIN_ACTIVE : TPU_PIN_INACTIVE);
tpu_pwm_timer_stop(tpd);
}
/* The timer must be running to modify the pin output configuration. */
tpu_pwm_timer_start(tpd);
tpu_pwm_set_pin(tpd, TPU_PIN_INACTIVE);
tpu_pwm_timer_stop(tpd);
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.