diff mbox series

clk: at91: avoid sleeping early

Message ID 20190920153906.20887-1-alexandre.belloni@bootlin.com (mailing list archive)
State Mainlined
Commit 658fd65cf0b0d511de1718e48d9a28844c385ae0
Headers show
Series clk: at91: avoid sleeping early | expand

Commit Message

Alexandre Belloni Sept. 20, 2019, 3:39 p.m. UTC
It is not allowed to sleep to early in the boot process and this may lead
to kernel issues if the bootloader didn't prepare the slow clock and main
clock.

This results in the following error and dump stack on the AriettaG25:
   bad: scheduling from the idle thread!

Ensure it is possible to sleep, else simply have a delay.

Reported-by: Uwe Kleine-König <u.kleine-koenig@pengutronix.de>
Signed-off-by: Alexandre Belloni <alexandre.belloni@bootlin.com>
---

Note that this was already discussed a while ago and Arnd said this approach was
reasonable:
  https://lore.kernel.org/lkml/6120818.MyeJZ74hYa@wuerfel/

 drivers/clk/at91/clk-main.c |  5 ++++-
 drivers/clk/at91/sckc.c     | 20 ++++++++++++++++----
 2 files changed, 20 insertions(+), 5 deletions(-)

Comments

Stephen Boyd Sept. 23, 2019, 4:58 p.m. UTC | #1
Quoting Alexandre Belloni (2019-09-20 08:39:06)
> It is not allowed to sleep to early in the boot process and this may lead
> to kernel issues if the bootloader didn't prepare the slow clock and main
> clock.
> 
> This results in the following error and dump stack on the AriettaG25:
>    bad: scheduling from the idle thread!
> 
> Ensure it is possible to sleep, else simply have a delay.
> 
> Reported-by: Uwe Kleine-König <u.kleine-koenig@pengutronix.de>
> Signed-off-by: Alexandre Belloni <alexandre.belloni@bootlin.com>
> ---
> 
> Note that this was already discussed a while ago and Arnd said this approach was
> reasonable:
>   https://lore.kernel.org/lkml/6120818.MyeJZ74hYa@wuerfel/

Does this need a Fixes: tag?
Alexandre Belloni Sept. 23, 2019, 9:34 p.m. UTC | #2
On 23/09/2019 09:58:47-0700, Stephen Boyd wrote:
> Quoting Alexandre Belloni (2019-09-20 08:39:06)
> > It is not allowed to sleep to early in the boot process and this may lead
> > to kernel issues if the bootloader didn't prepare the slow clock and main
> > clock.
> > 
> > This results in the following error and dump stack on the AriettaG25:
> >    bad: scheduling from the idle thread!
> > 
> > Ensure it is possible to sleep, else simply have a delay.
> > 
> > Reported-by: Uwe Kleine-König <u.kleine-koenig@pengutronix.de>
> > Signed-off-by: Alexandre Belloni <alexandre.belloni@bootlin.com>
> > ---
> > 
> > Note that this was already discussed a while ago and Arnd said this approach was
> > reasonable:
> >   https://lore.kernel.org/lkml/6120818.MyeJZ74hYa@wuerfel/
> 
> Does this need a Fixes: tag?
> 

I'm not sure how far this can get backported

Fixes: 80eded6ce8bb ("clk: at91: add slow clks driver")
Uwe Kleine-König Sept. 24, 2019, 12:21 p.m. UTC | #3
On Fri, Sep 20, 2019 at 05:39:06PM +0200, Alexandre Belloni wrote:
> It is not allowed to sleep to early in the boot process and this may lead
> to kernel issues if the bootloader didn't prepare the slow clock and main
> clock.
> 
> This results in the following error and dump stack on the AriettaG25:
>    bad: scheduling from the idle thread!
> 
> Ensure it is possible to sleep, else simply have a delay.
> 
> Reported-by: Uwe Kleine-König <u.kleine-koenig@pengutronix.de>
> Signed-off-by: Alexandre Belloni <alexandre.belloni@bootlin.com>

Tested-by: Uwe Kleine-König <u.kleine-koenig@pengutronix.de>

See below for a comment.

> Note that this was already discussed a while ago and Arnd said this approach was
> reasonable:
>   https://lore.kernel.org/lkml/6120818.MyeJZ74hYa@wuerfel/
> 
>  drivers/clk/at91/clk-main.c |  5 ++++-
>  drivers/clk/at91/sckc.c     | 20 ++++++++++++++++----
>  2 files changed, 20 insertions(+), 5 deletions(-)
> 
> diff --git a/drivers/clk/at91/clk-main.c b/drivers/clk/at91/clk-main.c
> index f607ee702c83..ccd48e7a3d74 100644
> --- a/drivers/clk/at91/clk-main.c
> +++ b/drivers/clk/at91/clk-main.c
> @@ -293,7 +293,10 @@ static int clk_main_probe_frequency(struct regmap *regmap)
>  		regmap_read(regmap, AT91_CKGR_MCFR, &mcfr);
>  		if (mcfr & AT91_PMC_MAINRDY)
>  			return 0;
> -		usleep_range(MAINF_LOOP_MIN_WAIT, MAINF_LOOP_MAX_WAIT);
> +		if (system_state < SYSTEM_RUNNING)
> +			udelay(MAINF_LOOP_MIN_WAIT);
> +		else
> +			usleep_range(MAINF_LOOP_MIN_WAIT, MAINF_LOOP_MAX_WAIT);

Given that this construct is introduced several times, I wonder if we
want something like:

	static inline void early_usleep_range(unsigned long min, unsigned long max)
	{
		if (system_state < SYSTEM_RUNNING)
			udelay(min);
		else
			usleep_range(min, max);
	}

Best regards
Uwe
Stephen Boyd Sept. 24, 2019, 8:20 p.m. UTC | #4
Quoting Uwe  (2019-09-24 05:21:47)
> On Fri, Sep 20, 2019 at 05:39:06PM +0200, Alexandre Belloni wrote:
> > Note that this was already discussed a while ago and Arnd said this approach was
> > reasonable:
> >   https://lore.kernel.org/lkml/6120818.MyeJZ74hYa@wuerfel/
> > 
> >  drivers/clk/at91/clk-main.c |  5 ++++-
> >  drivers/clk/at91/sckc.c     | 20 ++++++++++++++++----
> >  2 files changed, 20 insertions(+), 5 deletions(-)
> > 
> > diff --git a/drivers/clk/at91/clk-main.c b/drivers/clk/at91/clk-main.c
> > index f607ee702c83..ccd48e7a3d74 100644
> > --- a/drivers/clk/at91/clk-main.c
> > +++ b/drivers/clk/at91/clk-main.c
> > @@ -293,7 +293,10 @@ static int clk_main_probe_frequency(struct regmap *regmap)
> >               regmap_read(regmap, AT91_CKGR_MCFR, &mcfr);
> >               if (mcfr & AT91_PMC_MAINRDY)
> >                       return 0;
> > -             usleep_range(MAINF_LOOP_MIN_WAIT, MAINF_LOOP_MAX_WAIT);
> > +             if (system_state < SYSTEM_RUNNING)
> > +                     udelay(MAINF_LOOP_MIN_WAIT);
> > +             else
> > +                     usleep_range(MAINF_LOOP_MIN_WAIT, MAINF_LOOP_MAX_WAIT);
> 
> Given that this construct is introduced several times, I wonder if we
> want something like:
> 
>         static inline void early_usleep_range(unsigned long min, unsigned long max)
>         {
>                 if (system_state < SYSTEM_RUNNING)
>                         udelay(min);
>                 else
>                         usleep_range(min, max);
>         }
> 

Maybe, but I think the intent is to not encourage this behavior? So
providing a wrapper will make it "easy" and then we'll have to tell
users to stop calling it. Another idea would be to make usleep_range()
"do the right thing" and call udelay if the system isn't running. And
another idea from tlgx[1] is to pull the delay logic into another clk op
that we can call to see when the enable or prepare is done. That may be
possible by introducing another clk_ops callback that when present
indicates we should sleep or delay for so much time while waiting for
the prepare or enable to complete.

[1] https://lkml.kernel.org/r/alpine.DEB.2.11.1606061448010.28031@nanos
Alexandre Belloni Oct. 5, 2019, 8:05 p.m. UTC | #5
On 24/09/2019 13:20:15-0700, Stephen Boyd wrote:
> Quoting Uwe  (2019-09-24 05:21:47)
> > On Fri, Sep 20, 2019 at 05:39:06PM +0200, Alexandre Belloni wrote:
> > > Note that this was already discussed a while ago and Arnd said this approach was
> > > reasonable:
> > >   https://lore.kernel.org/lkml/6120818.MyeJZ74hYa@wuerfel/
> > > 
> > >  drivers/clk/at91/clk-main.c |  5 ++++-
> > >  drivers/clk/at91/sckc.c     | 20 ++++++++++++++++----
> > >  2 files changed, 20 insertions(+), 5 deletions(-)
> > > 
> > > diff --git a/drivers/clk/at91/clk-main.c b/drivers/clk/at91/clk-main.c
> > > index f607ee702c83..ccd48e7a3d74 100644
> > > --- a/drivers/clk/at91/clk-main.c
> > > +++ b/drivers/clk/at91/clk-main.c
> > > @@ -293,7 +293,10 @@ static int clk_main_probe_frequency(struct regmap *regmap)
> > >               regmap_read(regmap, AT91_CKGR_MCFR, &mcfr);
> > >               if (mcfr & AT91_PMC_MAINRDY)
> > >                       return 0;
> > > -             usleep_range(MAINF_LOOP_MIN_WAIT, MAINF_LOOP_MAX_WAIT);
> > > +             if (system_state < SYSTEM_RUNNING)
> > > +                     udelay(MAINF_LOOP_MIN_WAIT);
> > > +             else
> > > +                     usleep_range(MAINF_LOOP_MIN_WAIT, MAINF_LOOP_MAX_WAIT);
> > 
> > Given that this construct is introduced several times, I wonder if we
> > want something like:
> > 
> >         static inline void early_usleep_range(unsigned long min, unsigned long max)
> >         {
> >                 if (system_state < SYSTEM_RUNNING)
> >                         udelay(min);
> >                 else
> >                         usleep_range(min, max);
> >         }
> > 
> 
> Maybe, but I think the intent is to not encourage this behavior? So
> providing a wrapper will make it "easy" and then we'll have to tell
> users to stop calling it. Another idea would be to make usleep_range()
> "do the right thing" and call udelay if the system isn't running. And
> another idea from tlgx[1] is to pull the delay logic into another clk op
> that we can call to see when the enable or prepare is done. That may be
> possible by introducing another clk_ops callback that when present
> indicates we should sleep or delay for so much time while waiting for
> the prepare or enable to complete.
> 
> [1] https://lkml.kernel.org/r/alpine.DEB.2.11.1606061448010.28031@nanos
> 

Do you want me to implement that now or are you planning to apply the
patch in the meantime ?
Stephen Boyd Oct. 28, 2019, 2:54 p.m. UTC | #6
Quoting Alexandre Belloni (2019-10-05 13:05:21)
> On 24/09/2019 13:20:15-0700, Stephen Boyd wrote:
> > Quoting Uwe  (2019-09-24 05:21:47)
> > > On Fri, Sep 20, 2019 at 05:39:06PM +0200, Alexandre Belloni wrote:
> > > > Note that this was already discussed a while ago and Arnd said this approach was
> > > > reasonable:
> > > >   https://lore.kernel.org/lkml/6120818.MyeJZ74hYa@wuerfel/
> > > > 
> > > >  drivers/clk/at91/clk-main.c |  5 ++++-
> > > >  drivers/clk/at91/sckc.c     | 20 ++++++++++++++++----
> > > >  2 files changed, 20 insertions(+), 5 deletions(-)
> > > > 
> > > > diff --git a/drivers/clk/at91/clk-main.c b/drivers/clk/at91/clk-main.c
> > > > index f607ee702c83..ccd48e7a3d74 100644
> > > > --- a/drivers/clk/at91/clk-main.c
> > > > +++ b/drivers/clk/at91/clk-main.c
> > > > @@ -293,7 +293,10 @@ static int clk_main_probe_frequency(struct regmap *regmap)
> > > >               regmap_read(regmap, AT91_CKGR_MCFR, &mcfr);
> > > >               if (mcfr & AT91_PMC_MAINRDY)
> > > >                       return 0;
> > > > -             usleep_range(MAINF_LOOP_MIN_WAIT, MAINF_LOOP_MAX_WAIT);
> > > > +             if (system_state < SYSTEM_RUNNING)
> > > > +                     udelay(MAINF_LOOP_MIN_WAIT);
> > > > +             else
> > > > +                     usleep_range(MAINF_LOOP_MIN_WAIT, MAINF_LOOP_MAX_WAIT);
> > > 
> > > Given that this construct is introduced several times, I wonder if we
> > > want something like:
> > > 
> > >         static inline void early_usleep_range(unsigned long min, unsigned long max)
> > >         {
> > >                 if (system_state < SYSTEM_RUNNING)
> > >                         udelay(min);
> > >                 else
> > >                         usleep_range(min, max);
> > >         }
> > > 
> > 
> > Maybe, but I think the intent is to not encourage this behavior? So
> > providing a wrapper will make it "easy" and then we'll have to tell
> > users to stop calling it. Another idea would be to make usleep_range()
> > "do the right thing" and call udelay if the system isn't running. And
> > another idea from tlgx[1] is to pull the delay logic into another clk op
> > that we can call to see when the enable or prepare is done. That may be
> > possible by introducing another clk_ops callback that when present
> > indicates we should sleep or delay for so much time while waiting for
> > the prepare or enable to complete.
> > 
> > [1] https://lkml.kernel.org/r/alpine.DEB.2.11.1606061448010.28031@nanos
> > 
> 
> Do you want me to implement that now or are you planning to apply the
> patch in the meantime ?
> 
> 
 
I'll just apply this for now to clk-fixes and merge it up next week. It
would be great to do the other idea though, as a long term effort to
reduce all the busy loop code we have in clk drivers. No worries, I'll
put it on the todo list.
diff mbox series

Patch

diff --git a/drivers/clk/at91/clk-main.c b/drivers/clk/at91/clk-main.c
index f607ee702c83..ccd48e7a3d74 100644
--- a/drivers/clk/at91/clk-main.c
+++ b/drivers/clk/at91/clk-main.c
@@ -293,7 +293,10 @@  static int clk_main_probe_frequency(struct regmap *regmap)
 		regmap_read(regmap, AT91_CKGR_MCFR, &mcfr);
 		if (mcfr & AT91_PMC_MAINRDY)
 			return 0;
-		usleep_range(MAINF_LOOP_MIN_WAIT, MAINF_LOOP_MAX_WAIT);
+		if (system_state < SYSTEM_RUNNING)
+			udelay(MAINF_LOOP_MIN_WAIT);
+		else
+			usleep_range(MAINF_LOOP_MIN_WAIT, MAINF_LOOP_MAX_WAIT);
 	} while (time_before(prep_time, timeout));
 
 	return -ETIMEDOUT;
diff --git a/drivers/clk/at91/sckc.c b/drivers/clk/at91/sckc.c
index 9bfe9a28294a..fac0ca56d42d 100644
--- a/drivers/clk/at91/sckc.c
+++ b/drivers/clk/at91/sckc.c
@@ -76,7 +76,10 @@  static int clk_slow_osc_prepare(struct clk_hw *hw)
 
 	writel(tmp | osc->bits->cr_osc32en, sckcr);
 
-	usleep_range(osc->startup_usec, osc->startup_usec + 1);
+	if (system_state < SYSTEM_RUNNING)
+		udelay(osc->startup_usec);
+	else
+		usleep_range(osc->startup_usec, osc->startup_usec + 1);
 
 	return 0;
 }
@@ -187,7 +190,10 @@  static int clk_slow_rc_osc_prepare(struct clk_hw *hw)
 
 	writel(readl(sckcr) | osc->bits->cr_rcen, sckcr);
 
-	usleep_range(osc->startup_usec, osc->startup_usec + 1);
+	if (system_state < SYSTEM_RUNNING)
+		udelay(osc->startup_usec);
+	else
+		usleep_range(osc->startup_usec, osc->startup_usec + 1);
 
 	return 0;
 }
@@ -288,7 +294,10 @@  static int clk_sam9x5_slow_set_parent(struct clk_hw *hw, u8 index)
 
 	writel(tmp, sckcr);
 
-	usleep_range(SLOWCK_SW_TIME_USEC, SLOWCK_SW_TIME_USEC + 1);
+	if (system_state < SYSTEM_RUNNING)
+		udelay(SLOWCK_SW_TIME_USEC);
+	else
+		usleep_range(SLOWCK_SW_TIME_USEC, SLOWCK_SW_TIME_USEC + 1);
 
 	return 0;
 }
@@ -533,7 +542,10 @@  static int clk_sama5d4_slow_osc_prepare(struct clk_hw *hw)
 		return 0;
 	}
 
-	usleep_range(osc->startup_usec, osc->startup_usec + 1);
+	if (system_state < SYSTEM_RUNNING)
+		udelay(osc->startup_usec);
+	else
+		usleep_range(osc->startup_usec, osc->startup_usec + 1);
 	osc->prepared = true;
 
 	return 0;