/*
 * Slewing plls should be bought up at frequency which is in the middle of the
 * desired VCO range. So after bringing up the pll at calibration freq, set it
 * back to desired frequency(that was set by previous clk_set_rate).
 */
static int __calibrate_alpha_pll(struct alpha_pll_clk *pll)
{
	unsigned long calibration_freq, freq_hz;
	struct alpha_pll_vco_tbl *vco_tbl = pll->vco_tbl;
	u64 a_val;
	u32 l_val, vco_val;
	int rc;

	vco_val = find_vco(pll, pll->c.rate);
	if (IS_ERR_VALUE(vco_val)) {
		pr_err("alpha pll: not in a valid vco range\n");
		return -EINVAL;
	}
	/*
	 * As during slewing plls vco_sel won't be allowed to change, vco table
	 * should have only one entry table, i.e. index = 0, find the
	 * calibration frequency.
	 */
	calibration_freq = (vco_tbl[0].min_freq +
					vco_tbl[0].max_freq)/2;

	freq_hz = round_rate_up(pll, calibration_freq, &l_val, &a_val);
	if (freq_hz != calibration_freq) {
		pr_err("alpha_pll: call clk_set_rate with rounded rates!\n");
		return -EINVAL;
	}

	setup_alpha_pll_values(a_val, l_val, vco_tbl->vco_val, pll);

	/* Bringup the pll at calibration frequency */
	rc = __alpha_pll_enable(pll, false);
	if (rc) {
		pr_err("alpha pll calibration failed\n");
		return rc;
	}

	/*
	 * PLL is already running at calibration frequency.
	 * So slew pll to the previously set frequency.
	 */
	pr_debug("pll %s: setting back to required rate %lu\n", pll->c.dbg_name,
					pll->c.rate);
	freq_hz = round_rate_up(pll, pll->c.rate, &l_val, &a_val);
	setup_alpha_pll_values(a_val, l_val, UINT_MAX, pll);
	dyna_alpha_pll_dynamic_update(pll);

	return 0;
}
static int fabia_alpha_pll_set_rate(struct clk *c, unsigned long rate)
{
	struct alpha_pll_clk *pll = to_alpha_pll_clk(c);
	unsigned long flags, freq_hz;
	u32 l_val;
	u64 a_val;

	freq_hz = round_rate_up(pll, rate, &l_val, &a_val);
	if (freq_hz > rate + FABIA_RATE_MARGIN || freq_hz < rate) {
		pr_err("%s: Call clk_set_rate with rounded rates!\n",
						c->dbg_name);
		return -EINVAL;
	}

	spin_lock_irqsave(&c->lock, flags);
	/* Set the new L value */
	writel_relaxed(l_val, FABIA_L_REG(pll));
	if (pll->fabia_frac_offset)
		writel_relaxed(a_val, FABIA_FRAC_OFF(pll));
	else
		writel_relaxed(a_val, FABIA_FRAC_REG(pll));

	alpha_pll_dynamic_update(pll);

	spin_unlock_irqrestore(&c->lock, flags);
	return 0;
}
static long alpha_pll_round_rate(struct clk *c, unsigned long rate)
{
	struct alpha_pll_clk *pll = to_alpha_pll_clk(c);
	struct alpha_pll_vco_tbl *v = pll->vco_tbl;
	int ret;
	u32 l_val;
	unsigned long freq_hz;
	u64 a_val;
	int i;

	if (pll->no_prepared_reconfig && c->prepare_count)
		return -EINVAL;

	freq_hz = round_rate_up(pll, rate, &l_val, &a_val);
	if (rate < pll->min_supported_freq)
		return pll->min_supported_freq;
	if (pll->is_fabia)
		return freq_hz;

	ret = find_vco(pll, freq_hz);
	if (!IS_ERR_VALUE(ret))
		return freq_hz;

	freq_hz = 0;
	for (i = 0; i < pll->num_vco; i++) {
		if (is_better_rate(rate, freq_hz, v[i].min_freq))
			freq_hz = v[i].min_freq;
		if (is_better_rate(rate, freq_hz, v[i].max_freq))
			freq_hz = v[i].max_freq;
	}
	if (!freq_hz)
		return -EINVAL;
	return freq_hz;
}
static int alpha_pll_set_rate(struct clk *c, unsigned long rate)
{
	struct alpha_pll_clk *pll = to_alpha_pll_clk(c);
	struct alpha_pll_masks *masks = pll->masks;
	unsigned long flags, freq_hz;
	u32 regval, l_val;
	int vco_val;
	u64 a_val;

	freq_hz = round_rate_up(pll, rate, &l_val, &a_val);
	if (freq_hz != rate) {
		pr_err("alpha_pll: Call clk_set_rate with rounded rates!\n");
		return -EINVAL;
	}

	vco_val = find_vco(pll, freq_hz);
	if (IS_ERR_VALUE(vco_val)) {
		pr_err("alpha pll: not in a valid vco range\n");
		return -EINVAL;
	}

	/*
	 * Ensure PLL is off before changing rate. For optimization reasons,
	 * assume no downstream clock is actively using it. No support
	 * for dynamic update at the moment.
	 */
	spin_lock_irqsave(&c->lock, flags);
	if (c->count)
		alpha_pll_disable(c);

	a_val = a_val << (ALPHA_REG_BITWIDTH - ALPHA_BITWIDTH);

	writel_relaxed(l_val, L_REG(pll));
	__iowrite32_copy(A_REG(pll), &a_val, 2);

	if (masks->vco_mask) {
		regval = readl_relaxed(VCO_REG(pll));
		regval &= ~(masks->vco_mask << masks->vco_shift);
		regval |= vco_val << masks->vco_shift;
		writel_relaxed(regval, VCO_REG(pll));
	}

	regval = readl_relaxed(ALPHA_EN_REG(pll));
	regval |= masks->alpha_en_mask;
	writel_relaxed(regval, ALPHA_EN_REG(pll));

	if (c->count)
		alpha_pll_enable(c);

	spin_unlock_irqrestore(&c->lock, flags);
	return 0;
}
static void update_vco_tbl(struct alpha_pll_clk *pll)
{
	int i, l_val;
	u64 a_val;
	unsigned long hz;

	/* Round vco limits to valid rates */
	for (i = 0; i < pll->num_vco; i++) {
		hz = round_rate_up(pll, pll->vco_tbl[i].min_freq, &l_val,
					&a_val);
		pll->vco_tbl[i].min_freq = hz;

		hz = round_rate_down(pll, pll->vco_tbl[i].max_freq, &l_val,
					&a_val);
		pll->vco_tbl[i].max_freq = hz;
	}
}
static int dyna_alpha_pll_set_rate(struct clk *c, unsigned long rate)
{
	struct alpha_pll_clk *pll = to_alpha_pll_clk(c);
	unsigned long freq_hz, flags;
	u32 l_val, vco_val;
	u64 a_val;
	int ret;

	freq_hz = round_rate_up(pll, rate, &l_val, &a_val);
	if (freq_hz != rate) {
		pr_err("alpha_pll: Call clk_set_rate with rounded rates!\n");
		return -EINVAL;
	}

	vco_val = find_vco(pll, freq_hz);

	/*
	 * Dynamic pll update will not support switching frequencies across
	 * vco ranges. In those cases fall back to normal alpha set rate.
	 */
	if (pll->current_vco_val != vco_val) {
		ret = alpha_pll_set_rate(c, rate);
		if (!ret)
			pll->current_vco_val = vco_val;
		else
			return ret;
		return 0;
	}

	spin_lock_irqsave(&c->lock, flags);

	a_val = a_val << (ALPHA_REG_BITWIDTH - ALPHA_BITWIDTH);

	writel_relaxed(l_val, L_REG(pll));
	__iowrite32_copy(A_REG(pll), &a_val, 2);

	/* Ensure that the write above goes through before proceeding. */
	mb();

	if (c->count)
		dyna_alpha_pll_dynamic_update(pll);

	spin_unlock_irqrestore(&c->lock, flags);
	return 0;
}