|
@@ -90,3 +90,112 @@ static const struct clksel *_get_clksel_by_parent(struct clk_hw_omap *clk,
|
|
|
* will ensure that the write to the clksel_reg reaches its
|
|
|
* destination before returning -- important since PRM and CM register
|
|
|
* accesses can be quite slow compared to ARM cycles -- but does not
|
|
|
+ * take into account any time the hardware might take to switch the
|
|
|
+ * clock source.
|
|
|
+ */
|
|
|
+static void _write_clksel_reg(struct clk_hw_omap *clk, u32 field_val)
|
|
|
+{
|
|
|
+ u32 v;
|
|
|
+
|
|
|
+ v = __raw_readl(clk->clksel_reg);
|
|
|
+ v &= ~clk->clksel_mask;
|
|
|
+ v |= field_val << __ffs(clk->clksel_mask);
|
|
|
+ __raw_writel(v, clk->clksel_reg);
|
|
|
+
|
|
|
+ v = __raw_readl(clk->clksel_reg); /* OCP barrier */
|
|
|
+}
|
|
|
+
|
|
|
+/**
|
|
|
+ * _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_hw_omap *clk, u32 field_val)
|
|
|
+{
|
|
|
+ const struct clksel *clks;
|
|
|
+ const struct clksel_rate *clkr;
|
|
|
+ struct clk *parent;
|
|
|
+
|
|
|
+ parent = __clk_get_parent(clk->hw.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->hw.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_hw_omap *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->hw.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->hw.clk), div,
|
|
|
+ __clk_get_name(parent));
|
|
|
+ return ~0;
|
|
|
+ }
|
|
|
+
|
|
|
+ return clkr->val;
|
|
|
+}
|
|
|
+
|
|
|
+/**
|
|
|
+ * _read_divisor() - get current divisor applied to parent clock (from hdwr)
|
|
|
+ * @clk: OMAP struct clk to use.
|
|
|
+ *
|
|
|
+ * Read the current divisor register value for @clk that is programmed
|
|
|
+ * into the hardware, convert it into the actual divisor value, and
|
|
|
+ * return it; or return 0 on error.
|
|
|
+ */
|
|
|
+static u32 _read_divisor(struct clk_hw_omap *clk)
|
|
|
+{
|