in pwm-renesas-tpu.c [244:325]
static int tpu_pwm_config(struct pwm_chip *chip, struct pwm_device *_pwm,
int duty_ns, int period_ns)
{
static const unsigned int prescalers[] = { 1, 4, 16, 64 };
struct tpu_pwm_device *pwm = pwm_get_chip_data(_pwm);
struct tpu_device *tpu = to_tpu_device(chip);
unsigned int prescaler;
bool duty_only = false;
u32 clk_rate;
u32 period;
u32 duty;
int ret;
/*
* Pick a prescaler to avoid overflowing the counter.
* TODO: Pick the highest acceptable prescaler.
*/
clk_rate = clk_get_rate(tpu->clk);
for (prescaler = 0; prescaler < ARRAY_SIZE(prescalers); ++prescaler) {
period = clk_rate / prescalers[prescaler]
/ (NSEC_PER_SEC / period_ns);
if (period <= 0xffff)
break;
}
if (prescaler == ARRAY_SIZE(prescalers) || period == 0) {
dev_err(&tpu->pdev->dev, "clock rate mismatch\n");
return -ENOTSUPP;
}
if (duty_ns) {
duty = clk_rate / prescalers[prescaler]
/ (NSEC_PER_SEC / duty_ns);
if (duty > period)
return -EINVAL;
} else {
duty = 0;
}
dev_dbg(&tpu->pdev->dev,
"rate %u, prescaler %u, period %u, duty %u\n",
clk_rate, prescalers[prescaler], period, duty);
if (pwm->prescaler == prescaler && pwm->period == period)
duty_only = true;
pwm->prescaler = prescaler;
pwm->period = period;
pwm->duty = duty;
/* If the channel is disabled we're done. */
if (!pwm_is_enabled(_pwm))
return 0;
if (duty_only && pwm->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(pwm, TPU_TGRAn, pwm->duty);
dev_dbg(&tpu->pdev->dev, "%u: TGRA 0x%04x\n", pwm->channel,
pwm->duty);
} else {
/* Otherwise perform a full reconfiguration. */
ret = tpu_pwm_timer_start(pwm);
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(pwm, duty ? TPU_PIN_ACTIVE : TPU_PIN_INACTIVE);
tpu_pwm_timer_stop(pwm);
}
return 0;
}