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