static enum handoff alpha_pll_handoff(struct clk *c)
{
	struct alpha_pll_clk *pll = to_alpha_pll_clk(c);
	struct alpha_pll_masks *masks = pll->masks;
	u64 a_val;
	u32 alpha_en, l_val, regval;

	/* Set the PLL_HW_UPDATE_LOGIC_BYPASS bit before continuing */
	if (pll->dynamic_update) {
		regval = readl_relaxed(MODE_REG(pll));
		regval |= ALPHA_PLL_HW_UPDATE_LOGIC_BYPASS;
		writel_relaxed(regval, MODE_REG(pll));
	}

	update_vco_tbl(pll);

	if (!is_locked(pll)) {
		if (pll->slew) {
			if (c->rate && dyna_alpha_pll_set_rate(c, c->rate))
				WARN(1, "%s: Failed to configure rate\n",
					c->dbg_name);
		} else {
			if (c->rate && alpha_pll_set_rate(c, c->rate))
				WARN(1, "%s: Failed to configure rate\n",
					c->dbg_name);
		}
		__init_alpha_pll(c);
		return HANDOFF_DISABLED_CLK;
	} else if (pll->fsm_en_mask && !is_fsm_mode(MODE_REG(pll))) {
		WARN(1, "%s should be in FSM mode but is not\n", c->dbg_name);
	}

	l_val = readl_relaxed(L_REG(pll));
	/* read u64 in two steps to satisfy alignment constraint */
	a_val = readl_relaxed(A_REG(pll) + 0x4);
	a_val = a_val << 32 | readl_relaxed(A_REG(pll));
	/* get upper 32 bits */
	a_val = a_val >> (ALPHA_REG_BITWIDTH - ALPHA_BITWIDTH);

	alpha_en = readl_relaxed(ALPHA_EN_REG(pll));
	alpha_en &= masks->alpha_en_mask;
	if (!alpha_en)
		a_val = 0;

	c->rate = compute_rate(pll, l_val, a_val);

	/*
	 * Unconditionally vote for the PLL; it might be on because of
	 * another master's vote.
	 */
	if (pll->fsm_en_mask)
		__alpha_pll_vote_enable(pll);

	return HANDOFF_ENABLED_CLK;
}
Пример #2
0
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 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;
}
static void setup_alpha_pll_values(u64 a_val, u32 l_val, u32 vco_val,
				struct alpha_pll_clk *pll)
{
	struct alpha_pll_masks *masks = pll->masks;
	u32 regval;

	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 (vco_val != UINT_MAX) {
		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));
}