/** * omap36xx_gate_clk_enable_with_hsdiv_restore - enable clocks suffering * from HSDivider PWRDN problem Implements Errata ID: i556. * @clk: DPLL output struct clk * * 3630 only: dpll3_m3_ck, dpll4_m2_ck, dpll4_m3_ck, dpll4_m4_ck, * dpll4_m5_ck & dpll4_m6_ck dividers gets loaded with reset * valueafter their respective PWRDN bits are set. Any dummy write * (Any other value different from the Read value) to the * corresponding CM_CLKSEL register will refresh the dividers. */ static int omap36xx_gate_clk_enable_with_hsdiv_restore(struct clk_hw *clk) { struct clk_divider *parent; struct clk_hw *parent_hw; u32 dummy_v, orig_v; int ret; /* Clear PWRDN bit of HSDIVIDER */ ret = omap2_dflt_clk_enable(clk); /* Parent is the x2 node, get parent of parent for the m2 div */ parent_hw = __clk_get_hw(__clk_get_parent(__clk_get_parent(clk->clk))); parent = to_clk_divider(parent_hw); /* Restore the dividers */ if (!ret) { orig_v = ti_clk_ll_ops->clk_readl(parent->reg); dummy_v = orig_v; /* Write any other value different from the Read value */ dummy_v ^= (1 << parent->shift); ti_clk_ll_ops->clk_writel(dummy_v, parent->reg); /* Write the original divider */ ti_clk_ll_ops->clk_writel(orig_v, parent->reg); } return ret; }
static int clk_ddr_set_rate(struct clk_hw *hw, unsigned long rate, unsigned long parent_rate) { struct clk *parent = __clk_get_parent(hw->clk); struct clk *grand_p = __clk_get_parent(parent); /* Do nothing before ddr init */ if (!ddr_change_freq) return 0; if (IS_ERR_OR_NULL(parent) || IS_ERR_OR_NULL(grand_p)) { clk_err("fail to get parent or grand_parent!\n"); return -EINVAL; } clk_debug("%s: will set rate = %lu\n", __func__, rate); /* Func provided by ddr driver */ ddr_change_freq(rate/MHZ); parent->rate = parent->ops->recalc_rate(parent->hw, __clk_get_rate(grand_p)); return 0; }
static int clkout_clksel_set_rate(struct clk_hw *hw, unsigned long rate, unsigned long parent_rate) { struct clk_hw_clkout *clkout_clk = to_clk_hw_clkout(hw); struct clk_hw_omap *omap_clk = to_clk_hw_omap(hw); struct clkout_match *m = &clkout_clk->match; struct clk *clk = hw->clk; int ret; u32 v; ret = clkout_find_match(hw, rate, m); if (ret != 0) return ret; /* have to be exact now */ if (rate != m->best_rate) { pr_err("%s: Failed to find exact rate %lu (was %lu)\n", __func__, rate, m->best_rate); return -EINVAL; } /* switch parent */ if (m->best_clks->parent != __clk_get_parent(clk)) { __clk_set_parent(clk, m->best_clks->parent); __clk_reparent(clk, m->best_clks->parent); parent_rate = __clk_get_rate(__clk_get_parent(clk)); } /* we need to write the new value */ if (omap_clk->clksel_reg != clkout_clk->div_reg) { v = __raw_readl(omap_clk->clksel_reg); v &= ~omap_clk->clksel_mask; v |= m->best_clkr->val << __ffs(omap_clk->clksel_mask); __raw_writel(v, omap_clk->clksel_reg); v = __raw_readl(omap_clk->clksel_reg); /* OCP barrier */ v = __raw_readl(clkout_clk->div_reg); v &= ~clkout_clk->div_mask; v |= m->best_clkd->val << __ffs(clkout_clk->div_mask); __raw_writel(v, clkout_clk->div_reg); v = __raw_readl(clkout_clk->div_reg); /* OCP barrier */ } else { v = __raw_readl(omap_clk->clksel_reg); v &= ~(omap_clk->clksel_mask | clkout_clk->div_mask); v |= (m->best_clkr->val << __ffs(omap_clk->clksel_mask)) | (m->best_clkd->val << __ffs(clkout_clk->div_mask)); __raw_writel(v, omap_clk->clksel_reg); v = __raw_readl(omap_clk->clksel_reg); /* OCP barrier */ } return ret; }
/** * omap36xx_pwrdn_clk_enable_with_hsdiv_restore - enable clocks suffering * from HSDivider PWRDN problem Implements Errata ID: i556. * @clk: DPLL output struct clk * * 3630 only: dpll3_m3_ck, dpll4_m2_ck, dpll4_m3_ck, dpll4_m4_ck, * dpll4_m5_ck & dpll4_m6_ck dividers gets loaded with reset * valueafter their respective PWRDN bits are set. Any dummy write * (Any other value different from the Read value) to the * corresponding CM_CLKSEL register will refresh the dividers. */ int omap36xx_pwrdn_clk_enable_with_hsdiv_restore(struct clk_hw *clk) { struct clk_divider *parent; struct clk_hw *parent_hw; u32 dummy_v, orig_v; struct clk_hw_omap *omap_clk = to_clk_hw_omap(clk); int ret; /* Clear PWRDN bit of HSDIVIDER */ ret = omap2_dflt_clk_enable(clk); parent_hw = __clk_get_hw(__clk_get_parent(clk->clk)); parent = to_clk_divider(parent_hw); /* Restore the dividers */ if (!ret) { orig_v = omap2_clk_readl(omap_clk, parent->reg); dummy_v = orig_v; /* Write any other value different from the Read value */ dummy_v ^= (1 << parent->shift); omap2_clk_writel(dummy_v, omap_clk, parent->reg); /* Write the original divider */ omap2_clk_writel(orig_v, omap_clk, parent->reg); } return ret; }
/* * FIXME - setting the parent every time .prepare is invoked is inefficient. * This is better handled by a dedicated clock tree configuration mechanism at * init-time. Revisit this later when such a mechanism exists */ static int clk_sp810_timerclken_prepare(struct clk_hw *hw) { struct clk_sp810_timerclken *timerclken = to_clk_sp810_timerclken(hw); struct clk_sp810 *sp810 = timerclken->sp810; struct clk *old_parent = __clk_get_parent(hw->clk); struct clk *new_parent; if (!sp810->refclk) sp810->refclk = of_clk_get(sp810->node, sp810->refclk_index); if (!sp810->timclk) sp810->timclk = of_clk_get(sp810->node, sp810->timclk_index); if (WARN_ON(IS_ERR(sp810->refclk) || IS_ERR(sp810->timclk))) return -ENOENT; /* Select fastest parent */ if (clk_get_rate(sp810->refclk) > clk_get_rate(sp810->timclk)) new_parent = sp810->refclk; else new_parent = sp810->timclk; /* Switch the parent if necessary */ if (old_parent != new_parent) { clk_prepare(new_parent); clk_set_parent(hw->clk, new_parent); clk_unprepare(old_parent); } return 0; }
/** * _clksel_to_divisor() - turn clksel field value into integer divider * @clk: OMAP struct clk to use * @field_val: register field value to find * * Given a struct clk of a rate-selectable clksel clock, and a register field * value to search for, find the corresponding clock divisor. The register * field value should be pre-masked and shifted down so the LSB is at bit 0 * before calling. Returns 0 on error or returns the actual integer divisor * upon success. */ static u32 _clksel_to_divisor(struct clk *clk, u32 field_val) { const struct clksel *clks; const struct clksel_rate *clkr; struct clk *parent; parent = __clk_get_parent(clk); clks = _get_clksel_by_parent(clk, parent); if (!clks) return 0; for (clkr = clks->rates; clkr->div; clkr++) { if (!(clkr->flags & cpu_mask)) continue; if (clkr->val == field_val) break; } if (!clkr->div) { /* This indicates a data error */ WARN(1, "clock: %s: could not find fieldval %d for parent %s\n", __clk_get_name(clk), field_val, __clk_get_name(parent)); return 0; } return clkr->div; }
/** * _divisor_to_clksel() - turn clksel integer divisor into a field value * @clk: OMAP struct clk to use * @div: integer divisor to search for * * Given a struct clk of a rate-selectable clksel clock, and a clock * divisor, find the corresponding register field value. Returns the * register field value _before_ left-shifting (i.e., LSB is at bit * 0); or returns 0xFFFFFFFF (~0) upon error. */ static u32 _divisor_to_clksel(struct clk *clk, u32 div) { const struct clksel *clks; const struct clksel_rate *clkr; struct clk *parent; /* should never happen */ WARN_ON(div == 0); parent = __clk_get_parent(clk); clks = _get_clksel_by_parent(clk, parent); if (!clks) return ~0; for (clkr = clks->rates; clkr->div; clkr++) { if (!(clkr->flags & cpu_mask)) continue; if (clkr->div == div) break; } if (!clkr->div) { pr_err("clock: %s: could not find divisor %d for parent %s\n", __clk_get_name(clk), div, __clk_get_name(parent)); return ~0; } return clkr->val; }
/** * omap36xx_pwrdn_clk_enable_with_hsdiv_restore - enable clocks suffering * from HSDivider PWRDN problem Implements Errata ID: i556. * @clk: DPLL output struct clk * * 3630 only: dpll3_m3_ck, dpll4_m2_ck, dpll4_m3_ck, dpll4_m4_ck, * dpll4_m5_ck & dpll4_m6_ck dividers gets loaded with reset * valueafter their respective PWRDN bits are set. Any dummy write * (Any other value different from the Read value) to the * corresponding CM_CLKSEL register will refresh the dividers. */ int omap36xx_pwrdn_clk_enable_with_hsdiv_restore(struct clk_hw *clk) { struct clk_hw_omap *parent; struct clk_hw *parent_hw; u32 dummy_v, orig_v, clksel_shift; int ret; /* Clear PWRDN bit of HSDIVIDER */ ret = omap2_dflt_clk_enable(clk); parent_hw = __clk_get_hw(__clk_get_parent(clk->clk)); parent = to_clk_hw_omap(parent_hw); /* Restore the dividers */ if (!ret) { clksel_shift = __ffs(parent->clksel_mask); orig_v = __raw_readl(parent->clksel_reg); dummy_v = orig_v; /* Write any other value different from the Read value */ dummy_v ^= (1 << clksel_shift); __raw_writel(dummy_v, parent->clksel_reg); /* Write the original divider */ __raw_writel(orig_v, parent->clksel_reg); } return ret; }
/** * omap3_noncore_dpll_set_rate - set rate for a DPLL clock * @hw: pointer to the clock to set parent for * @rate: target rate for the clock * @parent_rate: rate of the parent clock * * Sets rate for a DPLL clock. First checks if the clock parent is * reference clock (in bypass mode, the rate of the clock can't be * changed) and proceeds with the rate change operation. Returns 0 * with success, negative error value otherwise. */ int omap3_noncore_dpll_set_rate(struct clk_hw *hw, unsigned long rate, unsigned long parent_rate) { struct clk_hw_omap *clk = to_clk_hw_omap(hw); struct dpll_data *dd; u16 freqsel = 0; int ret; if (!hw || !rate) return -EINVAL; dd = clk->dpll_data; if (!dd) return -EINVAL; if (__clk_get_hw(__clk_get_parent(hw->clk)) != __clk_get_hw(dd->clk_ref)) return -EINVAL; if (dd->last_rounded_rate == 0) return -EINVAL; /* Freqsel is available only on OMAP343X devices */ if (ti_clk_features.flags & TI_CLK_DPLL_HAS_FREQSEL) { freqsel = _omap3_dpll_compute_freqsel(clk, dd->last_rounded_n); WARN_ON(!freqsel); } pr_debug("%s: %s: set rate: locking rate to %lu.\n", __func__, __clk_get_name(hw->clk), rate); ret = omap3_noncore_dpll_program(clk, freqsel); return ret; }
/** * omap2_clksel_set_parent() - change a clock's parent clock * @clk: struct clk * of the child clock * @new_parent: struct clk * of the new parent clock * * This function is intended to be called only by the clock framework. * Change the parent clock of clock @clk to @new_parent. This is * intended to be used while @clk is disabled. This function does not * currently check the usecount of the clock, so if multiple drivers * are using the clock, and the parent is changed, they will all be * affected without any notification. Returns -EINVAL upon error, or * 0 upon success. */ int omap2_clksel_set_parent(struct clk *clk, struct clk *new_parent) { u32 field_val = 0; u32 parent_div; if (!clk->clksel || !clk->clksel_mask) return -EINVAL; parent_div = _get_div_and_fieldval(new_parent, clk, &field_val); if (!parent_div) return -EINVAL; _write_clksel_reg(clk, field_val); clk_reparent(clk, new_parent); /* CLKSEL clocks follow their parents' rates, divided by a divisor */ clk->rate = __clk_get_rate(new_parent); if (parent_div > 0) __clk_get_rate(clk) /= parent_div; pr_debug("clock: %s: set parent to %s (new rate %ld)\n", __clk_get_name(clk), __clk_get_name(__clk_get_parent(clk)), __clk_get_rate(clk)); return 0; }
/** * omap3_noncore_dpll_enable - instruct a DPLL to enter bypass or lock mode * @clk: pointer to a DPLL struct clk * * Instructs a non-CORE DPLL to enable, e.g., to enter bypass or lock. * The choice of modes depends on the DPLL's programmed rate: if it is * the same as the DPLL's parent clock, it will enter bypass; * otherwise, it will enter lock. This code will wait for the DPLL to * indicate readiness before returning, unless the DPLL takes too long * to enter the target state. Intended to be used as the struct clk's * enable function. If DPLL3 was passed in, or the DPLL does not * support low-power stop, or if the DPLL took too long to enter * bypass or lock, return -EINVAL; otherwise, return 0. */ int omap3_noncore_dpll_enable(struct clk_hw *hw) { struct clk_hw_omap *clk = to_clk_hw_omap(hw); int r; struct dpll_data *dd; struct clk *parent; dd = clk->dpll_data; if (!dd) return -EINVAL; if (clk->clkdm) { r = clkdm_clk_enable(clk->clkdm, hw->clk); if (r) { WARN(1, "%s: could not enable %s's clockdomain %s: %d\n", __func__, __clk_get_name(hw->clk), clk->clkdm->name, r); return r; } } parent = __clk_get_parent(hw->clk); if (__clk_get_rate(hw->clk) == __clk_get_rate(dd->clk_bypass)) { WARN_ON(parent != dd->clk_bypass); r = _omap3_noncore_dpll_bypass(clk); } else { WARN_ON(parent != dd->clk_ref); r = _omap3_noncore_dpll_lock(clk); } return r; }
static long div_round_rate(struct clk_hw *hw, unsigned long rate, unsigned long *parent_rate) { struct div_clk *d = to_div_clk(hw); bool set_parent = __clk_get_flags(hw->clk) & CLK_SET_RATE_PARENT; return __div_round_rate(&d->data, rate, __clk_get_parent(hw->clk), NULL, parent_rate, set_parent); }
/** * omap2_clksel_round_rate_div() - find divisor for the given clock and rate * @clk: OMAP struct clk to use * @target_rate: desired clock rate * @new_div: ptr to where we should store the divisor * * Finds 'best' divider value in an array based on the source and target * rates. The divider array must be sorted with smallest divider first. * This function is also used by the DPLL3 M2 divider code. * * Returns the rounded clock rate or returns 0xffffffff on error. */ u32 omap2_clksel_round_rate_div(struct clk *clk, unsigned long target_rate, u32 *new_div) { unsigned long test_rate; const struct clksel *clks; const struct clksel_rate *clkr; u32 last_div = 0; struct clk *parent; unsigned long parent_rate; const char *clk_name; parent = __clk_get_parent(clk); parent_rate = __clk_get_rate(parent); clk_name = __clk_get_name(clk); if (!clk->clksel || !clk->clksel_mask) return ~0; pr_debug("clock: clksel_round_rate_div: %s target_rate %ld\n", clk_name, target_rate); *new_div = 1; clks = _get_clksel_by_parent(clk, parent); if (!clks) return ~0; for (clkr = clks->rates; clkr->div; clkr++) { if (!(clkr->flags & cpu_mask)) continue; /* Sanity check */ if (clkr->div <= last_div) pr_err("clock: %s: clksel_rate table not sorted\n", clk_name); last_div = clkr->div; test_rate = parent_rate / clkr->div; if (test_rate <= target_rate) break; /* found it */ } if (!clkr->div) { pr_err("clock: %s: could not find divisor for target rate %ld for parent %s\n", clk_name, target_rate, __clk_get_name(parent)); return ~0; } *new_div = clkr->div; pr_debug("clock: new_div = %d, new_rate = %ld\n", *new_div, (parent_rate / clkr->div)); return parent_rate / clkr->div; }
static int clk_core_set_rate(struct clk_hw *hw, unsigned long rate, unsigned long parent_rate) { struct clk *parent = __clk_get_parent(hw->clk); struct clk *grand_p = __clk_get_parent(parent); int ret; if (IS_ERR_OR_NULL(parent) || IS_ERR_OR_NULL(grand_p)) { clk_err("fail to get parent or grand_parent!\n"); return -EINVAL; } ret = parent->ops->set_rate(parent->hw, rate, __clk_get_rate(grand_p)); parent->rate = parent->ops->recalc_rate(parent->hw, __clk_get_rate(grand_p)); return ret; }
static int clk_3288_i2s_set_rate(struct clk_hw *hw, unsigned long rate, unsigned long parent_rate) { struct clk *parent = __clk_get_parent(hw->clk); struct clk *grand_p = __clk_get_parent(parent); if (IS_ERR_OR_NULL(parent) || IS_ERR_OR_NULL(grand_p)) { return 0; } if (parent->ops->set_rate) { parent->ops->set_rate(parent->hw, rate/2, __clk_get_rate(grand_p)); parent->ops->set_rate(parent->hw, rate, __clk_get_rate(grand_p)); } return 0; }
long omap3_clkoutx2_round_rate(struct clk_hw *hw, unsigned long rate, unsigned long *prate) { const struct dpll_data *dd; u32 v; struct clk_hw_omap *pclk = NULL; if (!*prate) return 0; pclk = omap3_find_clkoutx2_dpll(hw); if (!pclk) return 0; dd = pclk->dpll_data; /* TYPE J does not have a clkoutx2 */ if (dd->flags & DPLL_J_TYPE) { *prate = __clk_round_rate(__clk_get_parent(pclk->hw.clk), rate); return *prate; } WARN_ON(!dd->enable_mask); v = omap2_clk_readl(pclk, dd->control_reg) & dd->enable_mask; v >>= __ffs(dd->enable_mask); /* If in bypass, the rate is fixed to the bypass rate*/ if (v != OMAP3XXX_EN_DPLL_LOCKED) return *prate; if (__clk_get_flags(hw->clk) & CLK_SET_RATE_PARENT) { unsigned long best_parent; best_parent = (rate / 2); *prate = __clk_round_rate(__clk_get_parent(hw->clk), best_parent); } return *prate * 2; }
/** * _lookup_dco - Lookup DCO used by j-type DPLL * @clk: pointer to a DPLL struct clk * @dco: digital control oscillator selector * @m: DPLL multiplier to set * @n: DPLL divider to set * * See 36xx TRM section 3.5.3.3.3.2 "Type B DPLL (Low-Jitter)" * * XXX This code is not needed for 3430/AM35xx; can it be optimized * out in non-multi-OMAP builds for those chips? */ static void _lookup_dco(struct clk_hw_omap *clk, u8 *dco, u16 m, u8 n) { unsigned long fint, clkinp; /* watch out for overflow */ clkinp = __clk_get_rate(__clk_get_parent(clk->hw.clk)); fint = (clkinp / n) * m; if (fint < 1000000000) *dco = 2; else *dco = 4; }
static long clk_mux_with_evendiv_determine_rate(struct clk_hw *div_hw, unsigned long rate, unsigned long *best_parent_rate, struct clk **best_parent_p) { struct clk *clk = div_hw->clk, *parent = NULL, *best_parent = NULL; int i, num_parents; unsigned long parent_rate = 0, best_prate = 0, best = 0, now = 0; parent = __clk_get_parent(clk); if(!parent){ best = __clk_get_rate(clk); goto out; } /* if NO_REPARENT flag set, pass through to current parent */ if (clk->flags & CLK_SET_RATE_NO_REPARENT) { best_prate = __clk_get_rate(parent); best = clk_div_round_rate_even(div_hw, rate, &best_prate); goto out; } /* find the parent that can provide the fastest rate <= rate */ num_parents = clk->num_parents; for (i = 0; i < num_parents; i++) { parent = clk_get_parent_by_index(clk, i); if (!parent) continue; parent_rate = __clk_get_rate(parent); now = clk_div_round_rate_even(div_hw, rate, &parent_rate); if (now <= rate && now > best) { best_parent = parent; best_prate = parent_rate; best = now; } } out: if(best_prate) *best_parent_rate = best_prate; if (best_parent) *best_parent_p = best_parent; clk_debug("clk name = %s, determine rate = %lu, best = %lu\n" "\tbest_parent name = %s, best_prate = %lu\n", clk->name, rate, best, __clk_get_name(*best_parent_p), *best_parent_rate); return best; }
static long clk_core_determine_rate(struct clk_hw *hw, unsigned long rate, unsigned long *best_parent_rate, struct clk **best_parent_p) { struct clk *parent = __clk_get_parent(hw->clk); if (IS_ERR_OR_NULL(parent)) { clk_err("fail to get parent!\n"); return 0; } return clk_round_rate(parent, rate); }
int s5p_spdif_set_rate(struct clk *clk, unsigned long rate) { struct clk *pclk; int ret; pclk = __clk_get_parent(clk); if (IS_ERR(pclk)) return -EINVAL; ret = pclk->ops->set_rate(pclk, rate); clk_put(pclk); return ret; }
unsigned long s5p_spdif_get_rate(struct clk *clk) { struct clk *pclk; int rate; pclk = __clk_get_parent(clk); if (IS_ERR(pclk)) return -EINVAL; rate = pclk->ops->get_rate(pclk); clk_put(pclk); return rate; }
static unsigned long clk_core_recalc_rate(struct clk_hw *hw, unsigned long parent_rate) { /* As parent rate could be changed in clk_core.set_rate * ops, the passing_in parent_rate may not be the newest * and we should use the parent->rate instead. As a side * effect, we should NOT directly set clk_core's parent * (apll) rate, otherwise we will get a wrong recalc rate * with clk_core_recalc_rate. */ struct clk *parent = __clk_get_parent(hw->clk); return clk_divider_recalc_rate(hw, __clk_get_rate(parent)); }
static long clk_factor_round_rate(struct clk_hw *hw, unsigned long rate, unsigned long *prate) { struct clk_fixed_factor *fix = to_clk_fixed_factor(hw); if (__clk_get_flags(hw->clk) & CLK_SET_RATE_PARENT) { unsigned long best_parent; best_parent = (rate / fix->mult) * fix->div; *prate = __clk_round_rate(__clk_get_parent(hw->clk), best_parent); } return (*prate / fix->div) * fix->mult; }
/** * omap2_clksel_recalc() - function ptr to pass via struct clk .recalc field * @clk: struct clk * * * This function is intended to be called only by the clock framework. * Each clksel clock should have its struct clk .recalc field set to this * function. Returns the clock's current rate, based on its parent's rate * and its current divisor setting in the hardware. */ unsigned long omap2_clksel_recalc(struct clk *clk) { unsigned long rate; u32 div = 0; struct clk *parent; div = _read_divisor(clk); if (div == 0) return __clk_get_rate(clk); parent = __clk_get_parent(clk); rate = __clk_get_rate(parent) / div; pr_debug("clock: %s: recalc'd rate is %ld (div %d)\n", __clk_get_name(clk), rate, div); return rate; }
/* * _dpll_test_fint - test whether an Fint value is valid for the DPLL * @clk: DPLL struct clk to test * @n: divider value (N) to test * * Tests whether a particular divider @n will result in a valid DPLL * internal clock frequency Fint. See the 34xx TRM 4.7.6.2 "DPLL Jitter * Correction". Returns 0 if OK, -1 if the enclosing loop can terminate * (assuming that it is counting N upwards), or -2 if the enclosing loop * should skip to the next iteration (again assuming N is increasing). */ static int _dpll_test_fint(struct clk *clk, u8 n) { struct dpll_data *dd; long fint, fint_min, fint_max; int ret = 0; dd = clk->dpll_data; /* DPLL divider must result in a valid jitter correction val */ fint = __clk_get_rate(__clk_get_parent(clk)) / n; if (cpu_is_omap24xx()) { /* Should not be called for OMAP2, so warn if it is called */ WARN(1, "No fint limits available for OMAP2!\n"); return DPLL_FINT_INVALID; } else if (cpu_is_omap3430()) { fint_min = OMAP3430_DPLL_FINT_BAND1_MIN; fint_max = OMAP3430_DPLL_FINT_BAND2_MAX; } else if (dd->flags & DPLL_J_TYPE) { fint_min = OMAP3PLUS_DPLL_FINT_JTYPE_MIN; fint_max = OMAP3PLUS_DPLL_FINT_JTYPE_MAX; } else { fint_min = OMAP3PLUS_DPLL_FINT_MIN; fint_max = OMAP3PLUS_DPLL_FINT_MAX; } if (fint < fint_min) { pr_debug("rejecting n=%d due to Fint failure, lowering max_divider\n", n); dd->max_divider = n; ret = DPLL_FINT_UNDERFLOW; } else if (fint > fint_max) { pr_debug("rejecting n=%d due to Fint failure, boosting min_divider\n", n); dd->min_divider = n; ret = DPLL_FINT_INVALID; } else if (cpu_is_omap3430() && fint > OMAP3430_DPLL_FINT_BAND1_MAX && fint < OMAP3430_DPLL_FINT_BAND2_MIN) { pr_debug("rejecting n=%d due to Fint failure\n", n); ret = DPLL_FINT_INVALID; } return ret; }
/** * omap2_init_clksel_parent() - set a clksel clk's parent field from the hdwr * @clk: OMAP clock struct ptr to use * * Given a pointer @clk to a source-selectable struct clk, read the * hardware register and determine what its parent is currently set * to. Update @clk's .parent field with the appropriate clk ptr. No * return value. */ void omap2_init_clksel_parent(struct clk *clk) { const struct clksel *clks; const struct clksel_rate *clkr; u32 r, found = 0; struct clk *parent; const char *clk_name; if (!clk->clksel || !clk->clksel_mask) return; parent = __clk_get_parent(clk); clk_name = __clk_get_name(clk); r = __raw_readl(clk->clksel_reg) & clk->clksel_mask; r >>= __ffs(clk->clksel_mask); for (clks = clk->clksel; clks->parent && !found; clks++) { for (clkr = clks->rates; clkr->div && !found; clkr++) { if (!(clkr->flags & cpu_mask)) continue; if (clkr->val == r) { if (parent != clks->parent) { pr_debug("clock: %s: inited parent to %s (was %s)\n", clk_name, __clk_get_name(clks->parent), ((parent) ? __clk_get_name(parent) : "NULL")); clk_reparent(clk, clks->parent); }; found = 1; } } } /* This indicates a data error */ WARN(!found, "clock: %s: init parent: could not find regval %0x\n", clk_name, r); return; }
/** * _get_div_and_fieldval() - find the new clksel divisor and field value to use * @src_clk: planned new parent struct clk * * @clk: struct clk * that is being reparented * @field_val: pointer to a u32 to contain the register data for the divisor * * Given an intended new parent struct clk * @src_clk, and the struct * clk * @clk to the clock that is being reparented, find the * appropriate rate divisor for the new clock (returned as the return * value), and the corresponding register bitfield data to program to * reach that divisor (returned in the u32 pointed to by @field_val). * Returns 0 on error, or returns the newly-selected divisor upon * success (in this latter case, the corresponding register bitfield * value is passed back in the variable pointed to by @field_val) */ static u8 _get_div_and_fieldval(struct clk *src_clk, struct clk *clk, u32 *field_val) { const struct clksel *clks; const struct clksel_rate *clkr, *max_clkr = NULL; u8 max_div = 0; clks = _get_clksel_by_parent(clk, src_clk); if (!clks) return 0; /* * Find the highest divisor (e.g., the one resulting in the * lowest rate) to use as the default. This should avoid * clock rates that are too high for the device. XXX A better * solution here would be to try to determine if there is a * divisor matching the original clock rate before the parent * switch, and if it cannot be found, to fall back to the * highest divisor. */ for (clkr = clks->rates; clkr->div; clkr++) { if (!(clkr->flags & cpu_mask)) continue; if (clkr->div > max_div) { max_div = clkr->div; max_clkr = clkr; } } if (max_div == 0) { /* This indicates an error in the clksel data */ WARN(1, "clock: %s: could not find divisor for parent %s\n", __clk_get_name(clk), __clk_get_name(__clk_get_parent(src_clk))); return 0; } *field_val = max_clkr->val; return max_div; }
/* * _dpll_test_fint - test whether an Fint value is valid for the DPLL * @clk: DPLL struct clk to test * @n: divider value (N) to test * * Tests whether a particular divider @n will result in a valid DPLL * internal clock frequency Fint. See the 34xx TRM 4.7.6.2 "DPLL Jitter * Correction". Returns 0 if OK, -1 if the enclosing loop can terminate * (assuming that it is counting N upwards), or -2 if the enclosing loop * should skip to the next iteration (again assuming N is increasing). */ static int _dpll_test_fint(struct clk_hw_omap *clk, unsigned int n) { struct dpll_data *dd; long fint, fint_min, fint_max; int ret = 0; dd = clk->dpll_data; /* DPLL divider must result in a valid jitter correction val */ fint = __clk_get_rate(__clk_get_parent(clk->hw.clk)) / n; if (dd->flags & DPLL_J_TYPE) { fint_min = OMAP3PLUS_DPLL_FINT_JTYPE_MIN; fint_max = OMAP3PLUS_DPLL_FINT_JTYPE_MAX; } else { fint_min = ti_clk_features.fint_min; fint_max = ti_clk_features.fint_max; } if (!fint_min || !fint_max) { WARN(1, "No fint limits available!\n"); return DPLL_FINT_INVALID; } if (fint < ti_clk_features.fint_min) { pr_debug("rejecting n=%d due to Fint failure, lowering max_divider\n", n); dd->max_divider = n; ret = DPLL_FINT_UNDERFLOW; } else if (fint > ti_clk_features.fint_max) { pr_debug("rejecting n=%d due to Fint failure, boosting min_divider\n", n); dd->min_divider = n; ret = DPLL_FINT_INVALID; } else if (fint > ti_clk_features.fint_band1_max && fint < ti_clk_features.fint_band2_min) { pr_debug("rejecting n=%d due to Fint failure\n", n); ret = DPLL_FINT_INVALID; } return ret; }
/** * _lookup_sddiv - Calculate sigma delta divider for j-type DPLL * @clk: pointer to a DPLL struct clk * @sd_div: target sigma-delta divider * @m: DPLL multiplier to set * @n: DPLL divider to set * * See 36xx TRM section 3.5.3.3.3.2 "Type B DPLL (Low-Jitter)" * * XXX This code is not needed for 3430/AM35xx; can it be optimized * out in non-multi-OMAP builds for those chips? */ static void _lookup_sddiv(struct clk_hw_omap *clk, u8 *sd_div, u16 m, u8 n) { unsigned long clkinp, sd; /* watch out for overflow */ int mod1, mod2; clkinp = __clk_get_rate(__clk_get_parent(clk->hw.clk)); /* * target sigma-delta to near 250MHz * sd = ceil[(m/(n+1)) * (clkinp_MHz / 250)] */ clkinp /= 100000; /* shift from MHz to 10*Hz for 38.4 and 19.2 */ mod1 = (clkinp * m) % (250 * n); sd = (clkinp * m) / (250 * n); mod2 = sd % 10; sd /= 10; if (mod1 || mod2) sd++; *sd_div = sd; }
/** * omap3_clkoutx2_recalc - recalculate DPLL X2 output virtual clock rate * @clk: DPLL output struct clk * * Using parent clock DPLL data, look up DPLL state. If locked, set our * rate to the dpll_clk * 2; otherwise, just use dpll_clk. */ unsigned long omap3_clkoutx2_recalc(struct clk_hw *hw, unsigned long parent_rate) { const struct dpll_data *dd; unsigned long rate; u32 v; struct clk_hw_omap *pclk = NULL; struct clk *parent; /* Walk up the parents of clk, looking for a DPLL */ do { do { parent = __clk_get_parent(hw->clk); hw = __clk_get_hw(parent); } while (hw && (__clk_get_flags(hw->clk) & CLK_IS_BASIC)); if (!hw) break; pclk = to_clk_hw_omap(hw); } while (pclk && !pclk->dpll_data); /* clk does not have a DPLL as a parent? error in the clock data */ if (!pclk) { WARN_ON(1); return 0; } dd = pclk->dpll_data; WARN_ON(!dd->enable_mask); v = __raw_readl(dd->control_reg) & dd->enable_mask; v >>= __ffs(dd->enable_mask); if ((v != OMAP3XXX_EN_DPLL_LOCKED) || (dd->flags & DPLL_J_TYPE)) rate = parent_rate; else rate = parent_rate * 2; return rate; }