clk: clk: Add functions to save/restore clock context en-masse
authorRuss Dill <Russ.Dill@ti.com>
Fri, 2 Nov 2018 10:28:01 +0000 (15:58 +0530)
committerTero Kristo <t-kristo@ti.com>
Tue, 6 Nov 2018 13:03:46 +0000 (15:03 +0200)
commit 8b95d1ce3300c411728954473316bd04d0ba9883 upstream

Deep enough power saving mode can result into losing context of the clock
registers also, and they need to be restored once coming back from the power
saving mode. Hence add functions to save/restore clock context.

Signed-off-by: Keerthy <j-keerthy@ti.com>
Signed-off-by: Russ Dill <Russ.Dill@ti.com>
drivers/clk/clk.c
include/linux/clk-provider.h
include/linux/clk.h

index d31055ae6ec6f94c23409df9f47936f09b54c1bf..8a0254a1c3037e52604a96c217880d420865e157 100644 (file)
@@ -923,6 +923,80 @@ static int clk_core_enable_lock(struct clk_core *core)
        return ret;
 }
 
+static int _clk_save_context(struct clk_core *clk)
+{
+       struct clk_core *child;
+       int ret = 0;
+
+       hlist_for_each_entry(child, &clk->children, child_node) {
+               ret = _clk_save_context(child);
+               if (ret < 0)
+                       return ret;
+       }
+
+       if (clk->ops && clk->ops->save_context)
+               ret = clk->ops->save_context(clk->hw);
+
+       return ret;
+}
+
+static void _clk_restore_context(struct clk_core *clk)
+{
+       struct clk_core *child;
+
+       if (clk->ops && clk->ops->restore_context)
+               clk->ops->restore_context(clk->hw);
+
+       hlist_for_each_entry(child, &clk->children, child_node)
+               _clk_restore_context(child);
+}
+
+/**
+ * clk_save_context - save clock context for poweroff
+ *
+ * Saves the context of the clock register for powerstates in which the
+ * contents of the registers will be lost. Occurs deep within the suspend
+ * code.  Returns 0 on success.
+ */
+int clk_save_context(void)
+{
+       struct clk_core *clk;
+       int ret;
+
+       hlist_for_each_entry(clk, &clk_root_list, child_node) {
+               ret = _clk_save_context(clk);
+               if (ret < 0)
+                       return ret;
+       }
+
+       hlist_for_each_entry(clk, &clk_orphan_list, child_node) {
+               ret = _clk_save_context(clk);
+               if (ret < 0)
+                       return ret;
+       }
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(clk_save_context);
+
+/**
+ * clk_restore_context - restore clock context after poweroff
+ *
+ * Restore the saved clock context upon resume.
+ *
+ */
+void clk_restore_context(void)
+{
+       struct clk_core *clk;
+
+       hlist_for_each_entry(clk, &clk_root_list, child_node)
+               _clk_restore_context(clk);
+
+       hlist_for_each_entry(clk, &clk_orphan_list, child_node)
+               _clk_restore_context(clk);
+}
+EXPORT_SYMBOL_GPL(clk_restore_context);
+
 /**
  * clk_enable - ungate a clock
  * @clk: the clk being ungated
index 08b1aa70a38d30a30cb4ae0f58bf7a68be92b15b..df7379da62695ec44d75ffe428a09d11604b1bcb 100644 (file)
@@ -119,6 +119,11 @@ struct clk_duty {
  *             Called with enable_lock held.  This function must not
  *             sleep.
  *
+ * @save_context: Save the context of the clock in prepration for poweroff.
+ *
+ * @restore_context: Restore the context of the clock after a restoration
+ *             of power.
+ *
  * @recalc_rate        Recalculate the rate of this clock, by querying hardware. The
  *             parent rate is an input parameter.  It is up to the caller to
  *             ensure that the prepare_mutex is held across this call.
@@ -223,6 +228,8 @@ struct clk_ops {
        void            (*disable)(struct clk_hw *hw);
        int             (*is_enabled)(struct clk_hw *hw);
        void            (*disable_unused)(struct clk_hw *hw);
+       int             (*save_context)(struct clk_hw *hw);
+       void            (*restore_context)(struct clk_hw *hw);
        unsigned long   (*recalc_rate)(struct clk_hw *hw,
                                        unsigned long parent_rate);
        long            (*round_rate)(struct clk_hw *hw, unsigned long rate,
index 4f750c481b82b29d05e136d9cea42e51e58199b9..7da754d79f9de0b2bbca3f2ebbe6b7938963a8cc 100644 (file)
@@ -629,6 +629,23 @@ struct clk *clk_get_parent(struct clk *clk);
  */
 struct clk *clk_get_sys(const char *dev_id, const char *con_id);
 
+/**
+ * clk_save_context - save clock context for poweroff
+ *
+ * Saves the context of the clock register for powerstates in which the
+ * contents of the registers will be lost. Occurs deep within the suspend
+ * code so locking is not necessary.
+ */
+int clk_save_context(void);
+
+/**
+ * clk_restore_context - restore clock context after poweroff
+ *
+ * This occurs with all clocks enabled. Occurs deep within the resume code
+ * so locking is not necessary.
+ */
+void clk_restore_context(void);
+
 #else /* !CONFIG_HAVE_CLK */
 
 static inline struct clk *clk_get(struct device *dev, const char *id)
@@ -728,6 +745,14 @@ static inline struct clk *clk_get_sys(const char *dev_id, const char *con_id)
 {
        return NULL;
 }
+
+static inline int clk_save_context(void)
+{
+       return 0;
+}
+
+static inline void clk_restore_context(void) {}
+
 #endif
 
 /* clk_prepare_enable helps cases using clk_enable in non-atomic context. */