diff options
Diffstat (limited to 'arch/arm/mach-omap2/dpll3xxx.c')
-rw-r--r-- | arch/arm/mach-omap2/dpll3xxx.c | 179 |
1 files changed, 119 insertions, 60 deletions
diff --git a/arch/arm/mach-omap2/dpll3xxx.c b/arch/arm/mach-omap2/dpll3xxx.c index ac3d789ac3cd..c2da2a0fe5ad 100644 --- a/arch/arm/mach-omap2/dpll3xxx.c +++ b/arch/arm/mach-omap2/dpll3xxx.c @@ -460,25 +460,24 @@ void omap3_noncore_dpll_disable(struct clk_hw *hw) /* Non-CORE DPLL rate set code */ /** - * omap3_noncore_dpll_set_rate - set non-core DPLL rate - * @clk: struct clk * of DPLL to set - * @rate: rounded target rate + * omap3_noncore_dpll_determine_rate - determine rate for a DPLL + * @hw: pointer to the clock to determine rate for + * @rate: target rate for the DPLL + * @best_parent_rate: pointer for returning best parent rate + * @best_parent_clk: pointer for returning best parent clock * - * 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. + * Determines which DPLL mode to use for reaching a desired target rate. + * Checks whether the DPLL shall be in bypass or locked mode, and if + * locked, calculates the M,N values for the DPLL via round-rate. + * Returns a positive clock rate with success, negative error value + * in failure. */ -int omap3_noncore_dpll_set_rate(struct clk_hw *hw, unsigned long rate, - unsigned long parent_rate) +long omap3_noncore_dpll_determine_rate(struct clk_hw *hw, unsigned long rate, + unsigned long *best_parent_rate, + struct clk_hw **best_parent_clk) { struct clk_hw_omap *clk = to_clk_hw_omap(hw); - struct clk *new_parent = NULL; - unsigned long rrate; - u16 freqsel = 0; struct dpll_data *dd; - int ret; if (!hw || !rate) return -EINVAL; @@ -489,61 +488,121 @@ int omap3_noncore_dpll_set_rate(struct clk_hw *hw, unsigned long rate, if (__clk_get_rate(dd->clk_bypass) == rate && (dd->modes & (1 << DPLL_LOW_POWER_BYPASS))) { - pr_debug("%s: %s: set rate: entering bypass.\n", - __func__, __clk_get_name(hw->clk)); + *best_parent_clk = __clk_get_hw(dd->clk_bypass); + } else { + rate = omap2_dpll_round_rate(hw, rate, best_parent_rate); + *best_parent_clk = __clk_get_hw(dd->clk_ref); + } + + *best_parent_rate = rate; + + return rate; +} + +/** + * omap3_noncore_dpll_set_parent - set parent for a DPLL clock + * @hw: pointer to the clock to set parent for + * @index: parent index to select + * + * Sets parent for a DPLL clock. This sets the DPLL into bypass or + * locked mode. Returns 0 with success, negative error value otherwise. + */ +int omap3_noncore_dpll_set_parent(struct clk_hw *hw, u8 index) +{ + struct clk_hw_omap *clk = to_clk_hw_omap(hw); + int ret; - __clk_prepare(dd->clk_bypass); - clk_enable(dd->clk_bypass); + if (!hw) + return -EINVAL; + + if (index) ret = _omap3_noncore_dpll_bypass(clk); - if (!ret) - new_parent = dd->clk_bypass; - clk_disable(dd->clk_bypass); - __clk_unprepare(dd->clk_bypass); - } else { - __clk_prepare(dd->clk_ref); - clk_enable(dd->clk_ref); - - /* XXX this check is probably pointless in the CCF context */ - if (dd->last_rounded_rate != rate) { - rrate = __clk_round_rate(hw->clk, rate); - if (rrate != rate) { - pr_warn("%s: %s: final rate %lu does not match desired rate %lu\n", - __func__, __clk_get_name(hw->clk), - rrate, rate); - rate = rrate; - } - } + else + ret = _omap3_noncore_dpll_lock(clk); - if (dd->last_rounded_rate == 0) - return -EINVAL; + return ret; +} - /* 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); - } +/** + * 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; - pr_debug("%s: %s: set rate: locking rate to %lu.\n", - __func__, __clk_get_name(hw->clk), rate); + if (__clk_get_parent(hw->clk) != dd->clk_ref) + return -EINVAL; + + if (dd->last_rounded_rate == 0) + return -EINVAL; - ret = omap3_noncore_dpll_program(clk, freqsel); - if (!ret) - new_parent = dd->clk_ref; - clk_disable(dd->clk_ref); - __clk_unprepare(dd->clk_ref); + /* 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); } - /* - * FIXME - this is all wrong. common code handles reparenting and - * migrating prepare/enable counts. dplls should be a multiplexer - * clock and this should be a set_parent operation so that all of that - * stuff is inherited for free - */ - if (!ret && clk_get_parent(hw->clk) != new_parent) - __clk_reparent(hw->clk, new_parent); + pr_debug("%s: %s: set rate: locking rate to %lu.\n", __func__, + __clk_get_name(hw->clk), rate); - return 0; + ret = omap3_noncore_dpll_program(clk, freqsel); + + return ret; +} + +/** + * omap3_noncore_dpll_set_rate_and_parent - set rate and parent for a DPLL clock + * @hw: pointer to the clock to set rate and parent for + * @rate: target rate for the DPLL + * @parent_rate: clock rate of the DPLL parent + * @index: new parent index for the DPLL, 0 - reference, 1 - bypass + * + * Sets rate and parent for a DPLL clock. If new parent is the bypass + * clock, only selects the parent. Otherwise proceeds with a rate + * change, as this will effectively also change the parent as the + * DPLL is put into locked mode. Returns 0 with success, negative error + * value otherwise. + */ +int omap3_noncore_dpll_set_rate_and_parent(struct clk_hw *hw, + unsigned long rate, + unsigned long parent_rate, + u8 index) +{ + int ret; + + if (!hw || !rate) + return -EINVAL; + + /* + * clk-ref at index[0], in which case we only need to set rate, + * the parent will be changed automatically with the lock sequence. + * With clk-bypass case we only need to change parent. + */ + if (index) + ret = omap3_noncore_dpll_set_parent(hw, index); + else + ret = omap3_noncore_dpll_set_rate(hw, rate, parent_rate); + + return ret; } /* DPLL autoidle read/set code */ |