diff mbox

ARM: shmobile: r8a7740: Add Suspend-To-RAM modes and CPUIdle

Message ID 1365609107-14290-1-git-send-email-hechtb+renesas@gmail.com (mailing list archive)
State New, archived
Headers show

Commit Message

Bastian Hecht April 10, 2013, 3:51 p.m. UTC
We add 2 sleep modes and register a CPUIdle driver that uses them.

- A3SM PLL0 on/off:     Power domain A3SM that contains the ARM core
                        and the 2nd level cache with either PLL0 on
			or off

As the suspend to memory mechanism we use A3SM PLL off.

The setup of the SYSC regarding the external IRQs is taken from
pm-sh7372.c from Magnus Damm.

Signed-off-by: Bastian Hecht <hechtb+renesas@gmail.com>
---
Ok I need to add some remarks here:

First this patch relies on a fix I've sent out today:
"ARM: hw_breakpoint: Do not use __cpuinitdata for dbg_cpu_pm_nb"
Without it we can't use suspend at all without a kernel panic.

Next you can test this in two ways:
Either add IRQF_NO_SUSPEND to the touchscreen driver st1232 at IRQ registration
(hack) or extend the irqpin driver to support wakeup devices properly, meaning
adding suspend and resume callbacks that handle it (proper way). Or you can add
no_console_suspend to the boot options in r8a7740-armadillo800eva.dts. This way
you can use the serial console as wakeup device.


 arch/arm/mach-shmobile/Makefile               |    2 +-
 arch/arm/mach-shmobile/include/mach/r8a7740.h |    2 +
 arch/arm/mach-shmobile/pm-r8a7740.c           |  220 ++++++++++++++++++++++++-
 arch/arm/mach-shmobile/sleep-r8a7740.S        |   68 ++++++++
 4 files changed, 289 insertions(+), 3 deletions(-)
 create mode 100644 arch/arm/mach-shmobile/sleep-r8a7740.S

Comments

Daniel Lezcano April 10, 2013, 10:35 p.m. UTC | #1
On 04/10/2013 05:51 PM, Bastian Hecht wrote:
> We add 2 sleep modes and register a CPUIdle driver that uses them.
> 
> - A3SM PLL0 on/off:     Power domain A3SM that contains the ARM core
>                         and the 2nd level cache with either PLL0 on
> 			or off
> 
> As the suspend to memory mechanism we use A3SM PLL off.
> 
> The setup of the SYSC regarding the external IRQs is taken from
> pm-sh7372.c from Magnus Damm.
> 
> Signed-off-by: Bastian Hecht <hechtb+renesas@gmail.com>
> ---
> Ok I need to add some remarks here:
> 
> First this patch relies on a fix I've sent out today:
> "ARM: hw_breakpoint: Do not use __cpuinitdata for dbg_cpu_pm_nb"
> Without it we can't use suspend at all without a kernel panic.
> 
> Next you can test this in two ways:
> Either add IRQF_NO_SUSPEND to the touchscreen driver st1232 at IRQ registration
> (hack) or extend the irqpin driver to support wakeup devices properly, meaning
> adding suspend and resume callbacks that handle it (proper way). Or you can add
> no_console_suspend to the boot options in r8a7740-armadillo800eva.dts. This way
> you can use the serial console as wakeup device.
> 
> 
>  arch/arm/mach-shmobile/Makefile               |    2 +-
>  arch/arm/mach-shmobile/include/mach/r8a7740.h |    2 +
>  arch/arm/mach-shmobile/pm-r8a7740.c           |  220 ++++++++++++++++++++++++-
>  arch/arm/mach-shmobile/sleep-r8a7740.S        |   68 ++++++++
>  4 files changed, 289 insertions(+), 3 deletions(-)
>  create mode 100644 arch/arm/mach-shmobile/sleep-r8a7740.S
> 
> diff --git a/arch/arm/mach-shmobile/Makefile b/arch/arm/mach-shmobile/Makefile
> index 068f1da..0568894 100644
> --- a/arch/arm/mach-shmobile/Makefile
> +++ b/arch/arm/mach-shmobile/Makefile
> @@ -30,7 +30,7 @@ obj-$(CONFIG_SUSPEND)		+= suspend.o
>  obj-$(CONFIG_CPU_IDLE)		+= cpuidle.o
>  obj-$(CONFIG_ARCH_SHMOBILE)	+= pm-rmobile.o
>  obj-$(CONFIG_ARCH_SH7372)	+= pm-sh7372.o sleep-sh7372.o
> -obj-$(CONFIG_ARCH_R8A7740)	+= pm-r8a7740.o
> +obj-$(CONFIG_ARCH_R8A7740)	+= pm-r8a7740.o sleep-r8a7740.o
>  obj-$(CONFIG_ARCH_R8A7779)	+= pm-r8a7779.o
>  obj-$(CONFIG_ARCH_SH73A0)	+= pm-sh73a0.o
>  
> diff --git a/arch/arm/mach-shmobile/include/mach/r8a7740.h b/arch/arm/mach-shmobile/include/mach/r8a7740.h
> index abdc4d4..ba4707b 100644
> --- a/arch/arm/mach-shmobile/include/mach/r8a7740.h
> +++ b/arch/arm/mach-shmobile/include/mach/r8a7740.h
> @@ -540,6 +540,8 @@ extern void r8a7740_add_standard_devices(void);
>  extern void r8a7740_clock_init(u8 md_ck);
>  extern void r8a7740_pinmux_init(void);
>  extern void r8a7740_pm_init(void);
> +extern void r8a7740_resume(void);
> +extern void r8a7740_shutdown(void);
>  
>  #ifdef CONFIG_PM
>  extern void __init r8a7740_init_pm_domains(void);
> diff --git a/arch/arm/mach-shmobile/pm-r8a7740.c b/arch/arm/mach-shmobile/pm-r8a7740.c
> index 40b87aa..d4613f0 100644
> --- a/arch/arm/mach-shmobile/pm-r8a7740.c
> +++ b/arch/arm/mach-shmobile/pm-r8a7740.c
> @@ -8,10 +8,54 @@
>   * License.  See the file "COPYING" in the main directory of this archive
>   * for more details.
>   */
> +#include <linux/bitrev.h>
>  #include <linux/console.h>
> +#include <linux/cpuidle.h>
> +#include <linux/module.h>
>  #include <linux/suspend.h>
> +#include <linux/io.h>
> +#include <asm/suspend.h>
> +#include <asm/cacheflush.h>
> +#include <asm/cpuidle.h>
> +#include <asm/hardware/cache-l2x0.h>
>  #include <mach/pm-rmobile.h>
>  #include <mach/common.h>
> +#include <mach/r8a7740.h>
> +
> +/* CPGA */
> +#define PLLC01STPCR 	IOMEM(0xe61500c8)
> +#define SYSTBCR		IOMEM(0xe6150024)
> +
> +/* SYSC */
> +#define STBCHR		IOMEM(0xe6180000)
> +#define STBCHRB		IOMEM(0xe6180040)
> +#define SPDCR		IOMEM(0xe6180008)
> +#define SBAR		IOMEM(0xe6180020)
> +#define SRSTFR		IOMEM(0xe61800B4)
> +#define WUPSMSK		IOMEM(0xe618002c)
> +#define WUPSMSK2	IOMEM(0xe6180048)
> +#define WUPSFAC		IOMEM(0xe6180098)
> +#define IRQCR 		IOMEM(0xe618022c)
> +#define IRQCR2 		IOMEM(0xe6180238)
> +#define IRQCR3		IOMEM(0xe6180244)
> +#define IRQCR4		IOMEM(0xe6180248)
> +
> +/* SRSTFR flags */
> +#define RAMST		(1 << 19)
> +#define RCLNKA		(1 << 7)
> +#define RCPRES		(1 << 5)
> +#define RCWD1		(1 << 4)
> +#define RPF		(1 << 0)
> +
> +/* INTC */
> +#define ICR1A		IOMEM(0xe6900000)
> +#define ICR2A		IOMEM(0xe6900004)
> +#define ICR3A		IOMEM(0xe6900008)
> +#define ICR4A		IOMEM(0xe690000c)
> +#define INTMSK00A	IOMEM(0xe6900040)
> +#define INTMSK10A	IOMEM(0xe6900044)
> +#define INTMSK20A	IOMEM(0xe6900048)
> +#define INTMSK30A	IOMEM(0xe690004c)
>  
>  #ifdef CONFIG_PM
>  static int r8a7740_pd_a4s_suspend(void)
> @@ -58,12 +102,183 @@ void __init r8a7740_init_pm_domains(void)
>  	rmobile_init_domains(r8a7740_pm_domains, ARRAY_SIZE(r8a7740_pm_domains));
>  	pm_genpd_add_subdomain_names("A4S", "A3SP");
>  }
> -
>  #endif /* CONFIG_PM */
>  
> +#if defined(CONFIG_SUSPEND) || defined(CONFIG_CPU_IDLE)
> +static void r8a7740_set_reset_vector(unsigned long address)
> +{
> +	__raw_writel(address, SBAR);
> +}
> +
> +static void r8a7740_icr_to_irqcr(unsigned long icr, u16 *irqcr1p, u16 *irqcr2p)
> +{
> +	u16 tmp, irqcr1, irqcr2;
> +	int k;
> +
> +	irqcr1 = 0;
> +	irqcr2 = 0;
> +
> +	/* convert INTCA ICR register layout to SYSC IRQCR+IRQCR2 */
> +	for (k = 0; k <= 7; k++) {
> +		tmp = (icr >> ((7 - k) * 4)) & 0xf;
> +		irqcr1 |= (tmp & 0x03) << (k * 2);
> +		irqcr2 |= (tmp >> 2) << (k * 2);
> +	}
> +
> +	*irqcr1p = irqcr1;
> +	*irqcr2p = irqcr2;
> +}
> +
> +static void r8a7740_setup_sysc(unsigned long msk, unsigned long msk2)
> +{
> +	u16 irqcrx_low, irqcrx_high, irqcry_low, irqcry_high;
> +	unsigned long tmp;
> +
> +	/* read IRQ0A -> IRQ15A mask */
> +	tmp = bitrev8(__raw_readb(INTMSK00A));
> +	tmp |= bitrev8(__raw_readb(INTMSK10A)) << 8;
> +
> +	/* setup WUPSMSK from clocks and external IRQ mask */
> +	msk = (~msk & 0xc030000f) | (tmp << 4);
> +	__raw_writel(msk, WUPSMSK);
> +
> +	/* propage level/edge trigger for external IRQ 0->15 */
> +	r8a7740_icr_to_irqcr(__raw_readl(ICR1A), &irqcrx_low, &irqcry_low);
> +	r8a7740_icr_to_irqcr(__raw_readl(ICR2A), &irqcrx_high, &irqcry_high);
> +	__raw_writel((irqcrx_high << 16) | irqcrx_low, IRQCR);
> +	__raw_writel((irqcry_high << 16) | irqcry_low, IRQCR2);
> +
> +	/* read IRQ16A -> IRQ31A mask */
> +	tmp = bitrev8(__raw_readb(INTMSK20A));
> +	tmp |= bitrev8(__raw_readb(INTMSK30A)) << 8;
> +
> +	/* setup WUPSMSK2 from clocks and external IRQ mask */
> +	msk2 = (~msk2 & 0x00030000) | tmp;
> +	__raw_writel(msk2, WUPSMSK2);
> +
> +	/* propage level/edge trigger for external IRQ 16->31 */
> +	r8a7740_icr_to_irqcr(__raw_readl(ICR3A), &irqcrx_low, &irqcry_low);
> +	r8a7740_icr_to_irqcr(__raw_readl(ICR4A), &irqcrx_high, &irqcry_high);
> +	__raw_writel((irqcrx_high << 16) | irqcrx_low, IRQCR3);
> +	__raw_writel((irqcry_high << 16) | irqcry_low, IRQCR4);
> +}
> +
> +static void r8a7740_prepare_wakeup(void)
> +{
> +	/* clear all flags that lead to a cold boot */
> +	__raw_writel(~(RAMST | RCLNKA | RCPRES | RCWD1 | RPF), SRSTFR);
> +	/* indicate warm boot */
> +	__raw_writel(0x80000000, STBCHRB);
> +	/* clear other flags checked by internal ROM boot loader */
> +	__raw_writel(0x00000000, STBCHR);
> +}
> +
> +static int r8a7740_do_suspend(unsigned long unused)
> +{
> +	/*
> +	 * cpu_suspend() guarantees that all data made it to the L2.
> +	 * Flush it out now and disable the cache controller.
> +	 */
> +	outer_flush_all();
> +	outer_disable();
> +
> +	r8a7740_shutdown();
> +
> +	/* in case WFI fails to enter the low power state, restore things */
> +	outer_resume();
> +
> +	return 0;
> +}
> +
> +static void r8a7740_enter_a3sm_common(int pllc0_on)
> +{
> +	u32 reg32;
> +
> +	if (pllc0_on)
> +		__raw_writel(0, PLLC01STPCR);
> +	else
> +		__raw_writel(1 << 28, PLLC01STPCR);
> +
> +	r8a7740_set_reset_vector(__pa(r8a7740_resume));
> +	r8a7740_prepare_wakeup();
> +	r8a7740_setup_sysc(1 << 0, 0);
> +
> +	/* Activate delayed shutdown of A3SM */
> +	reg32 = __raw_readl(SPDCR);
> +	reg32 |= (1 << 31) | (1 << 12);
> +	__raw_writel(reg32, SPDCR);
> +
> +	/* We activate CPU Core Standby as well here */
> +	reg32 = __raw_readl(SYSTBCR);
> +	reg32 |= (1 << 4);
> +	__raw_writel(reg32, SYSTBCR);
> +
> +	/* Clear Wakeup Factors and do suspend */
> +	reg32 = __raw_readl(WUPSFAC);
> +	cpu_suspend(0, r8a7740_do_suspend);
> +	outer_resume();
> +	reg32 = __raw_readl(WUPSFAC);
> +
> +	/* Clear CPU Core Standby flag for other WFI instructions */
> +	reg32 &= ~(1 << 4);
> +	__raw_writel(reg32, SYSTBCR);
> +
> +}
> +#endif /* CONFIG_SUSPEND || CONFIG_CPU_IDLE */
> +
> +#ifdef CONFIG_CPU_IDLE
> +static int r8a7740_enter_a3sm_pll_on(struct cpuidle_device *dev,
> +					struct cpuidle_driver *drv, int index)
> +{
> +	r8a7740_enter_a3sm_common(1);
> +	return 1;
> +}
> +
> +static int r8a7740_enter_a3sm_pll_off(struct cpuidle_device *dev,
> +					struct cpuidle_driver *drv, int index)
> +{
> +	r8a7740_enter_a3sm_common(0);
> +	return 2;
> +}
> +
> +static struct cpuidle_driver r8a7740_cpuidle_driver = {
> +	.name			= "r8a7740_cpuidle",
> +	.owner			= THIS_MODULE,
> +	.en_core_tk_irqen	= 1,
> +	.state_count		= 3,
> +	.safe_state_index	= 0, /* C1 */
> +	.states[0] = ARM_CPUIDLE_WFI_STATE,
> +	.states[0].enter = shmobile_enter_wfi,

The WFI macro already set a simple enter function doing cpu_do_idle.

We are trying to consolidate the cpuidle drivers across the different
platforms [1]. One of the objective is to move the cpuidle drivers to
drivers/cpuidle, a place where they belong to.

Do you think it is possible you split your code to have one part with
the cpuidle driver and another part with the PM code ?

[1] http://www.spinics.net/lists/arm-kernel/msg237282.html
[2] https://patchwork.kernel.org/patch/2365041/

> +	.states[1] = {
> +		.name = "C2",
> +		.desc = "A3SM PLL ON",
> +		.exit_latency = 40,
> +		.target_residency = 30 + 40,
> +		.flags = CPUIDLE_FLAG_TIME_VALID,
> +		.enter = r8a7740_enter_a3sm_pll_on,
> +	},
> +	.states[2] = {
> +		.name = "C3",
> +		.desc = "A3SM PLL OFF",
> +		.exit_latency = 120,
> +		.target_residency = 30 + 120,
> +		.flags = CPUIDLE_FLAG_TIME_VALID,
> +		.enter = r8a7740_enter_a3sm_pll_off,
> +	},
> +};
> +
> +static void r8a7740_cpuidle_init(void)
> +{
> +	shmobile_cpuidle_set_driver(&r8a7740_cpuidle_driver);
> +}
> +#else
> +static void r8a7740_cpuidle_init(void) {}
> +#endif /* CONFIG_CPU_IDLE */
> +
>  #ifdef CONFIG_SUSPEND
>  static int r8a7740_enter_suspend(suspend_state_t suspend_state)
>  {
> +	r8a7740_enter_a3sm_common(0);
>  	cpu_do_idle();
>  	return 0;
>  }
> @@ -74,9 +289,10 @@ static void r8a7740_suspend_init(void)
>  }
>  #else
>  static void r8a7740_suspend_init(void) {}
> -#endif
> +#endif /* CONFIG_SUSPEND */
>  
>  void __init r8a7740_pm_init(void)
>  {
>  	r8a7740_suspend_init();
> +	r8a7740_cpuidle_init();
>  }
> diff --git a/arch/arm/mach-shmobile/sleep-r8a7740.S b/arch/arm/mach-shmobile/sleep-r8a7740.S
> new file mode 100644
> index 0000000..0ae310d
> --- /dev/null
> +++ b/arch/arm/mach-shmobile/sleep-r8a7740.S
> @@ -0,0 +1,68 @@
> +/*
> + * Low level sleep code for the SoC r8a7740
> + *
> + * Copyright (C) 2013 Bastian Hecht
> + *
> + * This file is subject to the terms and conditions of the GNU General Public
> + * License.  See the file "COPYING" in the main directory of this archive
> + * for more details.
> + */
> +
> +#include <linux/linkage.h>
> +#include <linux/init.h>
> +#include <asm/memory.h>
> +
> +#if defined(CONFIG_SUSPEND) || defined(CONFIG_CPU_IDLE)
> +
> +/* r8a7740_shutdown expects L1 and L2 to be flushed */
> +	.text
> +ENTRY(r8a7740_shutdown)
> +	stmfd   sp!, {r4-r12, lr}
> +
> +	/* make sure the stack stays intact */
> +	bl      v7_flush_dcache_all
> +
> +	/*
> +	 * Clear the SCTLR.C bit to prevent further data cache
> +	 * allocation. Clearing SCTLR.C would make all the data accesses
> +	 * strongly ordered and would not hit the cache.
> +	 */
> +	mrc	p15, 0, r0, c1, c0, 0
> +	bic	r0, r0, #(1 << 2)		@ Disable the C bit
> +	mcr	p15, 0, r0, c1, c0, 0
> +	isb
> +
> +	bl      v7_flush_dcache_all
> +	bl	cpu_v7_do_idle
> +
> +	/* in rare cases when WFI fails we end up here and restore things */
> +	mrc	p15, 0, r0, c1, c0, 0
> +	orr	r0, r0, #(1 << 2)		@ Enable the C bit
> +	mcr	p15, 0, r0, c1, c0, 0
> +	isb
> +
> +	ldmfd   sp!, {r4-r12, pc}
> +ENDPROC(r8a7740)
> +
> +	.text
> +ENTRY(v7_cpu_resume)
> +	bl	v7_invalidate_l1
> +	b	cpu_resume
> +ENDPROC(v7_cpu_resume)
> +
> +/*
> + * The entry point of a warm reboot, used by wakeup scenarios
> + *
> + * The CPU jumps in this case to (0xfffff000 & SBAR), so we need
> + * to align this function properly.
> + * We use a long jump into the text segment and use the physical
> + * address as the MMU is still turned off.
> + */
> +	.align	12
> +	.text
> +ENTRY(r8a7740_resume)
> +	ldr	pc, 1f
> +1:	.long	v7_cpu_resume - PAGE_OFFSET + PLAT_PHYS_OFFSET
> +ENDPROC(r8a7740_resume_core_standby)
> +#endif
>
Bastian Hecht April 11, 2013, 7:07 a.m. UTC | #2
Hello Daniel,

thanks for the consolidation efforts.

2013/4/11 Daniel Lezcano <daniel.lezcano@linaro.org>:
> On 04/10/2013 05:51 PM, Bastian Hecht wrote:
>> We add 2 sleep modes and register a CPUIdle driver that uses them.
>>
>> - A3SM PLL0 on/off:     Power domain A3SM that contains the ARM core
>>                         and the 2nd level cache with either PLL0 on
>>                       or off
>>
>> As the suspend to memory mechanism we use A3SM PLL off.
>>
>> The setup of the SYSC regarding the external IRQs is taken from
>> pm-sh7372.c from Magnus Damm.
>>
>> Signed-off-by: Bastian Hecht <hechtb+renesas@gmail.com>
>> ---
>> Ok I need to add some remarks here:
>>
>> First this patch relies on a fix I've sent out today:
>> "ARM: hw_breakpoint: Do not use __cpuinitdata for dbg_cpu_pm_nb"
>> Without it we can't use suspend at all without a kernel panic.
>>
>> Next you can test this in two ways:
>> Either add IRQF_NO_SUSPEND to the touchscreen driver st1232 at IRQ registration
>> (hack) or extend the irqpin driver to support wakeup devices properly, meaning
>> adding suspend and resume callbacks that handle it (proper way). Or you can add
>> no_console_suspend to the boot options in r8a7740-armadillo800eva.dts. This way
>> you can use the serial console as wakeup device.
>>
>>
>>  arch/arm/mach-shmobile/Makefile               |    2 +-
>>  arch/arm/mach-shmobile/include/mach/r8a7740.h |    2 +
>>  arch/arm/mach-shmobile/pm-r8a7740.c           |  220 ++++++++++++++++++++++++-
>>  arch/arm/mach-shmobile/sleep-r8a7740.S        |   68 ++++++++
>>  4 files changed, 289 insertions(+), 3 deletions(-)
>>  create mode 100644 arch/arm/mach-shmobile/sleep-r8a7740.S
>>
>> diff --git a/arch/arm/mach-shmobile/Makefile b/arch/arm/mach-shmobile/Makefile
>> index 068f1da..0568894 100644
>> --- a/arch/arm/mach-shmobile/Makefile
>> +++ b/arch/arm/mach-shmobile/Makefile
>> @@ -30,7 +30,7 @@ obj-$(CONFIG_SUSPEND)               += suspend.o
>>  obj-$(CONFIG_CPU_IDLE)               += cpuidle.o
>>  obj-$(CONFIG_ARCH_SHMOBILE)  += pm-rmobile.o
>>  obj-$(CONFIG_ARCH_SH7372)    += pm-sh7372.o sleep-sh7372.o
>> -obj-$(CONFIG_ARCH_R8A7740)   += pm-r8a7740.o
>> +obj-$(CONFIG_ARCH_R8A7740)   += pm-r8a7740.o sleep-r8a7740.o
>>  obj-$(CONFIG_ARCH_R8A7779)   += pm-r8a7779.o
>>  obj-$(CONFIG_ARCH_SH73A0)    += pm-sh73a0.o
>>
>> diff --git a/arch/arm/mach-shmobile/include/mach/r8a7740.h b/arch/arm/mach-shmobile/include/mach/r8a7740.h
>> index abdc4d4..ba4707b 100644
>> --- a/arch/arm/mach-shmobile/include/mach/r8a7740.h
>> +++ b/arch/arm/mach-shmobile/include/mach/r8a7740.h
>> @@ -540,6 +540,8 @@ extern void r8a7740_add_standard_devices(void);
>>  extern void r8a7740_clock_init(u8 md_ck);
>>  extern void r8a7740_pinmux_init(void);
>>  extern void r8a7740_pm_init(void);
>> +extern void r8a7740_resume(void);
>> +extern void r8a7740_shutdown(void);
>>
>>  #ifdef CONFIG_PM
>>  extern void __init r8a7740_init_pm_domains(void);
>> diff --git a/arch/arm/mach-shmobile/pm-r8a7740.c b/arch/arm/mach-shmobile/pm-r8a7740.c
>> index 40b87aa..d4613f0 100644
>> --- a/arch/arm/mach-shmobile/pm-r8a7740.c
>> +++ b/arch/arm/mach-shmobile/pm-r8a7740.c
>> @@ -8,10 +8,54 @@
>>   * License.  See the file "COPYING" in the main directory of this archive
>>   * for more details.
>>   */
>> +#include <linux/bitrev.h>
>>  #include <linux/console.h>
>> +#include <linux/cpuidle.h>
>> +#include <linux/module.h>
>>  #include <linux/suspend.h>
>> +#include <linux/io.h>
>> +#include <asm/suspend.h>
>> +#include <asm/cacheflush.h>
>> +#include <asm/cpuidle.h>
>> +#include <asm/hardware/cache-l2x0.h>
>>  #include <mach/pm-rmobile.h>
>>  #include <mach/common.h>
>> +#include <mach/r8a7740.h>
>> +
>> +/* CPGA */
>> +#define PLLC01STPCR  IOMEM(0xe61500c8)
>> +#define SYSTBCR              IOMEM(0xe6150024)
>> +
>> +/* SYSC */
>> +#define STBCHR               IOMEM(0xe6180000)
>> +#define STBCHRB              IOMEM(0xe6180040)
>> +#define SPDCR                IOMEM(0xe6180008)
>> +#define SBAR         IOMEM(0xe6180020)
>> +#define SRSTFR               IOMEM(0xe61800B4)
>> +#define WUPSMSK              IOMEM(0xe618002c)
>> +#define WUPSMSK2     IOMEM(0xe6180048)
>> +#define WUPSFAC              IOMEM(0xe6180098)
>> +#define IRQCR                IOMEM(0xe618022c)
>> +#define IRQCR2               IOMEM(0xe6180238)
>> +#define IRQCR3               IOMEM(0xe6180244)
>> +#define IRQCR4               IOMEM(0xe6180248)
>> +
>> +/* SRSTFR flags */
>> +#define RAMST                (1 << 19)
>> +#define RCLNKA               (1 << 7)
>> +#define RCPRES               (1 << 5)
>> +#define RCWD1                (1 << 4)
>> +#define RPF          (1 << 0)
>> +
>> +/* INTC */
>> +#define ICR1A                IOMEM(0xe6900000)
>> +#define ICR2A                IOMEM(0xe6900004)
>> +#define ICR3A                IOMEM(0xe6900008)
>> +#define ICR4A                IOMEM(0xe690000c)
>> +#define INTMSK00A    IOMEM(0xe6900040)
>> +#define INTMSK10A    IOMEM(0xe6900044)
>> +#define INTMSK20A    IOMEM(0xe6900048)
>> +#define INTMSK30A    IOMEM(0xe690004c)
>>
>>  #ifdef CONFIG_PM
>>  static int r8a7740_pd_a4s_suspend(void)
>> @@ -58,12 +102,183 @@ void __init r8a7740_init_pm_domains(void)
>>       rmobile_init_domains(r8a7740_pm_domains, ARRAY_SIZE(r8a7740_pm_domains));
>>       pm_genpd_add_subdomain_names("A4S", "A3SP");
>>  }
>> -
>>  #endif /* CONFIG_PM */
>>
>> +#if defined(CONFIG_SUSPEND) || defined(CONFIG_CPU_IDLE)
>> +static void r8a7740_set_reset_vector(unsigned long address)
>> +{
>> +     __raw_writel(address, SBAR);
>> +}
>> +
>> +static void r8a7740_icr_to_irqcr(unsigned long icr, u16 *irqcr1p, u16 *irqcr2p)
>> +{
>> +     u16 tmp, irqcr1, irqcr2;
>> +     int k;
>> +
>> +     irqcr1 = 0;
>> +     irqcr2 = 0;
>> +
>> +     /* convert INTCA ICR register layout to SYSC IRQCR+IRQCR2 */
>> +     for (k = 0; k <= 7; k++) {
>> +             tmp = (icr >> ((7 - k) * 4)) & 0xf;
>> +             irqcr1 |= (tmp & 0x03) << (k * 2);
>> +             irqcr2 |= (tmp >> 2) << (k * 2);
>> +     }
>> +
>> +     *irqcr1p = irqcr1;
>> +     *irqcr2p = irqcr2;
>> +}
>> +
>> +static void r8a7740_setup_sysc(unsigned long msk, unsigned long msk2)
>> +{
>> +     u16 irqcrx_low, irqcrx_high, irqcry_low, irqcry_high;
>> +     unsigned long tmp;
>> +
>> +     /* read IRQ0A -> IRQ15A mask */
>> +     tmp = bitrev8(__raw_readb(INTMSK00A));
>> +     tmp |= bitrev8(__raw_readb(INTMSK10A)) << 8;
>> +
>> +     /* setup WUPSMSK from clocks and external IRQ mask */
>> +     msk = (~msk & 0xc030000f) | (tmp << 4);
>> +     __raw_writel(msk, WUPSMSK);
>> +
>> +     /* propage level/edge trigger for external IRQ 0->15 */
>> +     r8a7740_icr_to_irqcr(__raw_readl(ICR1A), &irqcrx_low, &irqcry_low);
>> +     r8a7740_icr_to_irqcr(__raw_readl(ICR2A), &irqcrx_high, &irqcry_high);
>> +     __raw_writel((irqcrx_high << 16) | irqcrx_low, IRQCR);
>> +     __raw_writel((irqcry_high << 16) | irqcry_low, IRQCR2);
>> +
>> +     /* read IRQ16A -> IRQ31A mask */
>> +     tmp = bitrev8(__raw_readb(INTMSK20A));
>> +     tmp |= bitrev8(__raw_readb(INTMSK30A)) << 8;
>> +
>> +     /* setup WUPSMSK2 from clocks and external IRQ mask */
>> +     msk2 = (~msk2 & 0x00030000) | tmp;
>> +     __raw_writel(msk2, WUPSMSK2);
>> +
>> +     /* propage level/edge trigger for external IRQ 16->31 */
>> +     r8a7740_icr_to_irqcr(__raw_readl(ICR3A), &irqcrx_low, &irqcry_low);
>> +     r8a7740_icr_to_irqcr(__raw_readl(ICR4A), &irqcrx_high, &irqcry_high);
>> +     __raw_writel((irqcrx_high << 16) | irqcrx_low, IRQCR3);
>> +     __raw_writel((irqcry_high << 16) | irqcry_low, IRQCR4);
>> +}
>> +
>> +static void r8a7740_prepare_wakeup(void)
>> +{
>> +     /* clear all flags that lead to a cold boot */
>> +     __raw_writel(~(RAMST | RCLNKA | RCPRES | RCWD1 | RPF), SRSTFR);
>> +     /* indicate warm boot */
>> +     __raw_writel(0x80000000, STBCHRB);
>> +     /* clear other flags checked by internal ROM boot loader */
>> +     __raw_writel(0x00000000, STBCHR);
>> +}
>> +
>> +static int r8a7740_do_suspend(unsigned long unused)
>> +{
>> +     /*
>> +      * cpu_suspend() guarantees that all data made it to the L2.
>> +      * Flush it out now and disable the cache controller.
>> +      */
>> +     outer_flush_all();
>> +     outer_disable();
>> +
>> +     r8a7740_shutdown();
>> +
>> +     /* in case WFI fails to enter the low power state, restore things */
>> +     outer_resume();
>> +
>> +     return 0;
>> +}
>> +
>> +static void r8a7740_enter_a3sm_common(int pllc0_on)
>> +{
>> +     u32 reg32;
>> +
>> +     if (pllc0_on)
>> +             __raw_writel(0, PLLC01STPCR);
>> +     else
>> +             __raw_writel(1 << 28, PLLC01STPCR);
>> +
>> +     r8a7740_set_reset_vector(__pa(r8a7740_resume));
>> +     r8a7740_prepare_wakeup();
>> +     r8a7740_setup_sysc(1 << 0, 0);
>> +
>> +     /* Activate delayed shutdown of A3SM */
>> +     reg32 = __raw_readl(SPDCR);
>> +     reg32 |= (1 << 31) | (1 << 12);
>> +     __raw_writel(reg32, SPDCR);
>> +
>> +     /* We activate CPU Core Standby as well here */
>> +     reg32 = __raw_readl(SYSTBCR);
>> +     reg32 |= (1 << 4);
>> +     __raw_writel(reg32, SYSTBCR);
>> +
>> +     /* Clear Wakeup Factors and do suspend */
>> +     reg32 = __raw_readl(WUPSFAC);
>> +     cpu_suspend(0, r8a7740_do_suspend);
>> +     outer_resume();
>> +     reg32 = __raw_readl(WUPSFAC);
>> +
>> +     /* Clear CPU Core Standby flag for other WFI instructions */
>> +     reg32 &= ~(1 << 4);
>> +     __raw_writel(reg32, SYSTBCR);
>> +
>> +}
>> +#endif /* CONFIG_SUSPEND || CONFIG_CPU_IDLE */
>> +
>> +#ifdef CONFIG_CPU_IDLE
>> +static int r8a7740_enter_a3sm_pll_on(struct cpuidle_device *dev,
>> +                                     struct cpuidle_driver *drv, int index)
>> +{
>> +     r8a7740_enter_a3sm_common(1);
>> +     return 1;
>> +}
>> +
>> +static int r8a7740_enter_a3sm_pll_off(struct cpuidle_device *dev,
>> +                                     struct cpuidle_driver *drv, int index)
>> +{
>> +     r8a7740_enter_a3sm_common(0);
>> +     return 2;
>> +}
>> +
>> +static struct cpuidle_driver r8a7740_cpuidle_driver = {
>> +     .name                   = "r8a7740_cpuidle",
>> +     .owner                  = THIS_MODULE,
>> +     .en_core_tk_irqen       = 1,
>> +     .state_count            = 3,
>> +     .safe_state_index       = 0, /* C1 */
>> +     .states[0] = ARM_CPUIDLE_WFI_STATE,
>> +     .states[0].enter = shmobile_enter_wfi,
>
> The WFI macro already set a simple enter function doing cpu_do_idle.

Oh yes, I'll remove it.

> We are trying to consolidate the cpuidle drivers across the different
> platforms [1]. One of the objective is to move the cpuidle drivers to
> drivers/cpuidle, a place where they belong to.
>
> Do you think it is possible you split your code to have one part with
> the cpuidle driver and another part with the PM code ?

Hmm. The CPUIdle part is enclosed in the #ifdef CONFIG_CPU_IDLE part anyway.
The other part (CONFIG_SUSPEND | CONFIG_CPU_IDLE) contains the part
that actually does drive down the hardware and is needed as soon as
SUSPEND or CPUIdle is used.

So is it split well enough already or does it help if I split my patch
into 2, first adding suspend and then CPUIdle to the same file? But
the outcome will be the same I think.

Cheers,

 Bastian
Daniel Lezcano April 11, 2013, 9:07 a.m. UTC | #3
On 04/11/2013 09:07 AM, Bastian Hecht wrote:
> Hello Daniel,
> 
> thanks for the consolidation efforts.
> 
> 2013/4/11 Daniel Lezcano <daniel.lezcano@linaro.org>:
>> On 04/10/2013 05:51 PM, Bastian Hecht wrote:
>>> We add 2 sleep modes and register a CPUIdle driver that uses them.
>>>
>>> - A3SM PLL0 on/off:     Power domain A3SM that contains the ARM core
>>>                         and the 2nd level cache with either PLL0 on
>>>                       or off
>>>
>>> As the suspend to memory mechanism we use A3SM PLL off.
>>>
>>> The setup of the SYSC regarding the external IRQs is taken from
>>> pm-sh7372.c from Magnus Damm.
>>>
>>> Signed-off-by: Bastian Hecht <hechtb+renesas@gmail.com>
>>> ---
>>> Ok I need to add some remarks here:
>>>
>>> First this patch relies on a fix I've sent out today:
>>> "ARM: hw_breakpoint: Do not use __cpuinitdata for dbg_cpu_pm_nb"
>>> Without it we can't use suspend at all without a kernel panic.
>>>
>>> Next you can test this in two ways:
>>> Either add IRQF_NO_SUSPEND to the touchscreen driver st1232 at IRQ registration
>>> (hack) or extend the irqpin driver to support wakeup devices properly, meaning
>>> adding suspend and resume callbacks that handle it (proper way). Or you can add
>>> no_console_suspend to the boot options in r8a7740-armadillo800eva.dts. This way
>>> you can use the serial console as wakeup device.
>>>
>>>
>>>  arch/arm/mach-shmobile/Makefile               |    2 +-
>>>  arch/arm/mach-shmobile/include/mach/r8a7740.h |    2 +
>>>  arch/arm/mach-shmobile/pm-r8a7740.c           |  220 ++++++++++++++++++++++++-
>>>  arch/arm/mach-shmobile/sleep-r8a7740.S        |   68 ++++++++
>>>  4 files changed, 289 insertions(+), 3 deletions(-)
>>>  create mode 100644 arch/arm/mach-shmobile/sleep-r8a7740.S
>>>
>>> diff --git a/arch/arm/mach-shmobile/Makefile b/arch/arm/mach-shmobile/Makefile
>>> index 068f1da..0568894 100644
>>> --- a/arch/arm/mach-shmobile/Makefile
>>> +++ b/arch/arm/mach-shmobile/Makefile
>>> @@ -30,7 +30,7 @@ obj-$(CONFIG_SUSPEND)               += suspend.o
>>>  obj-$(CONFIG_CPU_IDLE)               += cpuidle.o
>>>  obj-$(CONFIG_ARCH_SHMOBILE)  += pm-rmobile.o
>>>  obj-$(CONFIG_ARCH_SH7372)    += pm-sh7372.o sleep-sh7372.o
>>> -obj-$(CONFIG_ARCH_R8A7740)   += pm-r8a7740.o
>>> +obj-$(CONFIG_ARCH_R8A7740)   += pm-r8a7740.o sleep-r8a7740.o
>>>  obj-$(CONFIG_ARCH_R8A7779)   += pm-r8a7779.o
>>>  obj-$(CONFIG_ARCH_SH73A0)    += pm-sh73a0.o
>>>
>>> diff --git a/arch/arm/mach-shmobile/include/mach/r8a7740.h b/arch/arm/mach-shmobile/include/mach/r8a7740.h
>>> index abdc4d4..ba4707b 100644
>>> --- a/arch/arm/mach-shmobile/include/mach/r8a7740.h
>>> +++ b/arch/arm/mach-shmobile/include/mach/r8a7740.h
>>> @@ -540,6 +540,8 @@ extern void r8a7740_add_standard_devices(void);
>>>  extern void r8a7740_clock_init(u8 md_ck);
>>>  extern void r8a7740_pinmux_init(void);
>>>  extern void r8a7740_pm_init(void);
>>> +extern void r8a7740_resume(void);
>>> +extern void r8a7740_shutdown(void);
>>>
>>>  #ifdef CONFIG_PM
>>>  extern void __init r8a7740_init_pm_domains(void);
>>> diff --git a/arch/arm/mach-shmobile/pm-r8a7740.c b/arch/arm/mach-shmobile/pm-r8a7740.c
>>> index 40b87aa..d4613f0 100644
>>> --- a/arch/arm/mach-shmobile/pm-r8a7740.c
>>> +++ b/arch/arm/mach-shmobile/pm-r8a7740.c
>>> @@ -8,10 +8,54 @@
>>>   * License.  See the file "COPYING" in the main directory of this archive
>>>   * for more details.
>>>   */
>>> +#include <linux/bitrev.h>
>>>  #include <linux/console.h>
>>> +#include <linux/cpuidle.h>
>>> +#include <linux/module.h>
>>>  #include <linux/suspend.h>
>>> +#include <linux/io.h>
>>> +#include <asm/suspend.h>
>>> +#include <asm/cacheflush.h>
>>> +#include <asm/cpuidle.h>
>>> +#include <asm/hardware/cache-l2x0.h>
>>>  #include <mach/pm-rmobile.h>
>>>  #include <mach/common.h>
>>> +#include <mach/r8a7740.h>
>>> +
>>> +/* CPGA */
>>> +#define PLLC01STPCR  IOMEM(0xe61500c8)
>>> +#define SYSTBCR              IOMEM(0xe6150024)
>>> +
>>> +/* SYSC */
>>> +#define STBCHR               IOMEM(0xe6180000)
>>> +#define STBCHRB              IOMEM(0xe6180040)
>>> +#define SPDCR                IOMEM(0xe6180008)
>>> +#define SBAR         IOMEM(0xe6180020)
>>> +#define SRSTFR               IOMEM(0xe61800B4)
>>> +#define WUPSMSK              IOMEM(0xe618002c)
>>> +#define WUPSMSK2     IOMEM(0xe6180048)
>>> +#define WUPSFAC              IOMEM(0xe6180098)
>>> +#define IRQCR                IOMEM(0xe618022c)
>>> +#define IRQCR2               IOMEM(0xe6180238)
>>> +#define IRQCR3               IOMEM(0xe6180244)
>>> +#define IRQCR4               IOMEM(0xe6180248)
>>> +
>>> +/* SRSTFR flags */
>>> +#define RAMST                (1 << 19)
>>> +#define RCLNKA               (1 << 7)
>>> +#define RCPRES               (1 << 5)
>>> +#define RCWD1                (1 << 4)
>>> +#define RPF          (1 << 0)
>>> +
>>> +/* INTC */
>>> +#define ICR1A                IOMEM(0xe6900000)
>>> +#define ICR2A                IOMEM(0xe6900004)
>>> +#define ICR3A                IOMEM(0xe6900008)
>>> +#define ICR4A                IOMEM(0xe690000c)
>>> +#define INTMSK00A    IOMEM(0xe6900040)
>>> +#define INTMSK10A    IOMEM(0xe6900044)
>>> +#define INTMSK20A    IOMEM(0xe6900048)
>>> +#define INTMSK30A    IOMEM(0xe690004c)
>>>
>>>  #ifdef CONFIG_PM
>>>  static int r8a7740_pd_a4s_suspend(void)
>>> @@ -58,12 +102,183 @@ void __init r8a7740_init_pm_domains(void)
>>>       rmobile_init_domains(r8a7740_pm_domains, ARRAY_SIZE(r8a7740_pm_domains));
>>>       pm_genpd_add_subdomain_names("A4S", "A3SP");
>>>  }
>>> -
>>>  #endif /* CONFIG_PM */
>>>
>>> +#if defined(CONFIG_SUSPEND) || defined(CONFIG_CPU_IDLE)
>>> +static void r8a7740_set_reset_vector(unsigned long address)
>>> +{
>>> +     __raw_writel(address, SBAR);
>>> +}
>>> +
>>> +static void r8a7740_icr_to_irqcr(unsigned long icr, u16 *irqcr1p, u16 *irqcr2p)
>>> +{
>>> +     u16 tmp, irqcr1, irqcr2;
>>> +     int k;
>>> +
>>> +     irqcr1 = 0;
>>> +     irqcr2 = 0;
>>> +
>>> +     /* convert INTCA ICR register layout to SYSC IRQCR+IRQCR2 */
>>> +     for (k = 0; k <= 7; k++) {
>>> +             tmp = (icr >> ((7 - k) * 4)) & 0xf;
>>> +             irqcr1 |= (tmp & 0x03) << (k * 2);
>>> +             irqcr2 |= (tmp >> 2) << (k * 2);
>>> +     }
>>> +
>>> +     *irqcr1p = irqcr1;
>>> +     *irqcr2p = irqcr2;
>>> +}
>>> +
>>> +static void r8a7740_setup_sysc(unsigned long msk, unsigned long msk2)
>>> +{
>>> +     u16 irqcrx_low, irqcrx_high, irqcry_low, irqcry_high;
>>> +     unsigned long tmp;
>>> +
>>> +     /* read IRQ0A -> IRQ15A mask */
>>> +     tmp = bitrev8(__raw_readb(INTMSK00A));
>>> +     tmp |= bitrev8(__raw_readb(INTMSK10A)) << 8;
>>> +
>>> +     /* setup WUPSMSK from clocks and external IRQ mask */
>>> +     msk = (~msk & 0xc030000f) | (tmp << 4);
>>> +     __raw_writel(msk, WUPSMSK);
>>> +
>>> +     /* propage level/edge trigger for external IRQ 0->15 */
>>> +     r8a7740_icr_to_irqcr(__raw_readl(ICR1A), &irqcrx_low, &irqcry_low);
>>> +     r8a7740_icr_to_irqcr(__raw_readl(ICR2A), &irqcrx_high, &irqcry_high);
>>> +     __raw_writel((irqcrx_high << 16) | irqcrx_low, IRQCR);
>>> +     __raw_writel((irqcry_high << 16) | irqcry_low, IRQCR2);
>>> +
>>> +     /* read IRQ16A -> IRQ31A mask */
>>> +     tmp = bitrev8(__raw_readb(INTMSK20A));
>>> +     tmp |= bitrev8(__raw_readb(INTMSK30A)) << 8;
>>> +
>>> +     /* setup WUPSMSK2 from clocks and external IRQ mask */
>>> +     msk2 = (~msk2 & 0x00030000) | tmp;
>>> +     __raw_writel(msk2, WUPSMSK2);
>>> +
>>> +     /* propage level/edge trigger for external IRQ 16->31 */
>>> +     r8a7740_icr_to_irqcr(__raw_readl(ICR3A), &irqcrx_low, &irqcry_low);
>>> +     r8a7740_icr_to_irqcr(__raw_readl(ICR4A), &irqcrx_high, &irqcry_high);
>>> +     __raw_writel((irqcrx_high << 16) | irqcrx_low, IRQCR3);
>>> +     __raw_writel((irqcry_high << 16) | irqcry_low, IRQCR4);
>>> +}
>>> +
>>> +static void r8a7740_prepare_wakeup(void)
>>> +{
>>> +     /* clear all flags that lead to a cold boot */
>>> +     __raw_writel(~(RAMST | RCLNKA | RCPRES | RCWD1 | RPF), SRSTFR);
>>> +     /* indicate warm boot */
>>> +     __raw_writel(0x80000000, STBCHRB);
>>> +     /* clear other flags checked by internal ROM boot loader */
>>> +     __raw_writel(0x00000000, STBCHR);
>>> +}
>>> +
>>> +static int r8a7740_do_suspend(unsigned long unused)
>>> +{
>>> +     /*
>>> +      * cpu_suspend() guarantees that all data made it to the L2.
>>> +      * Flush it out now and disable the cache controller.
>>> +      */
>>> +     outer_flush_all();
>>> +     outer_disable();
>>> +
>>> +     r8a7740_shutdown();
>>> +
>>> +     /* in case WFI fails to enter the low power state, restore things */
>>> +     outer_resume();
>>> +
>>> +     return 0;
>>> +}
>>> +
>>> +static void r8a7740_enter_a3sm_common(int pllc0_on)
>>> +{
>>> +     u32 reg32;
>>> +
>>> +     if (pllc0_on)
>>> +             __raw_writel(0, PLLC01STPCR);
>>> +     else
>>> +             __raw_writel(1 << 28, PLLC01STPCR);
>>> +
>>> +     r8a7740_set_reset_vector(__pa(r8a7740_resume));
>>> +     r8a7740_prepare_wakeup();
>>> +     r8a7740_setup_sysc(1 << 0, 0);
>>> +
>>> +     /* Activate delayed shutdown of A3SM */
>>> +     reg32 = __raw_readl(SPDCR);
>>> +     reg32 |= (1 << 31) | (1 << 12);
>>> +     __raw_writel(reg32, SPDCR);
>>> +
>>> +     /* We activate CPU Core Standby as well here */
>>> +     reg32 = __raw_readl(SYSTBCR);
>>> +     reg32 |= (1 << 4);
>>> +     __raw_writel(reg32, SYSTBCR);
>>> +
>>> +     /* Clear Wakeup Factors and do suspend */
>>> +     reg32 = __raw_readl(WUPSFAC);
>>> +     cpu_suspend(0, r8a7740_do_suspend);
>>> +     outer_resume();
>>> +     reg32 = __raw_readl(WUPSFAC);
>>> +
>>> +     /* Clear CPU Core Standby flag for other WFI instructions */
>>> +     reg32 &= ~(1 << 4);
>>> +     __raw_writel(reg32, SYSTBCR);
>>> +
>>> +}
>>> +#endif /* CONFIG_SUSPEND || CONFIG_CPU_IDLE */
>>> +
>>> +#ifdef CONFIG_CPU_IDLE
>>> +static int r8a7740_enter_a3sm_pll_on(struct cpuidle_device *dev,
>>> +                                     struct cpuidle_driver *drv, int index)
>>> +{
>>> +     r8a7740_enter_a3sm_common(1);
>>> +     return 1;
>>> +}
>>> +
>>> +static int r8a7740_enter_a3sm_pll_off(struct cpuidle_device *dev,
>>> +                                     struct cpuidle_driver *drv, int index)
>>> +{
>>> +     r8a7740_enter_a3sm_common(0);
>>> +     return 2;
>>> +}
>>> +
>>> +static struct cpuidle_driver r8a7740_cpuidle_driver = {
>>> +     .name                   = "r8a7740_cpuidle",
>>> +     .owner                  = THIS_MODULE,
>>> +     .en_core_tk_irqen       = 1,
>>> +     .state_count            = 3,
>>> +     .safe_state_index       = 0, /* C1 */
>>> +     .states[0] = ARM_CPUIDLE_WFI_STATE,
>>> +     .states[0].enter = shmobile_enter_wfi,
>>
>> The WFI macro already set a simple enter function doing cpu_do_idle.
> 
> Oh yes, I'll remove it.
> 
>> We are trying to consolidate the cpuidle drivers across the different
>> platforms [1]. One of the objective is to move the cpuidle drivers to
>> drivers/cpuidle, a place where they belong to.
>>
>> Do you think it is possible you split your code to have one part with
>> the cpuidle driver and another part with the PM code ?
> 
> Hmm. The CPUIdle part is enclosed in the #ifdef CONFIG_CPU_IDLE part anyway.
> The other part (CONFIG_SUSPEND | CONFIG_CPU_IDLE) contains the part
> that actually does drive down the hardware and is needed as soon as
> SUSPEND or CPUIdle is used.
> 
> So is it split well enough already or does it help if I split my patch
> into 2, first adding suspend and then CPUIdle to the same file? But
> the outcome will be the same I think.


Well, if you can convert the pm functions into ops used in the cpuidle
driver and then create a file eg. cpuidle-r8a7740.c with the cpuidle
code calling the ops that will separate clearly the cpuidle code from
the pm code and make easier to move the cpuidle driver to the
drivers/cpuidle directory. Also, I think that will help to consolidate
the other shmobile cpuidle drivers, maybe into a single one.
Bastian Hecht April 11, 2013, 12:06 p.m. UTC | #4
Hi Daniel,

>>>> +
>>>> +static struct cpuidle_driver r8a7740_cpuidle_driver = {
>>>> +     .name                   = "r8a7740_cpuidle",
>>>> +     .owner                  = THIS_MODULE,
>>>> +     .en_core_tk_irqen       = 1,
>>>> +     .state_count            = 3,
>>>> +     .safe_state_index       = 0, /* C1 */
>>>> +     .states[0] = ARM_CPUIDLE_WFI_STATE,
>>>> +     .states[0].enter = shmobile_enter_wfi,
>>>
>>> The WFI macro already set a simple enter function doing cpu_do_idle.
>>
>> Oh yes, I'll remove it.
>>
>>> We are trying to consolidate the cpuidle drivers across the different
>>> platforms [1]. One of the objective is to move the cpuidle drivers to
>>> drivers/cpuidle, a place where they belong to.
>>>
>>> Do you think it is possible you split your code to have one part with
>>> the cpuidle driver and another part with the PM code ?
>>
>> Hmm. The CPUIdle part is enclosed in the #ifdef CONFIG_CPU_IDLE part anyway.
>> The other part (CONFIG_SUSPEND | CONFIG_CPU_IDLE) contains the part
>> that actually does drive down the hardware and is needed as soon as
>> SUSPEND or CPUIdle is used.
>>
>> So is it split well enough already or does it help if I split my patch
>> into 2, first adding suspend and then CPUIdle to the same file? But
>> the outcome will be the same I think.
>
>
> Well, if you can convert the pm functions into ops used in the cpuidle
> driver and then create a file eg. cpuidle-r8a7740.c with the cpuidle
> code calling the ops that will separate clearly the cpuidle code from
> the pm code and make easier to move the cpuidle driver to the
> drivers/cpuidle directory. Also, I think that will help to consolidate
> the other shmobile cpuidle drivers, maybe into a single one.
>
>

Ok I've prepared a new patchset and think the splitting looks good .
As this is a sensitive code area I want to retest it though and post
it in a couple of hours.

Cheers,

 Bastian
Bastian Hecht April 11, 2013, 2:09 p.m. UTC | #5
2013/4/11 Bastian Hecht <hechtb@gmail.com>:
> Hi Daniel,
>
>>>>> +
>>>>> +static struct cpuidle_driver r8a7740_cpuidle_driver = {
>>>>> +     .name                   = "r8a7740_cpuidle",
>>>>> +     .owner                  = THIS_MODULE,
>>>>> +     .en_core_tk_irqen       = 1,
>>>>> +     .state_count            = 3,
>>>>> +     .safe_state_index       = 0, /* C1 */
>>>>> +     .states[0] = ARM_CPUIDLE_WFI_STATE,
>>>>> +     .states[0].enter = shmobile_enter_wfi,
>>>>
>>>> The WFI macro already set a simple enter function doing cpu_do_idle.
>>>
>>> Oh yes, I'll remove it.
>>>
>>>> We are trying to consolidate the cpuidle drivers across the different
>>>> platforms [1]. One of the objective is to move the cpuidle drivers to
>>>> drivers/cpuidle, a place where they belong to.
>>>>
>>>> Do you think it is possible you split your code to have one part with
>>>> the cpuidle driver and another part with the PM code ?
>>>
>>> Hmm. The CPUIdle part is enclosed in the #ifdef CONFIG_CPU_IDLE part anyway.
>>> The other part (CONFIG_SUSPEND | CONFIG_CPU_IDLE) contains the part
>>> that actually does drive down the hardware and is needed as soon as
>>> SUSPEND or CPUIdle is used.
>>>
>>> So is it split well enough already or does it help if I split my patch
>>> into 2, first adding suspend and then CPUIdle to the same file? But
>>> the outcome will be the same I think.
>>
>>
>> Well, if you can convert the pm functions into ops used in the cpuidle
>> driver and then create a file eg. cpuidle-r8a7740.c with the cpuidle
>> code calling the ops that will separate clearly the cpuidle code from
>> the pm code and make easier to move the cpuidle driver to the
>> drivers/cpuidle directory. Also, I think that will help to consolidate
>> the other shmobile cpuidle drivers, maybe into a single one.
>>
>>
>
> Ok I've prepared a new patchset and think the splitting looks good .
> As this is a sensitive code area I want to retest it though and post
> it in a couple of hours.

Ok they are out and named:
[PATCH v2 1/2] ARM: shmobile: r8a7740: Add Suspend-To-RAM A3SM
[PATCH v2 2/2] ARM: shmobile: r8a7740: Add CPUIdle

> Cheers,
>
>  Bastian
Simon Horman April 12, 2013, 12:25 a.m. UTC | #6
On Thu, Apr 11, 2013 at 11:07:52AM +0200, Daniel Lezcano wrote:
> On 04/11/2013 09:07 AM, Bastian Hecht wrote:
> > Hello Daniel,
> > 
> > thanks for the consolidation efforts.
> > 
> > 2013/4/11 Daniel Lezcano <daniel.lezcano@linaro.org>:
> >> On 04/10/2013 05:51 PM, Bastian Hecht wrote:
> >>> We add 2 sleep modes and register a CPUIdle driver that uses them.
> >>>
> >>> - A3SM PLL0 on/off:     Power domain A3SM that contains the ARM core
> >>>                         and the 2nd level cache with either PLL0 on
> >>>                       or off
> >>>
> >>> As the suspend to memory mechanism we use A3SM PLL off.
> >>>
> >>> The setup of the SYSC regarding the external IRQs is taken from
> >>> pm-sh7372.c from Magnus Damm.
> >>>
> >>> Signed-off-by: Bastian Hecht <hechtb+renesas@gmail.com>
> >>> ---
> >>> Ok I need to add some remarks here:
> >>>
> >>> First this patch relies on a fix I've sent out today:
> >>> "ARM: hw_breakpoint: Do not use __cpuinitdata for dbg_cpu_pm_nb"
> >>> Without it we can't use suspend at all without a kernel panic.
> >>>
> >>> Next you can test this in two ways:
> >>> Either add IRQF_NO_SUSPEND to the touchscreen driver st1232 at IRQ registration
> >>> (hack) or extend the irqpin driver to support wakeup devices properly, meaning
> >>> adding suspend and resume callbacks that handle it (proper way). Or you can add
> >>> no_console_suspend to the boot options in r8a7740-armadillo800eva.dts. This way
> >>> you can use the serial console as wakeup device.
> >>>
> >>>
> >>>  arch/arm/mach-shmobile/Makefile               |    2 +-
> >>>  arch/arm/mach-shmobile/include/mach/r8a7740.h |    2 +
> >>>  arch/arm/mach-shmobile/pm-r8a7740.c           |  220 ++++++++++++++++++++++++-
> >>>  arch/arm/mach-shmobile/sleep-r8a7740.S        |   68 ++++++++
> >>>  4 files changed, 289 insertions(+), 3 deletions(-)
> >>>  create mode 100644 arch/arm/mach-shmobile/sleep-r8a7740.S
> >>>
> >>> diff --git a/arch/arm/mach-shmobile/Makefile b/arch/arm/mach-shmobile/Makefile
> >>> index 068f1da..0568894 100644
> >>> --- a/arch/arm/mach-shmobile/Makefile
> >>> +++ b/arch/arm/mach-shmobile/Makefile
> >>> @@ -30,7 +30,7 @@ obj-$(CONFIG_SUSPEND)               += suspend.o
> >>>  obj-$(CONFIG_CPU_IDLE)               += cpuidle.o
> >>>  obj-$(CONFIG_ARCH_SHMOBILE)  += pm-rmobile.o
> >>>  obj-$(CONFIG_ARCH_SH7372)    += pm-sh7372.o sleep-sh7372.o
> >>> -obj-$(CONFIG_ARCH_R8A7740)   += pm-r8a7740.o
> >>> +obj-$(CONFIG_ARCH_R8A7740)   += pm-r8a7740.o sleep-r8a7740.o
> >>>  obj-$(CONFIG_ARCH_R8A7779)   += pm-r8a7779.o
> >>>  obj-$(CONFIG_ARCH_SH73A0)    += pm-sh73a0.o
> >>>
> >>> diff --git a/arch/arm/mach-shmobile/include/mach/r8a7740.h b/arch/arm/mach-shmobile/include/mach/r8a7740.h
> >>> index abdc4d4..ba4707b 100644
> >>> --- a/arch/arm/mach-shmobile/include/mach/r8a7740.h
> >>> +++ b/arch/arm/mach-shmobile/include/mach/r8a7740.h
> >>> @@ -540,6 +540,8 @@ extern void r8a7740_add_standard_devices(void);
> >>>  extern void r8a7740_clock_init(u8 md_ck);
> >>>  extern void r8a7740_pinmux_init(void);
> >>>  extern void r8a7740_pm_init(void);
> >>> +extern void r8a7740_resume(void);
> >>> +extern void r8a7740_shutdown(void);
> >>>
> >>>  #ifdef CONFIG_PM
> >>>  extern void __init r8a7740_init_pm_domains(void);
> >>> diff --git a/arch/arm/mach-shmobile/pm-r8a7740.c b/arch/arm/mach-shmobile/pm-r8a7740.c
> >>> index 40b87aa..d4613f0 100644
> >>> --- a/arch/arm/mach-shmobile/pm-r8a7740.c
> >>> +++ b/arch/arm/mach-shmobile/pm-r8a7740.c
> >>> @@ -8,10 +8,54 @@
> >>>   * License.  See the file "COPYING" in the main directory of this archive
> >>>   * for more details.
> >>>   */
> >>> +#include <linux/bitrev.h>
> >>>  #include <linux/console.h>
> >>> +#include <linux/cpuidle.h>
> >>> +#include <linux/module.h>
> >>>  #include <linux/suspend.h>
> >>> +#include <linux/io.h>
> >>> +#include <asm/suspend.h>
> >>> +#include <asm/cacheflush.h>
> >>> +#include <asm/cpuidle.h>
> >>> +#include <asm/hardware/cache-l2x0.h>
> >>>  #include <mach/pm-rmobile.h>
> >>>  #include <mach/common.h>
> >>> +#include <mach/r8a7740.h>
> >>> +
> >>> +/* CPGA */
> >>> +#define PLLC01STPCR  IOMEM(0xe61500c8)
> >>> +#define SYSTBCR              IOMEM(0xe6150024)
> >>> +
> >>> +/* SYSC */
> >>> +#define STBCHR               IOMEM(0xe6180000)
> >>> +#define STBCHRB              IOMEM(0xe6180040)
> >>> +#define SPDCR                IOMEM(0xe6180008)
> >>> +#define SBAR         IOMEM(0xe6180020)
> >>> +#define SRSTFR               IOMEM(0xe61800B4)
> >>> +#define WUPSMSK              IOMEM(0xe618002c)
> >>> +#define WUPSMSK2     IOMEM(0xe6180048)
> >>> +#define WUPSFAC              IOMEM(0xe6180098)
> >>> +#define IRQCR                IOMEM(0xe618022c)
> >>> +#define IRQCR2               IOMEM(0xe6180238)
> >>> +#define IRQCR3               IOMEM(0xe6180244)
> >>> +#define IRQCR4               IOMEM(0xe6180248)
> >>> +
> >>> +/* SRSTFR flags */
> >>> +#define RAMST                (1 << 19)
> >>> +#define RCLNKA               (1 << 7)
> >>> +#define RCPRES               (1 << 5)
> >>> +#define RCWD1                (1 << 4)
> >>> +#define RPF          (1 << 0)
> >>> +
> >>> +/* INTC */
> >>> +#define ICR1A                IOMEM(0xe6900000)
> >>> +#define ICR2A                IOMEM(0xe6900004)
> >>> +#define ICR3A                IOMEM(0xe6900008)
> >>> +#define ICR4A                IOMEM(0xe690000c)
> >>> +#define INTMSK00A    IOMEM(0xe6900040)
> >>> +#define INTMSK10A    IOMEM(0xe6900044)
> >>> +#define INTMSK20A    IOMEM(0xe6900048)
> >>> +#define INTMSK30A    IOMEM(0xe690004c)
> >>>
> >>>  #ifdef CONFIG_PM
> >>>  static int r8a7740_pd_a4s_suspend(void)
> >>> @@ -58,12 +102,183 @@ void __init r8a7740_init_pm_domains(void)
> >>>       rmobile_init_domains(r8a7740_pm_domains, ARRAY_SIZE(r8a7740_pm_domains));
> >>>       pm_genpd_add_subdomain_names("A4S", "A3SP");
> >>>  }
> >>> -
> >>>  #endif /* CONFIG_PM */
> >>>
> >>> +#if defined(CONFIG_SUSPEND) || defined(CONFIG_CPU_IDLE)
> >>> +static void r8a7740_set_reset_vector(unsigned long address)
> >>> +{
> >>> +     __raw_writel(address, SBAR);
> >>> +}
> >>> +
> >>> +static void r8a7740_icr_to_irqcr(unsigned long icr, u16 *irqcr1p, u16 *irqcr2p)
> >>> +{
> >>> +     u16 tmp, irqcr1, irqcr2;
> >>> +     int k;
> >>> +
> >>> +     irqcr1 = 0;
> >>> +     irqcr2 = 0;
> >>> +
> >>> +     /* convert INTCA ICR register layout to SYSC IRQCR+IRQCR2 */
> >>> +     for (k = 0; k <= 7; k++) {
> >>> +             tmp = (icr >> ((7 - k) * 4)) & 0xf;
> >>> +             irqcr1 |= (tmp & 0x03) << (k * 2);
> >>> +             irqcr2 |= (tmp >> 2) << (k * 2);
> >>> +     }
> >>> +
> >>> +     *irqcr1p = irqcr1;
> >>> +     *irqcr2p = irqcr2;
> >>> +}
> >>> +
> >>> +static void r8a7740_setup_sysc(unsigned long msk, unsigned long msk2)
> >>> +{
> >>> +     u16 irqcrx_low, irqcrx_high, irqcry_low, irqcry_high;
> >>> +     unsigned long tmp;
> >>> +
> >>> +     /* read IRQ0A -> IRQ15A mask */
> >>> +     tmp = bitrev8(__raw_readb(INTMSK00A));
> >>> +     tmp |= bitrev8(__raw_readb(INTMSK10A)) << 8;
> >>> +
> >>> +     /* setup WUPSMSK from clocks and external IRQ mask */
> >>> +     msk = (~msk & 0xc030000f) | (tmp << 4);
> >>> +     __raw_writel(msk, WUPSMSK);
> >>> +
> >>> +     /* propage level/edge trigger for external IRQ 0->15 */
> >>> +     r8a7740_icr_to_irqcr(__raw_readl(ICR1A), &irqcrx_low, &irqcry_low);
> >>> +     r8a7740_icr_to_irqcr(__raw_readl(ICR2A), &irqcrx_high, &irqcry_high);
> >>> +     __raw_writel((irqcrx_high << 16) | irqcrx_low, IRQCR);
> >>> +     __raw_writel((irqcry_high << 16) | irqcry_low, IRQCR2);
> >>> +
> >>> +     /* read IRQ16A -> IRQ31A mask */
> >>> +     tmp = bitrev8(__raw_readb(INTMSK20A));
> >>> +     tmp |= bitrev8(__raw_readb(INTMSK30A)) << 8;
> >>> +
> >>> +     /* setup WUPSMSK2 from clocks and external IRQ mask */
> >>> +     msk2 = (~msk2 & 0x00030000) | tmp;
> >>> +     __raw_writel(msk2, WUPSMSK2);
> >>> +
> >>> +     /* propage level/edge trigger for external IRQ 16->31 */
> >>> +     r8a7740_icr_to_irqcr(__raw_readl(ICR3A), &irqcrx_low, &irqcry_low);
> >>> +     r8a7740_icr_to_irqcr(__raw_readl(ICR4A), &irqcrx_high, &irqcry_high);
> >>> +     __raw_writel((irqcrx_high << 16) | irqcrx_low, IRQCR3);
> >>> +     __raw_writel((irqcry_high << 16) | irqcry_low, IRQCR4);
> >>> +}
> >>> +
> >>> +static void r8a7740_prepare_wakeup(void)
> >>> +{
> >>> +     /* clear all flags that lead to a cold boot */
> >>> +     __raw_writel(~(RAMST | RCLNKA | RCPRES | RCWD1 | RPF), SRSTFR);
> >>> +     /* indicate warm boot */
> >>> +     __raw_writel(0x80000000, STBCHRB);
> >>> +     /* clear other flags checked by internal ROM boot loader */
> >>> +     __raw_writel(0x00000000, STBCHR);
> >>> +}
> >>> +
> >>> +static int r8a7740_do_suspend(unsigned long unused)
> >>> +{
> >>> +     /*
> >>> +      * cpu_suspend() guarantees that all data made it to the L2.
> >>> +      * Flush it out now and disable the cache controller.
> >>> +      */
> >>> +     outer_flush_all();
> >>> +     outer_disable();
> >>> +
> >>> +     r8a7740_shutdown();
> >>> +
> >>> +     /* in case WFI fails to enter the low power state, restore things */
> >>> +     outer_resume();
> >>> +
> >>> +     return 0;
> >>> +}
> >>> +
> >>> +static void r8a7740_enter_a3sm_common(int pllc0_on)
> >>> +{
> >>> +     u32 reg32;
> >>> +
> >>> +     if (pllc0_on)
> >>> +             __raw_writel(0, PLLC01STPCR);
> >>> +     else
> >>> +             __raw_writel(1 << 28, PLLC01STPCR);
> >>> +
> >>> +     r8a7740_set_reset_vector(__pa(r8a7740_resume));
> >>> +     r8a7740_prepare_wakeup();
> >>> +     r8a7740_setup_sysc(1 << 0, 0);
> >>> +
> >>> +     /* Activate delayed shutdown of A3SM */
> >>> +     reg32 = __raw_readl(SPDCR);
> >>> +     reg32 |= (1 << 31) | (1 << 12);
> >>> +     __raw_writel(reg32, SPDCR);
> >>> +
> >>> +     /* We activate CPU Core Standby as well here */
> >>> +     reg32 = __raw_readl(SYSTBCR);
> >>> +     reg32 |= (1 << 4);
> >>> +     __raw_writel(reg32, SYSTBCR);
> >>> +
> >>> +     /* Clear Wakeup Factors and do suspend */
> >>> +     reg32 = __raw_readl(WUPSFAC);
> >>> +     cpu_suspend(0, r8a7740_do_suspend);
> >>> +     outer_resume();
> >>> +     reg32 = __raw_readl(WUPSFAC);
> >>> +
> >>> +     /* Clear CPU Core Standby flag for other WFI instructions */
> >>> +     reg32 &= ~(1 << 4);
> >>> +     __raw_writel(reg32, SYSTBCR);
> >>> +
> >>> +}
> >>> +#endif /* CONFIG_SUSPEND || CONFIG_CPU_IDLE */
> >>> +
> >>> +#ifdef CONFIG_CPU_IDLE
> >>> +static int r8a7740_enter_a3sm_pll_on(struct cpuidle_device *dev,
> >>> +                                     struct cpuidle_driver *drv, int index)
> >>> +{
> >>> +     r8a7740_enter_a3sm_common(1);
> >>> +     return 1;
> >>> +}
> >>> +
> >>> +static int r8a7740_enter_a3sm_pll_off(struct cpuidle_device *dev,
> >>> +                                     struct cpuidle_driver *drv, int index)
> >>> +{
> >>> +     r8a7740_enter_a3sm_common(0);
> >>> +     return 2;
> >>> +}
> >>> +
> >>> +static struct cpuidle_driver r8a7740_cpuidle_driver = {
> >>> +     .name                   = "r8a7740_cpuidle",
> >>> +     .owner                  = THIS_MODULE,
> >>> +     .en_core_tk_irqen       = 1,
> >>> +     .state_count            = 3,
> >>> +     .safe_state_index       = 0, /* C1 */
> >>> +     .states[0] = ARM_CPUIDLE_WFI_STATE,
> >>> +     .states[0].enter = shmobile_enter_wfi,
> >>
> >> The WFI macro already set a simple enter function doing cpu_do_idle.
> > 
> > Oh yes, I'll remove it.
> > 
> >> We are trying to consolidate the cpuidle drivers across the different
> >> platforms [1]. One of the objective is to move the cpuidle drivers to
> >> drivers/cpuidle, a place where they belong to.
> >>
> >> Do you think it is possible you split your code to have one part with
> >> the cpuidle driver and another part with the PM code ?
> > 
> > Hmm. The CPUIdle part is enclosed in the #ifdef CONFIG_CPU_IDLE part anyway.
> > The other part (CONFIG_SUSPEND | CONFIG_CPU_IDLE) contains the part
> > that actually does drive down the hardware and is needed as soon as
> > SUSPEND or CPUIdle is used.
> > 
> > So is it split well enough already or does it help if I split my patch
> > into 2, first adding suspend and then CPUIdle to the same file? But
> > the outcome will be the same I think.
> 
> 
> Well, if you can convert the pm functions into ops used in the cpuidle
> driver and then create a file eg. cpuidle-r8a7740.c with the cpuidle
> code calling the ops that will separate clearly the cpuidle code from
> the pm code and make easier to move the cpuidle driver to the
> drivers/cpuidle directory. Also, I think that will help to consolidate
> the other shmobile cpuidle drivers, maybe into a single one.

I wonder if that is an appropriate pre-requisite for adding
r8a7940 cpuidle support. My preference, which may well be compatible
with your comments, is to bring support for that SoC up to the same level
as support for other shmobile SoCs and to treat consolidation as a
an area of work that is undertaken in parallel.
Bastian Hecht April 17, 2013, 12:06 p.m. UTC | #7
Hello Simon,

2013/4/12 Simon Horman <horms@verge.net.au>:
> On Thu, Apr 11, 2013 at 11:07:52AM +0200, Daniel Lezcano wrote:
>> On 04/11/2013 09:07 AM, Bastian Hecht wrote:
>> > Hello Daniel,
>> >
>> > thanks for the consolidation efforts.
>> >
>> > 2013/4/11 Daniel Lezcano <daniel.lezcano@linaro.org>:
>> >> On 04/10/2013 05:51 PM, Bastian Hecht wrote:
>> >>> We add 2 sleep modes and register a CPUIdle driver that uses them.
>> >>>
>> >>> - A3SM PLL0 on/off:     Power domain A3SM that contains the ARM core
>> >>>                         and the 2nd level cache with either PLL0 on
>> >>>                       or off
>> >>>
>> >>> As the suspend to memory mechanism we use A3SM PLL off.
>> >>>
>> >>> The setup of the SYSC regarding the external IRQs is taken from
>> >>> pm-sh7372.c from Magnus Damm.
>> >>>
>> >>> Signed-off-by: Bastian Hecht <hechtb+renesas@gmail.com>
>> >>> ---
>> >>> Ok I need to add some remarks here:
>> >>>
>> >>> First this patch relies on a fix I've sent out today:
>> >>> "ARM: hw_breakpoint: Do not use __cpuinitdata for dbg_cpu_pm_nb"
>> >>> Without it we can't use suspend at all without a kernel panic.
>> >>>
>> >>> Next you can test this in two ways:
>> >>> Either add IRQF_NO_SUSPEND to the touchscreen driver st1232 at IRQ registration
>> >>> (hack) or extend the irqpin driver to support wakeup devices properly, meaning
>> >>> adding suspend and resume callbacks that handle it (proper way). Or you can add
>> >>> no_console_suspend to the boot options in r8a7740-armadillo800eva.dts. This way
>> >>> you can use the serial console as wakeup device.
>> >>>
>> >>>
>> >>>  arch/arm/mach-shmobile/Makefile               |    2 +-
>> >>>  arch/arm/mach-shmobile/include/mach/r8a7740.h |    2 +
>> >>>  arch/arm/mach-shmobile/pm-r8a7740.c           |  220 ++++++++++++++++++++++++-
>> >>>  arch/arm/mach-shmobile/sleep-r8a7740.S        |   68 ++++++++
>> >>>  4 files changed, 289 insertions(+), 3 deletions(-)
>> >>>  create mode 100644 arch/arm/mach-shmobile/sleep-r8a7740.S
>> >>>
>> >>> diff --git a/arch/arm/mach-shmobile/Makefile b/arch/arm/mach-shmobile/Makefile
>> >>> index 068f1da..0568894 100644
>> >>> --- a/arch/arm/mach-shmobile/Makefile
>> >>> +++ b/arch/arm/mach-shmobile/Makefile
>> >>> @@ -30,7 +30,7 @@ obj-$(CONFIG_SUSPEND)               += suspend.o
>> >>>  obj-$(CONFIG_CPU_IDLE)               += cpuidle.o
>> >>>  obj-$(CONFIG_ARCH_SHMOBILE)  += pm-rmobile.o
>> >>>  obj-$(CONFIG_ARCH_SH7372)    += pm-sh7372.o sleep-sh7372.o
>> >>> -obj-$(CONFIG_ARCH_R8A7740)   += pm-r8a7740.o
>> >>> +obj-$(CONFIG_ARCH_R8A7740)   += pm-r8a7740.o sleep-r8a7740.o
>> >>>  obj-$(CONFIG_ARCH_R8A7779)   += pm-r8a7779.o
>> >>>  obj-$(CONFIG_ARCH_SH73A0)    += pm-sh73a0.o
>> >>>
>> >>> diff --git a/arch/arm/mach-shmobile/include/mach/r8a7740.h b/arch/arm/mach-shmobile/include/mach/r8a7740.h
>> >>> index abdc4d4..ba4707b 100644
>> >>> --- a/arch/arm/mach-shmobile/include/mach/r8a7740.h
>> >>> +++ b/arch/arm/mach-shmobile/include/mach/r8a7740.h
>> >>> @@ -540,6 +540,8 @@ extern void r8a7740_add_standard_devices(void);
>> >>>  extern void r8a7740_clock_init(u8 md_ck);
>> >>>  extern void r8a7740_pinmux_init(void);
>> >>>  extern void r8a7740_pm_init(void);
>> >>> +extern void r8a7740_resume(void);
>> >>> +extern void r8a7740_shutdown(void);
>> >>>
>> >>>  #ifdef CONFIG_PM
>> >>>  extern void __init r8a7740_init_pm_domains(void);
>> >>> diff --git a/arch/arm/mach-shmobile/pm-r8a7740.c b/arch/arm/mach-shmobile/pm-r8a7740.c
>> >>> index 40b87aa..d4613f0 100644
>> >>> --- a/arch/arm/mach-shmobile/pm-r8a7740.c
>> >>> +++ b/arch/arm/mach-shmobile/pm-r8a7740.c
>> >>> @@ -8,10 +8,54 @@
>> >>>   * License.  See the file "COPYING" in the main directory of this archive
>> >>>   * for more details.
>> >>>   */
>> >>> +#include <linux/bitrev.h>
>> >>>  #include <linux/console.h>
>> >>> +#include <linux/cpuidle.h>
>> >>> +#include <linux/module.h>
>> >>>  #include <linux/suspend.h>
>> >>> +#include <linux/io.h>
>> >>> +#include <asm/suspend.h>
>> >>> +#include <asm/cacheflush.h>
>> >>> +#include <asm/cpuidle.h>
>> >>> +#include <asm/hardware/cache-l2x0.h>
>> >>>  #include <mach/pm-rmobile.h>
>> >>>  #include <mach/common.h>
>> >>> +#include <mach/r8a7740.h>
>> >>> +
>> >>> +/* CPGA */
>> >>> +#define PLLC01STPCR  IOMEM(0xe61500c8)
>> >>> +#define SYSTBCR              IOMEM(0xe6150024)
>> >>> +
>> >>> +/* SYSC */
>> >>> +#define STBCHR               IOMEM(0xe6180000)
>> >>> +#define STBCHRB              IOMEM(0xe6180040)
>> >>> +#define SPDCR                IOMEM(0xe6180008)
>> >>> +#define SBAR         IOMEM(0xe6180020)
>> >>> +#define SRSTFR               IOMEM(0xe61800B4)
>> >>> +#define WUPSMSK              IOMEM(0xe618002c)
>> >>> +#define WUPSMSK2     IOMEM(0xe6180048)
>> >>> +#define WUPSFAC              IOMEM(0xe6180098)
>> >>> +#define IRQCR                IOMEM(0xe618022c)
>> >>> +#define IRQCR2               IOMEM(0xe6180238)
>> >>> +#define IRQCR3               IOMEM(0xe6180244)
>> >>> +#define IRQCR4               IOMEM(0xe6180248)
>> >>> +
>> >>> +/* SRSTFR flags */
>> >>> +#define RAMST                (1 << 19)
>> >>> +#define RCLNKA               (1 << 7)
>> >>> +#define RCPRES               (1 << 5)
>> >>> +#define RCWD1                (1 << 4)
>> >>> +#define RPF          (1 << 0)
>> >>> +
>> >>> +/* INTC */
>> >>> +#define ICR1A                IOMEM(0xe6900000)
>> >>> +#define ICR2A                IOMEM(0xe6900004)
>> >>> +#define ICR3A                IOMEM(0xe6900008)
>> >>> +#define ICR4A                IOMEM(0xe690000c)
>> >>> +#define INTMSK00A    IOMEM(0xe6900040)
>> >>> +#define INTMSK10A    IOMEM(0xe6900044)
>> >>> +#define INTMSK20A    IOMEM(0xe6900048)
>> >>> +#define INTMSK30A    IOMEM(0xe690004c)
>> >>>
>> >>>  #ifdef CONFIG_PM
>> >>>  static int r8a7740_pd_a4s_suspend(void)
>> >>> @@ -58,12 +102,183 @@ void __init r8a7740_init_pm_domains(void)
>> >>>       rmobile_init_domains(r8a7740_pm_domains, ARRAY_SIZE(r8a7740_pm_domains));
>> >>>       pm_genpd_add_subdomain_names("A4S", "A3SP");
>> >>>  }
>> >>> -
>> >>>  #endif /* CONFIG_PM */
>> >>>
>> >>> +#if defined(CONFIG_SUSPEND) || defined(CONFIG_CPU_IDLE)
>> >>> +static void r8a7740_set_reset_vector(unsigned long address)
>> >>> +{
>> >>> +     __raw_writel(address, SBAR);
>> >>> +}
>> >>> +
>> >>> +static void r8a7740_icr_to_irqcr(unsigned long icr, u16 *irqcr1p, u16 *irqcr2p)
>> >>> +{
>> >>> +     u16 tmp, irqcr1, irqcr2;
>> >>> +     int k;
>> >>> +
>> >>> +     irqcr1 = 0;
>> >>> +     irqcr2 = 0;
>> >>> +
>> >>> +     /* convert INTCA ICR register layout to SYSC IRQCR+IRQCR2 */
>> >>> +     for (k = 0; k <= 7; k++) {
>> >>> +             tmp = (icr >> ((7 - k) * 4)) & 0xf;
>> >>> +             irqcr1 |= (tmp & 0x03) << (k * 2);
>> >>> +             irqcr2 |= (tmp >> 2) << (k * 2);
>> >>> +     }
>> >>> +
>> >>> +     *irqcr1p = irqcr1;
>> >>> +     *irqcr2p = irqcr2;
>> >>> +}
>> >>> +
>> >>> +static void r8a7740_setup_sysc(unsigned long msk, unsigned long msk2)
>> >>> +{
>> >>> +     u16 irqcrx_low, irqcrx_high, irqcry_low, irqcry_high;
>> >>> +     unsigned long tmp;
>> >>> +
>> >>> +     /* read IRQ0A -> IRQ15A mask */
>> >>> +     tmp = bitrev8(__raw_readb(INTMSK00A));
>> >>> +     tmp |= bitrev8(__raw_readb(INTMSK10A)) << 8;
>> >>> +
>> >>> +     /* setup WUPSMSK from clocks and external IRQ mask */
>> >>> +     msk = (~msk & 0xc030000f) | (tmp << 4);
>> >>> +     __raw_writel(msk, WUPSMSK);
>> >>> +
>> >>> +     /* propage level/edge trigger for external IRQ 0->15 */
>> >>> +     r8a7740_icr_to_irqcr(__raw_readl(ICR1A), &irqcrx_low, &irqcry_low);
>> >>> +     r8a7740_icr_to_irqcr(__raw_readl(ICR2A), &irqcrx_high, &irqcry_high);
>> >>> +     __raw_writel((irqcrx_high << 16) | irqcrx_low, IRQCR);
>> >>> +     __raw_writel((irqcry_high << 16) | irqcry_low, IRQCR2);
>> >>> +
>> >>> +     /* read IRQ16A -> IRQ31A mask */
>> >>> +     tmp = bitrev8(__raw_readb(INTMSK20A));
>> >>> +     tmp |= bitrev8(__raw_readb(INTMSK30A)) << 8;
>> >>> +
>> >>> +     /* setup WUPSMSK2 from clocks and external IRQ mask */
>> >>> +     msk2 = (~msk2 & 0x00030000) | tmp;
>> >>> +     __raw_writel(msk2, WUPSMSK2);
>> >>> +
>> >>> +     /* propage level/edge trigger for external IRQ 16->31 */
>> >>> +     r8a7740_icr_to_irqcr(__raw_readl(ICR3A), &irqcrx_low, &irqcry_low);
>> >>> +     r8a7740_icr_to_irqcr(__raw_readl(ICR4A), &irqcrx_high, &irqcry_high);
>> >>> +     __raw_writel((irqcrx_high << 16) | irqcrx_low, IRQCR3);
>> >>> +     __raw_writel((irqcry_high << 16) | irqcry_low, IRQCR4);
>> >>> +}
>> >>> +
>> >>> +static void r8a7740_prepare_wakeup(void)
>> >>> +{
>> >>> +     /* clear all flags that lead to a cold boot */
>> >>> +     __raw_writel(~(RAMST | RCLNKA | RCPRES | RCWD1 | RPF), SRSTFR);
>> >>> +     /* indicate warm boot */
>> >>> +     __raw_writel(0x80000000, STBCHRB);
>> >>> +     /* clear other flags checked by internal ROM boot loader */
>> >>> +     __raw_writel(0x00000000, STBCHR);
>> >>> +}
>> >>> +
>> >>> +static int r8a7740_do_suspend(unsigned long unused)
>> >>> +{
>> >>> +     /*
>> >>> +      * cpu_suspend() guarantees that all data made it to the L2.
>> >>> +      * Flush it out now and disable the cache controller.
>> >>> +      */
>> >>> +     outer_flush_all();
>> >>> +     outer_disable();
>> >>> +
>> >>> +     r8a7740_shutdown();
>> >>> +
>> >>> +     /* in case WFI fails to enter the low power state, restore things */
>> >>> +     outer_resume();
>> >>> +
>> >>> +     return 0;
>> >>> +}
>> >>> +
>> >>> +static void r8a7740_enter_a3sm_common(int pllc0_on)
>> >>> +{
>> >>> +     u32 reg32;
>> >>> +
>> >>> +     if (pllc0_on)
>> >>> +             __raw_writel(0, PLLC01STPCR);
>> >>> +     else
>> >>> +             __raw_writel(1 << 28, PLLC01STPCR);
>> >>> +
>> >>> +     r8a7740_set_reset_vector(__pa(r8a7740_resume));
>> >>> +     r8a7740_prepare_wakeup();
>> >>> +     r8a7740_setup_sysc(1 << 0, 0);
>> >>> +
>> >>> +     /* Activate delayed shutdown of A3SM */
>> >>> +     reg32 = __raw_readl(SPDCR);
>> >>> +     reg32 |= (1 << 31) | (1 << 12);
>> >>> +     __raw_writel(reg32, SPDCR);
>> >>> +
>> >>> +     /* We activate CPU Core Standby as well here */
>> >>> +     reg32 = __raw_readl(SYSTBCR);
>> >>> +     reg32 |= (1 << 4);
>> >>> +     __raw_writel(reg32, SYSTBCR);
>> >>> +
>> >>> +     /* Clear Wakeup Factors and do suspend */
>> >>> +     reg32 = __raw_readl(WUPSFAC);
>> >>> +     cpu_suspend(0, r8a7740_do_suspend);
>> >>> +     outer_resume();
>> >>> +     reg32 = __raw_readl(WUPSFAC);
>> >>> +
>> >>> +     /* Clear CPU Core Standby flag for other WFI instructions */
>> >>> +     reg32 &= ~(1 << 4);
>> >>> +     __raw_writel(reg32, SYSTBCR);
>> >>> +
>> >>> +}
>> >>> +#endif /* CONFIG_SUSPEND || CONFIG_CPU_IDLE */
>> >>> +
>> >>> +#ifdef CONFIG_CPU_IDLE
>> >>> +static int r8a7740_enter_a3sm_pll_on(struct cpuidle_device *dev,
>> >>> +                                     struct cpuidle_driver *drv, int index)
>> >>> +{
>> >>> +     r8a7740_enter_a3sm_common(1);
>> >>> +     return 1;
>> >>> +}
>> >>> +
>> >>> +static int r8a7740_enter_a3sm_pll_off(struct cpuidle_device *dev,
>> >>> +                                     struct cpuidle_driver *drv, int index)
>> >>> +{
>> >>> +     r8a7740_enter_a3sm_common(0);
>> >>> +     return 2;
>> >>> +}
>> >>> +
>> >>> +static struct cpuidle_driver r8a7740_cpuidle_driver = {
>> >>> +     .name                   = "r8a7740_cpuidle",
>> >>> +     .owner                  = THIS_MODULE,
>> >>> +     .en_core_tk_irqen       = 1,
>> >>> +     .state_count            = 3,
>> >>> +     .safe_state_index       = 0, /* C1 */
>> >>> +     .states[0] = ARM_CPUIDLE_WFI_STATE,
>> >>> +     .states[0].enter = shmobile_enter_wfi,
>> >>
>> >> The WFI macro already set a simple enter function doing cpu_do_idle.
>> >
>> > Oh yes, I'll remove it.
>> >
>> >> We are trying to consolidate the cpuidle drivers across the different
>> >> platforms [1]. One of the objective is to move the cpuidle drivers to
>> >> drivers/cpuidle, a place where they belong to.
>> >>
>> >> Do you think it is possible you split your code to have one part with
>> >> the cpuidle driver and another part with the PM code ?
>> >
>> > Hmm. The CPUIdle part is enclosed in the #ifdef CONFIG_CPU_IDLE part anyway.
>> > The other part (CONFIG_SUSPEND | CONFIG_CPU_IDLE) contains the part
>> > that actually does drive down the hardware and is needed as soon as
>> > SUSPEND or CPUIdle is used.
>> >
>> > So is it split well enough already or does it help if I split my patch
>> > into 2, first adding suspend and then CPUIdle to the same file? But
>> > the outcome will be the same I think.
>>
>>
>> Well, if you can convert the pm functions into ops used in the cpuidle
>> driver and then create a file eg. cpuidle-r8a7740.c with the cpuidle
>> code calling the ops that will separate clearly the cpuidle code from
>> the pm code and make easier to move the cpuidle driver to the
>> drivers/cpuidle directory. Also, I think that will help to consolidate
>> the other shmobile cpuidle drivers, maybe into a single one.
>
> I wonder if that is an appropriate pre-requisite for adding
> r8a7940 cpuidle support. My preference, which may well be compatible
> with your comments, is to bring support for that SoC up to the same level
> as support for other shmobile SoCs and to treat consolidation as a
> an area of work that is undertaken in parallel.

I just realize that I've overlooked this comment. Sorry for that. I
thought about the same:
The advantages of Daniel's way vs the temporary cluttering of our
arch/arm/mach-shmobile tree.
I'm really open for both ways - I think the extra cpuidle file doesn't
hurt too much and it would be OK to go with it, but I can merge the 2
patches if you strongly prefer the other way.

Cheers,

 Bastian
Simon Horman April 18, 2013, 1:43 p.m. UTC | #8
On Wed, Apr 17, 2013 at 02:06:20PM +0200, Bastian Hecht wrote:
> Hello Simon,
> 
> 2013/4/12 Simon Horman <horms@verge.net.au>:
> > On Thu, Apr 11, 2013 at 11:07:52AM +0200, Daniel Lezcano wrote:
> >> On 04/11/2013 09:07 AM, Bastian Hecht wrote:
> >> > Hello Daniel,
> >> >
> >> > thanks for the consolidation efforts.
> >> >
> >> > 2013/4/11 Daniel Lezcano <daniel.lezcano@linaro.org>:
> >> >> On 04/10/2013 05:51 PM, Bastian Hecht wrote:
> >> >>> We add 2 sleep modes and register a CPUIdle driver that uses them.
> >> >>>
> >> >>> - A3SM PLL0 on/off:     Power domain A3SM that contains the ARM core
> >> >>>                         and the 2nd level cache with either PLL0 on
> >> >>>                       or off
> >> >>>
> >> >>> As the suspend to memory mechanism we use A3SM PLL off.
> >> >>>
> >> >>> The setup of the SYSC regarding the external IRQs is taken from
> >> >>> pm-sh7372.c from Magnus Damm.
> >> >>>
> >> >>> Signed-off-by: Bastian Hecht <hechtb+renesas@gmail.com>
> >> >>> ---
> >> >>> Ok I need to add some remarks here:
> >> >>>
> >> >>> First this patch relies on a fix I've sent out today:
> >> >>> "ARM: hw_breakpoint: Do not use __cpuinitdata for dbg_cpu_pm_nb"
> >> >>> Without it we can't use suspend at all without a kernel panic.
> >> >>>
> >> >>> Next you can test this in two ways:
> >> >>> Either add IRQF_NO_SUSPEND to the touchscreen driver st1232 at IRQ registration
> >> >>> (hack) or extend the irqpin driver to support wakeup devices properly, meaning
> >> >>> adding suspend and resume callbacks that handle it (proper way). Or you can add
> >> >>> no_console_suspend to the boot options in r8a7740-armadillo800eva.dts. This way
> >> >>> you can use the serial console as wakeup device.
> >> >>>
> >> >>>
> >> >>>  arch/arm/mach-shmobile/Makefile               |    2 +-
> >> >>>  arch/arm/mach-shmobile/include/mach/r8a7740.h |    2 +
> >> >>>  arch/arm/mach-shmobile/pm-r8a7740.c           |  220 ++++++++++++++++++++++++-
> >> >>>  arch/arm/mach-shmobile/sleep-r8a7740.S        |   68 ++++++++
> >> >>>  4 files changed, 289 insertions(+), 3 deletions(-)
> >> >>>  create mode 100644 arch/arm/mach-shmobile/sleep-r8a7740.S
> >> >>>
> >> >>> diff --git a/arch/arm/mach-shmobile/Makefile b/arch/arm/mach-shmobile/Makefile
> >> >>> index 068f1da..0568894 100644
> >> >>> --- a/arch/arm/mach-shmobile/Makefile
> >> >>> +++ b/arch/arm/mach-shmobile/Makefile
> >> >>> @@ -30,7 +30,7 @@ obj-$(CONFIG_SUSPEND)               += suspend.o
> >> >>>  obj-$(CONFIG_CPU_IDLE)               += cpuidle.o
> >> >>>  obj-$(CONFIG_ARCH_SHMOBILE)  += pm-rmobile.o
> >> >>>  obj-$(CONFIG_ARCH_SH7372)    += pm-sh7372.o sleep-sh7372.o
> >> >>> -obj-$(CONFIG_ARCH_R8A7740)   += pm-r8a7740.o
> >> >>> +obj-$(CONFIG_ARCH_R8A7740)   += pm-r8a7740.o sleep-r8a7740.o
> >> >>>  obj-$(CONFIG_ARCH_R8A7779)   += pm-r8a7779.o
> >> >>>  obj-$(CONFIG_ARCH_SH73A0)    += pm-sh73a0.o
> >> >>>
> >> >>> diff --git a/arch/arm/mach-shmobile/include/mach/r8a7740.h b/arch/arm/mach-shmobile/include/mach/r8a7740.h
> >> >>> index abdc4d4..ba4707b 100644
> >> >>> --- a/arch/arm/mach-shmobile/include/mach/r8a7740.h
> >> >>> +++ b/arch/arm/mach-shmobile/include/mach/r8a7740.h
> >> >>> @@ -540,6 +540,8 @@ extern void r8a7740_add_standard_devices(void);
> >> >>>  extern void r8a7740_clock_init(u8 md_ck);
> >> >>>  extern void r8a7740_pinmux_init(void);
> >> >>>  extern void r8a7740_pm_init(void);
> >> >>> +extern void r8a7740_resume(void);
> >> >>> +extern void r8a7740_shutdown(void);
> >> >>>
> >> >>>  #ifdef CONFIG_PM
> >> >>>  extern void __init r8a7740_init_pm_domains(void);
> >> >>> diff --git a/arch/arm/mach-shmobile/pm-r8a7740.c b/arch/arm/mach-shmobile/pm-r8a7740.c
> >> >>> index 40b87aa..d4613f0 100644
> >> >>> --- a/arch/arm/mach-shmobile/pm-r8a7740.c
> >> >>> +++ b/arch/arm/mach-shmobile/pm-r8a7740.c
> >> >>> @@ -8,10 +8,54 @@
> >> >>>   * License.  See the file "COPYING" in the main directory of this archive
> >> >>>   * for more details.
> >> >>>   */
> >> >>> +#include <linux/bitrev.h>
> >> >>>  #include <linux/console.h>
> >> >>> +#include <linux/cpuidle.h>
> >> >>> +#include <linux/module.h>
> >> >>>  #include <linux/suspend.h>
> >> >>> +#include <linux/io.h>
> >> >>> +#include <asm/suspend.h>
> >> >>> +#include <asm/cacheflush.h>
> >> >>> +#include <asm/cpuidle.h>
> >> >>> +#include <asm/hardware/cache-l2x0.h>
> >> >>>  #include <mach/pm-rmobile.h>
> >> >>>  #include <mach/common.h>
> >> >>> +#include <mach/r8a7740.h>
> >> >>> +
> >> >>> +/* CPGA */
> >> >>> +#define PLLC01STPCR  IOMEM(0xe61500c8)
> >> >>> +#define SYSTBCR              IOMEM(0xe6150024)
> >> >>> +
> >> >>> +/* SYSC */
> >> >>> +#define STBCHR               IOMEM(0xe6180000)
> >> >>> +#define STBCHRB              IOMEM(0xe6180040)
> >> >>> +#define SPDCR                IOMEM(0xe6180008)
> >> >>> +#define SBAR         IOMEM(0xe6180020)
> >> >>> +#define SRSTFR               IOMEM(0xe61800B4)
> >> >>> +#define WUPSMSK              IOMEM(0xe618002c)
> >> >>> +#define WUPSMSK2     IOMEM(0xe6180048)
> >> >>> +#define WUPSFAC              IOMEM(0xe6180098)
> >> >>> +#define IRQCR                IOMEM(0xe618022c)
> >> >>> +#define IRQCR2               IOMEM(0xe6180238)
> >> >>> +#define IRQCR3               IOMEM(0xe6180244)
> >> >>> +#define IRQCR4               IOMEM(0xe6180248)
> >> >>> +
> >> >>> +/* SRSTFR flags */
> >> >>> +#define RAMST                (1 << 19)
> >> >>> +#define RCLNKA               (1 << 7)
> >> >>> +#define RCPRES               (1 << 5)
> >> >>> +#define RCWD1                (1 << 4)
> >> >>> +#define RPF          (1 << 0)
> >> >>> +
> >> >>> +/* INTC */
> >> >>> +#define ICR1A                IOMEM(0xe6900000)
> >> >>> +#define ICR2A                IOMEM(0xe6900004)
> >> >>> +#define ICR3A                IOMEM(0xe6900008)
> >> >>> +#define ICR4A                IOMEM(0xe690000c)
> >> >>> +#define INTMSK00A    IOMEM(0xe6900040)
> >> >>> +#define INTMSK10A    IOMEM(0xe6900044)
> >> >>> +#define INTMSK20A    IOMEM(0xe6900048)
> >> >>> +#define INTMSK30A    IOMEM(0xe690004c)
> >> >>>
> >> >>>  #ifdef CONFIG_PM
> >> >>>  static int r8a7740_pd_a4s_suspend(void)
> >> >>> @@ -58,12 +102,183 @@ void __init r8a7740_init_pm_domains(void)
> >> >>>       rmobile_init_domains(r8a7740_pm_domains, ARRAY_SIZE(r8a7740_pm_domains));
> >> >>>       pm_genpd_add_subdomain_names("A4S", "A3SP");
> >> >>>  }
> >> >>> -
> >> >>>  #endif /* CONFIG_PM */
> >> >>>
> >> >>> +#if defined(CONFIG_SUSPEND) || defined(CONFIG_CPU_IDLE)
> >> >>> +static void r8a7740_set_reset_vector(unsigned long address)
> >> >>> +{
> >> >>> +     __raw_writel(address, SBAR);
> >> >>> +}
> >> >>> +
> >> >>> +static void r8a7740_icr_to_irqcr(unsigned long icr, u16 *irqcr1p, u16 *irqcr2p)
> >> >>> +{
> >> >>> +     u16 tmp, irqcr1, irqcr2;
> >> >>> +     int k;
> >> >>> +
> >> >>> +     irqcr1 = 0;
> >> >>> +     irqcr2 = 0;
> >> >>> +
> >> >>> +     /* convert INTCA ICR register layout to SYSC IRQCR+IRQCR2 */
> >> >>> +     for (k = 0; k <= 7; k++) {
> >> >>> +             tmp = (icr >> ((7 - k) * 4)) & 0xf;
> >> >>> +             irqcr1 |= (tmp & 0x03) << (k * 2);
> >> >>> +             irqcr2 |= (tmp >> 2) << (k * 2);
> >> >>> +     }
> >> >>> +
> >> >>> +     *irqcr1p = irqcr1;
> >> >>> +     *irqcr2p = irqcr2;
> >> >>> +}
> >> >>> +
> >> >>> +static void r8a7740_setup_sysc(unsigned long msk, unsigned long msk2)
> >> >>> +{
> >> >>> +     u16 irqcrx_low, irqcrx_high, irqcry_low, irqcry_high;
> >> >>> +     unsigned long tmp;
> >> >>> +
> >> >>> +     /* read IRQ0A -> IRQ15A mask */
> >> >>> +     tmp = bitrev8(__raw_readb(INTMSK00A));
> >> >>> +     tmp |= bitrev8(__raw_readb(INTMSK10A)) << 8;
> >> >>> +
> >> >>> +     /* setup WUPSMSK from clocks and external IRQ mask */
> >> >>> +     msk = (~msk & 0xc030000f) | (tmp << 4);
> >> >>> +     __raw_writel(msk, WUPSMSK);
> >> >>> +
> >> >>> +     /* propage level/edge trigger for external IRQ 0->15 */
> >> >>> +     r8a7740_icr_to_irqcr(__raw_readl(ICR1A), &irqcrx_low, &irqcry_low);
> >> >>> +     r8a7740_icr_to_irqcr(__raw_readl(ICR2A), &irqcrx_high, &irqcry_high);
> >> >>> +     __raw_writel((irqcrx_high << 16) | irqcrx_low, IRQCR);
> >> >>> +     __raw_writel((irqcry_high << 16) | irqcry_low, IRQCR2);
> >> >>> +
> >> >>> +     /* read IRQ16A -> IRQ31A mask */
> >> >>> +     tmp = bitrev8(__raw_readb(INTMSK20A));
> >> >>> +     tmp |= bitrev8(__raw_readb(INTMSK30A)) << 8;
> >> >>> +
> >> >>> +     /* setup WUPSMSK2 from clocks and external IRQ mask */
> >> >>> +     msk2 = (~msk2 & 0x00030000) | tmp;
> >> >>> +     __raw_writel(msk2, WUPSMSK2);
> >> >>> +
> >> >>> +     /* propage level/edge trigger for external IRQ 16->31 */
> >> >>> +     r8a7740_icr_to_irqcr(__raw_readl(ICR3A), &irqcrx_low, &irqcry_low);
> >> >>> +     r8a7740_icr_to_irqcr(__raw_readl(ICR4A), &irqcrx_high, &irqcry_high);
> >> >>> +     __raw_writel((irqcrx_high << 16) | irqcrx_low, IRQCR3);
> >> >>> +     __raw_writel((irqcry_high << 16) | irqcry_low, IRQCR4);
> >> >>> +}
> >> >>> +
> >> >>> +static void r8a7740_prepare_wakeup(void)
> >> >>> +{
> >> >>> +     /* clear all flags that lead to a cold boot */
> >> >>> +     __raw_writel(~(RAMST | RCLNKA | RCPRES | RCWD1 | RPF), SRSTFR);
> >> >>> +     /* indicate warm boot */
> >> >>> +     __raw_writel(0x80000000, STBCHRB);
> >> >>> +     /* clear other flags checked by internal ROM boot loader */
> >> >>> +     __raw_writel(0x00000000, STBCHR);
> >> >>> +}
> >> >>> +
> >> >>> +static int r8a7740_do_suspend(unsigned long unused)
> >> >>> +{
> >> >>> +     /*
> >> >>> +      * cpu_suspend() guarantees that all data made it to the L2.
> >> >>> +      * Flush it out now and disable the cache controller.
> >> >>> +      */
> >> >>> +     outer_flush_all();
> >> >>> +     outer_disable();
> >> >>> +
> >> >>> +     r8a7740_shutdown();
> >> >>> +
> >> >>> +     /* in case WFI fails to enter the low power state, restore things */
> >> >>> +     outer_resume();
> >> >>> +
> >> >>> +     return 0;
> >> >>> +}
> >> >>> +
> >> >>> +static void r8a7740_enter_a3sm_common(int pllc0_on)
> >> >>> +{
> >> >>> +     u32 reg32;
> >> >>> +
> >> >>> +     if (pllc0_on)
> >> >>> +             __raw_writel(0, PLLC01STPCR);
> >> >>> +     else
> >> >>> +             __raw_writel(1 << 28, PLLC01STPCR);
> >> >>> +
> >> >>> +     r8a7740_set_reset_vector(__pa(r8a7740_resume));
> >> >>> +     r8a7740_prepare_wakeup();
> >> >>> +     r8a7740_setup_sysc(1 << 0, 0);
> >> >>> +
> >> >>> +     /* Activate delayed shutdown of A3SM */
> >> >>> +     reg32 = __raw_readl(SPDCR);
> >> >>> +     reg32 |= (1 << 31) | (1 << 12);
> >> >>> +     __raw_writel(reg32, SPDCR);
> >> >>> +
> >> >>> +     /* We activate CPU Core Standby as well here */
> >> >>> +     reg32 = __raw_readl(SYSTBCR);
> >> >>> +     reg32 |= (1 << 4);
> >> >>> +     __raw_writel(reg32, SYSTBCR);
> >> >>> +
> >> >>> +     /* Clear Wakeup Factors and do suspend */
> >> >>> +     reg32 = __raw_readl(WUPSFAC);
> >> >>> +     cpu_suspend(0, r8a7740_do_suspend);
> >> >>> +     outer_resume();
> >> >>> +     reg32 = __raw_readl(WUPSFAC);
> >> >>> +
> >> >>> +     /* Clear CPU Core Standby flag for other WFI instructions */
> >> >>> +     reg32 &= ~(1 << 4);
> >> >>> +     __raw_writel(reg32, SYSTBCR);
> >> >>> +
> >> >>> +}
> >> >>> +#endif /* CONFIG_SUSPEND || CONFIG_CPU_IDLE */
> >> >>> +
> >> >>> +#ifdef CONFIG_CPU_IDLE
> >> >>> +static int r8a7740_enter_a3sm_pll_on(struct cpuidle_device *dev,
> >> >>> +                                     struct cpuidle_driver *drv, int index)
> >> >>> +{
> >> >>> +     r8a7740_enter_a3sm_common(1);
> >> >>> +     return 1;
> >> >>> +}
> >> >>> +
> >> >>> +static int r8a7740_enter_a3sm_pll_off(struct cpuidle_device *dev,
> >> >>> +                                     struct cpuidle_driver *drv, int index)
> >> >>> +{
> >> >>> +     r8a7740_enter_a3sm_common(0);
> >> >>> +     return 2;
> >> >>> +}
> >> >>> +
> >> >>> +static struct cpuidle_driver r8a7740_cpuidle_driver = {
> >> >>> +     .name                   = "r8a7740_cpuidle",
> >> >>> +     .owner                  = THIS_MODULE,
> >> >>> +     .en_core_tk_irqen       = 1,
> >> >>> +     .state_count            = 3,
> >> >>> +     .safe_state_index       = 0, /* C1 */
> >> >>> +     .states[0] = ARM_CPUIDLE_WFI_STATE,
> >> >>> +     .states[0].enter = shmobile_enter_wfi,
> >> >>
> >> >> The WFI macro already set a simple enter function doing cpu_do_idle.
> >> >
> >> > Oh yes, I'll remove it.
> >> >
> >> >> We are trying to consolidate the cpuidle drivers across the different
> >> >> platforms [1]. One of the objective is to move the cpuidle drivers to
> >> >> drivers/cpuidle, a place where they belong to.
> >> >>
> >> >> Do you think it is possible you split your code to have one part with
> >> >> the cpuidle driver and another part with the PM code ?
> >> >
> >> > Hmm. The CPUIdle part is enclosed in the #ifdef CONFIG_CPU_IDLE part anyway.
> >> > The other part (CONFIG_SUSPEND | CONFIG_CPU_IDLE) contains the part
> >> > that actually does drive down the hardware and is needed as soon as
> >> > SUSPEND or CPUIdle is used.
> >> >
> >> > So is it split well enough already or does it help if I split my patch
> >> > into 2, first adding suspend and then CPUIdle to the same file? But
> >> > the outcome will be the same I think.
> >>
> >>
> >> Well, if you can convert the pm functions into ops used in the cpuidle
> >> driver and then create a file eg. cpuidle-r8a7740.c with the cpuidle
> >> code calling the ops that will separate clearly the cpuidle code from
> >> the pm code and make easier to move the cpuidle driver to the
> >> drivers/cpuidle directory. Also, I think that will help to consolidate
> >> the other shmobile cpuidle drivers, maybe into a single one.
> >
> > I wonder if that is an appropriate pre-requisite for adding
> > r8a7940 cpuidle support. My preference, which may well be compatible
> > with your comments, is to bring support for that SoC up to the same level
> > as support for other shmobile SoCs and to treat consolidation as a
> > an area of work that is undertaken in parallel.
> 
> I just realize that I've overlooked this comment. Sorry for that. I
> thought about the same:
> The advantages of Daniel's way vs the temporary cluttering of our
> arch/arm/mach-shmobile tree.
> I'm really open for both ways - I think the extra cpuidle file doesn't
> hurt too much and it would be OK to go with it, but I can merge the 2
> patches if you strongly prefer the other way.

I believe that I prefer 2 patches.
diff mbox

Patch

diff --git a/arch/arm/mach-shmobile/Makefile b/arch/arm/mach-shmobile/Makefile
index 068f1da..0568894 100644
--- a/arch/arm/mach-shmobile/Makefile
+++ b/arch/arm/mach-shmobile/Makefile
@@ -30,7 +30,7 @@  obj-$(CONFIG_SUSPEND)		+= suspend.o
 obj-$(CONFIG_CPU_IDLE)		+= cpuidle.o
 obj-$(CONFIG_ARCH_SHMOBILE)	+= pm-rmobile.o
 obj-$(CONFIG_ARCH_SH7372)	+= pm-sh7372.o sleep-sh7372.o
-obj-$(CONFIG_ARCH_R8A7740)	+= pm-r8a7740.o
+obj-$(CONFIG_ARCH_R8A7740)	+= pm-r8a7740.o sleep-r8a7740.o
 obj-$(CONFIG_ARCH_R8A7779)	+= pm-r8a7779.o
 obj-$(CONFIG_ARCH_SH73A0)	+= pm-sh73a0.o
 
diff --git a/arch/arm/mach-shmobile/include/mach/r8a7740.h b/arch/arm/mach-shmobile/include/mach/r8a7740.h
index abdc4d4..ba4707b 100644
--- a/arch/arm/mach-shmobile/include/mach/r8a7740.h
+++ b/arch/arm/mach-shmobile/include/mach/r8a7740.h
@@ -540,6 +540,8 @@  extern void r8a7740_add_standard_devices(void);
 extern void r8a7740_clock_init(u8 md_ck);
 extern void r8a7740_pinmux_init(void);
 extern void r8a7740_pm_init(void);
+extern void r8a7740_resume(void);
+extern void r8a7740_shutdown(void);
 
 #ifdef CONFIG_PM
 extern void __init r8a7740_init_pm_domains(void);
diff --git a/arch/arm/mach-shmobile/pm-r8a7740.c b/arch/arm/mach-shmobile/pm-r8a7740.c
index 40b87aa..d4613f0 100644
--- a/arch/arm/mach-shmobile/pm-r8a7740.c
+++ b/arch/arm/mach-shmobile/pm-r8a7740.c
@@ -8,10 +8,54 @@ 
  * License.  See the file "COPYING" in the main directory of this archive
  * for more details.
  */
+#include <linux/bitrev.h>
 #include <linux/console.h>
+#include <linux/cpuidle.h>
+#include <linux/module.h>
 #include <linux/suspend.h>
+#include <linux/io.h>
+#include <asm/suspend.h>
+#include <asm/cacheflush.h>
+#include <asm/cpuidle.h>
+#include <asm/hardware/cache-l2x0.h>
 #include <mach/pm-rmobile.h>
 #include <mach/common.h>
+#include <mach/r8a7740.h>
+
+/* CPGA */
+#define PLLC01STPCR 	IOMEM(0xe61500c8)
+#define SYSTBCR		IOMEM(0xe6150024)
+
+/* SYSC */
+#define STBCHR		IOMEM(0xe6180000)
+#define STBCHRB		IOMEM(0xe6180040)
+#define SPDCR		IOMEM(0xe6180008)
+#define SBAR		IOMEM(0xe6180020)
+#define SRSTFR		IOMEM(0xe61800B4)
+#define WUPSMSK		IOMEM(0xe618002c)
+#define WUPSMSK2	IOMEM(0xe6180048)
+#define WUPSFAC		IOMEM(0xe6180098)
+#define IRQCR 		IOMEM(0xe618022c)
+#define IRQCR2 		IOMEM(0xe6180238)
+#define IRQCR3		IOMEM(0xe6180244)
+#define IRQCR4		IOMEM(0xe6180248)
+
+/* SRSTFR flags */
+#define RAMST		(1 << 19)
+#define RCLNKA		(1 << 7)
+#define RCPRES		(1 << 5)
+#define RCWD1		(1 << 4)
+#define RPF		(1 << 0)
+
+/* INTC */
+#define ICR1A		IOMEM(0xe6900000)
+#define ICR2A		IOMEM(0xe6900004)
+#define ICR3A		IOMEM(0xe6900008)
+#define ICR4A		IOMEM(0xe690000c)
+#define INTMSK00A	IOMEM(0xe6900040)
+#define INTMSK10A	IOMEM(0xe6900044)
+#define INTMSK20A	IOMEM(0xe6900048)
+#define INTMSK30A	IOMEM(0xe690004c)
 
 #ifdef CONFIG_PM
 static int r8a7740_pd_a4s_suspend(void)
@@ -58,12 +102,183 @@  void __init r8a7740_init_pm_domains(void)
 	rmobile_init_domains(r8a7740_pm_domains, ARRAY_SIZE(r8a7740_pm_domains));
 	pm_genpd_add_subdomain_names("A4S", "A3SP");
 }
-
 #endif /* CONFIG_PM */
 
+#if defined(CONFIG_SUSPEND) || defined(CONFIG_CPU_IDLE)
+static void r8a7740_set_reset_vector(unsigned long address)
+{
+	__raw_writel(address, SBAR);
+}
+
+static void r8a7740_icr_to_irqcr(unsigned long icr, u16 *irqcr1p, u16 *irqcr2p)
+{
+	u16 tmp, irqcr1, irqcr2;
+	int k;
+
+	irqcr1 = 0;
+	irqcr2 = 0;
+
+	/* convert INTCA ICR register layout to SYSC IRQCR+IRQCR2 */
+	for (k = 0; k <= 7; k++) {
+		tmp = (icr >> ((7 - k) * 4)) & 0xf;
+		irqcr1 |= (tmp & 0x03) << (k * 2);
+		irqcr2 |= (tmp >> 2) << (k * 2);
+	}
+
+	*irqcr1p = irqcr1;
+	*irqcr2p = irqcr2;
+}
+
+static void r8a7740_setup_sysc(unsigned long msk, unsigned long msk2)
+{
+	u16 irqcrx_low, irqcrx_high, irqcry_low, irqcry_high;
+	unsigned long tmp;
+
+	/* read IRQ0A -> IRQ15A mask */
+	tmp = bitrev8(__raw_readb(INTMSK00A));
+	tmp |= bitrev8(__raw_readb(INTMSK10A)) << 8;
+
+	/* setup WUPSMSK from clocks and external IRQ mask */
+	msk = (~msk & 0xc030000f) | (tmp << 4);
+	__raw_writel(msk, WUPSMSK);
+
+	/* propage level/edge trigger for external IRQ 0->15 */
+	r8a7740_icr_to_irqcr(__raw_readl(ICR1A), &irqcrx_low, &irqcry_low);
+	r8a7740_icr_to_irqcr(__raw_readl(ICR2A), &irqcrx_high, &irqcry_high);
+	__raw_writel((irqcrx_high << 16) | irqcrx_low, IRQCR);
+	__raw_writel((irqcry_high << 16) | irqcry_low, IRQCR2);
+
+	/* read IRQ16A -> IRQ31A mask */
+	tmp = bitrev8(__raw_readb(INTMSK20A));
+	tmp |= bitrev8(__raw_readb(INTMSK30A)) << 8;
+
+	/* setup WUPSMSK2 from clocks and external IRQ mask */
+	msk2 = (~msk2 & 0x00030000) | tmp;
+	__raw_writel(msk2, WUPSMSK2);
+
+	/* propage level/edge trigger for external IRQ 16->31 */
+	r8a7740_icr_to_irqcr(__raw_readl(ICR3A), &irqcrx_low, &irqcry_low);
+	r8a7740_icr_to_irqcr(__raw_readl(ICR4A), &irqcrx_high, &irqcry_high);
+	__raw_writel((irqcrx_high << 16) | irqcrx_low, IRQCR3);
+	__raw_writel((irqcry_high << 16) | irqcry_low, IRQCR4);
+}
+
+static void r8a7740_prepare_wakeup(void)
+{
+	/* clear all flags that lead to a cold boot */
+	__raw_writel(~(RAMST | RCLNKA | RCPRES | RCWD1 | RPF), SRSTFR);
+	/* indicate warm boot */
+	__raw_writel(0x80000000, STBCHRB);
+	/* clear other flags checked by internal ROM boot loader */
+	__raw_writel(0x00000000, STBCHR);
+}
+
+static int r8a7740_do_suspend(unsigned long unused)
+{
+	/*
+	 * cpu_suspend() guarantees that all data made it to the L2.
+	 * Flush it out now and disable the cache controller.
+	 */
+	outer_flush_all();
+	outer_disable();
+
+	r8a7740_shutdown();
+
+	/* in case WFI fails to enter the low power state, restore things */
+	outer_resume();
+
+	return 0;
+}
+
+static void r8a7740_enter_a3sm_common(int pllc0_on)
+{
+	u32 reg32;
+
+	if (pllc0_on)
+		__raw_writel(0, PLLC01STPCR);
+	else
+		__raw_writel(1 << 28, PLLC01STPCR);
+
+	r8a7740_set_reset_vector(__pa(r8a7740_resume));
+	r8a7740_prepare_wakeup();
+	r8a7740_setup_sysc(1 << 0, 0);
+
+	/* Activate delayed shutdown of A3SM */
+	reg32 = __raw_readl(SPDCR);
+	reg32 |= (1 << 31) | (1 << 12);
+	__raw_writel(reg32, SPDCR);
+
+	/* We activate CPU Core Standby as well here */
+	reg32 = __raw_readl(SYSTBCR);
+	reg32 |= (1 << 4);
+	__raw_writel(reg32, SYSTBCR);
+
+	/* Clear Wakeup Factors and do suspend */
+	reg32 = __raw_readl(WUPSFAC);
+	cpu_suspend(0, r8a7740_do_suspend);
+	outer_resume();
+	reg32 = __raw_readl(WUPSFAC);
+
+	/* Clear CPU Core Standby flag for other WFI instructions */
+	reg32 &= ~(1 << 4);
+	__raw_writel(reg32, SYSTBCR);
+
+}
+#endif /* CONFIG_SUSPEND || CONFIG_CPU_IDLE */
+
+#ifdef CONFIG_CPU_IDLE
+static int r8a7740_enter_a3sm_pll_on(struct cpuidle_device *dev,
+					struct cpuidle_driver *drv, int index)
+{
+	r8a7740_enter_a3sm_common(1);
+	return 1;
+}
+
+static int r8a7740_enter_a3sm_pll_off(struct cpuidle_device *dev,
+					struct cpuidle_driver *drv, int index)
+{
+	r8a7740_enter_a3sm_common(0);
+	return 2;
+}
+
+static struct cpuidle_driver r8a7740_cpuidle_driver = {
+	.name			= "r8a7740_cpuidle",
+	.owner			= THIS_MODULE,
+	.en_core_tk_irqen	= 1,
+	.state_count		= 3,
+	.safe_state_index	= 0, /* C1 */
+	.states[0] = ARM_CPUIDLE_WFI_STATE,
+	.states[0].enter = shmobile_enter_wfi,
+	.states[1] = {
+		.name = "C2",
+		.desc = "A3SM PLL ON",
+		.exit_latency = 40,
+		.target_residency = 30 + 40,
+		.flags = CPUIDLE_FLAG_TIME_VALID,
+		.enter = r8a7740_enter_a3sm_pll_on,
+	},
+	.states[2] = {
+		.name = "C3",
+		.desc = "A3SM PLL OFF",
+		.exit_latency = 120,
+		.target_residency = 30 + 120,
+		.flags = CPUIDLE_FLAG_TIME_VALID,
+		.enter = r8a7740_enter_a3sm_pll_off,
+	},
+};
+
+static void r8a7740_cpuidle_init(void)
+{
+	shmobile_cpuidle_set_driver(&r8a7740_cpuidle_driver);
+}
+#else
+static void r8a7740_cpuidle_init(void) {}
+#endif /* CONFIG_CPU_IDLE */
+
 #ifdef CONFIG_SUSPEND
 static int r8a7740_enter_suspend(suspend_state_t suspend_state)
 {
+	r8a7740_enter_a3sm_common(0);
 	cpu_do_idle();
 	return 0;
 }
@@ -74,9 +289,10 @@  static void r8a7740_suspend_init(void)
 }
 #else
 static void r8a7740_suspend_init(void) {}
-#endif
+#endif /* CONFIG_SUSPEND */
 
 void __init r8a7740_pm_init(void)
 {
 	r8a7740_suspend_init();
+	r8a7740_cpuidle_init();
 }
diff --git a/arch/arm/mach-shmobile/sleep-r8a7740.S b/arch/arm/mach-shmobile/sleep-r8a7740.S
new file mode 100644
index 0000000..0ae310d
--- /dev/null
+++ b/arch/arm/mach-shmobile/sleep-r8a7740.S
@@ -0,0 +1,68 @@ 
+/*
+ * Low level sleep code for the SoC r8a7740
+ *
+ * Copyright (C) 2013 Bastian Hecht
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License.  See the file "COPYING" in the main directory of this archive
+ * for more details.
+ */
+
+#include <linux/linkage.h>
+#include <linux/init.h>
+#include <asm/memory.h>
+
+#if defined(CONFIG_SUSPEND) || defined(CONFIG_CPU_IDLE)
+
+/* r8a7740_shutdown expects L1 and L2 to be flushed */
+	.text
+ENTRY(r8a7740_shutdown)
+	stmfd   sp!, {r4-r12, lr}
+
+	/* make sure the stack stays intact */
+	bl      v7_flush_dcache_all
+
+	/*
+	 * Clear the SCTLR.C bit to prevent further data cache
+	 * allocation. Clearing SCTLR.C would make all the data accesses
+	 * strongly ordered and would not hit the cache.
+	 */
+	mrc	p15, 0, r0, c1, c0, 0
+	bic	r0, r0, #(1 << 2)		@ Disable the C bit
+	mcr	p15, 0, r0, c1, c0, 0
+	isb
+
+	bl      v7_flush_dcache_all
+	bl	cpu_v7_do_idle
+
+	/* in rare cases when WFI fails we end up here and restore things */
+	mrc	p15, 0, r0, c1, c0, 0
+	orr	r0, r0, #(1 << 2)		@ Enable the C bit
+	mcr	p15, 0, r0, c1, c0, 0
+	isb
+
+	ldmfd   sp!, {r4-r12, pc}
+ENDPROC(r8a7740)
+
+	.text
+ENTRY(v7_cpu_resume)
+	bl	v7_invalidate_l1
+	b	cpu_resume
+ENDPROC(v7_cpu_resume)
+
+/*
+ * The entry point of a warm reboot, used by wakeup scenarios
+ *
+ * The CPU jumps in this case to (0xfffff000 & SBAR), so we need
+ * to align this function properly.
+ * We use a long jump into the text segment and use the physical
+ * address as the MMU is still turned off.
+ */
+	.align	12
+	.text
+ENTRY(r8a7740_resume)
+	ldr	pc, 1f
+1:	.long	v7_cpu_resume - PAGE_OFFSET + PLAT_PHYS_OFFSET
+ENDPROC(r8a7740_resume_core_standby)
+#endif
-- 
1.7.9.5