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