Introduce prepare_hw and prepare_done to support calling
clk_prepare_enable in early kernel booting where we still
can't schedule.

The prepare_hw callback is intended to do the hw part
initialization of prepare work. It should cooperate with
prepare_done callback to do the whole prepare work.
The clock core will check @prepare_done in sleep or
polling way according to system state to decide whether the
whole prepare work is done.

Suggested-by: Thomas Gleixner <[email protected]>
Signed-off-by: Dong Aisheng <[email protected]>
---
 drivers/clk/clk.c            | 57 ++++++++++++++++++++++++++++++++++++++++++--
 include/linux/clk-provider.h | 32 +++++++++++++++++++++++++
 2 files changed, 87 insertions(+), 2 deletions(-)

diff --git a/drivers/clk/clk.c b/drivers/clk/clk.c
index d584004f7af7..7dcb34c75a9f 100644
--- a/drivers/clk/clk.c
+++ b/drivers/clk/clk.c
@@ -12,6 +12,7 @@
 #include <linux/clk.h>
 #include <linux/clk-provider.h>
 #include <linux/clk/clk-conf.h>
+#include <linux/delay.h>
 #include <linux/module.h>
 #include <linux/mutex.h>
 #include <linux/spinlock.h>
@@ -60,6 +61,8 @@ struct clk_core {
        bool                    orphan;
        unsigned int            enable_count;
        unsigned int            prepare_count;
+       unsigned long           delay_min;
+       unsigned long           delay_max;
        unsigned long           min_rate;
        unsigned long           max_rate;
        unsigned long           accuracy;
@@ -566,6 +569,8 @@ EXPORT_SYMBOL_GPL(__clk_mux_determine_rate_closest);
 
 static void clk_core_unprepare(struct clk_core *core)
 {
+       unsigned long timeout;
+
        lockdep_assert_held(&prepare_lock);
 
        if (!core)
@@ -584,8 +589,30 @@ static void clk_core_unprepare(struct clk_core *core)
 
        trace_clk_unprepare(core);
 
-       if (core->ops->unprepare)
+       if (core->ops->unprepare) {
                core->ops->unprepare(core->hw);
+       } else if (core->ops->unprepare_hw) {
+               core->ops->unprepare_hw(core->hw);
+               if (core->ops->unprepare_done) {
+                       timeout = jiffies + msecs_to_jiffies(10);
+                       while (!core->ops->unprepare_done(core->hw)) {
+                               if (time_after(jiffies, timeout)) {
+                                       pr_err("%s: clock %s unprepare 
timeout\n",
+                                               __func__, core->name);
+                                       break;
+                               }
+                               if (system_state == SYSTEM_BOOTING)
+                                       /*
+                                        * Busy loop as we can't schedule in
+                                        * early boot
+                                        */
+                                       continue;
+                               else
+                                       usleep_range(core->delay_min,
+                                                    core->delay_max);
+                       }
+               }
+       }
 
        trace_clk_unprepare_complete(core);
        clk_core_unprepare(core->parent);
@@ -615,6 +642,7 @@ EXPORT_SYMBOL_GPL(clk_unprepare);
 
 static int clk_core_prepare(struct clk_core *core)
 {
+       unsigned long timeout;
        int ret = 0;
 
        lockdep_assert_held(&prepare_lock);
@@ -629,8 +657,31 @@ static int clk_core_prepare(struct clk_core *core)
 
                trace_clk_prepare(core);
 
-               if (core->ops->prepare)
+               if (core->ops->prepare) {
                        ret = core->ops->prepare(core->hw);
+               } else if (core->ops->prepare_hw) {
+                       ret = core->ops->prepare_hw(core->hw);
+                       if (!ret && core->ops->prepare_done) {
+                               timeout = jiffies + msecs_to_jiffies(10);
+                               while (!core->ops->prepare_done(core->hw)) {
+                                       if (time_after(jiffies, timeout)) {
+                                               pr_err("%s: clock %s prepare 
timeout\n",
+                                                       __func__, core->name);
+                                               ret = -ETIMEDOUT;
+                                               break;
+                                       }
+                                       if (system_state == SYSTEM_BOOTING)
+                                               /*
+                                                * Busy loop as we can't
+                                                * schedule in early boot
+                                                */
+                                               continue;
+                                       else
+                                               usleep_range(core->delay_min,
+                                                            core->delay_max);
+                               }
+                       }
+               }
 
                trace_clk_prepare_complete(core);
 
@@ -2490,6 +2541,8 @@ struct clk *clk_register(struct device *dev, struct 
clk_hw *hw)
        core->hw = hw;
        core->flags = hw->init->flags;
        core->num_parents = hw->init->num_parents;
+       core->delay_min = hw->init->delay_min;
+       core->delay_max = hw->init->delay_max;
        core->min_rate = 0;
        core->max_rate = ULONG_MAX;
        hw->core = core;
diff --git a/include/linux/clk-provider.h b/include/linux/clk-provider.h
index fb39d5add173..b37174360f1c 100644
--- a/include/linux/clk-provider.h
+++ b/include/linux/clk-provider.h
@@ -72,10 +72,34 @@ struct clk_rate_request {
  *             do any initialisation that may sleep. Called with
  *             prepare_lock held.
  *
+ * @prepare_hw:        Prepare the clock hw for enabling. This callback is 
intended
+ *             to do the hw part initialization of prepare work. It should
+ *             cooperate with @prepare_done callback to do the whole prepare
+ *             work. The clock core will check @prepare_done in sleep or
+ *             polling way according to system state to decide whether the
+ *             whole prepare work is done. Optional if @prepare is used.
+ *             This function must not sleep.
+ *
+ * @prepare_done: Queries the hardware to determine if the clock hw is 
prepared.
+ *             Optional, if this op is not set then the prepare simply return.
+ *             This function must not sleep.
+ *
  * @unprepare: Release the clock from its prepared state. This will typically
  *             undo any work done in the @prepare callback. Called with
  *             prepare_lock held.
  *
+ * @unprepare_hw: Release the clock from its prepared hw state. This will
+ *             typically undo any work done in the @prepare_hw callback.
+ *             It should cooperate with @unprepare_done callback to
+ *             do the whole unprepare work. The clock core will check
+ *             @unprepare_done in either sleep or polling way according to
+ *             system state to decide whether the whole unprepare work is done.
+ *             Optional if @prepare is used. This function must not sleep.
+ *
+ * @unprepare_done: Queries the hardware to determine if the clock hw
+ *             is unprepared. Optional, if this op is not set then the
+ *             unprepare simply return. This function must not sleep.
+ *
  * @is_prepared: Queries the hardware to determine if the clock is prepared.
  *             This function is allowed to sleep. Optional, if this op is not
  *             set then the prepare count will be used.
@@ -189,7 +213,11 @@ struct clk_rate_request {
  */
 struct clk_ops {
        int             (*prepare)(struct clk_hw *hw);
+       int             (*prepare_hw)(struct clk_hw *hw);
+       int             (*prepare_done)(struct clk_hw *hw);
        void            (*unprepare)(struct clk_hw *hw);
+       void            (*unprepare_hw)(struct clk_hw *hw);
+       int             (*unprepare_done)(struct clk_hw *hw);
        int             (*is_prepared)(struct clk_hw *hw);
        void            (*unprepare_unused)(struct clk_hw *hw);
        int             (*enable)(struct clk_hw *hw);
@@ -226,6 +254,8 @@ struct clk_ops {
  * @parent_names: array of string names for all possible parents
  * @num_parents: number of possible parents
  * @flags: framework-level hints and quirks
+ * @delay_min: min delays in us for clock hw prepare
+ * @delay_max: max delays in us for clock hw prepare
  */
 struct clk_init_data {
        const char              *name;
@@ -233,6 +263,8 @@ struct clk_init_data {
        const char              * const *parent_names;
        u8                      num_parents;
        unsigned long           flags;
+       unsigned int            delay_min;
+       unsigned int            delay_max;
 };
 
 /**
-- 
1.9.1

Reply via email to