From: jason.chen@freescale.com (Jason Chen)
To: linux-arm-kernel@lists.infradead.org
Subject: [PATCH 2/2] ARM: imx6q: add cpu suspend/resume support in IRAM
Date: Wed, 4 Jan 2012 15:24:07 +0800 [thread overview]
Message-ID: <20120104072405.GA2687@weitway> (raw)
In-Reply-To: <20120103142450.GE13477@S2100-06.ap.freescale.net>
hi, Shawn,
I'll separate this patches and update with your comments.
On Tue, Jan 03, 2012 at 10:24:51PM +0800, Shawn Guo wrote:
> On Sat, Dec 31, 2011 at 02:23:44PM +0800, Jason Chen wrote:
> > For most power saving, this patch make arm core stop and
>
> s/most/more?
>
> Without this patch, the 'mem' state already powers arm core off.
>
> > ddr go into self-refresh mode.
> >
> > It already be tested on Freescale imx6q-arm2 board.
> >
>
> I managed to apply the patch on v3.2-rc7 and tested it on ARM2 board,
> but it does not work for me.
>
> > Signed-off-by: Jason Chen <jason.chen@linaro.org>
> > Signed-off-by: Jason Chen <jason.chen@freescale.com>
> > ---
> > arch/arm/mach-imx/Makefile | 2 +-
> > arch/arm/mach-imx/clock-imx6q.c | 37 +++--
> > arch/arm/mach-imx/gpc.c | 21 ++
> > arch/arm/mach-imx/pm-imx6q.c | 128 +++++++++++++-
> > arch/arm/mach-imx/suspend-imx6q.S | 308 +++++++++++++++++++++++++++++++
> > arch/arm/plat-mxc/include/mach/common.h | 2 +
> > arch/arm/plat-mxc/include/mach/mx6q.h | 6 +
> > 7 files changed, 487 insertions(+), 17 deletions(-)
> >
>
> The checkpatch.pl reports 3 warnings.
>
> > diff --git a/arch/arm/mach-imx/Makefile b/arch/arm/mach-imx/Makefile
> > index 88a3966..0b07eef 100644
> > --- a/arch/arm/mach-imx/Makefile
> > +++ b/arch/arm/mach-imx/Makefile
> > @@ -72,7 +72,7 @@ AFLAGS_head-v7.o :=-Wa,-march=armv7-a
> > obj-$(CONFIG_SMP) += platsmp.o
> > obj-$(CONFIG_HOTPLUG_CPU) += hotplug.o
> > obj-$(CONFIG_LOCAL_TIMERS) += localtimer.o
> > -obj-$(CONFIG_SOC_IMX6Q) += clock-imx6q.o mach-imx6q.o pm-imx6q.o
> > +obj-$(CONFIG_SOC_IMX6Q) += clock-imx6q.o mach-imx6q.o pm-imx6q.o suspend-imx6q.o
> >
> > # i.MX5 based machines
> > obj-$(CONFIG_MACH_MX51_BABBAGE) += mach-mx51_babbage.o
> > diff --git a/arch/arm/mach-imx/clock-imx6q.c b/arch/arm/mach-imx/clock-imx6q.c
> > index 56a7a3f..b7fecad 100644
> > --- a/arch/arm/mach-imx/clock-imx6q.c
> > +++ b/arch/arm/mach-imx/clock-imx6q.c
> > @@ -115,6 +115,8 @@
> > #define CG14 28
> > #define CG15 30
> >
> > +#define BM_CCR_RBC_EN (0x1 << 27)
> > +
> > #define BM_CCSR_PLL1_SW_SEL (0x1 << 2)
> > #define BM_CCSR_STEP_SEL (0x1 << 8)
> >
> > @@ -1916,33 +1918,44 @@ static struct clk_lookup lookups[] = {
> >
> > int imx6q_set_lpm(enum mxc_cpu_pwr_mode mode)
> > {
> > - u32 val = readl_relaxed(CLPCR);
> > + u32 clpcr = readl_relaxed(CLPCR);
> > + u32 ccr = readl_relaxed(CCR);
> >
> > - val &= ~BM_CLPCR_LPM;
> > + clpcr &= ~(BM_CLPCR_LPM | BM_CLPCR_VSTBY | BM_CLPCR_SBYOS
> > + | BM_CLPCR_STBY_COUNT | BM_CLPCR_WB_PER_AT_LPM);
> > + ccr &= ~(BM_CCR_RBC_EN);
> > switch (mode) {
> > case WAIT_CLOCKED:
> > break;
> > case WAIT_UNCLOCKED:
> > - val |= 0x1 << BP_CLPCR_LPM;
> > + clpcr |= 0x1 << BP_CLPCR_LPM;
> > break;
> > case STOP_POWER_ON:
> > - val |= 0x2 << BP_CLPCR_LPM;
> > + clpcr |= 0x2 << BP_CLPCR_LPM;
> > break;
> > case WAIT_UNCLOCKED_POWER_OFF:
> > - val |= 0x1 << BP_CLPCR_LPM;
> > - val &= ~BM_CLPCR_VSTBY;
> > - val &= ~BM_CLPCR_SBYOS;
> > + clpcr |= 0x1 << BP_CLPCR_LPM;
> > break;
> > case STOP_POWER_OFF:
> > - val |= 0x2 << BP_CLPCR_LPM;
> > - val |= 0x3 << BP_CLPCR_STBY_COUNT;
> > - val |= BM_CLPCR_VSTBY;
> > - val |= BM_CLPCR_SBYOS;
> > + clpcr |= 0x2 << BP_CLPCR_LPM;
> > + clpcr |= 0x3 << BP_CLPCR_STBY_COUNT;
> > + clpcr |= BM_CLPCR_VSTBY;
> > + clpcr |= BM_CLPCR_SBYOS;
> > + break;
> > + case ARM_POWER_OFF:
>
> I'm unsure we need this new state.
>
> > + clpcr |= 0x2 << BP_CLPCR_LPM;
> > + clpcr |= 0x3 << BP_CLPCR_STBY_COUNT;
> > + clpcr |= BM_CLPCR_VSTBY;
> > + clpcr |= BM_CLPCR_SBYOS;
> > + clpcr |= BM_CLPCR_WB_PER_AT_LPM;
> > + /* assert anatop_reg_bypass signal */
> > + ccr |= BM_CCR_RBC_EN;
>
> It seems that the difference between STOP_POWER_OFF and ARM_POWER_OFF
> is all about bits BM_CLPCR_WB_PER_AT_LPM and BM_CCR_RBC_EN. Can you
> elaborate why these two bits need to be set for suspend with DDR in
> self-refresh state?
>
> > break;
> > default:
> > return -EINVAL;
> > }
> > - writel_relaxed(val, CLPCR);
> > + writel_relaxed(clpcr, CLPCR);
> > + writel_relaxed(ccr, CCR);
> >
> > return 0;
> > }
> > diff --git a/arch/arm/mach-imx/gpc.c b/arch/arm/mach-imx/gpc.c
> > index e1537f9..8fc255b 100644
> > --- a/arch/arm/mach-imx/gpc.c
> > +++ b/arch/arm/mach-imx/gpc.c
> > @@ -17,15 +17,36 @@
> > #include <linux/of_irq.h>
> > #include <asm/hardware/gic.h>
> >
> > +#define GPC_CNTR 0x000
>
> Where is it used?
>
> > #define GPC_IMR1 0x008
> > +#define GPC_ISR1 0x018
> > +#define GPC_ISR2 0x01c
> > +#define GPC_ISR3 0x020
> > +#define GPC_ISR4 0x024
>
> It seems that only definition GPC_ISR1 is used?
>
> > #define GPC_PGC_CPU_PDN 0x2a0
> >
> > #define IMR_NUM 4
> > +#define ISR_NUM 4
> >
> > static void __iomem *gpc_base;
> > static u32 gpc_wake_irqs[IMR_NUM];
> > static u32 gpc_saved_imrs[IMR_NUM];
> >
> > +bool imx_gpc_wake_irq_pending(void)
> > +{
> > + void __iomem *reg_isr1 = gpc_base + GPC_ISR1;
> > + int i;
> > + u32 val;
> > +
> > + for (i = 0; i < ISR_NUM; i++) {
> > + val = readl_relaxed(reg_isr1 + i * 4);
> > + if (val & gpc_wake_irqs[i])
> > + return true;
> > + }
> > +
> > + return false;
> > +}
> > +
> > void imx_gpc_pre_suspend(void)
> > {
> > void __iomem *reg_imr1 = gpc_base + GPC_IMR1;
> > diff --git a/arch/arm/mach-imx/pm-imx6q.c b/arch/arm/mach-imx/pm-imx6q.c
> > index f20f191..6e45245 100644
> > --- a/arch/arm/mach-imx/pm-imx6q.c
> > +++ b/arch/arm/mach-imx/pm-imx6q.c
> > @@ -13,31 +13,73 @@
> > #include <linux/init.h>
> > #include <linux/io.h>
> > #include <linux/of.h>
> > +#include <linux/of_address.h>
> > #include <linux/suspend.h>
> > #include <asm/cacheflush.h>
> > #include <asm/proc-fns.h>
> > #include <asm/suspend.h>
> > +#include <asm/mach/map.h>
> > #include <asm/hardware/cache-l2x0.h>
> > +#include <mach/iram.h>
> > #include <mach/common.h>
> > #include <mach/hardware.h>
> >
> > +struct imx_iram_pm {
> > + void *iram_cpaddr;
> > + unsigned long suspend_iram_paddr;
> > + unsigned long suspend_iram_size;
> > + void *suspend_iram_vaddr;
> > + void *reg_ptr[4];
> > +} imx6q_iram_pm;
> > +
> > extern unsigned long phys_l2x0_saved_regs;
> > +extern void imx6q_suspend(void);
> > +static void (*suspend_in_iram)(unsigned long iram_paddr,
> > + unsigned long iram_vaddr,
> > + unsigned long iram_size);
> >
> > static int imx6q_suspend_finish(unsigned long val)
> > {
> > - cpu_do_idle();
> > + if ((val == PM_SUSPEND_MEM) && suspend_in_iram) {
> > + suspend_in_iram((unsigned long)imx6q_iram_pm.suspend_iram_paddr,
> > + (unsigned long)imx6q_iram_pm.suspend_iram_vaddr,
> > + (unsigned long)imx6q_iram_pm.suspend_iram_size);
> > + } else
>
> The brace pair {} is not needed.
>
> > + cpu_do_idle();
> > +
> > return 0;
> > }
> >
> > +static void imx6q_prepare_suspend_iram(void)
> > +{
> > + unsigned long *iram_stack = imx6q_iram_pm.suspend_iram_vaddr
> > + + imx6q_iram_pm.suspend_iram_size;
> > +
> > + *(--iram_stack) = (unsigned long)imx6q_iram_pm.reg_ptr[3];
> > + *(--iram_stack) = (unsigned long)imx6q_iram_pm.reg_ptr[2];
> > + *(--iram_stack) = (unsigned long)imx6q_iram_pm.reg_ptr[1];
> > + *(--iram_stack) = (unsigned long)imx6q_iram_pm.reg_ptr[0];
> > +}
> > +
>
> I'm not sure we need to do this. I feel we have done too much in
> suspend_in_iram(). See comments on suspend_in_iram below.
>
> > static int imx6q_pm_enter(suspend_state_t state)
> > {
> > switch (state) {
> > + case PM_SUSPEND_STANDBY:
> > case PM_SUSPEND_MEM:
> > - imx6q_set_lpm(STOP_POWER_OFF);
> > + if (imx_gpc_wake_irq_pending())
> > + return 0;
> > +
> > + if (state == PM_SUSPEND_STANDBY)
> > + imx6q_set_lpm(STOP_POWER_OFF);
> > + else
> > + imx6q_set_lpm(ARM_POWER_OFF);
> > +
>
> I do not think we need to distinguish these two states, STOP_POWER_OFF
> and ARM_POWER_OFF. When I added STOP_POWER_OFF support, the DDR self
> refresh support was missed there. When this feature is available, we
> can just add it on top of STOP_POWER_OFF rather than inventing a new
> state.
>
> > imx_gpc_pre_suspend();
> > imx_set_cpu_jump(0, v7_cpu_resume);
> > + if (state == PM_SUSPEND_MEM)
> > + imx6q_prepare_suspend_iram();
> > /* Zzz ... */
> > - cpu_suspend(0, imx6q_suspend_finish);
> > + cpu_suspend(state, imx6q_suspend_finish);
> > imx_smp_prepare();
> > imx_gpc_post_resume();
> > break;
> > @@ -48,11 +90,55 @@ static int imx6q_pm_enter(suspend_state_t state)
> > return 0;
> > }
> >
> > +static int imx6q_pm_valid(suspend_state_t state)
> > +{
> > + return (state > PM_SUSPEND_ON && state <= PM_SUSPEND_MAX);
> > +}
> > +
> > static const struct platform_suspend_ops imx6q_pm_ops = {
> > .enter = imx6q_pm_enter,
> > - .valid = suspend_valid_only_mem,
> > + .valid = imx6q_pm_valid,
> > };
> >
> > +static int __init imx6q_pm_iram_of_init(void)
> > +{
> > + struct device_node *np;
> > +
> > + /*
> > + * these register may already ioremaped, need figure out
> > + * one way to save vmalloc space.
> > + */
> > + np = of_find_compatible_node(NULL, NULL, "fsl,imx6q-src");
> > + imx6q_iram_pm.reg_ptr[0] = of_iomap(np, 0);
> > + if (!imx6q_iram_pm.reg_ptr[0])
> > + goto err0;
> > +
> > + np = of_find_compatible_node(NULL, NULL, "fsl,imx6q-iomuxc");
> > + imx6q_iram_pm.reg_ptr[1] = of_iomap(np, 0);
> > + if (!imx6q_iram_pm.reg_ptr[1])
> > + goto err1;
> > +
> > + np = of_find_compatible_node(NULL, NULL, "fsl,imx6q-mmdc");
> > + imx6q_iram_pm.reg_ptr[2] = of_iomap(np, 0);
> > + if (!imx6q_iram_pm.reg_ptr[2])
> > + goto err2;
> > +
> > + np = of_find_compatible_node(NULL, NULL, "fsl,imx6q-anatop");
> > + imx6q_iram_pm.reg_ptr[3] = of_iomap(np, 0);
> > + if (!imx6q_iram_pm.reg_ptr[3])
> > + goto err3;
>
> This is not really nice and necessary, if we can limit the work done
> in suspend_in_iram().
>
> > +
> > + return 0;
> > +err3:
> > + iounmap(imx6q_iram_pm.reg_ptr[2]);
> > +err2:
> > + iounmap(imx6q_iram_pm.reg_ptr[1]);
> > +err1:
> > + iounmap(imx6q_iram_pm.reg_ptr[0]);
> > +err0:
> > + return -EINVAL;
> > +}
> > +
> > void __init imx6q_pm_init(void)
> > {
> > /*
> > @@ -67,4 +153,38 @@ void __init imx6q_pm_init(void)
> > phys_l2x0_saved_regs = __pa(&l2x0_saved_regs);
> >
> > suspend_set_ops(&imx6q_pm_ops);
> > +
> > + /* Move suspend routine into iRAM */
> > + imx6q_iram_pm.suspend_iram_size = SZ_4K;
> > + imx6q_iram_pm.iram_cpaddr = iram_alloc(imx6q_iram_pm.suspend_iram_size,
> > + &imx6q_iram_pm.suspend_iram_paddr);
> > + if (imx6q_iram_pm.iram_cpaddr) {
> > + if (imx6q_pm_iram_of_init() < 0) {
> > + iram_free(imx6q_iram_pm.suspend_iram_paddr,
> > + imx6q_iram_pm.suspend_iram_size);
> > + return;
> > + }
> > + /*
> > + * Need to remap the area here since we want the memory region
> > + * to be noncached & executable.
> > + */
> > + imx6q_iram_pm.suspend_iram_vaddr =
> > + __arm_ioremap(imx6q_iram_pm.suspend_iram_paddr,
> > + imx6q_iram_pm.suspend_iram_size,
> > + MT_MEMORY_NONCACHED);
> > + pr_info("cpaddr = %p suspend_iram_base=%p\n",
>
> pr_debug?
>
> > + imx6q_iram_pm.iram_cpaddr,
> > + imx6q_iram_pm.suspend_iram_vaddr);
> > +
> > + /*
> > + * Need to run the suspend code from IRAM as the DDR needs
> > + * to be put into low power mode manually.
> > + */
> > + memcpy(imx6q_iram_pm.iram_cpaddr, imx6q_suspend,
> > + imx6q_iram_pm.suspend_iram_size);
>
> Please try to use fncpy (arch/arm/include/asm/fncpy.h)
>
> > +
> > + suspend_in_iram = (void *)imx6q_iram_pm.suspend_iram_vaddr;
> > +
> > + } else
> > + suspend_in_iram = NULL;
>
> The suspend_in_iram is already initialized as NULL, I guess.
>
> > }
> > diff --git a/arch/arm/mach-imx/suspend-imx6q.S b/arch/arm/mach-imx/suspend-imx6q.S
> > new file mode 100644
> > index 0000000..f803e2b
> > --- /dev/null
> > +++ b/arch/arm/mach-imx/suspend-imx6q.S
> > @@ -0,0 +1,308 @@
> > +/*
> > + * Copyright 2011 Freescale Semiconductor, Inc.
> > + * Copyright 2011 Linaro Ltd.
> > + *
> > + * The code contained herein is licensed under the GNU General Public
> > + * License. You may obtain a copy of the GNU General Public License
> > + * Version 2 or later at the following locations:
> > + *
> > + * http://www.opensource.org/licenses/gpl-license.html
> > + * http://www.gnu.org/copyleft/gpl.html
> > + */
> > +
> > +#include <linux/linkage.h>
> > +#include <mach/hardware.h>
> > +#include <asm/hardware/cache-l2x0.h>
> > +
> > +#define SRC_GPR1_OFFSET 0x020
> > +#define SRC_GPR2_OFFSET 0x024
> > +#define MMDC_MAPSR_OFFSET 0x404
> > +#define MMDC_MAPSR_PSS (1 << 4)
> > +#define MMDC_MAPSR_PSD (1 << 0)
> > +#define ANATOP_REG_2P5 0x130
> > +
> > +.macro ddr_io_save
> > + ldr r4, [r9, #0x5ac] /* DRAM_DQM0 */
>
> If we have macro defined for DRAM_DQM0, we can save the magic number
> and the comment. It applies to the same cases all over the ddr_io_xxx
> functions.
>
> > + ldr r5, [r9, #0x5b4] /* DRAM_DQM1 */
> > + ldr r6, [r9, #0x528] /* DRAM_DQM2 */
> > + ldr r7, [r9, #0x520] /* DRAM_DQM3 */
> > + stmfd sp!, {r4-r7}
> > +
> > + ldr r4, [r9, #0x514] /* DRAM_DQM4 */
> > + ldr r5, [r9, #0x510] /* DRAM_DQM5 */
> > + ldr r6, [r9, #0x5bc] /* DRAM_DQM6 */
> > + ldr r7, [r9, #0x5c4] /* DRAM_DQM7 */
> > + stmfd sp!, {r4-r7}
> > +
> > + ldr r4, [r9, #0x56c] /* DRAM_CAS */
> > + ldr r5, [r9, #0x578] /* DRAM_RAS */
> > + ldr r6, [r9, #0x588] /* DRAM_SDCLK_0 */
> > + ldr r7, [r9, #0x594] /* DRAM_SDCLK_1 */
> > + stmfd sp!, {r4-r7}
> > +
> > + ldr r5, [r9, #0x750] /* DDRMODE_CTL */
> > + ldr r6, [r9, #0x774] /* DDRMODE */
> > + stmfd sp!, {r5-r6}
> > +
> > + ldr r4, [r9, #0x5a8] /* DRAM_SDQS0 */
> > + ldr r5, [r9, #0x5b0] /* DRAM_SDQS1 */
> > + ldr r6, [r9, #0x524] /* DRAM_SDQS2 */
> > + ldr r7, [r9, #0x51c] /* DRAM_SDQS3 */
> > + stmfd sp!, {r4-r7}
> > +
> > + ldr r4, [r9, #0x518] /* DRAM_SDQS4 */
> > + ldr r5, [r9, #0x50c] /* DRAM_SDQS5 */
> > + ldr r6, [r9, #0x5b8] /* DRAM_SDQS6 */
> > + ldr r7, [r9, #0x5c0] /* DRAM_SDQS7 */
> > + stmfd sp!, {r4-r7}
> > +
> > + ldr r4, [r9, #0x784] /* GPR_B0DS */
> > + ldr r5, [r9, #0x788] /* GPR_B1DS */
> > + ldr r6, [r9, #0x794] /* GPR_B2DS */
> > + ldr r7, [r9, #0x79c] /* GPR_B3DS */
> > + stmfd sp!, {r4-r7}
> > +
> > + ldr r4, [r9, #0x7a0] /* GPR_B4DS */
> > + ldr r5, [r9, #0x7a4] /* GPR_B5DS */
> > + ldr r6, [r9, #0x7a8] /* GPR_B6DS */
> > + ldr r7, [r9, #0x748] /* GPR_B7DS */
> > + stmfd sp!, {r4-r7}
> > +
> > + ldr r5, [r9, #0x74c] /* GPR_ADDS*/
> > + ldr r6, [r9, #0x59c] /* DRAM_SODT0*/
> > + ldr r7, [r9, #0x5a0] /* DRAM_SODT1*/
> > + stmfd sp!, {r5-r7}
> > +.endm
> > +
> > +.macro ddr_io_restore
> > + ldmfd sp!, {r5-r7}
> > + str r5, [r9, #0x74c] /* GPR_ADDS*/
> > + str r6, [r9, #0x59c] /* DRAM_SODT0*/
> > + str r7, [r9, #0x5a0] /* DRAM_SODT1*/
> > +
> > + ldmfd sp!, {r4-r7}
> > + str r4, [r9, #0x7a0] /* GPR_B4DS */
> > + str r5, [r9, #0x7a4] /* GPR_B5DS */
> > + str r6, [r9, #0x7a8] /* GPR_B6DS */
> > + str r7, [r9, #0x748] /* GPR_B7DS */
> > +
> > + ldmfd sp!, {r4-r7}
> > + str r4, [r9, #0x784] /* GPR_B0DS */
> > + str r5, [r9, #0x788] /* GPR_B1DS */
> > + str r6, [r9, #0x794] /* GPR_B2DS */
> > + str r7, [r9, #0x79c] /* GPR_B3DS */
> > +
> > + ldmfd sp!, {r4-r7}
> > + str r4, [r9, #0x518] /* DRAM_SDQS4 */
> > + str r5, [r9, #0x50c] /* DRAM_SDQS5 */
> > + str r6, [r9, #0x5b8] /* DRAM_SDQS6 */
> > + str r7, [r9, #0x5c0] /* DRAM_SDQS7 */
> > +
> > + ldmfd sp!, {r4-r7}
> > + str r4, [r9, #0x5a8] /* DRAM_SDQS0 */
> > + str r5, [r9, #0x5b0] /* DRAM_SDQS1 */
> > + str r6, [r9, #0x524] /* DRAM_SDQS2 */
> > + str r7, [r9, #0x51c] /* DRAM_SDQS3 */
> > +
> > + ldmfd sp!, {r5-r6}
> > + str r5, [r9, #0x750] /* DDRMODE_CTL */
> > + str r6, [r9, #0x774] /* DDRMODE */
> > +
> > + ldmfd sp!, {r4-r7}
> > + str r4, [r9, #0x56c] /* DRAM_CAS */
> > + str r5, [r9, #0x578] /* DRAM_RAS */
> > + str r6, [r9, #0x588] /* DRAM_SDCLK_0 */
> > + str r7, [r9, #0x594] /* DRAM_SDCLK_1 */
> > +
> > + ldmfd sp!, {r4-r7}
> > + str r4, [r9, #0x514] /* DRAM_DQM4 */
> > + str r5, [r9, #0x510] /* DRAM_DQM5 */
> > + str r6, [r9, #0x5bc] /* DRAM_DQM6 */
> > + str r7, [r9, #0x5c4] /* DRAM_DQM7 */
> > +
> > + ldmfd sp!, {r4-r7}
> > + str r4, [r9, #0x5ac] /* DRAM_DQM0 */
> > + str r5, [r9, #0x5b4] /* DRAM_DQM1 */
> > + str r6, [r9, #0x528] /* DRAM_DQM2 */
> > + str r7, [r9, #0x520] /* DRAM_DQM3 */
> > +.endm
> > +
> > +.macro ddr_io_set_lpm
> > + mov r4, #0
> > + str r4, [r9, #0x5ac] /* DRAM_DQM0 */
> > + str r4, [r9, #0x5b4] /* DRAM_DQM1 */
> > + str r4, [r9, #0x528] /* DRAM_DQM2 */
> > + str r4, [r9, #0x520] /* DRAM_DQM3 */
> > +
> > + str r4, [r9, #0x514] /* DRAM_DQM4 */
> > + str r4, [r9, #0x510] /* DRAM_DQM5 */
> > + str r4, [r9, #0x5bc] /* DRAM_DQM6 */
> > + str r4, [r9, #0x5c4] /* DRAM_DQM7 */
> > +
> > + str r4, [r9, #0x56c] /* DRAM_CAS */
> > + str r4, [r9, #0x578] /* DRAM_RAS */
> > + str r4, [r9, #0x588] /* DRAM_SDCLK_0 */
> > + str r4, [r9, #0x594] /* DRAM_SDCLK_1 */
> > +
> > + str r4, [r9, #0x750] /* DDRMODE_CTL */
> > + str r4, [r9, #0x774] /* DDRMODE */
> > +
> > + str r4, [r9, #0x5a8] /* DRAM_SDQS0 */
> > + str r4, [r9, #0x5b0] /* DRAM_SDQS1 */
> > + str r4, [r9, #0x524] /* DRAM_SDQS2 */
> > + str r4, [r9, #0x51c] /* DRAM_SDQS3 */
> > +
> > + str r4, [r9, #0x518] /* DRAM_SDQS4 */
> > + str r4, [r9, #0x50c] /* DRAM_SDQS5 */
> > + str r4, [r9, #0x5b8] /* DRAM_SDQS6 */
> > + str r4, [r9, #0x5c0] /* DRAM_SDQS7 */
> > +
> > + str r4, [r9, #0x784] /* GPR_B0DS */
> > + str r4, [r9, #0x788] /* GPR_B1DS */
> > + str r4, [r9, #0x794] /* GPR_B2DS */
> > + str r4, [r9, #0x79c] /* GPR_B3DS */
> > +
> > + str r4, [r9, #0x7a0] /* GPR_B4DS */
> > + str r4, [r9, #0x7a4] /* GPR_B5DS */
> > + str r4, [r9, #0x7a8] /* GPR_B6DS */
> > + str r4, [r9, #0x748] /* GPR_B7DS */
> > +
> > + str r4, [r9, #0x74c] /* GPR_ADDS*/
> > + str r4, [r9, #0x59c] /* DRAM_SODT0*/
> > + str r4, [r9, #0x5a0] /* DRAM_SODT1*/
> > +.endm
> > +
> > +/*
> > + * imx6q_suspend:
> > + *
> > + * Suspend the processor (eg, wait for interrupt).
> > + * Set the DDR into Self Refresh
> > + * IRQs are already disabled.
> > + *
> > + * Registers usage in the imx6q_suspend:
> > + *
> > + * r0: suspend_iram_paddr
> > + * r1: suspend_iram_vaddr
> > + * r2: suspend_iram_size
> > + *
> > + * r8: src_base pointer
> > + * r9: iomux_base pointer
> > + * r10: mmdc_base pointer
> > + * r11: anatop_base pointer
> > + * sp: iram stack
> > + *
> > + * Corrupted registers: r0-r3
> > + */
> > +
> > +ENTRY(imx6q_suspend)
> > + mov r3, sp @ save sp
> > + add sp, r1, r2 @ set sp to top iram stack
> > + sub sp, sp, #16 @ 4 regs ptr
> > + stmfd sp!, {r4 - r12, lr} @ save registers
> > +
> > + add r4, r1, r2
> > + ldr r8, [r4, #-16] @ r8 = src_base pointer
> > + ldr r9, [r4, #-12] @ r9 = iomux_base pointer
> > + ldr r10, [r4, #-8] @ r10 = mmdc_base pointer
> > + ldr r11, [r4, #-4] @ r11 = anatop_base pointer
> > +
> > + /* should not access sp in ddr until resume with cache MMU on */
> > + stmfd sp!, {r3} @ save old sp
> > +
> > + ldr r4, [r8, #SRC_GPR1_OFFSET] @ r8 = src_base pointer
> > + stmfd sp!, {r4} @ save old resume func
> > +
> > + /* Enable weak 2P5 linear regulator */
> > + ldr r4, [r11, #ANATOP_REG_2P5] @ r11 = anatop_base pointer
> > + orr r4, r4, #0x40000
> > + str r4, [r11, #ANATOP_REG_2P5]
> > + mov r6, #1
> > +wait: ldr r4, [r11, #ANATOP_REG_2P5]
> > + and r4, r4, r6, lsl #17 @ output ok?
> > + cmp r4, #0
> > + beq wait
> > +
> > + /* save ddr iomux regs */
> > + ddr_io_save
> > +
>
> Do we really need to have all above codes running in iram? My
> understanding is only the portion of the codes that actually puts
> DDR into self-refresh state and the codes after that need to run
> in iram.
>
> > + /* set ddr to low power mode */
> > + ldr r4, [r10, #MMDC_MAPSR_OFFSET] @ r10 = mmdc_base pointer
> > + bic r4, #MMDC_MAPSR_PSD
> > + str r4, [r10, #MMDC_MAPSR_OFFSET]
> > +refresh:
> > + ldr r4, [r10, #MMDC_MAPSR_OFFSET]
> > + and r4, r4, #MMDC_MAPSR_PSS
> > + cmp r4, #0
> > + beq refresh
> > +
> > + ddr_io_set_lpm
> > +
> > + /* save resume pointer into SRC_GPR1, sp pointer into SRC_GPR2 */
> > + ldr r4, =imx6q_suspend
> > + ldr r5, =imx6q_resume
> > + sub r5, r5, r4 @ r5 = resmue offset
> > + add r5, r0, r5 @ r0 = suspend_iram_paddr, r5 = resmue phy addr
> > + str r5, [r8, #SRC_GPR1_OFFSET] @ r8 = src_base pointer
> > + sub r5, sp, r1 @ r1 = suspend_iram_vaddr, r5 = sp offset
> > + add r5, r0, r5 @ r0 = suspend_iram_paddr, r5 = sp phy addr
> > + str r5, [r8, #SRC_GPR2_OFFSET] @ r8 = src_base pointer
> > +
> > + /* execute a wfi instruction to let SOC go into stop mode */
> > + dsb
> > + wfi
> > +
> > + nop
> > + nop
> > + nop
> > + nop
> > +
> > + /*
> > + * if go here, means there is a wakeup irq pending, we should resume
> > + * system immediately.
> > + */
>
> I'm wondering how this will happen exactly. Is this a real case in
> practical world? If yes, do you have the steps to reproduce it? I'm
> actually asking if this resume path has been tested yet.
>
> > + ddr_io_restore
> > +
> > + /* Disable weak 2P5 linear regulator */
> > + ldr r4, [r11, #ANATOP_REG_2P5] @ r11 = anatop_base pointer
> > + bic r4, #0x40000
> > + str r4, [r11, #ANATOP_REG_2P5]
> > +
> > + ldmfd sp!, {r4} @ drop old resmue func ptr
> > + ldmfd sp!, {r3}
> > + ldmfd sp!, {r4 - r12, lr}
> > + mov sp, r3
> > + mov pc, lr
> > +
> > +/*
> > + * when SOC exit stop mode, arm core restart from here, currently
> > + * are running with MMU off.
> > + */
> > +imx6q_resume:
> > + ldr r0, =MX6Q_SRC_BASE_ADDR
> > + mov r1, #0x0
> > + str r1, [r0, #SRC_GPR1_OFFSET] @ clear SRC_GPR1
> > + ldr sp, [r0, #SRC_GPR2_OFFSET]
> > + str r1, [r0, #SRC_GPR2_OFFSET] @ clear SRC_GPR2
> > +
> > + ldr r9, =MX6Q_IOMUXC_BASE_ADDR
> > + ddr_io_restore
> > +
> > + ldr r0, =MX6Q_MMDC0_BASE_ADDR
> > + ldr r1, [r0, #MMDC_MAPSR_OFFSET]
> > + orr r1, #MMDC_MAPSR_PSD
> > + str r1, [r0, #MMDC_MAPSR_OFFSET]
> > +
> > + /* Disable weak 2P5 linear regulator */
> > + ldr r0, =MX6Q_ANATOP_BASE_ADDR
> > + ldr r1, [r0, #ANATOP_REG_2P5]
> > + bic r1, #0x40000
> > + str r1, [r0, #ANATOP_REG_2P5]
> > +
> > + ldmfd sp!, {r2} @ resmue func ptr
> > + ldmfd sp!, {r3}
> > + ldmfd sp!, {r4 - r12, lr}
> > + mov sp, r3
> > +
> > + /* return back */
> > + mov pc, r2
> > +ENDPROC(imx6q_suspend)
> > diff --git a/arch/arm/plat-mxc/include/mach/common.h b/arch/arm/plat-mxc/include/mach/common.h
> > index 83cca9b..0e1e652 100644
> > --- a/arch/arm/plat-mxc/include/mach/common.h
> > +++ b/arch/arm/plat-mxc/include/mach/common.h
> > @@ -82,6 +82,7 @@ enum mxc_cpu_pwr_mode {
> > WAIT_UNCLOCKED_POWER_OFF, /* WAIT + SRPG */
> > STOP_POWER_ON, /* just STOP */
> > STOP_POWER_OFF, /* STOP + SRPG */
> > + ARM_POWER_OFF, /* STOP + SRPG + ARM power off */
>
> I'm not sure that we need this new mode at all. From the comment,
> the 'ARM power off' is the difference between ARM_POWER_OFF and
> STOP_POWER_OFF. But it's not really the case.
>
> Regards,
> Shawn
>
> > };
> >
> > extern void mx5_cpu_lp_set(enum mxc_cpu_pwr_mode mode);
> > @@ -123,6 +124,7 @@ extern void imx_set_cpu_jump(int cpu, void *jump_addr);
> > extern void imx_src_init(void);
> > extern void imx_src_prepare_restart(void);
> > extern void imx_gpc_init(void);
> > +extern bool imx_gpc_wake_irq_pending(void);
> > extern void imx_gpc_pre_suspend(void);
> > extern void imx_gpc_post_resume(void);
> > extern void imx51_babbage_common_init(void);
> > diff --git a/arch/arm/plat-mxc/include/mach/mx6q.h b/arch/arm/plat-mxc/include/mach/mx6q.h
> > index 254a561..eea2968 100644
> > --- a/arch/arm/plat-mxc/include/mach/mx6q.h
> > +++ b/arch/arm/plat-mxc/include/mach/mx6q.h
> > @@ -27,6 +27,12 @@
> > #define MX6Q_CCM_SIZE 0x4000
> > #define MX6Q_ANATOP_BASE_ADDR 0x020c8000
> > #define MX6Q_ANATOP_SIZE 0x1000
> > +#define MX6Q_SRC_BASE_ADDR 0x020d8000
> > +#define MX6Q_SRC_SIZE 0x4000
> > +#define MX6Q_IOMUXC_BASE_ADDR 0x020e0000
> > +#define MX6Q_IOMUXC_SIZE 0x4000
> > +#define MX6Q_MMDC0_BASE_ADDR 0x021b0000
> > +#define MX6Q_MMDC0_SIZE 0x4000
> > #define MX6Q_UART4_BASE_ADDR 0x021f0000
> > #define MX6Q_UART4_SIZE 0x4000
> >
> > --
> > 1.7.4.1
>
>
> _______________________________________________
> linux-arm-kernel mailing list
> linux-arm-kernel at lists.infradead.org
> http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
>
next prev parent reply other threads:[~2012-01-04 7:24 UTC|newest]
Thread overview: 8+ messages / expand[flat|nested] mbox.gz Atom feed top
2011-12-31 6:23 [PATCH 2/2] ARM: imx6q: add cpu suspend/resume support in IRAM Jason Chen
2012-01-03 14:24 ` Shawn Guo
2012-01-04 7:24 ` Jason Chen [this message]
2012-01-04 9:13 ` Russell King - ARM Linux
2012-01-05 7:32 ` Shawn Guo
2012-01-04 11:02 ` Linus Walleij
2012-01-05 7:58 ` Shawn Guo
2012-01-05 7:53 ` Jason Chen
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20120104072405.GA2687@weitway \
--to=jason.chen@freescale.com \
--cc=linux-arm-kernel@lists.infradead.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.