[PATCH RFC 1/7] clk: add prepare_hw and prepare_done support

Dong Aisheng aisheng.dong at nxp.com
Wed Jun 29 06:52:09 PDT 2016


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 <tglx at linutronix.de>
Signed-off-by: Dong Aisheng <aisheng.dong at nxp.com>
---
 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




More information about the linux-arm-kernel mailing list