linux-kernel.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH RFC 0/7] support clk setting during kernel early boot
@ 2016-06-29 13:52 Dong Aisheng
  2016-06-29 13:52 ` [PATCH RFC 1/7] clk: add prepare_hw and prepare_done support Dong Aisheng
                   ` (7 more replies)
  0 siblings, 8 replies; 13+ messages in thread
From: Dong Aisheng @ 2016-06-29 13:52 UTC (permalink / raw)
  To: linux-clk
  Cc: linux-kernel, sboyd, mturquette, shawnguo, linux-arm-kernel,
	tglx, aisheng.dong, stefan, l.stach, cyrille.pitchen, manabian,
	anson.huang

Recently several people met the kernel complaining
"bad: scheduling from the idle thread!" issue which caused by
sleeping during kernel early booting phase by calling clk
APIs like clk_prepare_enable.

See:
https://lkml.org/lkml/fancy/2016/1/29/695
https://lkml.org/lkml/2016/6/10/779
http://www.spinics.net/lists/linux-clk/msg08635.html

The calling sequence simply could be like:
start_kernel
  ->time_init
    ->of_clk_init
      ->clk_core_prepare
        ->clk_pllv3_prepare
          ->usleep_range
            ->dequeue_task_idle

This issue is mainly caused during time_init, the irq is still
not enabled and scheduler is still not ready, thus there's no way
to allow sleep at that time.

However, there're many exist platforms calling clk_prepare_enable/
clk_get_rate/clk_set_parent at that time in CLK_OF_DECLARE init
function.
e.g
drivers/clk/imx/clk-{soc}.c
drivers/clk/rockchip/clk-rk3188.c
drivers/clk/ti/clk-44xx.c
...

And irqchip and clock source is also initialized before it which
may requires to do clk settings.

Furthermore, current clk framework also supports critical clocks
flagged by CLK_IS_CRITICAL which will be prepared and
enabled during clk_register by clk core, that is also happened
quite early in of_clk_init usually.

And clk framework also supports assign default clk rate and parent for
each registered clk provider which also happens early in of_clk_init.
(see of_clk_set_defaults())

Above are all possible cases which may cause sleeping during kernel
early booting.

So it seems we'd like to have the requirement to make kernel support
calling clk APIs during kernel early boot without sleep.

Thomas suggested a solution to fix at core layer instead of hacks
at each platform driver at here during our early discussion.
https://lkml.org/lkml/fancy/2016/1/29/695

The basic idea behind is spliting the sleepable API into two part and
let the clk core to handle if sleep or polling according to system state.
e.g.
clk_ops.prepare()       Legacy callback
clk_ops.prepare_hw()    Callback which writes to the hardware
clk_ops.prepare_done()  Callback which checks whether the prepare is completed

So now the core can do:

clk_prepare()
{
        /* Legacy code should go away */
        if (ops->prepare) {
              ops->prepare();
              return;
        }

        if (ops->prepare_hw)
              ops->prepare_hw();

        if (!ops->prepare_done())
                return;

        if (early_boot_or_suspend_resume()) {
                /*
                 * Busy loop when we can't schedule in early boot,
                 * suspend and resume.
                 */
                while (!ops->prepare_done())
                      ;
        } else {
                while (!ops->prepare_done())
                      usleep(clk->prepare_delay);
        }
}

As a side effect that allows us to remove the gazillion of

            while (!hw_ready)
                  usleep();

copies all over the place and we have a single point of control where we allow
the clocks to busy loop.

This RFC gives a draft implementation and aim to start a dicussion for
the known issues during the implementation.
1) Thomas's original proposal seems like to to remove legacy code
(ops->prepare) in the future.
However, we might not be able to do it due to there may be some special
clocks which can't avoid sleep and can't be divided into xxx_hw and xxx_done.
e.g. I2C clocks, virtual clocks (drivers/clk/imx/clk-cpu.c)
and clocks operate via sync conmunication with firmware(i.MX8).

For those clocks, we may not be able to convert them and still have to
keep the exist callbacks for them which will make us to increase a lot
new callbacks.

2) Do we need define each sleep range for each sleep cases?
Current only one sleep range(delay_min and delay_max) interface
defined in clk_init_data for all sleeping user cases including
set_rate/set_parent/..?
The benefit is avoid defining more x_delay_min and x_delay_max.
The drawback is the small range may causing more interrupts
for big range.
e.g.
set_rate	delay min 50ns	max 500ns
set_parent	delay min 10ns	max 20ns
Then range becomes usleep_range(10, 500)

3) core level sleep timeout is 10ms, probably may be small.

4) This RFC only implements prepare_hw/set_rate_hw/set_parent_hw.
In theory, all callbacks protected by prepare_lock(mutex) may need
the same implementation.

The complete list may be:
prepare_hw
prepare_done
unprepare_hw
unprepare_done
set_rate_hw
set_rate_done
set_parent_hw
set_parent_done
recalc_rate_hw
recalc_rate_done
round_rate_hw
round_rate_done
determine_rate_hw
determine_rate_done
set_rate_and_parent_hw
set_rate_and_parent_done
recalc_accuracy_hw
recalc_accuracy_done
get_phase_hw
get_phase_hw_done
set_phase_hw
set_phase_hw_done
(get_rate & get_parent do not need)

Since many of them is actually mostly implemented unsleepable already,
e.g. recalc_rate, recalc_accuracy, get/set_phase
but due to the definition allows them sleep, we may still have to add
xxx_hw and xxx_done for them.
Not sure if any better idea to avoid adding so many new callbacks.

5) The most important issue may be callbacks (round_rate and determine_rate)
which may recursively call its parent.
For this case, we may not be able to convert it to xx_hw type.
e.g. drivers/clk/clk-divider.c
.round_rate
-> clk_divider_round_rate
   -> clk_hw_round_rate(clk_hw_get_parent(hw), rate) if CLK_SET_RATE_PARENT

clk_hw_round_rate is sleepable in theory.
Thus we may not convert .round_rate to .round_rate_hw since we can't
predict whether it's parent round_rate is sleepable or not.

This issue may cause us unable to convert the common clk-divider and clk-mux
which could be a potential issue since clk_set_rate will indirectly call
.round_rate and .determinte_rate which may results in possible sleep.

Any good ideas for this issue?

6) Not sure your guys's option on calling clk_prepare which acqures a mutex
when irq is disalbed.
Ideally we probably may should not call mutex_lock with irq disabled.
But seems we may can't avoid and during booting, it seems still safe to
call it.

Dong Aisheng (6):
  clk: add prepare_hw and prepare_done support
  clk: add set_rate_hw and set_rate_done
  clk: add set_parent_hw and set_parent_done
  clk: use polling way for SYSTEM_SUSPEND state too
  clk: imx: pllv3: convert to prepare_hw and set_rate_hw
  clk: imx: clk-busy: convert to set_rate_hw and set_parent_hw

Thomas Gleixner (1):
  PM: add SYSTEM_SUSPEND system_state

 drivers/clk/clk.c            | 119 +++++++++++++++++++++++++++++++++++++++++--
 drivers/clk/imx/clk-busy.c   |  52 +++++++++----------
 drivers/clk/imx/clk-pllv3.c  |  64 +++++++++++------------
 include/linux/clk-provider.h |  61 ++++++++++++++++++++++
 include/linux/kernel.h       |   1 +
 kernel/power/hibernate.c     |   7 +++
 kernel/power/suspend.c       |   4 ++
 7 files changed, 245 insertions(+), 63 deletions(-)

-- 
1.9.1

^ permalink raw reply	[flat|nested] 13+ messages in thread

* [PATCH RFC 1/7] clk: add prepare_hw and prepare_done support
  2016-06-29 13:52 [PATCH RFC 0/7] support clk setting during kernel early boot Dong Aisheng
@ 2016-06-29 13:52 ` Dong Aisheng
  2016-07-05 19:53   ` Grygorii Strashko
  2016-06-29 13:52 ` [PATCH RFC 2/7] clk: add set_rate_hw and set_rate_done Dong Aisheng
                   ` (6 subsequent siblings)
  7 siblings, 1 reply; 13+ messages in thread
From: Dong Aisheng @ 2016-06-29 13:52 UTC (permalink / raw)
  To: linux-clk
  Cc: linux-kernel, sboyd, mturquette, shawnguo, linux-arm-kernel,
	tglx, aisheng.dong, stefan, l.stach, cyrille.pitchen, manabian,
	anson.huang

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@linutronix.de>
Signed-off-by: Dong Aisheng <aisheng.dong@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

^ permalink raw reply related	[flat|nested] 13+ messages in thread

* [PATCH RFC 2/7] clk: add set_rate_hw and set_rate_done
  2016-06-29 13:52 [PATCH RFC 0/7] support clk setting during kernel early boot Dong Aisheng
  2016-06-29 13:52 ` [PATCH RFC 1/7] clk: add prepare_hw and prepare_done support Dong Aisheng
@ 2016-06-29 13:52 ` Dong Aisheng
  2016-06-29 13:52 ` [PATCH RFC 3/7] clk: add set_parent_hw and set_parent_done Dong Aisheng
                   ` (5 subsequent siblings)
  7 siblings, 0 replies; 13+ messages in thread
From: Dong Aisheng @ 2016-06-29 13:52 UTC (permalink / raw)
  To: linux-clk
  Cc: linux-kernel, sboyd, mturquette, shawnguo, linux-arm-kernel,
	tglx, aisheng.dong, stefan, l.stach, cyrille.pitchen, manabian,
	anson.huang

Introduce set_rate_hw and set_rate_done to support setting rate
in early kernel booting where we still can't schedule.

Change the rate of this clock hw. This callback is intended
to do the hw part setting of @set_rate work. It should
cooperate with @set_rate_done callback to do the whole
set rate work. The clock core will check @set_rate_done in
either sleep or polling way according to system state to
decide whether the whole set rate work is done.

Suggested-by: Thomas Gleixner <tglx@linutronix.de>
Signed-off-by: Dong Aisheng <aisheng.dong@nxp.com>
---
 drivers/clk/clk.c            | 32 ++++++++++++++++++++++++++++++--
 include/linux/clk-provider.h | 15 +++++++++++++++
 2 files changed, 45 insertions(+), 2 deletions(-)

diff --git a/drivers/clk/clk.c b/drivers/clk/clk.c
index 7dcb34c75a9f..0d031b280c9a 100644
--- a/drivers/clk/clk.c
+++ b/drivers/clk/clk.c
@@ -1471,8 +1471,10 @@ static void clk_change_rate(struct clk_core *core)
 	struct hlist_node *tmp;
 	unsigned long old_rate;
 	unsigned long best_parent_rate = 0;
+	unsigned long timeout;
 	bool skip_set_rate = false;
 	struct clk_core *old_parent;
+	int ret;
 
 	old_rate = core->rate;
 
@@ -1509,8 +1511,34 @@ static void clk_change_rate(struct clk_core *core)
 
 	trace_clk_set_rate(core, core->new_rate);
 
-	if (!skip_set_rate && core->ops->set_rate)
-		core->ops->set_rate(core->hw, core->new_rate, best_parent_rate);
+	if (!skip_set_rate) {
+		if (core->ops->set_rate) {
+			core->ops->set_rate(core->hw, core->new_rate,
+					    best_parent_rate);
+		} else if (core->ops->set_rate_hw) {
+			ret = core->ops->set_rate_hw(core->hw, core->new_rate,
+						     best_parent_rate);
+			if (!ret && core->ops->set_rate_done) {
+				timeout = jiffies + msecs_to_jiffies(10);
+				while (!core->ops->set_rate_done(core->hw)) {
+					if (time_after(jiffies, timeout)) {
+						pr_err("%s: clock %s set rate 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_set_rate_complete(core, core->new_rate);
 
diff --git a/include/linux/clk-provider.h b/include/linux/clk-provider.h
index b37174360f1c..3dcb99ad6bd2 100644
--- a/include/linux/clk-provider.h
+++ b/include/linux/clk-provider.h
@@ -162,6 +162,18 @@ struct clk_rate_request {
  *		which is likely helpful for most .set_rate implementation.
  *		Returns 0 on success, -EERROR otherwise.
  *
+ * @set_rate_hw: Change the rate of this clock hw. This callback is intended
+ *		to do the hw part setting of @set_rate work. It should
+ *		cooperate with @set_rate_done callback to do the whole
+ *		set rate work. The clock core will check @set_rate_done in
+ *		either sleep or polling way according to system state to
+ *		decide whether the whole set rate work is done. Optional
+ *		if @set_rate is used. This function must not sleep.
+ *
+ * @set_rate_done: Queries the hardware to determine if the clock hw is
+ *		prepared. Optional, if this op is not set then the set rate
+ *		simply return. This function must not sleep.
+ *
  * @set_rate_and_parent: Change the rate and the parent of this clock. The
  *		requested rate is specified by the second argument, which
  *		should typically be the return of .round_rate call.  The
@@ -234,6 +246,9 @@ struct clk_ops {
 	u8		(*get_parent)(struct clk_hw *hw);
 	int		(*set_rate)(struct clk_hw *hw, unsigned long rate,
 				    unsigned long parent_rate);
+	int		(*set_rate_hw)(struct clk_hw *hw, unsigned long rate,
+				    unsigned long parent_rate);
+	int		(*set_rate_done)(struct clk_hw *hw);
 	int		(*set_rate_and_parent)(struct clk_hw *hw,
 				    unsigned long rate,
 				    unsigned long parent_rate, u8 index);
-- 
1.9.1

^ permalink raw reply related	[flat|nested] 13+ messages in thread

* [PATCH RFC 3/7] clk: add set_parent_hw and set_parent_done
  2016-06-29 13:52 [PATCH RFC 0/7] support clk setting during kernel early boot Dong Aisheng
  2016-06-29 13:52 ` [PATCH RFC 1/7] clk: add prepare_hw and prepare_done support Dong Aisheng
  2016-06-29 13:52 ` [PATCH RFC 2/7] clk: add set_rate_hw and set_rate_done Dong Aisheng
@ 2016-06-29 13:52 ` Dong Aisheng
  2016-06-29 13:52 ` [PATCH RFC 4/7] PM: add SYSTEM_SUSPEND system_state Dong Aisheng
                   ` (4 subsequent siblings)
  7 siblings, 0 replies; 13+ messages in thread
From: Dong Aisheng @ 2016-06-29 13:52 UTC (permalink / raw)
  To: linux-clk
  Cc: linux-kernel, sboyd, mturquette, shawnguo, linux-arm-kernel,
	tglx, aisheng.dong, stefan, l.stach, cyrille.pitchen, manabian,
	anson.huang

Introduce set_parent_hw and set_parent_done to support setting
parent in early kernel booting where we still can't schedule.

Change the input source of this clock hw; This callback
is intended to do the hw part setting of @set_parent work. It
should cooperate with @set_parent_done callback to do the whole
set parent work. The clock core will check @set_parent_done
in either sleep or polling way according to system state to
decide whether the whole set rate work is done.

Suggested-by: Thomas Gleixner <tglx@linutronix.de>
Signed-off-by: Dong Aisheng <aisheng.dong@nxp.com>
---
 drivers/clk/clk.c            | 26 +++++++++++++++++++++++++-
 include/linux/clk-provider.h | 14 ++++++++++++++
 2 files changed, 39 insertions(+), 1 deletion(-)

diff --git a/drivers/clk/clk.c b/drivers/clk/clk.c
index 0d031b280c9a..9369dbb71118 100644
--- a/drivers/clk/clk.c
+++ b/drivers/clk/clk.c
@@ -1236,14 +1236,38 @@ static int __clk_set_parent(struct clk_core *core, struct clk_core *parent,
 	unsigned long flags;
 	int ret = 0;
 	struct clk_core *old_parent;
+	unsigned long timeout;
 
 	old_parent = __clk_set_parent_before(core, parent);
 
 	trace_clk_set_parent(core, parent);
 
 	/* change clock input source */
-	if (parent && core->ops->set_parent)
+	if (parent && core->ops->set_parent) {
 		ret = core->ops->set_parent(core->hw, p_index);
+	} else if (parent && core->ops->set_parent_hw) {
+		ret = core->ops->set_parent_hw(core->hw, p_index);
+		if (!ret && core->ops->set_parent_done) {
+			timeout = jiffies + msecs_to_jiffies(10);
+			while (!core->ops->set_parent_done(core->hw)) {
+				if (time_after(jiffies, timeout)) {
+					pr_err("%s: clock %s set parent 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_set_parent_complete(core, parent);
 
diff --git a/include/linux/clk-provider.h b/include/linux/clk-provider.h
index 3dcb99ad6bd2..16fa75cdd656 100644
--- a/include/linux/clk-provider.h
+++ b/include/linux/clk-provider.h
@@ -146,6 +146,18 @@ struct clk_rate_request {
  *		array index into the value programmed into the hardware.
  *		Returns 0 on success, -EERROR otherwise.
  *
+ * @set_parent_hw: Change the input source of this clock hw;  This callback
+ *		is intended to do the hw part setting of @set_parent work. It
+ *		should cooperate with @set_parent_done callback to do the whole
+ *		set parent work. The clock core will check @set_parent_done
+ *		in either sleep or polling way according to system state to
+ *		decide whether the whole set rate work is done. Optional
+ *		if @set_parent is used. This function must not sleep.
+ *
+ * @set_parent_done: Queries the hardware to determine if the set parent is
+ *		done. Optional, if this op is not set then the set parent
+ *		simply return. This function must not sleep.
+ *
  * @get_parent:	Queries the hardware to determine the parent of a clock.  The
  *		return value is a u8 which specifies the index corresponding to
  *		the parent clock.  This index can be applied to either the
@@ -243,6 +255,8 @@ struct clk_ops {
 	int		(*determine_rate)(struct clk_hw *hw,
 					  struct clk_rate_request *req);
 	int		(*set_parent)(struct clk_hw *hw, u8 index);
+	int		(*set_parent_hw)(struct clk_hw *hw, u8 index);
+	int		(*set_parent_done)(struct clk_hw *hw);
 	u8		(*get_parent)(struct clk_hw *hw);
 	int		(*set_rate)(struct clk_hw *hw, unsigned long rate,
 				    unsigned long parent_rate);
-- 
1.9.1

^ permalink raw reply related	[flat|nested] 13+ messages in thread

* [PATCH RFC 4/7] PM: add SYSTEM_SUSPEND system_state
  2016-06-29 13:52 [PATCH RFC 0/7] support clk setting during kernel early boot Dong Aisheng
                   ` (2 preceding siblings ...)
  2016-06-29 13:52 ` [PATCH RFC 3/7] clk: add set_parent_hw and set_parent_done Dong Aisheng
@ 2016-06-29 13:52 ` Dong Aisheng
  2016-06-29 13:52 ` [PATCH RFC 5/7] clk: use polling way for SYSTEM_SUSPEND state too Dong Aisheng
                   ` (3 subsequent siblings)
  7 siblings, 0 replies; 13+ messages in thread
From: Dong Aisheng @ 2016-06-29 13:52 UTC (permalink / raw)
  To: linux-clk
  Cc: linux-kernel, sboyd, mturquette, shawnguo, linux-arm-kernel,
	tglx, aisheng.dong, stefan, l.stach, cyrille.pitchen, manabian,
	anson.huang

From: Thomas Gleixner <tglx@linutronix.de>

During the late system suspend phase, the arch irq is disabled
and sleeping is not allowed.
Let's add SYSTEM_SUSPEND to represent this state. The reset of
the system can then check this flag to do the correct work.

[ Aisheng: back ported from rt tree and did comments change ]
Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
Signed-off-by: Dong Aisheng <aisheng.dong@nxp.com>
---
 include/linux/kernel.h   | 1 +
 kernel/power/hibernate.c | 7 +++++++
 kernel/power/suspend.c   | 4 ++++
 3 files changed, 12 insertions(+)

diff --git a/include/linux/kernel.h b/include/linux/kernel.h
index 94aa10ffe156..92a42312b749 100644
--- a/include/linux/kernel.h
+++ b/include/linux/kernel.h
@@ -491,6 +491,7 @@ extern enum system_states {
 	SYSTEM_HALT,
 	SYSTEM_POWER_OFF,
 	SYSTEM_RESTART,
+	SYSTEM_SUSPEND,
 } system_state;
 
 #define TAINT_PROPRIETARY_MODULE	0
diff --git a/kernel/power/hibernate.c b/kernel/power/hibernate.c
index fca9254280ee..36f420e54848 100644
--- a/kernel/power/hibernate.c
+++ b/kernel/power/hibernate.c
@@ -285,6 +285,8 @@ static int create_image(int platform_mode)
 
 	local_irq_disable();
 
+	system_state = SYSTEM_SUSPEND;
+
 	error = syscore_suspend();
 	if (error) {
 		printk(KERN_ERR "PM: Some system devices failed to power down, "
@@ -314,6 +316,7 @@ static int create_image(int platform_mode)
 	syscore_resume();
 
  Enable_irqs:
+	system_state = SYSTEM_RUNNING;
 	local_irq_enable();
 
  Enable_cpus:
@@ -438,6 +441,7 @@ static int resume_target_kernel(bool platform_mode)
 		goto Enable_cpus;
 
 	local_irq_disable();
+	system_state = SYSTEM_SUSPEND;
 
 	error = syscore_suspend();
 	if (error)
@@ -471,6 +475,7 @@ static int resume_target_kernel(bool platform_mode)
 	syscore_resume();
 
  Enable_irqs:
+	system_state = SYSTEM_RUNNING;
 	local_irq_enable();
 
  Enable_cpus:
@@ -556,6 +561,7 @@ int hibernation_platform_enter(void)
 		goto Enable_cpus;
 
 	local_irq_disable();
+	system_state = SYSTEM_SUSPEND;
 	syscore_suspend();
 	if (pm_wakeup_pending()) {
 		error = -EAGAIN;
@@ -568,6 +574,7 @@ int hibernation_platform_enter(void)
 
  Power_up:
 	syscore_resume();
+	system_state = SYSTEM_RUNNING;
 	local_irq_enable();
 
  Enable_cpus:
diff --git a/kernel/power/suspend.c b/kernel/power/suspend.c
index 5b70d64b871e..1258d093ae48 100644
--- a/kernel/power/suspend.c
+++ b/kernel/power/suspend.c
@@ -359,6 +359,8 @@ static int suspend_enter(suspend_state_t state, bool *wakeup)
 	arch_suspend_disable_irqs();
 	BUG_ON(!irqs_disabled());
 
+	system_state = SYSTEM_SUSPEND;
+
 	error = syscore_suspend();
 	if (!error) {
 		*wakeup = pm_wakeup_pending();
@@ -375,6 +377,8 @@ static int suspend_enter(suspend_state_t state, bool *wakeup)
 		syscore_resume();
 	}
 
+	system_state = SYSTEM_RUNNING;
+
 	arch_suspend_enable_irqs();
 	BUG_ON(irqs_disabled());
 
-- 
1.9.1

^ permalink raw reply related	[flat|nested] 13+ messages in thread

* [PATCH RFC 5/7] clk: use polling way for SYSTEM_SUSPEND state too
  2016-06-29 13:52 [PATCH RFC 0/7] support clk setting during kernel early boot Dong Aisheng
                   ` (3 preceding siblings ...)
  2016-06-29 13:52 ` [PATCH RFC 4/7] PM: add SYSTEM_SUSPEND system_state Dong Aisheng
@ 2016-06-29 13:52 ` Dong Aisheng
  2016-06-29 13:52 ` [PATCH RFC 6/7] clk: imx: pllv3: convert to prepare_hw and set_rate_hw Dong Aisheng
                   ` (2 subsequent siblings)
  7 siblings, 0 replies; 13+ messages in thread
From: Dong Aisheng @ 2016-06-29 13:52 UTC (permalink / raw)
  To: linux-clk
  Cc: linux-kernel, sboyd, mturquette, shawnguo, linux-arm-kernel,
	tglx, aisheng.dong, stefan, l.stach, cyrille.pitchen, manabian,
	anson.huang

During SYSTEM_SUSPEND state, the arch irq is disabled and there's
no way to sleep. Using polling for it too.

Signed-off-by: Dong Aisheng <aisheng.dong@nxp.com>
---
 drivers/clk/clk.c | 12 ++++++++----
 1 file changed, 8 insertions(+), 4 deletions(-)

diff --git a/drivers/clk/clk.c b/drivers/clk/clk.c
index 9369dbb71118..5091ec9bd665 100644
--- a/drivers/clk/clk.c
+++ b/drivers/clk/clk.c
@@ -601,7 +601,8 @@ static void clk_core_unprepare(struct clk_core *core)
 						__func__, core->name);
 					break;
 				}
-				if (system_state == SYSTEM_BOOTING)
+				if (system_state == SYSTEM_BOOTING ||
+				    system_state == SYSTEM_SUSPEND)
 					/*
 					 * Busy loop as we can't schedule in
 					 * early boot
@@ -670,7 +671,8 @@ static int clk_core_prepare(struct clk_core *core)
 						ret = -ETIMEDOUT;
 						break;
 					}
-					if (system_state == SYSTEM_BOOTING)
+					if (system_state == SYSTEM_BOOTING ||
+					    system_state == SYSTEM_SUSPEND)
 						/*
 						 * Busy loop as we can't
 						 * schedule in early boot
@@ -1256,7 +1258,8 @@ static int __clk_set_parent(struct clk_core *core, struct clk_core *parent,
 					ret = -ETIMEDOUT;
 					break;
 				}
-				if (system_state == SYSTEM_BOOTING)
+				if (system_state == SYSTEM_BOOTING ||
+				    system_state == SYSTEM_SUSPEND)
 					/*
 					 * Busy loop as we can't
 					 * schedule in early boot
@@ -1550,7 +1553,8 @@ static void clk_change_rate(struct clk_core *core)
 							__func__, core->name);
 						break;
 					}
-					if (system_state == SYSTEM_BOOTING)
+					if (system_state == SYSTEM_BOOTING ||
+					    system_state == SYSTEM_SUSPEND)
 						/*
 						 * Busy loop as we can't
 						 * schedule in early boot
-- 
1.9.1

^ permalink raw reply related	[flat|nested] 13+ messages in thread

* [PATCH RFC 6/7] clk: imx: pllv3: convert to prepare_hw and set_rate_hw
  2016-06-29 13:52 [PATCH RFC 0/7] support clk setting during kernel early boot Dong Aisheng
                   ` (4 preceding siblings ...)
  2016-06-29 13:52 ` [PATCH RFC 5/7] clk: use polling way for SYSTEM_SUSPEND state too Dong Aisheng
@ 2016-06-29 13:52 ` Dong Aisheng
  2016-07-02 20:30   ` Fabio Estevam
  2016-06-29 13:52 ` [PATCH RFC 7/7] clk: imx: clk-busy: convert to set_rate_hw and set_parent_hw Dong Aisheng
  2016-07-02  1:12 ` [PATCH RFC 0/7] support clk setting during kernel early boot Stephen Boyd
  7 siblings, 1 reply; 13+ messages in thread
From: Dong Aisheng @ 2016-06-29 13:52 UTC (permalink / raw)
  To: linux-clk
  Cc: linux-kernel, sboyd, mturquette, shawnguo, linux-arm-kernel,
	tglx, aisheng.dong, stefan, l.stach, cyrille.pitchen, manabian,
	anson.huang

Convert to prepare_hw and set_rate_hw and let the clk core
to handle the polling process automatically.

Signed-off-by: Dong Aisheng <aisheng.dong@nxp.com>
---
 drivers/clk/imx/clk-pllv3.c | 64 ++++++++++++++++++++++-----------------------
 1 file changed, 32 insertions(+), 32 deletions(-)

diff --git a/drivers/clk/imx/clk-pllv3.c b/drivers/clk/imx/clk-pllv3.c
index 4826b3c9e19e..78184310ad35 100644
--- a/drivers/clk/imx/clk-pllv3.c
+++ b/drivers/clk/imx/clk-pllv3.c
@@ -49,27 +49,6 @@ struct clk_pllv3 {
 
 #define to_clk_pllv3(_hw) container_of(_hw, struct clk_pllv3, hw)
 
-static int clk_pllv3_wait_lock(struct clk_pllv3 *pll)
-{
-	unsigned long timeout = jiffies + msecs_to_jiffies(10);
-	u32 val = readl_relaxed(pll->base) & pll->powerdown;
-
-	/* No need to wait for lock when pll is not powered up */
-	if ((pll->powerup_set && !val) || (!pll->powerup_set && val))
-		return 0;
-
-	/* Wait for PLL to lock */
-	do {
-		if (readl_relaxed(pll->base) & BM_PLL_LOCK)
-			break;
-		if (time_after(jiffies, timeout))
-			break;
-		usleep_range(50, 500);
-	} while (1);
-
-	return readl_relaxed(pll->base) & BM_PLL_LOCK ? 0 : -ETIMEDOUT;
-}
-
 static int clk_pllv3_prepare(struct clk_hw *hw)
 {
 	struct clk_pllv3 *pll = to_clk_pllv3(hw);
@@ -82,7 +61,7 @@ static int clk_pllv3_prepare(struct clk_hw *hw)
 		val &= ~BM_PLL_POWER;
 	writel_relaxed(val, pll->base);
 
-	return clk_pllv3_wait_lock(pll);
+	return 0;
 }
 
 static void clk_pllv3_unprepare(struct clk_hw *hw)
@@ -126,6 +105,18 @@ static long clk_pllv3_round_rate(struct clk_hw *hw, unsigned long rate,
 					    parent_rate * 20;
 }
 
+static int clk_pllv3_set_rate_done(struct clk_hw *hw)
+{
+	struct clk_pllv3 *pll = to_clk_pllv3(hw);
+	u32 val = readl_relaxed(pll->base) & pll->powerdown;
+
+	/* always return true if pll is not powered up */
+	if ((pll->powerup_set && !val) || (!pll->powerup_set && val))
+		return 1;
+
+	return readl_relaxed(pll->base) & BM_PLL_LOCK;
+}
+
 static int clk_pllv3_set_rate(struct clk_hw *hw, unsigned long rate,
 		unsigned long parent_rate)
 {
@@ -144,16 +135,18 @@ static int clk_pllv3_set_rate(struct clk_hw *hw, unsigned long rate,
 	val |= (div << pll->div_shift);
 	writel_relaxed(val, pll->base);
 
-	return clk_pllv3_wait_lock(pll);
+	return 0;
 }
 
 static const struct clk_ops clk_pllv3_ops = {
-	.prepare	= clk_pllv3_prepare,
+	.prepare_hw	= clk_pllv3_prepare,
+	.prepare_done	= clk_pllv3_is_prepared,
 	.unprepare	= clk_pllv3_unprepare,
 	.is_prepared	= clk_pllv3_is_prepared,
 	.recalc_rate	= clk_pllv3_recalc_rate,
 	.round_rate	= clk_pllv3_round_rate,
-	.set_rate	= clk_pllv3_set_rate,
+	.set_rate_hw	= clk_pllv3_set_rate,
+	.set_rate_done	= clk_pllv3_set_rate_done,
 };
 
 static unsigned long clk_pllv3_sys_recalc_rate(struct clk_hw *hw,
@@ -199,16 +192,18 @@ static int clk_pllv3_sys_set_rate(struct clk_hw *hw, unsigned long rate,
 	val |= div;
 	writel_relaxed(val, pll->base);
 
-	return clk_pllv3_wait_lock(pll);
+	return 0;
 }
 
 static const struct clk_ops clk_pllv3_sys_ops = {
-	.prepare	= clk_pllv3_prepare,
+	.prepare_hw	= clk_pllv3_prepare,
+	.prepare_done	= clk_pllv3_is_prepared,
 	.unprepare	= clk_pllv3_unprepare,
 	.is_prepared	= clk_pllv3_is_prepared,
 	.recalc_rate	= clk_pllv3_sys_recalc_rate,
 	.round_rate	= clk_pllv3_sys_round_rate,
-	.set_rate	= clk_pllv3_sys_set_rate,
+	.set_rate_hw	= clk_pllv3_sys_set_rate,
+	.set_rate_done	= clk_pllv3_set_rate_done,
 };
 
 static unsigned long clk_pllv3_av_recalc_rate(struct clk_hw *hw,
@@ -272,16 +267,18 @@ static int clk_pllv3_av_set_rate(struct clk_hw *hw, unsigned long rate,
 	writel_relaxed(mfn, pll->base + PLL_NUM_OFFSET);
 	writel_relaxed(mfd, pll->base + PLL_DENOM_OFFSET);
 
-	return clk_pllv3_wait_lock(pll);
+	return 0;
 }
 
 static const struct clk_ops clk_pllv3_av_ops = {
-	.prepare	= clk_pllv3_prepare,
+	.prepare_hw	= clk_pllv3_prepare,
+	.prepare_done	= clk_pllv3_is_prepared,
 	.unprepare	= clk_pllv3_unprepare,
 	.is_prepared	= clk_pllv3_is_prepared,
 	.recalc_rate	= clk_pllv3_av_recalc_rate,
 	.round_rate	= clk_pllv3_av_round_rate,
-	.set_rate	= clk_pllv3_av_set_rate,
+	.set_rate_hw	= clk_pllv3_av_set_rate,
+	.set_rate_done	= clk_pllv3_set_rate_done,
 };
 
 static unsigned long clk_pllv3_enet_recalc_rate(struct clk_hw *hw,
@@ -293,7 +290,8 @@ static unsigned long clk_pllv3_enet_recalc_rate(struct clk_hw *hw,
 }
 
 static const struct clk_ops clk_pllv3_enet_ops = {
-	.prepare	= clk_pllv3_prepare,
+	.prepare_hw	= clk_pllv3_prepare,
+	.prepare_done	= clk_pllv3_is_prepared,
 	.unprepare	= clk_pllv3_unprepare,
 	.is_prepared	= clk_pllv3_is_prepared,
 	.recalc_rate	= clk_pllv3_enet_recalc_rate,
@@ -347,6 +345,8 @@ struct clk *imx_clk_pllv3(enum imx_pllv3_type type, const char *name,
 	init.flags = 0;
 	init.parent_names = &parent_name;
 	init.num_parents = 1;
+	init.delay_min = 50;
+	init.delay_max = 500;
 
 	pll->hw.init = &init;
 
-- 
1.9.1

^ permalink raw reply related	[flat|nested] 13+ messages in thread

* [PATCH RFC 7/7] clk: imx: clk-busy: convert to set_rate_hw and set_parent_hw
  2016-06-29 13:52 [PATCH RFC 0/7] support clk setting during kernel early boot Dong Aisheng
                   ` (5 preceding siblings ...)
  2016-06-29 13:52 ` [PATCH RFC 6/7] clk: imx: pllv3: convert to prepare_hw and set_rate_hw Dong Aisheng
@ 2016-06-29 13:52 ` Dong Aisheng
  2016-07-02  1:12 ` [PATCH RFC 0/7] support clk setting during kernel early boot Stephen Boyd
  7 siblings, 0 replies; 13+ messages in thread
From: Dong Aisheng @ 2016-06-29 13:52 UTC (permalink / raw)
  To: linux-clk
  Cc: linux-kernel, sboyd, mturquette, shawnguo, linux-arm-kernel,
	tglx, aisheng.dong, stefan, l.stach, cyrille.pitchen, manabian,
	anson.huang

Convert to prepare_hw and set_rate_hw and let the clk core
to handle the polling process automatically.

Signed-off-by: Dong Aisheng <aisheng.dong@nxp.com>
---
 drivers/clk/imx/clk-busy.c | 52 +++++++++++++++++++++++-----------------------
 1 file changed, 26 insertions(+), 26 deletions(-)

diff --git a/drivers/clk/imx/clk-busy.c b/drivers/clk/imx/clk-busy.c
index 5cc99590f9a3..5af622fc726b 100644
--- a/drivers/clk/imx/clk-busy.c
+++ b/drivers/clk/imx/clk-busy.c
@@ -18,17 +18,6 @@
 #include <linux/err.h>
 #include "clk.h"
 
-static int clk_busy_wait(void __iomem *reg, u8 shift)
-{
-	unsigned long timeout = jiffies + msecs_to_jiffies(10);
-
-	while (readl_relaxed(reg) & (1 << shift))
-		if (time_after(jiffies, timeout))
-			return -ETIMEDOUT;
-
-	return 0;
-}
-
 struct clk_busy_divider {
 	struct clk_divider div;
 	const struct clk_ops *div_ops;
@@ -36,6 +25,11 @@ struct clk_busy_divider {
 	u8 shift;
 };
 
+static int clk_is_busy(void __iomem *reg, u8 shift)
+{
+	return readl_relaxed(reg) & (1 << shift);
+}
+
 static inline struct clk_busy_divider *to_clk_busy_divider(struct clk_hw *hw)
 {
 	struct clk_divider *div = to_clk_divider(hw);
@@ -59,23 +53,26 @@ static long clk_busy_divider_round_rate(struct clk_hw *hw, unsigned long rate,
 	return busy->div_ops->round_rate(&busy->div.hw, rate, prate);
 }
 
-static int clk_busy_divider_set_rate(struct clk_hw *hw, unsigned long rate,
-		unsigned long parent_rate)
+static int clk_busy_divider_set_rate_hw_done(struct clk_hw *hw)
 {
 	struct clk_busy_divider *busy = to_clk_busy_divider(hw);
-	int ret;
 
-	ret = busy->div_ops->set_rate(&busy->div.hw, rate, parent_rate);
-	if (!ret)
-		ret = clk_busy_wait(busy->reg, busy->shift);
+	return !clk_is_busy(busy->reg, busy->shift);
+}
+
+static int clk_busy_divider_set_rate_hw(struct clk_hw *hw, unsigned long rate,
+		unsigned long parent_rate)
+{
+	struct clk_busy_divider *busy = to_clk_busy_divider(hw);
 
-	return ret;
+	return busy->div_ops->set_rate(&busy->div.hw, rate, parent_rate);
 }
 
 static struct clk_ops clk_busy_divider_ops = {
 	.recalc_rate = clk_busy_divider_recalc_rate,
 	.round_rate = clk_busy_divider_round_rate,
-	.set_rate = clk_busy_divider_set_rate,
+	.set_rate_hw = clk_busy_divider_set_rate_hw,
+	.set_rate_done = clk_busy_divider_set_rate_hw_done,
 };
 
 struct clk *imx_clk_busy_divider(const char *name, const char *parent_name,
@@ -135,21 +132,24 @@ static u8 clk_busy_mux_get_parent(struct clk_hw *hw)
 	return busy->mux_ops->get_parent(&busy->mux.hw);
 }
 
-static int clk_busy_mux_set_parent(struct clk_hw *hw, u8 index)
+static int clk_busy_mux_set_parent_done(struct clk_hw *hw)
 {
 	struct clk_busy_mux *busy = to_clk_busy_mux(hw);
-	int ret;
 
-	ret = busy->mux_ops->set_parent(&busy->mux.hw, index);
-	if (!ret)
-		ret = clk_busy_wait(busy->reg, busy->shift);
+	return !clk_is_busy(busy->reg, busy->shift);
+}
+
+static int clk_busy_mux_set_parent_hw(struct clk_hw *hw, u8 index)
+{
+	struct clk_busy_mux *busy = to_clk_busy_mux(hw);
 
-	return ret;
+	return busy->mux_ops->set_parent(&busy->mux.hw, index);
 }
 
 static struct clk_ops clk_busy_mux_ops = {
 	.get_parent = clk_busy_mux_get_parent,
-	.set_parent = clk_busy_mux_set_parent,
+	.set_parent_hw = clk_busy_mux_set_parent_hw,
+	.set_parent_done = clk_busy_mux_set_parent_done,
 };
 
 struct clk *imx_clk_busy_mux(const char *name, void __iomem *reg, u8 shift,
-- 
1.9.1

^ permalink raw reply related	[flat|nested] 13+ messages in thread

* Re: [PATCH RFC 0/7] support clk setting during kernel early boot
  2016-06-29 13:52 [PATCH RFC 0/7] support clk setting during kernel early boot Dong Aisheng
                   ` (6 preceding siblings ...)
  2016-06-29 13:52 ` [PATCH RFC 7/7] clk: imx: clk-busy: convert to set_rate_hw and set_parent_hw Dong Aisheng
@ 2016-07-02  1:12 ` Stephen Boyd
  2016-07-02 23:12   ` Stefan Agner
  7 siblings, 1 reply; 13+ messages in thread
From: Stephen Boyd @ 2016-07-02  1:12 UTC (permalink / raw)
  To: Dong Aisheng
  Cc: linux-clk, linux-kernel, mturquette, shawnguo, linux-arm-kernel,
	tglx, stefan, l.stach, cyrille.pitchen, manabian, anson.huang

On 06/29, Dong Aisheng wrote:
> Recently several people met the kernel complaining
> "bad: scheduling from the idle thread!" issue which caused by
> sleeping during kernel early booting phase by calling clk
> APIs like clk_prepare_enable.
> 
> See:
> https://lkml.org/lkml/fancy/2016/1/29/695
> https://lkml.org/lkml/2016/6/10/779
> http://www.spinics.net/lists/linux-clk/msg08635.html

That last one was another bug that happened to trigger this
problem mistakenly. I doubt critical clks are an issue (more
below).

> 
> The calling sequence simply could be like:
> start_kernel
>   ->time_init
>     ->of_clk_init
>       ->clk_core_prepare
>         ->clk_pllv3_prepare
>           ->usleep_range
>             ->dequeue_task_idle
> 
> This issue is mainly caused during time_init, the irq is still
> not enabled and scheduler is still not ready, thus there's no way
> to allow sleep at that time.
> 
> However, there're many exist platforms calling clk_prepare_enable/
> clk_get_rate/clk_set_parent at that time in CLK_OF_DECLARE init
> function.
> e.g
> drivers/clk/imx/clk-{soc}.c
> drivers/clk/rockchip/clk-rk3188.c
> drivers/clk/ti/clk-44xx.c
> ...
> 
> And irqchip and clock source is also initialized before it which
> may requires to do clk settings.
> 
> Furthermore, current clk framework also supports critical clocks
> flagged by CLK_IS_CRITICAL which will be prepared and
> enabled during clk_register by clk core, that is also happened
> quite early in of_clk_init usually.
> 
> And clk framework also supports assign default clk rate and parent for
> each registered clk provider which also happens early in of_clk_init.
> (see of_clk_set_defaults())
> 
> Above are all possible cases which may cause sleeping during kernel
> early booting.

How many of these cases are really happening and causing problems
though?

> 
> So it seems we'd like to have the requirement to make kernel support
> calling clk APIs during kernel early boot without sleep.

I wonder if the problem is more that the framework doesn't know
the hardware state of on/off when it initializes? So we call the
clk_ops prepare/enable functions when we really shouldn't be
doing that at all because the clk is already prepared/enabled.
Presumably for critical clks, we shouldn't go and touch any
hardware to turn them on, because by definition they're critical
and should already be on anyway.

> 
> Since many of them is actually mostly implemented unsleepable already,
> e.g. recalc_rate, recalc_accuracy, get/set_phase
> but due to the definition allows them sleep, we may still have to add
> xxx_hw and xxx_done for them.
> Not sure if any better idea to avoid adding so many new callbacks.
> 
> 5) The most important issue may be callbacks (round_rate and determine_rate)
> which may recursively call its parent.
> For this case, we may not be able to convert it to xx_hw type.
> e.g. drivers/clk/clk-divider.c
> .round_rate
> -> clk_divider_round_rate
>    -> clk_hw_round_rate(clk_hw_get_parent(hw), rate) if CLK_SET_RATE_PARENT
> 
> clk_hw_round_rate is sleepable in theory.
> Thus we may not convert .round_rate to .round_rate_hw since we can't
> predict whether it's parent round_rate is sleepable or not.
> 
> This issue may cause us unable to convert the common clk-divider and clk-mux
> which could be a potential issue since clk_set_rate will indirectly call
> .round_rate and .determinte_rate which may results in possible sleep.
> 
> Any good ideas for this issue?
> 
> 6) Not sure your guys's option on calling clk_prepare which acqures a mutex
> when irq is disalbed.
> Ideally we probably may should not call mutex_lock with irq disabled.
> But seems we may can't avoid and during booting, it seems still safe to
> call it.
> 

It seems fine to call mutex lock during of_clk_init(). At least
from what I can tell we're saved here because there's only one
task during of_clk_init() time so we're not able to be blocked by
any other task and fall into the mutex slowpath.

The problem seems to be entering the scheduler directly from
usleep_range() called from clk ops because that effectively
becomes a scheduler call. Given that of_clk_init() is mostly for
clk drivers to register clks for their timers and interrupt
controllers, we should be able to avoid making any clk_ops calls
besides recalculating rates and checking if clks are enabled or
not. All other clks should be registered and configured from
device drivers later on when the scheduler is running.

-- 
Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum,
a Linux Foundation Collaborative Project

^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: [PATCH RFC 6/7] clk: imx: pllv3: convert to prepare_hw and set_rate_hw
  2016-06-29 13:52 ` [PATCH RFC 6/7] clk: imx: pllv3: convert to prepare_hw and set_rate_hw Dong Aisheng
@ 2016-07-02 20:30   ` Fabio Estevam
  0 siblings, 0 replies; 13+ messages in thread
From: Fabio Estevam @ 2016-07-02 20:30 UTC (permalink / raw)
  To: Dong Aisheng
  Cc: linux-clk, linux-kernel, Stephen Boyd, Michael Turquette,
	Shawn Guo, linux-arm-kernel, Thomas Gleixner, Stefan Agner,
	Lucas Stach, Cyrille Pitchen, manabian, Yongcai Huang

Hi Dong,

On Wed, Jun 29, 2016 at 10:52 AM, Dong Aisheng <aisheng.dong@nxp.com> wrote:

Thanks for working on this series.

Tested the whole series on a mx7d-sdb and it does fix the several clk
warnings that we currently have.

> +static int clk_pllv3_set_rate_done(struct clk_hw *hw)
> +{
> +       struct clk_pllv3 *pll = to_clk_pllv3(hw);
> +       u32 val = readl_relaxed(pll->base) & pll->powerdown;

I had to change this to:

--- a/drivers/clk/imx/clk-pllv3.c
+++ b/drivers/clk/imx/clk-pllv3.c
@@ -108,7 +108,7 @@ static long clk_pllv3_round_rate(struct clk_hw *hw, unsigned
 static int clk_pllv3_set_rate_done(struct clk_hw *hw)
 {
        struct clk_pllv3 *pll = to_clk_pllv3(hw);
-       u32 val = readl_relaxed(pll->base) & pll->powerdown;
+       u32 val = readl_relaxed(pll->base) & pll->power_bit;

,so that it can build against linux-next.

^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: [PATCH RFC 0/7] support clk setting during kernel early boot
  2016-07-02  1:12 ` [PATCH RFC 0/7] support clk setting during kernel early boot Stephen Boyd
@ 2016-07-02 23:12   ` Stefan Agner
  2016-07-03 12:05     ` Fabio Estevam
  0 siblings, 1 reply; 13+ messages in thread
From: Stefan Agner @ 2016-07-02 23:12 UTC (permalink / raw)
  To: festevam, Dong Aisheng
  Cc: Stephen Boyd, linux-clk, linux-kernel, mturquette, shawnguo,
	linux-arm-kernel, tglx, l.stach, cyrille.pitchen, manabian,
	anson.huang

On 2016-07-01 18:12, Stephen Boyd wrote:
> On 06/29, Dong Aisheng wrote:
>> Recently several people met the kernel complaining
>> "bad: scheduling from the idle thread!" issue which caused by
>> sleeping during kernel early booting phase by calling clk
>> APIs like clk_prepare_enable.
>>
>> See:
>> https://lkml.org/lkml/fancy/2016/1/29/695
>> https://lkml.org/lkml/2016/6/10/779
>> http://www.spinics.net/lists/linux-clk/msg08635.html
> 
> That last one was another bug that happened to trigger this
> problem mistakenly. I doubt critical clks are an issue (more
> below).
> 
>>
>> The calling sequence simply could be like:
>> start_kernel
>>   ->time_init
>>     ->of_clk_init
>>       ->clk_core_prepare
>>         ->clk_pllv3_prepare
>>           ->usleep_range
>>             ->dequeue_task_idle
>>
>> This issue is mainly caused during time_init, the irq is still
>> not enabled and scheduler is still not ready, thus there's no way
>> to allow sleep at that time.
>>
>> However, there're many exist platforms calling clk_prepare_enable/
>> clk_get_rate/clk_set_parent at that time in CLK_OF_DECLARE init
>> function.
>> e.g
>> drivers/clk/imx/clk-{soc}.c
>> drivers/clk/rockchip/clk-rk3188.c
>> drivers/clk/ti/clk-44xx.c
>> ...
>>
>> And irqchip and clock source is also initialized before it which
>> may requires to do clk settings.
>>
>> Furthermore, current clk framework also supports critical clocks
>> flagged by CLK_IS_CRITICAL which will be prepared and
>> enabled during clk_register by clk core, that is also happened
>> quite early in of_clk_init usually.
>>
>> And clk framework also supports assign default clk rate and parent for
>> each registered clk provider which also happens early in of_clk_init.
>> (see of_clk_set_defaults())
>>
>> Above are all possible cases which may cause sleeping during kernel
>> early booting.
> 
> How many of these cases are really happening and causing problems
> though?
> 
>>
>> So it seems we'd like to have the requirement to make kernel support
>> calling clk APIs during kernel early boot without sleep.
> 
> I wonder if the problem is more that the framework doesn't know
> the hardware state of on/off when it initializes? So we call the
> clk_ops prepare/enable functions when we really shouldn't be
> doing that at all because the clk is already prepared/enabled.
> Presumably for critical clks, we shouldn't go and touch any
> hardware to turn them on, because by definition they're critical
> and should already be on anyway.

I found that remark interesting, and agree here with Stephen, critical
clks should be already on and everything else should be controlled from
drivers.

With that in mind I went on and looked again what is currently (after
the parents enable patchset) still wrong on i.MX 7. Turned out to be not
that much, and I think it should be fixable with that:
https://lkml.org/lkml/2016/7/2/138

--
Stefan

^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: [PATCH RFC 0/7] support clk setting during kernel early boot
  2016-07-02 23:12   ` Stefan Agner
@ 2016-07-03 12:05     ` Fabio Estevam
  0 siblings, 0 replies; 13+ messages in thread
From: Fabio Estevam @ 2016-07-03 12:05 UTC (permalink / raw)
  To: Stefan Agner
  Cc: Dong Aisheng, Stephen Boyd, linux-clk, linux-kernel,
	Michael Turquette, Shawn Guo, linux-arm-kernel, Thomas Gleixner,
	Lucas Stach, Cyrille Pitchen, manabian, Yongcai Huang

Hi Stefan,

On Sat, Jul 2, 2016 at 8:12 PM, Stefan Agner <stefan@agner.ch> wrote:

> I found that remark interesting, and agree here with Stephen, critical
> clks should be already on and everything else should be controlled from
> drivers.
>
> With that in mind I went on and looked again what is currently (after
> the parents enable patchset) still wrong on i.MX 7. Turned out to be not
> that much, and I think it should be fixable with that:
> https://lkml.org/lkml/2016/7/2/138

If I only applied your patch against linux-next I still get:
http://pastebin.com/zx7kAzfd

^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: [PATCH RFC 1/7] clk: add prepare_hw and prepare_done support
  2016-06-29 13:52 ` [PATCH RFC 1/7] clk: add prepare_hw and prepare_done support Dong Aisheng
@ 2016-07-05 19:53   ` Grygorii Strashko
  0 siblings, 0 replies; 13+ messages in thread
From: Grygorii Strashko @ 2016-07-05 19:53 UTC (permalink / raw)
  To: Dong Aisheng, linux-clk
  Cc: anson.huang, manabian, mturquette, sboyd, linux-kernel, stefan,
	tglx, shawnguo, cyrille.pitchen, linux-arm-kernel, l.stach

On 06/29/2016 04:52 PM, Dong Aisheng wrote:
> 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@linutronix.de>
> Signed-off-by: Dong Aisheng <aisheng.dong@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)

It looks like there could be a small problem :( The system_state will be changed from
SYSTEM_BOOTING --> SYSTEM_RUNNING too late during boot, even after all initcalls are completed and
drivers probed. As result, all clk APIs will be switched to polling mode not only at early boot, but also
during late boot and this might introduce some boot delays, because most of clk manipulations are 
done at boot time.

> +						/*
> +						 * 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.

[..]

PS. I've found this while tried to enable ___might_sleep() functionality early during boot
for debugging purposes (boot is good stress test). And tried to add smth. like SYSTEM_BOOTING_LATE
and set it right after scheduler is fully operational during the boot [1].
Not sure I've selected right place where "scheduler is fully operational", but it was ok for debugging :P

[1]
https://git.ti.com/~gragst/ti-linux-kernel/gragsts-ti-linux-kernel/commit/5777eba0ad40c687b666a7d0df7ae4567b8aced7
-- 
regards,
-grygorii

^ permalink raw reply	[flat|nested] 13+ messages in thread

end of thread, other threads:[~2016-07-05 19:54 UTC | newest]

Thread overview: 13+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2016-06-29 13:52 [PATCH RFC 0/7] support clk setting during kernel early boot Dong Aisheng
2016-06-29 13:52 ` [PATCH RFC 1/7] clk: add prepare_hw and prepare_done support Dong Aisheng
2016-07-05 19:53   ` Grygorii Strashko
2016-06-29 13:52 ` [PATCH RFC 2/7] clk: add set_rate_hw and set_rate_done Dong Aisheng
2016-06-29 13:52 ` [PATCH RFC 3/7] clk: add set_parent_hw and set_parent_done Dong Aisheng
2016-06-29 13:52 ` [PATCH RFC 4/7] PM: add SYSTEM_SUSPEND system_state Dong Aisheng
2016-06-29 13:52 ` [PATCH RFC 5/7] clk: use polling way for SYSTEM_SUSPEND state too Dong Aisheng
2016-06-29 13:52 ` [PATCH RFC 6/7] clk: imx: pllv3: convert to prepare_hw and set_rate_hw Dong Aisheng
2016-07-02 20:30   ` Fabio Estevam
2016-06-29 13:52 ` [PATCH RFC 7/7] clk: imx: clk-busy: convert to set_rate_hw and set_parent_hw Dong Aisheng
2016-07-02  1:12 ` [PATCH RFC 0/7] support clk setting during kernel early boot Stephen Boyd
2016-07-02 23:12   ` Stefan Agner
2016-07-03 12:05     ` Fabio Estevam

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).