|
@@ -199,3 +199,116 @@ static u32 _divisor_to_clksel(struct clk_hw_omap *clk, u32 div)
|
|
|
*/
|
|
|
static u32 _read_divisor(struct clk_hw_omap *clk)
|
|
|
{
|
|
|
+ u32 v;
|
|
|
+
|
|
|
+ if (!clk->clksel || !clk->clksel_mask)
|
|
|
+ return 0;
|
|
|
+
|
|
|
+ v = __raw_readl(clk->clksel_reg);
|
|
|
+ v &= clk->clksel_mask;
|
|
|
+ v >>= __ffs(clk->clksel_mask);
|
|
|
+
|
|
|
+ return _clksel_to_divisor(clk, v);
|
|
|
+}
|
|
|
+
|
|
|
+/* Public functions */
|
|
|
+
|
|
|
+/**
|
|
|
+ * 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_hw_omap *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->hw.clk);
|
|
|
+ clk_name = __clk_get_name(clk->hw.clk);
|
|
|
+ parent_rate = __clk_get_rate(parent);
|
|
|
+
|
|
|
+ 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;
|
|
|
+}
|
|
|
+
|
|
|
+/*
|
|
|
+ * Clocktype interface functions to the OMAP clock code
|
|
|
+ * (i.e., those used in struct clk field function pointers, etc.)
|
|
|
+ */
|
|
|
+
|
|
|
+/**
|
|
|
+ * omap2_clksel_find_parent_index() - return the array index of the current
|
|
|
+ * hardware parent of @hw
|
|
|
+ * @hw: struct clk_hw * to find the current hardware parent of
|
|
|
+ *
|
|
|
+ * Given a struct clk_hw pointer @hw to the 'hw' member of a struct
|
|
|
+ * clk_hw_omap record representing a source-selectable hardware clock,
|
|
|
+ * read the hardware register and determine what its parent is
|
|
|
+ * currently set to. Intended to be called only by the common clock
|
|
|
+ * framework struct clk_hw_ops.get_parent function pointer. Return
|
|
|
+ * the array index of this parent clock upon success -- there is no
|
|
|
+ * way to return an error, so if we encounter an error, just WARN()
|
|
|
+ * and pretend that we know that we're doing.
|
|
|
+ */
|
|
|
+u8 omap2_clksel_find_parent_index(struct clk_hw *hw)
|
|
|
+{
|
|
|
+ struct clk_hw_omap *clk = to_clk_hw_omap(hw);
|
|
|
+ const struct clksel *clks;
|
|
|
+ const struct clksel_rate *clkr;
|
|
|
+ u32 r, found = 0;
|
|
|
+ struct clk *parent;
|
|
|
+ const char *clk_name;
|
|
|
+ int ret = 0, f = 0;
|