in pwm-sun4i.c [168:229]
static int sun4i_pwm_calculate(struct sun4i_pwm_chip *sun4i_pwm,
const struct pwm_state *state,
u32 *dty, u32 *prd, unsigned int *prsclr,
bool *bypass)
{
u64 clk_rate, div = 0;
unsigned int prescaler = 0;
clk_rate = clk_get_rate(sun4i_pwm->clk);
*bypass = sun4i_pwm->data->has_direct_mod_clk_output &&
state->enabled &&
(state->period * clk_rate >= NSEC_PER_SEC) &&
(state->period * clk_rate < 2 * NSEC_PER_SEC) &&
(state->duty_cycle * clk_rate * 2 >= NSEC_PER_SEC);
/* Skip calculation of other parameters if we bypass them */
if (*bypass)
return 0;
if (sun4i_pwm->data->has_prescaler_bypass) {
/* First, test without any prescaler when available */
prescaler = PWM_PRESCAL_MASK;
/*
* When not using any prescaler, the clock period in nanoseconds
* is not an integer so round it half up instead of
* truncating to get less surprising values.
*/
div = clk_rate * state->period + NSEC_PER_SEC / 2;
do_div(div, NSEC_PER_SEC);
if (div - 1 > PWM_PRD_MASK)
prescaler = 0;
}
if (prescaler == 0) {
/* Go up from the first divider */
for (prescaler = 0; prescaler < PWM_PRESCAL_MASK; prescaler++) {
unsigned int pval = prescaler_table[prescaler];
if (!pval)
continue;
div = clk_rate;
do_div(div, pval);
div = div * state->period;
do_div(div, NSEC_PER_SEC);
if (div - 1 <= PWM_PRD_MASK)
break;
}
if (div - 1 > PWM_PRD_MASK)
return -EINVAL;
}
*prd = div;
div *= state->duty_cycle;
do_div(div, state->period);
*dty = div;
*prsclr = prescaler;
return 0;
}