ARM: imx6q: add cpu suspend/resume support in IRAM
Subsystems (3)
PATCH 2/2 ARM: imx6q: add cpu suspend/resume support in IRAM
For most power saving, this patch make arm core stop and ddr go into self-refresh mode.
It already be tested on Freescale imx6q-arm2 board.
--- 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(-)
arch/arm/mach-imx/Makefile +1 -1
@@ -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
arch/arm/mach-imx/clock-imx6q.c +25 -12
@@ -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 : + 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 ;break ;default :return -EINVAL ; } -writel_relaxed (val,CLPCR ); +writel_relaxed (clpcr,CLPCR ); +writel_relaxed (ccr,CCR );return 0 ; }
arch/arm/mach-imx/gpc.c +21 -0
@@ -17,15 +17,36 @@#include <linux/of_irq.h> #include <asm/hardware/gic.h> +#define GPC_CNTR 0x000#define GPC_IMR1 0x008 +#define GPC_ISR1 0x018 +#define GPC_ISR2 0x01c +#define GPC_ISR3 0x020 +#define GPC_ISR4 0x024#define GPC_PGC_CPU_PDN 0x2a0#define IMR_NUM 4 +#define ISR_NUM 4static 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 ;
arch/arm/mach-imx/pm-imx6q.c +124 -4
170 lines not on this page · raw message
arch/arm/mach-imx/suspend-imx6q.S new file +308 -0
308 lines not on this page · raw message
arch/arm/plat-mxc/include/mach/common.h +2 -0
@@ -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 */ };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 );
arch/arm/plat-mxc/include/mach/mx6q.h +6 -0
@@ -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
Signature · 1 line
1.7.4.1
PATCH 2/2 ARM: imx6q: add cpu suspend/resume support in IRAM
Quoted 1 line · Jason Chen
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.
Quoted 4 lines
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.
Quoted 12 lines
Signed-off-by Jason Chen <[email protected]>Signed-off-by Jason Chen <[email protected]>--- 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.
Quoted 56 lines
arch/arm/mach-imx/Makefile +1
-1@@ -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.oarch/arm/mach-imx/clock-imx6q.c +10
-7@@ -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 :
arch/arm/mach-imx/clock-imx6q.c static struct clk_lookup lookups[] = { · line 1945
Quoted 10 lines
arch/arm/mach-imx/clock-imx6q.c +6
-4- 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.
arch/arm/mach-imx/clock-imx6q.c static struct clk_lookup lookups[] = { · line 1952
Quoted 7 lines
arch/arm/mach-imx/clock-imx6q.c +7
-0+ 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?
Quoted 15 lines
arch/arm/mach-imx/clock-imx6q.c +2
-1break ;default :return -EINVAL ; } -writel_relaxed (val,CLPCR ); +writel_relaxed (clpcr,CLPCR ); +writel_relaxed (ccr,CCR );return 0 ; }arch/arm/mach-imx/gpc.c +0
-0
arch/arm/mach-imx/gpc.c line 20
Quoted 4 lines
arch/arm/mach-imx/gpc.c +1
-0@@ -17,15 +17,36 @@#include <linux/of_irq.h> #include <asm/hardware/gic.h> +#define GPC_CNTR 0x000
Where is it used?
arch/arm/mach-imx/gpc.c line 25
Quoted 5 lines
arch/arm/mach-imx/gpc.c +4
-0#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?
Quoted 58 lines
arch/arm/mach-imx/gpc.c +16
-0#define GPC_PGC_CPU_PDN 0x2a0#define IMR_NUM 4 +#define ISR_NUM 4static 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 ;arch/arm/mach-imx/pm-imx6q.c +14
-0@@ -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,
arch/arm/mach-imx/pm-imx6q.c line 47
Quoted 10 lines
arch/arm/mach-imx/pm-imx6q.c +6
-1+ 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.
Quoted 6 lines
arch/arm/mach-imx/pm-imx6q.c +3
-0+cpu_do_idle (); +return 0 ; } +static void imx6q_prepare_suspend_iram (void )
arch/arm/mach-imx/pm-imx6q.c line 63
Quoted 10 lines
arch/arm/mach-imx/pm-imx6q.c +10
-0+{ +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.
Quoted 4 lines
arch/arm/mach-imx/pm-imx6q.c +1
-0static int imx6q_pm_enter (suspend_state_t state) {switch (state) { +case PM_SUSPEND_STANDBY :
arch/arm/mach-imx/pm-imx6q.c line 76
Quoted 10 lines
arch/arm/mach-imx/pm-imx6q.c +8
-1case 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.
Quoted 42 lines
arch/arm/mach-imx/pm-imx6q.c +26
-2imx_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;
arch/arm/mach-imx/pm-imx6q.c static int imx6q_pm_enter(suspend_state_t state) · line 129
Quoted 10 lines
arch/arm/mach-imx/pm-imx6q.c +10
-0+ + 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().
Quoted 29 lines
arch/arm/mach-imx/pm-imx6q.c +22
-0+ +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 ;
arch/arm/mach-imx/pm-imx6q.c void __init imx6q_pm_init(void) · line 175
Quoted 10 lines
arch/arm/mach-imx/pm-imx6q.c +10
-0+ } +/* +* 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?
arch/arm/mach-imx/pm-imx6q.c void __init imx6q_pm_init(void) · line 184
Quoted 9 lines
arch/arm/mach-imx/pm-imx6q.c +9
-0+ 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)
arch/arm/mach-imx/pm-imx6q.c void __init imx6q_pm_init(void) · line 189
Quoted 5 lines
arch/arm/mach-imx/pm-imx6q.c +5
-0+ + 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.
Quoted 22 lines
arch/arm/mach-imx/pm-imx6q.c +0
-0}arch/arm/mach-imx/suspend-imx6q.S new file +15
-0@@ -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>
arch/arm/mach-imx/suspend-imx6q.S line 25
Quoted 10 lines
arch/arm/mach-imx/suspend-imx6q.S new file +10
-0+ +#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.
Quoted 192 lines
arch/arm/mach-imx/suspend-imx6q.S new file +192
-0+ 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
arch/arm/mach-imx/suspend-imx6q.S line 227
Quoted 10 lines
arch/arm/mach-imx/suspend-imx6q.S new file +10
-0+ 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.
Quoted 25 lines
arch/arm/mach-imx/suspend-imx6q.S new file +25
-0+ /* 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
arch/arm/mach-imx/suspend-imx6q.S line 262
Quoted 10 lines
arch/arm/mach-imx/suspend-imx6q.S new file +10
-0+ + 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.
Quoted 51 lines
arch/arm/mach-imx/suspend-imx6q.S new file +46
-0+ 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)arch/arm/plat-mxc/include/mach/common.h +0
-0
arch/arm/plat-mxc/include/mach/common.h enum mxc_cpu_pwr_mode { · line 85
Quoted 4 lines
arch/arm/plat-mxc/include/mach/common.h +1
-0@@ -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
Quoted 30 lines
arch/arm/plat-mxc/include/mach/common.h +1
-0};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 );arch/arm/plat-mxc/include/mach/mx6q.h +6
-0@@ -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 0x4000Signature · 1 line
1.7.4.1
PATCH 2/2 ARM: imx6q: add cpu suspend/resume support in IRAM
hi, Shawn,
I'll separate this patches and update with your comments.
Quoted 757 lines · Shawn Guo
Quoted 1 line · Jason Chen
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.
Quoted 4 lines
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.
Quoted 12 lines
Signed-off-by Jason Chen <[email protected]>Signed-off-by Jason Chen <[email protected]>--- 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.
Quoted 56 lines
arch/arm/mach-imx/Makefile +1
-1@@ -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.oarch/arm/mach-imx/clock-imx6q.c +10
-7@@ -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 :arch/arm/mach-imx/clock-imx6q.c static struct clk_lookup lookups[] = { · line 1945
Quoted 10 lines
arch/arm/mach-imx/clock-imx6q.c +6
-4- 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.
arch/arm/mach-imx/clock-imx6q.c static struct clk_lookup lookups[] = { · line 1952
Quoted 7 lines
arch/arm/mach-imx/clock-imx6q.c +7
-0+ 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_OFFandARM_POWER_OFFis all about bitsBM_CLPCR_WB_PER_AT_LPMandBM_CCR_RBC_EN. Can you elaborate why these two bits need to be set for suspend with DDR in self-refresh state?Quoted 15 lines
arch/arm/mach-imx/clock-imx6q.c +2
-1break ;default :return -EINVAL ; } -writel_relaxed (val,CLPCR ); +writel_relaxed (clpcr,CLPCR ); +writel_relaxed (ccr,CCR );return 0 ; }arch/arm/mach-imx/gpc.c +0
-0arch/arm/mach-imx/gpc.c line 20
Quoted 4 lines
arch/arm/mach-imx/gpc.c +1
-0@@ -17,15 +17,36 @@#include <linux/of_irq.h> #include <asm/hardware/gic.h> +#define GPC_CNTR 0x000Where is it used?
arch/arm/mach-imx/gpc.c line 25
Quoted 5 lines
arch/arm/mach-imx/gpc.c +4
-0#define GPC_IMR1 0x008 +#define GPC_ISR1 0x018 +#define GPC_ISR2 0x01c +#define GPC_ISR3 0x020 +#define GPC_ISR4 0x024It seems that only definition
GPC_ISR1is used?Quoted 58 lines
arch/arm/mach-imx/gpc.c +16
-0#define GPC_PGC_CPU_PDN 0x2a0#define IMR_NUM 4 +#define ISR_NUM 4static 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 ;arch/arm/mach-imx/pm-imx6q.c +14
-0@@ -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,arch/arm/mach-imx/pm-imx6q.c line 47
Quoted 10 lines
arch/arm/mach-imx/pm-imx6q.c +6
-1+ 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.
Quoted 6 lines
arch/arm/mach-imx/pm-imx6q.c +3
-0+cpu_do_idle (); +return 0 ; } +static void imx6q_prepare_suspend_iram (void )arch/arm/mach-imx/pm-imx6q.c line 63
Quoted 10 lines
arch/arm/mach-imx/pm-imx6q.c +10
-0+{ +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 onsuspend_in_irambelow.Quoted 4 lines
arch/arm/mach-imx/pm-imx6q.c +1
-0static int imx6q_pm_enter (suspend_state_t state) {switch (state) { +case PM_SUSPEND_STANDBY :arch/arm/mach-imx/pm-imx6q.c line 76
Quoted 10 lines
arch/arm/mach-imx/pm-imx6q.c +8
-1case 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_OFFandARM_POWER_OFF. When I addedSTOP_POWER_OFFsupport, the DDR self refresh support was missed there. When this feature is available, we can just add it on top ofSTOP_POWER_OFFrather than inventing a new state.Quoted 42 lines
arch/arm/mach-imx/pm-imx6q.c +26
-2imx_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;arch/arm/mach-imx/pm-imx6q.c static int imx6q_pm_enter(suspend_state_t state) · line 129
Quoted 10 lines
arch/arm/mach-imx/pm-imx6q.c +10
-0+ + 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().Quoted 29 lines
arch/arm/mach-imx/pm-imx6q.c +22
-0+ +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 ;arch/arm/mach-imx/pm-imx6q.c void __init imx6q_pm_init(void) · line 175
Quoted 10 lines
arch/arm/mach-imx/pm-imx6q.c +10
-0+ } +/* +* 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?arch/arm/mach-imx/pm-imx6q.c void __init imx6q_pm_init(void) · line 184
Quoted 9 lines
arch/arm/mach-imx/pm-imx6q.c +9
-0+ 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)arch/arm/mach-imx/pm-imx6q.c void __init imx6q_pm_init(void) · line 189
Quoted 5 lines
arch/arm/mach-imx/pm-imx6q.c +5
-0+ + suspend_in_iram = (void *)imx6q_iram_pm.suspend_iram_vaddr; + + }else + suspend_in_iram =NULL ;The
suspend_in_iramis already initialized as NULL, I guess.Quoted 22 lines
arch/arm/mach-imx/pm-imx6q.c +0
-0}arch/arm/mach-imx/suspend-imx6q.S new file +15
-0@@ -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>arch/arm/mach-imx/suspend-imx6q.S line 25
Quoted 10 lines
arch/arm/mach-imx/suspend-imx6q.S new file +10
-0+ +#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 theddr_io_xxxfunctions.Quoted 192 lines
arch/arm/mach-imx/suspend-imx6q.S new file +192
-0+ 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, #0x40000arch/arm/mach-imx/suspend-imx6q.S line 227
Quoted 10 lines
arch/arm/mach-imx/suspend-imx6q.S new file +10
-0+ 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.
Quoted 25 lines
arch/arm/mach-imx/suspend-imx6q.S new file +25
-0+ /* 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 + wfiarch/arm/mach-imx/suspend-imx6q.S line 262
Quoted 10 lines
arch/arm/mach-imx/suspend-imx6q.S new file +10
-0+ + 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.
Quoted 51 lines
arch/arm/mach-imx/suspend-imx6q.S new file +46
-0+ 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)arch/arm/plat-mxc/include/mach/common.h +0
-0arch/arm/plat-mxc/include/mach/common.h enum mxc_cpu_pwr_mode { · line 85
Quoted 4 lines
arch/arm/plat-mxc/include/mach/common.h +1
-0@@ -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_OFFandSTOP_POWER_OFF. But it's not really the case.Regards,
ShawnQuoted 30 lines
arch/arm/plat-mxc/include/mach/common.h +1
-0};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 );arch/arm/plat-mxc/include/mach/mx6q.h +6
-0@@ -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 0x4000Signature · 1 line
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
PATCH 2/2 ARM: imx6q: add cpu suspend/resume support in IRAM
In reply to Jason Chenarch/arm/mach-imx/pm-imx6q.c void __init imx6q_pm_init(void) · line 174
Quoted 4 lines · Jason Chen
arch/arm/mach-imx/pm-imx6q.c +4
-0+ imx6q_iram_pm.suspend_iram_vaddr = +__arm_ioremap (imx6q_iram_pm.suspend_iram_paddr, + imx6q_iram_pm.suspend_iram_size, +MT_MEMORY_NONCACHED );
Stop going beneath the API covers.
PATCH 2/2 ARM: imx6q: add cpu suspend/resume support in IRAM
Quoted 8 lines · Russell King - ARM Linux
arch/arm/mach-imx/pm-imx6q.c void __init imx6q_pm_init(void) · line 174
Quoted 4 lines · Jason Chen
arch/arm/mach-imx/pm-imx6q.c +4
-0+ imx6q_iram_pm.suspend_iram_vaddr = +__arm_ioremap (imx6q_iram_pm.suspend_iram_paddr, + imx6q_iram_pm.suspend_iram_size, +MT_MEMORY_NONCACHED );Stop going beneath the API covers.
Assume that you are suggesting __arch_ioremap() should be used instead, correct?
Signature · 2 lines
Regards, Shawn
PATCH 2/2 ARM: imx6q: add cpu suspend/resume support in IRAM
In reply to Jason ChenQuoted 9 lines · Jason Chen
?void
__initimx6q_pm_init(void)
?{
? ? ? ?/*(quoted diff) +2
-0@@ -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 */
(...)
Quoted 1 line
arch/arm/mach-imx/suspend-imx6q.S +0
-0
(...)
Quoted 6 lines
+.macro
ddr_io_save
+ ? ? ? ldr ? ? r4, [r9, #0x5ac] /*DRAM_DQM0*/
+ ? ? ? ldr ? ? r5, [r9, #0x5b4] /*DRAM_DQM1*/
+ ? ? ? ldr ? ? r6, [r9, #0x528] /*DRAM_DQM2*/
+ ? ? ? ldr ? ? r7, [r9, #0x520] /*DRAM_DQM3*/
+ ? ? ? stmfd ? sp!, {r4-r7}
(etc)
This is not an elegant solution. I know other platforms have done similar things but it's still not elegant.
What we need it real handling of the on-chip memory pool and possibility to compile and link directly into the on-chip memory areas.
Compare to what we have for TCM:Documentation/arm/tcm.txt
Implemented in:arch/arm/kernel/tcm.c
Linker trickery in:arch/arm/kernel/vmlinux.lds.S
An approach similar to this would make it possible to:
- Manage the on-chip heap in a structured way
- Write the above assembler code in C and compile it directly into on-chip RAM
- Share the same code with other archs and possibly merge it with some of the TCM handling code top create common onchip RAM handling
- Optionally free up the .TEXT memory pages used by this code after copying it to on-chip RAM (if that RAM is persistent, as it seems to be)
- Make it possible to define MT_MEMORY_ONCHIP (if MT_MEMORY_ITCM isn't good enough) in arch/arm/include/asm/mach/map.h so everybody can use this.
It is also a bit more work and patch iteration involved. But the end solution is very appealing IMO. Also, multi-platform binaries will complicate the above a bit (especially if, say, two platforms have their on-chip RAM in the same location...) but I am sure it can be solved with proper cleverness and step-by-step measures.
Yours,
Linus Walleij
PATCH 2/2 ARM: imx6q: add cpu suspend/resume support in IRAM
Quoted 56 lines · Linus Walleij
Quoted 9 lines · Jason Chen
?void
__initimx6q_pm_init(void)
?{
? ? ? ?/*(quoted diff) +2
-0@@ -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 */(...)
Quoted 1 line
arch/arm/mach-imx/suspend-imx6q.S +0
-0(...)
Quoted 6 lines
+.macro
ddr_io_save
+ ? ? ? ldr ? ? r4, [r9, #0x5ac] /*DRAM_DQM0*/
+ ? ? ? ldr ? ? r5, [r9, #0x5b4] /*DRAM_DQM1*/
+ ? ? ? ldr ? ? r6, [r9, #0x528] /*DRAM_DQM2*/
+ ? ? ? ldr ? ? r7, [r9, #0x520] /*DRAM_DQM3*/
+ ? ? ? stmfd ? sp!, {r4-r7}(etc)
This is not an elegant solution. I know other platforms have done similar things but it's still not elegant.
What we need it real handling of the on-chip memory pool and possibility to compile and link directly into the on-chip memory areas.
Compare to what we have for TCM:
Documentation/arm/tcm.txt
Implemented in:arch/arm/kernel/tcm.c
Linker trickery in:arch/arm/kernel/vmlinux.lds.SAn approach similar to this would make it possible to:
- Manage the on-chip heap in a structured way
- Write the above assembler code in C and compile it directly into on-chip RAM
- Share the same code with other archs and possibly merge it with some of the TCM handling code top create common onchip RAM handling
- Optionally free up the .TEXT memory pages used by this code after copying it to on-chip RAM (if that RAM is persistent, as it seems to be)
- Make it possible to define
MT_MEMORY_ONCHIP(ifMT_MEMORY_ITCMisn't good enough) inarch/arm/include/asm/mach/map.hso everybody can use this.
Quoted 7 lines
It is also a bit more work and patch iteration involved. But the end solution is very appealing IMO. Also, multi-platform binaries will complicate the above a bit (especially if, say, two platforms have their on-chip RAM in the same location...) but I am sure it can be solved with proper cleverness and step-by-step measures.
It looks really interesting and appealing. We absolutely should take a deep look into the suggestion. Thanks, Linus.
Signature · 2 lines
Regards, Shawn
PATCH 2/2 ARM: imx6q: add cpu suspend/resume support in IRAM
Quoted 64 lines · Shawn Guo
Quoted 56 lines · Linus Walleij
Quoted 9 lines · Jason Chen
?void
__initimx6q_pm_init(void)
?{
? ? ? ?/*(quoted diff) +2
-0@@ -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 */(...)
Quoted 1 line
arch/arm/mach-imx/suspend-imx6q.S +0
-0(...)
Quoted 6 lines
+.macro
ddr_io_save
+ ? ? ? ldr ? ? r4, [r9, #0x5ac] /*DRAM_DQM0*/
+ ? ? ? ldr ? ? r5, [r9, #0x5b4] /*DRAM_DQM1*/
+ ? ? ? ldr ? ? r6, [r9, #0x528] /*DRAM_DQM2*/
+ ? ? ? ldr ? ? r7, [r9, #0x520] /*DRAM_DQM3*/
+ ? ? ? stmfd ? sp!, {r4-r7}(etc)
This is not an elegant solution. I know other platforms have done similar things but it's still not elegant.
What we need it real handling of the on-chip memory pool and possibility to compile and link directly into the on-chip memory areas.
Compare to what we have for TCM:
Documentation/arm/tcm.txt
Implemented in:arch/arm/kernel/tcm.c
Linker trickery in:arch/arm/kernel/vmlinux.lds.SAn approach similar to this would make it possible to:
- Manage the on-chip heap in a structured way
- Write the above assembler code in C and compile it directly into on-chip RAM
- Share the same code with other archs and possibly merge it with some of the TCM handling code top create common onchip RAM handling
- Optionally free up the .TEXT memory pages used by this code after copying it to on-chip RAM (if that RAM is persistent, as it seems to be)
- Make it possible to define
MT_MEMORY_ONCHIP(ifMT_MEMORY_ITCMisn't good enough) inarch/arm/include/asm/mach/map.hso everybody can use this.Quoted 7 lines
It is also a bit more work and patch iteration involved. But the end solution is very appealing IMO. Also, multi-platform binaries will complicate the above a bit (especially if, say, two platforms have their on-chip RAM in the same location...) but I am sure it can be solved with proper cleverness and step-by-step measures.
Quoted 3 lines
It looks really interesting and appealing. We absolutely should take a deep look into the suggestion. Thanks, Linus.
Agree, thanks, Linus.
Quoted 10 lines
Signature · 8 lines
Regards, Shawn _______________________________________________ linux-arm-kernel mailing list linux-arm-kernel at lists.infradead.org http://lists.infradead.org/mailman/listinfo/linux-arm-kernel