static int sun4i_pwm_calculate()

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