|
@@ -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 **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 = dd->clk_bypass;
|
|
|
+ } else {
|
|
|
+ rate = omap2_dpll_round_rate(hw, rate, best_parent_rate);
|
|
|
+ *best_parent_clk = 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 */
|