diff mbox

[07/12] pm: at91: the standby mode uses the same sram function as the suspend to memory mode

Message ID 1421741825-18226-8-git-send-email-wenyou.yang@atmel.com (mailing list archive)
State New, archived
Headers show

Commit Message

Wenyou Yang Jan. 20, 2015, 8:17 a.m. UTC
To simply the PM code, the suspend to standby mode uses the same sram function
as the suspend to memory mode, running in the internal SRAM,
instead of the respective code for each mode.

But for the suspend to standby mode, the master clock doesn't
switch to the slow clock,  and the main oscillator doesn't
turn off as well.

Signed-off-by: Wenyou Yang <wenyou.yang@atmel.com>
---
 arch/arm/mach-at91/pm.c           |   81 ++++++++++++++++---------------------
 arch/arm/mach-at91/pm.h           |    6 +++
 arch/arm/mach-at91/pm_slowclock.S |   18 +++++++++
 3 files changed, 59 insertions(+), 46 deletions(-)

Comments

Alexandre Belloni Jan. 23, 2015, 10:30 a.m. UTC | #1
On 20/01/2015 at 16:17:00 +0800, Wenyou Yang wrote :
> To simply the PM code, the suspend to standby mode uses the same sram function
> as the suspend to memory mode, running in the internal SRAM,
> instead of the respective code for each mode.
> 
> But for the suspend to standby mode, the master clock doesn't
> switch to the slow clock,  and the main oscillator doesn't
> turn off as well.
> 
> Signed-off-by: Wenyou Yang <wenyou.yang@atmel.com>

That looks quite better, thanks.
Acked-by: Alexandre Belloni <alexandre.belloni@free-electrons.com>

> ---
>  arch/arm/mach-at91/pm.c           |   81 ++++++++++++++++---------------------
>  arch/arm/mach-at91/pm.h           |    6 +++
>  arch/arm/mach-at91/pm_slowclock.S |   18 +++++++++
>  3 files changed, 59 insertions(+), 46 deletions(-)
> 
> diff --git a/arch/arm/mach-at91/pm.c b/arch/arm/mach-at91/pm.c
> index 691e6db..a1010f0 100644
> --- a/arch/arm/mach-at91/pm.c
> +++ b/arch/arm/mach-at91/pm.c
> @@ -145,62 +145,51 @@ extern void at91_slow_clock(void __iomem *pmc, void __iomem *ramc0,
>  			    void __iomem *ramc1, int memctrl);
>  extern u32 at91_slow_clock_sz;
>  
> +static void at91_pm_suspend(suspend_state_t state)
> +{
> +	unsigned int pm_data = at91_pm_data.memctrl;
> +
> +	pm_data |= (state == PM_SUSPEND_MEM) ?
> +			(AT91_PM_SLOW_CLOCK << AT91_PM_MODE_OFFSET) : 0;
> +
> +	slow_clock(at91_pmc_base, at91_ramc_base[0],
> +			at91_ramc_base[1], pm_data);
> +}
> +
>  static int at91_pm_enter(suspend_state_t state)
>  {
>  	at91_pinctrl_gpio_suspend();
>  
>  	switch (state) {
> +	/*
> +	 * Suspend-to-RAM is like STANDBY plus slow clock mode, so
> +	 * drivers must suspend more deeply, the master clock switches
> +	 * to the clk32k and turns off the main oscillator
> +	 *
> +	 * STANDBY mode has *all* drivers suspended; ignores irqs not
> +	 * marked as 'wakeup' event sources; and reduces DRAM power.
> +	 * But otherwise it's identical to PM_SUSPEND_ON:  cpu idle, and
> +	 * nothing fancy done with main or cpu clocks.
> +	 */
> +	case PM_SUSPEND_MEM:
> +	case PM_SUSPEND_STANDBY:
>  		/*
> -		 * Suspend-to-RAM is like STANDBY plus slow clock mode, so
> -		 * drivers must suspend more deeply:  only the master clock
> -		 * controller may be using the main oscillator.
> +		 * Ensure that clocks are in a valid state.
>  		 */
> -		case PM_SUSPEND_MEM:
> -			/*
> -			 * Ensure that clocks are in a valid state.
> -			 */
> -			if (!at91_pm_verify_clocks())
> -				goto error;
> -
> -			/*
> -			 * Enter slow clock mode by switching over to clk32k and
> -			 * turning off the main oscillator; reverse on wakeup.
> -			 */
> -			if (slow_clock) {
> -				slow_clock(at91_pmc_base, at91_ramc_base[0],
> -					   at91_ramc_base[1],
> -					   at91_pm_data.memctrl);
> -				break;
> -			} else {
> -				pr_info("AT91: PM - no slow clock mode enabled ...\n");
> -				/* FALLTHROUGH leaving master clock alone */
> -			}
> +		if (!at91_pm_verify_clocks())
> +			goto error;
>  
> -		/*
> -		 * STANDBY mode has *all* drivers suspended; ignores irqs not
> -		 * marked as 'wakeup' event sources; and reduces DRAM power.
> -		 * But otherwise it's identical to PM_SUSPEND_ON:  cpu idle, and
> -		 * nothing fancy done with main or cpu clocks.
> -		 */
> -		case PM_SUSPEND_STANDBY:
> -			/*
> -			 * NOTE: the Wait-for-Interrupt instruction needs to be
> -			 * in icache so no SDRAM accesses are needed until the
> -			 * wakeup IRQ occurs and self-refresh is terminated.
> -			 * For ARM 926 based chips, this requirement is weaker
> -			 * as at91sam9 can access a RAM in self-refresh mode.
> -			 */
> -			if (at91_pm_standby)
> -				at91_pm_standby();
> -			break;
> +		at91_pm_suspend(state);
>  
> -		case PM_SUSPEND_ON:
> -			cpu_do_idle();
> -			break;
> +		break;
>  
> -		default:
> -			pr_debug("AT91: PM - bogus suspend state %d\n", state);
> -			goto error;
> +	case PM_SUSPEND_ON:
> +		cpu_do_idle();
> +		break;
> +
> +	default:
> +		pr_debug("AT91: PM - bogus suspend state %d\n", state);
> +		goto error;
>  	}
>  
>  error:
> diff --git a/arch/arm/mach-at91/pm.h b/arch/arm/mach-at91/pm.h
> index 7664d39..fbfea42 100644
> --- a/arch/arm/mach-at91/pm.h
> +++ b/arch/arm/mach-at91/pm.h
> @@ -15,6 +15,12 @@
>  
>  #include <mach/at91_ramc.h>
>  
> +#define AT91_PM_MEMCTRL_MASK	0x0f
> +#define AT91_PM_MODE_OFFSET	4
> +#define AT91_PM_MODE_MASK	0x0f
> +
> +#define	AT91_PM_SLOW_CLOCK	0x01
> +
>  /*
>   * On all at91 except rm9200 and x40 have the System Controller starts
>   * at address 0xffffc000 and has a size of 16KiB.
> diff --git a/arch/arm/mach-at91/pm_slowclock.S b/arch/arm/mach-at91/pm_slowclock.S
> index 634d819..92d7e63 100644
> --- a/arch/arm/mach-at91/pm_slowclock.S
> +++ b/arch/arm/mach-at91/pm_slowclock.S
> @@ -15,12 +15,15 @@
>  #include <linux/clk/at91_pmc.h>
>  #include <mach/at91_ramc.h>
>  
> +#include "pm.h"
> +
>  pmc	.req	r0
>  sdramc	.req	r1
>  ramc1	.req	r2
>  memctrl	.req	r3
>  tmp1	.req	r4
>  tmp2	.req	r5
> +mode	.req	r6
>  
>  /*
>   * Wait until master clock is ready (after switching master clock source)
> @@ -72,6 +75,13 @@ ENTRY(at91_slow_clock)
>  	mov	tmp1, #0
>  	mcr	p15, 0, tmp1, c7, c10, 4
>  
> +	mov	tmp1, memctrl
> +	mov	tmp2, tmp1, lsr#AT91_PM_MODE_OFFSET
> +	and	mode, tmp2, #AT91_PM_MODE_MASK
> +
> +	mov	tmp1, memctrl
> +	and	memctrl, tmp1, #AT91_PM_MEMCTRL_MASK
> +
>  	cmp	memctrl, #AT91_MEMCTRL_MC
>  	bne	ddr_sr_enable
>  
> @@ -144,6 +154,9 @@ sdr_sr_enable:
>  	str	tmp1, [sdramc, #AT91_SDRAMC_LPR]
>  
>  sdr_sr_done:
> +	tst	mode, #AT91_PM_SLOW_CLOCK
> +	beq	skip_disable_clock
> +
>  	/* Save Master clock setting */
>  	ldr	tmp1, [pmc, #AT91_PMC_MCKR]
>  	str	tmp1, .saved_mckr
> @@ -169,9 +182,13 @@ sdr_sr_done:
>  	bic	tmp1, tmp1, #AT91_PMC_MOSCEN
>  	str	tmp1, [pmc, #AT91_CKGR_MOR]
>  
> +skip_disable_clock:
>  	/* Wait for interrupt */
>  	mcr	p15, 0, tmp1, c7, c0, 4
>  
> +	tst	mode, #AT91_PM_SLOW_CLOCK
> +	beq	skip_enable_clock
> +
>  	/* Turn on the main oscillator */
>  	ldr	tmp1, [pmc, #AT91_CKGR_MOR]
>  	orr	tmp1, tmp1, #AT91_PMC_MOSCEN
> @@ -199,6 +216,7 @@ sdr_sr_done:
>  
>  	wait_mckrdy
>  
> +skip_enable_clock:
>  	/*
>  	 * at91rm9200 Memory controller
>  	 * Do nothing - self-refresh is automatically disabled.
> -- 
> 1.7.9.5
>
Sylvain Rochet Jan. 23, 2015, 4:50 p.m. UTC | #2
Hello Wenyou,


On Tue, Jan 20, 2015 at 04:17:00PM +0800, Wenyou Yang wrote:
> 
> diff --git a/arch/arm/mach-at91/pm.c b/arch/arm/mach-at91/pm.c
> index 691e6db..a1010f0 100644
> --- a/arch/arm/mach-at91/pm.c
> +++ b/arch/arm/mach-at91/pm.c


> @@ -145,62 +145,51 @@ extern void at91_slow_clock(void __iomem *pmc, void __iomem *ramc0,
>  			    void __iomem *ramc1, int memctrl);
>  extern u32 at91_slow_clock_sz;
>  
> +static void at91_pm_suspend(suspend_state_t state)
> +{
      (...)
> +	slow_clock(at91_pmc_base, at91_ramc_base[0],
> +			at91_ramc_base[1], pm_data);
> +}


> -			if (slow_clock) {
> -				slow_clock(at91_pmc_base, at91_ramc_base[0],
> -					   at91_ramc_base[1],
> -					   at91_pm_data.memctrl);
      (...)
> +               at91_pm_suspend(state);


By doing that you removed the condition "if (slow_clock)".

But slow_clock can still be NULL, see commit d2e4679, there are multiple 
reasons which ends up with a NULL slow_clock.


Sylvain
Sylvain Rochet Jan. 23, 2015, 5:32 p.m. UTC | #3
Hello Wenyou,

On Tue, Jan 20, 2015 at 04:17:00PM +0800, Wenyou Yang wrote:
> 
> diff --git a/arch/arm/mach-at91/pm.c b/arch/arm/mach-at91/pm.c
> index 691e6db..a1010f0 100644
> --- a/arch/arm/mach-at91/pm.c
> +++ b/arch/arm/mach-at91/pm.c
  (...)
>  static int at91_pm_enter(suspend_state_t state)
>  {
>  	at91_pinctrl_gpio_suspend();
>  
>  	switch (state) {
> +	/*
> +	 * Suspend-to-RAM is like STANDBY plus slow clock mode, so
> +	 * drivers must suspend more deeply, the master clock switches
> +	 * to the clk32k and turns off the main oscillator
> +	 *
> +	 * STANDBY mode has *all* drivers suspended; ignores irqs not
> +	 * marked as 'wakeup' event sources; and reduces DRAM power.
> +	 * But otherwise it's identical to PM_SUSPEND_ON:  cpu idle, and
> +	 * nothing fancy done with main or cpu clocks.
> +	 */
> +	case PM_SUSPEND_MEM:
> +	case PM_SUSPEND_STANDBY:
   (...)
> -		case PM_SUSPEND_MEM:
> -			/*
> -			 * Ensure that clocks are in a valid state.
> -			 */
> -			if (!at91_pm_verify_clocks())
> -				goto error;
   (...)
> +		if (!at91_pm_verify_clocks())
> +			goto error;
>  
   (...)
> -		case PM_SUSPEND_STANDBY:
> -			/*
> -			 * NOTE: the Wait-for-Interrupt instruction needs to be

By doing that at91_pm_verify_clocks() is now called for both MEM and 
STANDBY targets.

In my opinion this function is misnamed and should be called 
at91_pm_verify_clocks_for_slow_clock_mode(). This function actually 
checks if we can safely switch to slow clock mode, if some peripherals 
are still using the master clock, we abort the suspend because we can't 
suspend in good condition. Hard unclocking peripherals which ask for a 
soft stop, like USB controllers, is something we should avoid doing.

This function checks if USB PLL and PLL B are stopped, if PCK0..PCK3 are 
stopped too (or just using the 32k clock). If all drivers suspended 
correctly this is the state we expect and we can suspend in a deep 
state.

Not this is currently not the case in linux-next, suspend/resume support 
to all Atmel USB drivers (ehci-atmel,ohci-at91,atmel_usba,at91_udc) are 
in my series:
 [PATCHv7 0/6] USB: host: Atmel OHCI and EHCI drivers improvements
   <1421761144-11767-1-git-send-email-sylvain.rochet@finsecur.com>
 [PATCHv6 0/5] USB: gadget: atmel_usba_udc: Driver improvements 
   <1421945805-31129-1-git-send-email-sylvain.rochet@finsecur.com>

We are not going to change any clock for STANDBY target, there is no 
clock to check, so we don't need to call at91_pm_verify_clocks() for 
this target.

Sylvain
Alexandre Belloni Jan. 23, 2015, 11:02 p.m. UTC | #4
On 23/01/2015 at 18:32:34 +0100, Sylvain Rochet wrote :
> Hello Wenyou,
> 
> On Tue, Jan 20, 2015 at 04:17:00PM +0800, Wenyou Yang wrote:
> > 
> > diff --git a/arch/arm/mach-at91/pm.c b/arch/arm/mach-at91/pm.c
> > index 691e6db..a1010f0 100644
> > --- a/arch/arm/mach-at91/pm.c
> > +++ b/arch/arm/mach-at91/pm.c
>   (...)
> >  static int at91_pm_enter(suspend_state_t state)
> >  {
> >  	at91_pinctrl_gpio_suspend();
> >  
> >  	switch (state) {
> > +	/*
> > +	 * Suspend-to-RAM is like STANDBY plus slow clock mode, so
> > +	 * drivers must suspend more deeply, the master clock switches
> > +	 * to the clk32k and turns off the main oscillator
> > +	 *
> > +	 * STANDBY mode has *all* drivers suspended; ignores irqs not
> > +	 * marked as 'wakeup' event sources; and reduces DRAM power.
> > +	 * But otherwise it's identical to PM_SUSPEND_ON:  cpu idle, and
> > +	 * nothing fancy done with main or cpu clocks.
> > +	 */
> > +	case PM_SUSPEND_MEM:
> > +	case PM_SUSPEND_STANDBY:
>    (...)
> > -		case PM_SUSPEND_MEM:
> > -			/*
> > -			 * Ensure that clocks are in a valid state.
> > -			 */
> > -			if (!at91_pm_verify_clocks())
> > -				goto error;
>    (...)
> > +		if (!at91_pm_verify_clocks())
> > +			goto error;
> >  
>    (...)
> > -		case PM_SUSPEND_STANDBY:
> > -			/*
> > -			 * NOTE: the Wait-for-Interrupt instruction needs to be
> 
> By doing that at91_pm_verify_clocks() is now called for both MEM and 
> STANDBY targets.
> 
> In my opinion this function is misnamed and should be called 
> at91_pm_verify_clocks_for_slow_clock_mode(). This function actually 
> checks if we can safely switch to slow clock mode, if some peripherals 
> are still using the master clock, we abort the suspend because we can't 
> suspend in good condition. Hard unclocking peripherals which ask for a 
> soft stop, like USB controllers, is something we should avoid doing.
> 
> This function checks if USB PLL and PLL B are stopped, if PCK0..PCK3 are 
> stopped too (or just using the 32k clock). If all drivers suspended 
> correctly this is the state we expect and we can suspend in a deep 
> state.
> 
> Not this is currently not the case in linux-next, suspend/resume support 
> to all Atmel USB drivers (ehci-atmel,ohci-at91,atmel_usba,at91_udc) are 
> in my series:
>  [PATCHv7 0/6] USB: host: Atmel OHCI and EHCI drivers improvements
>    <1421761144-11767-1-git-send-email-sylvain.rochet@finsecur.com>
>  [PATCHv6 0/5] USB: gadget: atmel_usba_udc: Driver improvements 
>    <1421945805-31129-1-git-send-email-sylvain.rochet@finsecur.com>
> 
> We are not going to change any clock for STANDBY target, there is no 
> clock to check, so we don't need to call at91_pm_verify_clocks() for 
> this target.
> 

I think we should actually stop checking those clocks. In the meantime,
you are right and at91_pm_verify_clocks must not be called
unconditionally.
Alexandre Belloni Jan. 23, 2015, 11:13 p.m. UTC | #5
On 23/01/2015 at 17:50:20 +0100, Sylvain Rochet wrote :
> Hello Wenyou,
> 
> 
> On Tue, Jan 20, 2015 at 04:17:00PM +0800, Wenyou Yang wrote:
> > 
> > diff --git a/arch/arm/mach-at91/pm.c b/arch/arm/mach-at91/pm.c
> > index 691e6db..a1010f0 100644
> > --- a/arch/arm/mach-at91/pm.c
> > +++ b/arch/arm/mach-at91/pm.c
> 
> 
> > @@ -145,62 +145,51 @@ extern void at91_slow_clock(void __iomem *pmc, void __iomem *ramc0,
> >  			    void __iomem *ramc1, int memctrl);
> >  extern u32 at91_slow_clock_sz;
> >  
> > +static void at91_pm_suspend(suspend_state_t state)
> > +{
>       (...)
> > +	slow_clock(at91_pmc_base, at91_ramc_base[0],
> > +			at91_ramc_base[1], pm_data);
> > +}
> 
> 
> > -			if (slow_clock) {
> > -				slow_clock(at91_pmc_base, at91_ramc_base[0],
> > -					   at91_ramc_base[1],
> > -					   at91_pm_data.memctrl);
>       (...)
> > +               at91_pm_suspend(state);
> 
> 
> By doing that you removed the condition "if (slow_clock)".
> 
> But slow_clock can still be NULL, see commit d2e4679, there are multiple 
> reasons which ends up with a NULL slow_clock.
> 

I would fix that by not calling suspend_set_ops(&at91_pm_ops) when
slow_clock is NULL in patch 6 (quick and easy) or copying the whole
at91_pm_sram_init() in at91_pm_init() and handle failures from there.
Wenyou Yang Jan. 26, 2015, 3:06 a.m. UTC | #6
Hi Sylvain,

Thank you for review.

> -----Original Message-----
> From: Sylvain Rochet [mailto:sylvain.rochet@finsecur.com]
> Sent: Saturday, January 24, 2015 1:33 AM
> To: Yang, Wenyou
> Cc: Ferre, Nicolas; linux@arm.linux.org.uk; linux-kernel@vger.kernel.org;
> alexandre.belloni@free-electrons.com; peda@axentia.se; linux-arm-
> kernel@lists.infradead.org
> Subject: Re: [PATCH 07/12] pm: at91: the standby mode uses the same sram
> function as the suspend to memory mode
> 
> Hello Wenyou,
> 
> On Tue, Jan 20, 2015 at 04:17:00PM +0800, Wenyou Yang wrote:
> >
> > diff --git a/arch/arm/mach-at91/pm.c b/arch/arm/mach-at91/pm.c index
> > 691e6db..a1010f0 100644
> > --- a/arch/arm/mach-at91/pm.c
> > +++ b/arch/arm/mach-at91/pm.c
>   (...)
> >  static int at91_pm_enter(suspend_state_t state)  {
> >  	at91_pinctrl_gpio_suspend();
> >
> >  	switch (state) {
> > +	/*
> > +	 * Suspend-to-RAM is like STANDBY plus slow clock mode, so
> > +	 * drivers must suspend more deeply, the master clock switches
> > +	 * to the clk32k and turns off the main oscillator
> > +	 *
> > +	 * STANDBY mode has *all* drivers suspended; ignores irqs not
> > +	 * marked as 'wakeup' event sources; and reduces DRAM power.
> > +	 * But otherwise it's identical to PM_SUSPEND_ON:  cpu idle, and
> > +	 * nothing fancy done with main or cpu clocks.
> > +	 */
> > +	case PM_SUSPEND_MEM:
> > +	case PM_SUSPEND_STANDBY:
>    (...)
> > -		case PM_SUSPEND_MEM:
> > -			/*
> > -			 * Ensure that clocks are in a valid state.
> > -			 */
> > -			if (!at91_pm_verify_clocks())
> > -				goto error;
>    (...)
> > +		if (!at91_pm_verify_clocks())
> > +			goto error;
> >
>    (...)
> > -		case PM_SUSPEND_STANDBY:
> > -			/*
> > -			 * NOTE: the Wait-for-Interrupt instruction needs to be
> 
> By doing that at91_pm_verify_clocks() is now called for both MEM and STANDBY
> targets.
> 
> In my opinion this function is misnamed and should be called
> at91_pm_verify_clocks_for_slow_clock_mode(). This function actually checks if
> we can safely switch to slow clock mode, if some peripherals are still using the
> master clock, we abort the suspend because we can't suspend in good condition.
> Hard unclocking peripherals which ask for a soft stop, like USB controllers, is
> something we should avoid doing.
> 
> This function checks if USB PLL and PLL B are stopped, if PCK0..PCK3 are
> stopped too (or just using the 32k clock). If all drivers suspended correctly this is
> the state we expect and we can suspend in a deep state.
> 
> Not this is currently not the case in linux-next, suspend/resume support to all Atmel
> USB drivers (ehci-atmel,ohci-at91,atmel_usba,at91_udc) are in my series:
>  [PATCHv7 0/6] USB: host: Atmel OHCI and EHCI drivers improvements
>    <1421761144-11767-1-git-send-email-sylvain.rochet@finsecur.com>
>  [PATCHv6 0/5] USB: gadget: atmel_usba_udc: Driver improvements
>    <1421945805-31129-1-git-send-email-sylvain.rochet@finsecur.com>
> 
> We are not going to change any clock for STANDBY target, there is no clock to
> check, so we don't need to call at91_pm_verify_clocks() for this target.
I will change in the next version.
Thanks.

> 
> Sylvain

Best Regards,
Wenyou Yang
Wenyou Yang Jan. 26, 2015, 3:08 a.m. UTC | #7
Hi Alexandre,

Thank you for review.

> -----Original Message-----
> From: Alexandre Belloni [mailto:alexandre.belloni@free-electrons.com]
> Sent: Saturday, January 24, 2015 7:02 AM
> To: Sylvain Rochet
> Cc: Yang, Wenyou; Ferre, Nicolas; linux@arm.linux.org.uk; linux-
> kernel@vger.kernel.org; peda@axentia.se; linux-arm-kernel@lists.infradead.org
> Subject: Re: [PATCH 07/12] pm: at91: the standby mode uses the same sram
> function as the suspend to memory mode
> 
> On 23/01/2015 at 18:32:34 +0100, Sylvain Rochet wrote :
> > Hello Wenyou,
> >
> > On Tue, Jan 20, 2015 at 04:17:00PM +0800, Wenyou Yang wrote:
> > >
> > > diff --git a/arch/arm/mach-at91/pm.c b/arch/arm/mach-at91/pm.c index
> > > 691e6db..a1010f0 100644
> > > --- a/arch/arm/mach-at91/pm.c
> > > +++ b/arch/arm/mach-at91/pm.c
> >   (...)
> > >  static int at91_pm_enter(suspend_state_t state)  {
> > >  	at91_pinctrl_gpio_suspend();
> > >
> > >  	switch (state) {
> > > +	/*
> > > +	 * Suspend-to-RAM is like STANDBY plus slow clock mode, so
> > > +	 * drivers must suspend more deeply, the master clock switches
> > > +	 * to the clk32k and turns off the main oscillator
> > > +	 *
> > > +	 * STANDBY mode has *all* drivers suspended; ignores irqs not
> > > +	 * marked as 'wakeup' event sources; and reduces DRAM power.
> > > +	 * But otherwise it's identical to PM_SUSPEND_ON:  cpu idle, and
> > > +	 * nothing fancy done with main or cpu clocks.
> > > +	 */
> > > +	case PM_SUSPEND_MEM:
> > > +	case PM_SUSPEND_STANDBY:
> >    (...)
> > > -		case PM_SUSPEND_MEM:
> > > -			/*
> > > -			 * Ensure that clocks are in a valid state.
> > > -			 */
> > > -			if (!at91_pm_verify_clocks())
> > > -				goto error;
> >    (...)
> > > +		if (!at91_pm_verify_clocks())
> > > +			goto error;
> > >
> >    (...)
> > > -		case PM_SUSPEND_STANDBY:
> > > -			/*
> > > -			 * NOTE: the Wait-for-Interrupt instruction needs to be
> >
> > By doing that at91_pm_verify_clocks() is now called for both MEM and
> > STANDBY targets.
> >
> > In my opinion this function is misnamed and should be called
> > at91_pm_verify_clocks_for_slow_clock_mode(). This function actually
> > checks if we can safely switch to slow clock mode, if some peripherals
> > are still using the master clock, we abort the suspend because we
> > can't suspend in good condition. Hard unclocking peripherals which ask
> > for a soft stop, like USB controllers, is something we should avoid doing.
> >
> > This function checks if USB PLL and PLL B are stopped, if PCK0..PCK3
> > are stopped too (or just using the 32k clock). If all drivers
> > suspended correctly this is the state we expect and we can suspend in
> > a deep state.
> >
> > Not this is currently not the case in linux-next, suspend/resume
> > support to all Atmel USB drivers
> > (ehci-atmel,ohci-at91,atmel_usba,at91_udc) are in my series:
> >  [PATCHv7 0/6] USB: host: Atmel OHCI and EHCI drivers improvements
> >    <1421761144-11767-1-git-send-email-sylvain.rochet@finsecur.com>
> >  [PATCHv6 0/5] USB: gadget: atmel_usba_udc: Driver improvements
> >    <1421945805-31129-1-git-send-email-sylvain.rochet@finsecur.com>
> >
> > We are not going to change any clock for STANDBY target, there is no
> > clock to check, so we don't need to call at91_pm_verify_clocks() for
> > this target.
> >
> 
> I think we should actually stop checking those clocks. In the meantime, you are
> right and at91_pm_verify_clocks must not be called unconditionally.

Thanks.

I will change, the at91_pm_verify_clocks is only called for suspend to memory mode, not for the standby.

> 
> --
> Alexandre Belloni, Free Electrons
> Embedded Linux, Kernel and Android engineering http://free-electrons.com

Best Regards,
Wenyou Yang
Wenyou Yang Jan. 27, 2015, 3:08 a.m. UTC | #8
Hi Alexandre,

> -----Original Message-----
> From: Alexandre Belloni [mailto:alexandre.belloni@free-electrons.com]
> Sent: Saturday, January 24, 2015 7:13 AM
> To: Sylvain Rochet
> Cc: Yang, Wenyou; Ferre, Nicolas; linux@arm.linux.org.uk; linux-
> kernel@vger.kernel.org; peda@axentia.se; linux-arm-kernel@lists.infradead.org
> Subject: Re: [PATCH 07/12] pm: at91: the standby mode uses the same sram
> function as the suspend to memory mode
> 
> On 23/01/2015 at 17:50:20 +0100, Sylvain Rochet wrote :
> > Hello Wenyou,
> >
> >
> > On Tue, Jan 20, 2015 at 04:17:00PM +0800, Wenyou Yang wrote:
> > >
> > > diff --git a/arch/arm/mach-at91/pm.c b/arch/arm/mach-at91/pm.c index
> > > 691e6db..a1010f0 100644
> > > --- a/arch/arm/mach-at91/pm.c
> > > +++ b/arch/arm/mach-at91/pm.c
> >
> >
> > > @@ -145,62 +145,51 @@ extern void at91_slow_clock(void __iomem *pmc,
> void __iomem *ramc0,
> > >  			    void __iomem *ramc1, int memctrl);  extern u32
> > > at91_slow_clock_sz;
> > >
> > > +static void at91_pm_suspend(suspend_state_t state) {
> >       (...)
> > > +	slow_clock(at91_pmc_base, at91_ramc_base[0],
> > > +			at91_ramc_base[1], pm_data);
> > > +}
> >
> >
> > > -			if (slow_clock) {
> > > -				slow_clock(at91_pmc_base, at91_ramc_base[0],
> > > -					   at91_ramc_base[1],
> > > -					   at91_pm_data.memctrl);
> >       (...)
> > > +               at91_pm_suspend(state);
> >
> >
> > By doing that you removed the condition "if (slow_clock)".
> >
> > But slow_clock can still be NULL, see commit d2e4679, there are
> > multiple reasons which ends up with a NULL slow_clock.
> >
> 
> I would fix that by not calling suspend_set_ops(&at91_pm_ops) when slow_clock
> is NULL in patch 6 (quick and easy) or copying the whole
> at91_pm_sram_init() in at91_pm_init() and handle failures from there.
Thank you for suggestion, I will fix it.

> 
> 
> --
> Alexandre Belloni, Free Electrons
> Embedded Linux, Kernel and Android engineering http://free-electrons.com

Best Regards,
Wenyou Yang
diff mbox

Patch

diff --git a/arch/arm/mach-at91/pm.c b/arch/arm/mach-at91/pm.c
index 691e6db..a1010f0 100644
--- a/arch/arm/mach-at91/pm.c
+++ b/arch/arm/mach-at91/pm.c
@@ -145,62 +145,51 @@  extern void at91_slow_clock(void __iomem *pmc, void __iomem *ramc0,
 			    void __iomem *ramc1, int memctrl);
 extern u32 at91_slow_clock_sz;
 
+static void at91_pm_suspend(suspend_state_t state)
+{
+	unsigned int pm_data = at91_pm_data.memctrl;
+
+	pm_data |= (state == PM_SUSPEND_MEM) ?
+			(AT91_PM_SLOW_CLOCK << AT91_PM_MODE_OFFSET) : 0;
+
+	slow_clock(at91_pmc_base, at91_ramc_base[0],
+			at91_ramc_base[1], pm_data);
+}
+
 static int at91_pm_enter(suspend_state_t state)
 {
 	at91_pinctrl_gpio_suspend();
 
 	switch (state) {
+	/*
+	 * Suspend-to-RAM is like STANDBY plus slow clock mode, so
+	 * drivers must suspend more deeply, the master clock switches
+	 * to the clk32k and turns off the main oscillator
+	 *
+	 * STANDBY mode has *all* drivers suspended; ignores irqs not
+	 * marked as 'wakeup' event sources; and reduces DRAM power.
+	 * But otherwise it's identical to PM_SUSPEND_ON:  cpu idle, and
+	 * nothing fancy done with main or cpu clocks.
+	 */
+	case PM_SUSPEND_MEM:
+	case PM_SUSPEND_STANDBY:
 		/*
-		 * Suspend-to-RAM is like STANDBY plus slow clock mode, so
-		 * drivers must suspend more deeply:  only the master clock
-		 * controller may be using the main oscillator.
+		 * Ensure that clocks are in a valid state.
 		 */
-		case PM_SUSPEND_MEM:
-			/*
-			 * Ensure that clocks are in a valid state.
-			 */
-			if (!at91_pm_verify_clocks())
-				goto error;
-
-			/*
-			 * Enter slow clock mode by switching over to clk32k and
-			 * turning off the main oscillator; reverse on wakeup.
-			 */
-			if (slow_clock) {
-				slow_clock(at91_pmc_base, at91_ramc_base[0],
-					   at91_ramc_base[1],
-					   at91_pm_data.memctrl);
-				break;
-			} else {
-				pr_info("AT91: PM - no slow clock mode enabled ...\n");
-				/* FALLTHROUGH leaving master clock alone */
-			}
+		if (!at91_pm_verify_clocks())
+			goto error;
 
-		/*
-		 * STANDBY mode has *all* drivers suspended; ignores irqs not
-		 * marked as 'wakeup' event sources; and reduces DRAM power.
-		 * But otherwise it's identical to PM_SUSPEND_ON:  cpu idle, and
-		 * nothing fancy done with main or cpu clocks.
-		 */
-		case PM_SUSPEND_STANDBY:
-			/*
-			 * NOTE: the Wait-for-Interrupt instruction needs to be
-			 * in icache so no SDRAM accesses are needed until the
-			 * wakeup IRQ occurs and self-refresh is terminated.
-			 * For ARM 926 based chips, this requirement is weaker
-			 * as at91sam9 can access a RAM in self-refresh mode.
-			 */
-			if (at91_pm_standby)
-				at91_pm_standby();
-			break;
+		at91_pm_suspend(state);
 
-		case PM_SUSPEND_ON:
-			cpu_do_idle();
-			break;
+		break;
 
-		default:
-			pr_debug("AT91: PM - bogus suspend state %d\n", state);
-			goto error;
+	case PM_SUSPEND_ON:
+		cpu_do_idle();
+		break;
+
+	default:
+		pr_debug("AT91: PM - bogus suspend state %d\n", state);
+		goto error;
 	}
 
 error:
diff --git a/arch/arm/mach-at91/pm.h b/arch/arm/mach-at91/pm.h
index 7664d39..fbfea42 100644
--- a/arch/arm/mach-at91/pm.h
+++ b/arch/arm/mach-at91/pm.h
@@ -15,6 +15,12 @@ 
 
 #include <mach/at91_ramc.h>
 
+#define AT91_PM_MEMCTRL_MASK	0x0f
+#define AT91_PM_MODE_OFFSET	4
+#define AT91_PM_MODE_MASK	0x0f
+
+#define	AT91_PM_SLOW_CLOCK	0x01
+
 /*
  * On all at91 except rm9200 and x40 have the System Controller starts
  * at address 0xffffc000 and has a size of 16KiB.
diff --git a/arch/arm/mach-at91/pm_slowclock.S b/arch/arm/mach-at91/pm_slowclock.S
index 634d819..92d7e63 100644
--- a/arch/arm/mach-at91/pm_slowclock.S
+++ b/arch/arm/mach-at91/pm_slowclock.S
@@ -15,12 +15,15 @@ 
 #include <linux/clk/at91_pmc.h>
 #include <mach/at91_ramc.h>
 
+#include "pm.h"
+
 pmc	.req	r0
 sdramc	.req	r1
 ramc1	.req	r2
 memctrl	.req	r3
 tmp1	.req	r4
 tmp2	.req	r5
+mode	.req	r6
 
 /*
  * Wait until master clock is ready (after switching master clock source)
@@ -72,6 +75,13 @@  ENTRY(at91_slow_clock)
 	mov	tmp1, #0
 	mcr	p15, 0, tmp1, c7, c10, 4
 
+	mov	tmp1, memctrl
+	mov	tmp2, tmp1, lsr#AT91_PM_MODE_OFFSET
+	and	mode, tmp2, #AT91_PM_MODE_MASK
+
+	mov	tmp1, memctrl
+	and	memctrl, tmp1, #AT91_PM_MEMCTRL_MASK
+
 	cmp	memctrl, #AT91_MEMCTRL_MC
 	bne	ddr_sr_enable
 
@@ -144,6 +154,9 @@  sdr_sr_enable:
 	str	tmp1, [sdramc, #AT91_SDRAMC_LPR]
 
 sdr_sr_done:
+	tst	mode, #AT91_PM_SLOW_CLOCK
+	beq	skip_disable_clock
+
 	/* Save Master clock setting */
 	ldr	tmp1, [pmc, #AT91_PMC_MCKR]
 	str	tmp1, .saved_mckr
@@ -169,9 +182,13 @@  sdr_sr_done:
 	bic	tmp1, tmp1, #AT91_PMC_MOSCEN
 	str	tmp1, [pmc, #AT91_CKGR_MOR]
 
+skip_disable_clock:
 	/* Wait for interrupt */
 	mcr	p15, 0, tmp1, c7, c0, 4
 
+	tst	mode, #AT91_PM_SLOW_CLOCK
+	beq	skip_enable_clock
+
 	/* Turn on the main oscillator */
 	ldr	tmp1, [pmc, #AT91_CKGR_MOR]
 	orr	tmp1, tmp1, #AT91_PMC_MOSCEN
@@ -199,6 +216,7 @@  sdr_sr_done:
 
 	wait_mckrdy
 
+skip_enable_clock:
 	/*
 	 * at91rm9200 Memory controller
 	 * Do nothing - self-refresh is automatically disabled.