Merge remote-tracking branch 'xen-tip/linux-next'
[deliverable/linux.git] / drivers / clk / sunxi-ng / ccu_mux.c
index 58fc36e7dcce260530d5757aab7866130051f570..a43ad52a957dcbd104ae29a67ecdf4e057038bf1 100644 (file)
@@ -8,7 +8,9 @@
  * the License, or (at your option) any later version.
  */
 
+#include <linux/clk.h>
 #include <linux/clk-provider.h>
+#include <linux/delay.h>
 
 #include "ccu_gate.h"
 #include "ccu_mux.h"
@@ -18,8 +20,9 @@ void ccu_mux_helper_adjust_parent_for_prediv(struct ccu_common *common,
                                             int parent_index,
                                             unsigned long *parent_rate)
 {
-       u8 prediv = 1;
+       u16 prediv = 1;
        u32 reg;
+       int i;
 
        if (!((common->features & CCU_FEATURE_FIXED_PREDIV) ||
              (common->features & CCU_FEATURE_VARIABLE_PREDIV)))
@@ -32,8 +35,9 @@ void ccu_mux_helper_adjust_parent_for_prediv(struct ccu_common *common,
        }
 
        if (common->features & CCU_FEATURE_FIXED_PREDIV)
-               if (parent_index == cm->fixed_prediv.index)
-                       prediv = cm->fixed_prediv.div;
+               for (i = 0; i < cm->n_predivs; i++)
+                       if (parent_index == cm->fixed_predivs[i].index)
+                               prediv = cm->fixed_predivs[i].div;
 
        if (common->features & CCU_FEATURE_VARIABLE_PREDIV)
                if (parent_index == cm->variable_prediv.index) {
@@ -107,6 +111,15 @@ u8 ccu_mux_helper_get_parent(struct ccu_common *common,
        parent = reg >> cm->shift;
        parent &= (1 << cm->width) - 1;
 
+       if (cm->table) {
+               int num_parents = clk_hw_get_num_parents(&common->hw);
+               int i;
+
+               for (i = 0; i < num_parents; i++)
+                       if (cm->table[i] == parent)
+                               return i;
+       }
+
        return parent;
 }
 
@@ -117,6 +130,9 @@ int ccu_mux_helper_set_parent(struct ccu_common *common,
        unsigned long flags;
        u32 reg;
 
+       if (cm->table)
+               index = cm->table[index];
+
        spin_lock_irqsave(common->lock, flags);
 
        reg = readl(common->base + common->reg);
@@ -185,3 +201,37 @@ const struct clk_ops ccu_mux_ops = {
        .determine_rate = __clk_mux_determine_rate,
        .recalc_rate    = ccu_mux_recalc_rate,
 };
+
+/*
+ * This clock notifier is called when the frequency of the of the parent
+ * PLL clock is to be changed. The idea is to switch the parent to a
+ * stable clock, such as the main oscillator, while the PLL frequency
+ * stabilizes.
+ */
+static int ccu_mux_notifier_cb(struct notifier_block *nb,
+                              unsigned long event, void *data)
+{
+       struct ccu_mux_nb *mux = to_ccu_mux_nb(nb);
+       int ret = 0;
+
+       if (event == PRE_RATE_CHANGE) {
+               mux->original_index = ccu_mux_helper_get_parent(mux->common,
+                                                               mux->cm);
+               ret = ccu_mux_helper_set_parent(mux->common, mux->cm,
+                                               mux->bypass_index);
+       } else if (event == POST_RATE_CHANGE) {
+               ret = ccu_mux_helper_set_parent(mux->common, mux->cm,
+                                               mux->original_index);
+       }
+
+       udelay(mux->delay_us);
+
+       return notifier_from_errno(ret);
+}
+
+int ccu_mux_notifier_register(struct clk *clk, struct ccu_mux_nb *mux_nb)
+{
+       mux_nb->clk_nb.notifier_call = ccu_mux_notifier_cb;
+
+       return clk_notifier_register(clk, &mux_nb->clk_nb);
+}
This page took 0.036307 seconds and 5 git commands to generate.