|
@@ -613,3 +613,65 @@ void omap3_dpll_deny_idle(struct clk_hw_omap *clk)
|
|
|
dd = clk->dpll_data;
|
|
|
|
|
|
if (!dd->autoidle_reg)
|
|
|
+ return;
|
|
|
+
|
|
|
+ v = __raw_readl(dd->autoidle_reg);
|
|
|
+ v &= ~dd->autoidle_mask;
|
|
|
+ v |= DPLL_AUTOIDLE_DISABLE << __ffs(dd->autoidle_mask);
|
|
|
+ __raw_writel(v, dd->autoidle_reg);
|
|
|
+
|
|
|
+}
|
|
|
+
|
|
|
+/* Clock control for DPLL outputs */
|
|
|
+
|
|
|
+/**
|
|
|
+ * omap3_clkoutx2_recalc - recalculate DPLL X2 output virtual clock rate
|
|
|
+ * @clk: DPLL output struct clk
|
|
|
+ *
|
|
|
+ * Using parent clock DPLL data, look up DPLL state. If locked, set our
|
|
|
+ * rate to the dpll_clk * 2; otherwise, just use dpll_clk.
|
|
|
+ */
|
|
|
+unsigned long omap3_clkoutx2_recalc(struct clk_hw *hw,
|
|
|
+ unsigned long parent_rate)
|
|
|
+{
|
|
|
+ const struct dpll_data *dd;
|
|
|
+ unsigned long rate;
|
|
|
+ u32 v;
|
|
|
+ struct clk_hw_omap *pclk = NULL;
|
|
|
+ struct clk *parent;
|
|
|
+
|
|
|
+ /* Walk up the parents of clk, looking for a DPLL */
|
|
|
+ do {
|
|
|
+ do {
|
|
|
+ parent = __clk_get_parent(hw->clk);
|
|
|
+ hw = __clk_get_hw(parent);
|
|
|
+ } while (hw && (__clk_get_flags(hw->clk) & CLK_IS_BASIC));
|
|
|
+ if (!hw)
|
|
|
+ break;
|
|
|
+ pclk = to_clk_hw_omap(hw);
|
|
|
+ } while (pclk && !pclk->dpll_data);
|
|
|
+
|
|
|
+ /* clk does not have a DPLL as a parent? error in the clock data */
|
|
|
+ if (!pclk) {
|
|
|
+ WARN_ON(1);
|
|
|
+ return 0;
|
|
|
+ }
|
|
|
+
|
|
|
+ dd = pclk->dpll_data;
|
|
|
+
|
|
|
+ WARN_ON(!dd->enable_mask);
|
|
|
+
|
|
|
+ v = __raw_readl(dd->control_reg) & dd->enable_mask;
|
|
|
+ v >>= __ffs(dd->enable_mask);
|
|
|
+ if ((v != OMAP3XXX_EN_DPLL_LOCKED) || (dd->flags & DPLL_J_TYPE))
|
|
|
+ rate = parent_rate;
|
|
|
+ else
|
|
|
+ rate = parent_rate * 2;
|
|
|
+ return rate;
|
|
|
+}
|
|
|
+
|
|
|
+/* OMAP3/4 non-CORE DPLL clkops */
|
|
|
+const struct clk_hw_omap_ops clkhwops_omap3_dpll = {
|
|
|
+ .allow_idle = omap3_dpll_allow_idle,
|
|
|
+ .deny_idle = omap3_dpll_deny_idle,
|
|
|
+};
|