/** * 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 *clk) { int r; struct dpll_data *dd; dd = clk->dpll_data; if (!dd) return -EINVAL; if (clk->rate == dd->clk_bypass->rate) { WARN_ON(clk->parent != dd->clk_bypass); r = _omap3_noncore_dpll_bypass(clk); } else { WARN_ON(clk->parent != dd->clk_ref); r = _omap3_noncore_dpll_lock(clk); } /* *FIXME: this is dubious - if clk->rate has changed, what about * propagating? */ if (!r) clk->rate = omap2_get_dpll_rate(clk); return r; }
/** * 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. */ static int omap3_noncore_dpll_enable(struct clk *clk) { int r; struct dpll_data *dd; if (clk == &dpll3_ck) return -EINVAL; dd = clk->dpll_data; if (!dd) return -EINVAL; /* * Ensure M/N register is confgured before * Enable it, otherwise DPLL will fail to lock */ if (clk->rate == 0) { WARN_ON(dd->default_rate == 0); r = omap3_noncore_dpll_set_rate(clk, dd->default_rate); if (r) return r; } if (clk->rate == dd->clk_bypass->rate) { WARN_ON(clk->parent != dd->clk_bypass); r = _omap3_noncore_dpll_bypass(clk); } else { WARN_ON(clk->parent != dd->clk_ref); r = _omap3_noncore_dpll_lock(clk); } /* FIXME: this is dubious - if clk->rate has changed, what about propagating? */ if (!r) clk->rate = omap2_get_dpll_rate(clk); return r; }
static int omap3_noncore_dpll_enable(struct clk *clk) { int r; struct dpll_data *dd; if (clk == &dpll3_ck) return -EINVAL; dd = clk->dpll_data; if (!dd) return -EINVAL; if (clk->rate == dd->clk_bypass->rate) { WARN_ON(clk->parent != dd->clk_bypass); r = _omap3_noncore_dpll_bypass(clk); } else { WARN_ON(clk->parent != dd->clk_ref); r = _omap3_noncore_dpll_lock(clk); } if (!r) clk->rate = omap2_get_dpll_rate(clk); return r; }
unsigned long omap4460_mpu_dpll_recalc(struct clk *clk) { struct dpll_data *dd; u32 v; if (!clk || !clk->parent) return -EINVAL; dd = clk->parent->dpll_data; if (!dd) return -EINVAL; v = __raw_readl(dd->mult_div1_reg); if (v & OMAP4460_DCC_EN_MASK) return omap2_get_dpll_rate(clk->parent) * 2; else return omap2_get_dpll_rate(clk->parent); }
/** * omap3_dpll_recalc - recalculate DPLL rate * @clk: DPLL struct clk * @parent_rate: rate of the DPLL's parent clock * @rate_storage: flag indicating whether current or temporary rate is changing * * Recalculate and propagate the DPLL rate. */ static void omap3_dpll_recalc(struct clk *clk, unsigned long parent_rate, u8 rate_storage) { unsigned long rate; rate = omap2_get_dpll_rate(clk, parent_rate); if (rate_storage == CURRENT_RATE) clk->rate = rate; else if (rate_storage == TEMP_RATE) clk->temp_rate = rate; }
/* This actually returns the rate of core_ck, not dpll_ck. */ static u32 omap2_get_dpll_rate_24xx(struct clk *tclk) { long long dpll_clk; u8 amult; dpll_clk = omap2_get_dpll_rate(tclk); amult = cm_read_mod_reg(PLL_MOD, CM_CLKSEL2); amult &= OMAP24XX_CORE_CLK_SRC_MASK; dpll_clk *= amult; return dpll_clk; }
/** * omap3_noncore_dpll_set_rate - set non-core DPLL rate * @clk: struct clk * of DPLL to set * @rate: rounded target rate * * Set the DPLL CLKOUT to the target rate. If the DPLL can enter * low-power bypass, and the target rate is the bypass source clock * rate, then configure the DPLL for bypass. Otherwise, round the * target rate if it hasn't been done already, then program and lock * the DPLL. Returns -EINVAL upon error, or 0 upon success. */ static int omap3_noncore_dpll_set_rate(struct clk *clk, unsigned long rate) { u16 freqsel; struct dpll_data *dd; int ret; if (!clk || !rate) return -EINVAL; dd = clk->dpll_data; if (!dd) return -EINVAL; if (rate == omap2_get_dpll_rate(clk, clk->parent->rate)) return 0; if (dd->bypass_clk->rate == rate && (clk->dpll_data->modes & (1 << DPLL_LOW_POWER_BYPASS))) { pr_debug("clock: %s: set rate: entering bypass.\n", clk->name); ret = _omap3_noncore_dpll_bypass(clk); if (!ret) clk->rate = rate; } else { if (dd->last_rounded_rate != rate) omap2_dpll_round_rate(clk, rate); if (dd->last_rounded_rate == 0) return -EINVAL; freqsel = _omap3_dpll_compute_freqsel(clk, dd->last_rounded_n); if (!freqsel) WARN_ON(1); pr_debug("clock: %s: set rate: locking rate to %lu.\n", clk->name, rate); ret = omap3_noncore_dpll_program(clk, dd->last_rounded_m, dd->last_rounded_n, freqsel); if (!ret) clk->rate = rate; } return 0; }
/** * omap2xxx_clk_get_core_rate - return the CORE_CLK rate * @clk: pointer to the combined dpll_ck + core_ck (currently "dpll_ck") * * Returns the CORE_CLK rate. CORE_CLK can have one of three rate * sources on OMAP2xxx: the DPLL CLKOUT rate, DPLL CLKOUTX2, or 32KHz * (the latter is unusual). This currently should be called with * struct clk *dpll_ck, which is a composite clock of dpll_ck and * core_ck. */ unsigned long omap2xxx_clk_get_core_rate(struct clk *clk) { long long core_clk; u32 v; core_clk = omap2_get_dpll_rate(clk); v = omap2_cm_read_mod_reg(PLL_MOD, CM_CLKSEL2); v &= OMAP24XX_CORE_CLK_SRC_MASK; if (v == CORE_CLK_SRC_32K) core_clk = 32768; else core_clk *= v; return core_clk; }
/** * omap2xxx_clk_get_core_rate - return the CORE_CLK rate * * Returns the CORE_CLK rate. CORE_CLK can have one of three rate * sources on OMAP2xxx: the DPLL CLKOUT rate, DPLL CLKOUTX2, or 32KHz * (the latter is unusual). This currently should be called with * struct clk *dpll_ck, which is a composite clock of dpll_ck and * core_ck. */ unsigned long omap2xxx_clk_get_core_rate(void) { long long core_clk; u32 v; WARN_ON(!dpll_core_ck); core_clk = omap2_get_dpll_rate(dpll_core_ck); v = omap2xxx_cm_get_core_clk_src(); if (v == CORE_CLK_SRC_32K) core_clk = 32768; else core_clk *= v; return core_clk; }
/** * omap4_dpll_regm4xen_recalc - compute DPLL rate, considering REGM4XEN bit * @clk: struct clk * of the DPLL to compute the rate for * * Compute the output rate for the OMAP4 DPLL represented by @clk. * Takes the REGM4XEN bit into consideration, which is needed for the * OMAP4 ABE DPLL. Returns the DPLL's output rate (before M-dividers) * upon success, or 0 upon error. */ unsigned long omap4_dpll_regm4xen_recalc(struct clk *clk) { u32 v; unsigned long rate; struct dpll_data *dd; if (!clk || !clk->dpll_data) return 0; dd = clk->dpll_data; rate = omap2_get_dpll_rate(clk); /* regm4xen adds a multiplier of 4 to DPLL calculations */ v = __raw_readl(dd->control_reg); if (v & OMAP4430_DPLL_REGM4XEN_MASK) rate *= OMAP4430_REGM4XEN_MULT; return rate; }
unsigned long omap4_dpll_regm4xen_recalc(struct clk *clk) { unsigned long rate; u32 reg; struct dpll_data *dd; if (!clk || !clk->dpll_data) return -EINVAL; dd = clk->dpll_data; rate = omap2_get_dpll_rate(clk); /* regm4xen adds a multiplier of 4 to DPLL calculations */ reg = __raw_readl(dd->control_reg); if (reg & (DPLL_REGM4XEN_ENABLE << OMAP4430_DPLL_REGM4XEN_SHIFT)) rate *= OMAP4430_REGM4XEN_MULT; return rate; }
/** * omap4_dpll_regm4xen_recalc - compute DPLL rate, considering REGM4XEN bit * @clk: struct clk * of the DPLL to compute the rate for * * Compute the output rate for the OMAP4 DPLL represented by @clk. * Takes the REGM4XEN bit into consideration, which is needed for the * OMAP4 ABE DPLL. Returns the DPLL's output rate (before M-dividers) * upon success, or 0 upon error. */ unsigned long omap4_dpll_regm4xen_recalc(struct clk_hw *hw, unsigned long parent_rate) { struct clk_hw_omap *clk = to_clk_hw_omap(hw); u32 v; unsigned long rate; struct dpll_data *dd; if (!clk || !clk->dpll_data) return 0; dd = clk->dpll_data; rate = omap2_get_dpll_rate(clk); /* regm4xen adds a multiplier of 4 to DPLL calculations */ v = ti_clk_ll_ops->clk_readl(dd->control_reg); if (v & OMAP4430_DPLL_REGM4XEN_MASK) rate *= OMAP4430_REGM4XEN_MULT; return rate; }
/** * omap3_dpll_recalc - recalculate DPLL rate * @clk: DPLL struct clk * * Recalculate and propagate the DPLL rate. */ static void omap3_dpll_recalc(struct clk *clk) { clk->rate = omap2_get_dpll_rate(clk); propagate_rate(clk); }
/** * omap3_dpll_recalc - recalculate DPLL rate * @clk: DPLL struct clk * * Recalculate and propagate the DPLL rate. */ unsigned long omap3_dpll_recalc(struct clk_hw *hw, unsigned long parent_rate) { struct clk_hw_omap *clk = to_clk_hw_omap(hw); return omap2_get_dpll_rate(clk); }
/** * omap3_noncore_dpll_set_rate - set non-core DPLL rate * @clk: struct clk * of DPLL to set * @rate: rounded target rate * * Set the DPLL CLKOUT to the target rate. If the DPLL can enter * low-power bypass, and the target rate is the bypass source clock * rate, then configure the DPLL for bypass. Otherwise, round the * target rate if it hasn't been done already, then program and lock * the DPLL. Returns -EINVAL upon error, or 0 upon success. */ int omap3_noncore_dpll_set_rate(struct clk *clk, unsigned long rate) { struct clk *new_parent = NULL; u16 freqsel = 0; struct dpll_data *dd; int ret; unsigned long orig_rate = 0; if (!clk || !rate) return -EINVAL; dd = clk->dpll_data; if (!dd) return -EINVAL; if (rate == omap2_get_dpll_rate(clk)) return 0; /* * Ensure both the bypass and ref clocks are enabled prior to * doing anything; we need the bypass clock running to reprogram * the DPLL. */ omap2_clk_enable(dd->clk_bypass); omap2_clk_enable(dd->clk_ref); if (dd->clk_bypass->rate == rate && (clk->dpll_data->modes & (1 << DPLL_LOW_POWER_BYPASS))) { pr_debug("clock: %s: set rate: entering bypass.\n", clk->name); ret = _omap3_noncore_dpll_bypass(clk); if (!ret) new_parent = dd->clk_bypass; } else { /* * On 4460, the MPU clk for frequencies higher than 1Ghz * is sourced from CLKOUTX2_M3, instead of CLKOUT_M2, while * value of M3 is fixed to 1. Hence for frequencies higher * than 1 Ghz, lock the DPLL at half the rate so the * CLKOUTX2_M3 then matches the requested rate. */ if (cpu_is_omap4460() && !strcmp(clk->name, "dpll_mpu_ck") && (rate > 1000000000)) { orig_rate = rate; rate = rate/2; } if (dd->last_rounded_rate != rate) rate = clk->round_rate(clk, rate); if (dd->last_rounded_rate == 0) return -EINVAL; /* No freqsel on OMAP4 and OMAP3630 */ if (!cpu_is_omap44xx() && !cpu_is_omap3630()) { freqsel = _omap3_dpll_compute_freqsel(clk, dd->last_rounded_n); if (!freqsel) WARN_ON(1); } /* Set the rate back to original for book keeping*/ if (orig_rate) rate = orig_rate; pr_debug("clock: %s: set rate: locking rate to %lu.\n", clk->name, rate); ret = omap3_noncore_dpll_program(clk, dd->last_rounded_m, dd->last_rounded_n, freqsel, orig_rate); if (!ret) new_parent = dd->clk_ref; } if (!ret) { /* * Switch the parent clock in the heirarchy, and make sure * that the new parent's usecount is correct. Note: we * enable the new parent before disabling the old to avoid * any unnecessary hardware disable->enable transitions. */ if (clk->usecount) { omap2_clk_enable(new_parent); omap2_clk_disable(clk->parent); } clk_reparent(clk, new_parent); clk->rate = rate; } omap2_clk_disable(dd->clk_ref); omap2_clk_disable(dd->clk_bypass); return 0; }
/** * omap3_dpll_recalc - recalculate DPLL rate * @clk: DPLL struct clk * * Recalculate and propagate the DPLL rate. */ unsigned long omap3_dpll_recalc(struct clk *clk) { return omap2_get_dpll_rate(clk); }
static int omap3_noncore_dpll_set_rate(struct clk *clk, unsigned long rate) { struct clk *new_parent = NULL; u16 freqsel; struct dpll_data *dd; int ret; if (!clk || !rate) return -EINVAL; dd = clk->dpll_data; if (!dd) return -EINVAL; if (rate == omap2_get_dpll_rate(clk)) return 0; omap2_clk_enable(dd->clk_bypass); omap2_clk_enable(dd->clk_ref); if (dd->clk_bypass->rate == rate && (clk->dpll_data->modes & (1 << DPLL_LOW_POWER_BYPASS))) { pr_debug("clock: %s: set rate: entering bypass.\n", clk->name); ret = _omap3_noncore_dpll_bypass(clk); if (!ret) new_parent = dd->clk_bypass; } else { if (dd->last_rounded_rate != rate) omap2_dpll_round_rate(clk, rate); if (dd->last_rounded_rate == 0) return -EINVAL; freqsel = _omap3_dpll_compute_freqsel(clk, dd->last_rounded_n); if (!freqsel) WARN_ON(1); pr_debug("clock: %s: set rate: locking rate to %lu.\n", clk->name, rate); ret = omap3_noncore_dpll_program(clk, dd->last_rounded_m, dd->last_rounded_n, freqsel); if (!ret) new_parent = dd->clk_ref; } if (!ret) { if (clk->usecount) { omap2_clk_enable(new_parent); omap2_clk_disable(clk->parent); } clk_reparent(clk, new_parent); clk->rate = rate; } omap2_clk_disable(dd->clk_ref); omap2_clk_disable(dd->clk_bypass); return 0; }
/** * omap3_noncore_dpll_set_rate - set non-core DPLL rate * @clk: struct clk * of DPLL to set * @rate: rounded target rate * * Set the DPLL CLKOUT to the target rate. If the DPLL can enter * low-power bypass, and the target rate is the bypass source clock * rate, then configure the DPLL for bypass. Otherwise, round the * target rate if it hasn't been done already, then program and lock * the DPLL. Returns -EINVAL upon error, or 0 upon success. */ int omap3_noncore_dpll_set_rate(struct clk *clk, unsigned long rate) { struct clk *new_parent = NULL; u16 freqsel = 0; struct dpll_data *dd; int ret; if (!clk || !rate) return -EINVAL; dd = clk->dpll_data; if (!dd) return -EINVAL; if (rate == omap2_get_dpll_rate(clk)) return 0; /* * Ensure both the bypass and ref clocks are enabled prior to * doing anything; we need the bypass clock running to reprogram * the DPLL. */ omap2_clk_enable(dd->clk_bypass); omap2_clk_enable(dd->clk_ref); if (dd->clk_bypass->rate == rate && (clk->dpll_data->modes & (1 << DPLL_LOW_POWER_BYPASS))) { pr_debug("clock: %s: set rate: entering bypass.\n", clk->name); ret = _omap3_noncore_dpll_bypass(clk); if (!ret) new_parent = dd->clk_bypass; } else { if (dd->last_rounded_rate != rate) rate = clk->round_rate(clk, rate); if (dd->last_rounded_rate == 0) return -EINVAL; /* No freqsel on OMAP4 and OMAP3630 */ if (!cpu_is_omap44xx() && !cpu_is_omap3630()) { freqsel = _omap3_dpll_compute_freqsel(clk, dd->last_rounded_n); if (!freqsel) WARN_ON(1); } pr_debug("clock: %s: set rate: locking rate to %lu.\n", clk->name, rate); ret = omap3_noncore_dpll_program(clk, dd->last_rounded_m, dd->last_rounded_n, freqsel); if (!ret) new_parent = dd->clk_ref; } if (!ret) { /* * Switch the parent clock in the hierarchy, and make sure * that the new parent's usecount is correct. Note: we * enable the new parent before disabling the old to avoid * any unnecessary hardware disable->enable transitions. */ if (clk->usecount) { omap2_clk_enable(new_parent); omap2_clk_disable(clk->parent); } clk_reparent(clk, new_parent); clk->rate = rate; } omap2_clk_disable(dd->clk_ref); omap2_clk_disable(dd->clk_bypass); return 0; }
int omap4460_mpu_dpll_set_rate(struct clk *clk, unsigned long rate) { struct dpll_data *dd; u32 v; unsigned long dpll_rate; if (!clk || !rate || !clk->parent) return -EINVAL; dd = clk->parent->dpll_data; if (!dd) return -EINVAL; if (!clk->parent->set_rate) return -EINVAL; if (rate > clk->rate) omap4460_mpu_dpll_update_children(rate); /* * On OMAP4460, to obtain MPU DPLL frequency higher * than 1GHz, DCC (Duty Cycle Correction) needs to * be enabled. * And needs to be kept disabled for < 1 Ghz. */ dpll_rate = omap2_get_dpll_rate(clk->parent); if (rate <= OMAP_1_5GHz) { /* If DCC is enabled, disable it */ v = __raw_readl(dd->mult_div1_reg); if (v & OMAP4460_DCC_EN_MASK) { v &= ~OMAP4460_DCC_EN_MASK; __raw_writel(v, dd->mult_div1_reg); } if (rate != dpll_rate) clk->parent->set_rate(clk->parent, rate); } else { /* * On 4460, the MPU clk for frequencies higher than 1Ghz * is sourced from CLKOUTX2_M3, instead of CLKOUT_M2, while * value of M3 is fixed to 1. Hence for frequencies higher * than 1 Ghz, lock the DPLL at half the rate so the * CLKOUTX2_M3 then matches the requested rate. */ if (rate != dpll_rate * 2) clk->parent->set_rate(clk->parent, rate / 2); v = __raw_readl(dd->mult_div1_reg); v &= ~OMAP4460_DCC_COUNT_MAX_MASK; v |= (5 << OMAP4460_DCC_COUNT_MAX_SHIFT); __raw_writel(v, dd->mult_div1_reg); v |= OMAP4460_DCC_EN_MASK; __raw_writel(v, dd->mult_div1_reg); } if (rate < clk->rate) omap4460_mpu_dpll_update_children(rate); clk->rate = rate; return 0; }