diff options
author | Ranjani <ra5478@freescale.com> | 2010-07-23 14:16:42 -0500 |
---|---|---|
committer | Ranjani <ra5478@freescale.com> | 2010-07-23 17:20:14 -0500 |
commit | 079f9fa4e862d6ec40b361217ecebb53a7533fad (patch) | |
tree | 990d2966df25830e579f36d229371043fa73b7f5 /arch | |
parent | 74af4f274a4c47f3462c63eef94f45ae9bd57c84 (diff) |
ENGR00125546: MX508: Add STOP mode support.
Add support for MX508 to enter STOP mode. The DDR needs to put into
self-refresh manually, hence suspend code needs to run from OCRAM.
Signed-off-by: Ranjani Vaidyanathan <ra5478@freescale.com>
Diffstat (limited to 'arch')
-rw-r--r-- | arch/arm/mach-mx5/Makefile | 8 | ||||
-rw-r--r-- | arch/arm/mach-mx5/clock_mx50.c | 5 | ||||
-rw-r--r-- | arch/arm/mach-mx5/mx50_arm2_pmic_mc13892.c | 6 | ||||
-rw-r--r-- | arch/arm/mach-mx5/mx50_suspend.S | 220 | ||||
-rw-r--r-- | arch/arm/mach-mx5/pm.c | 95 | ||||
-rw-r--r-- | arch/arm/mach-mx5/system.c | 7 | ||||
-rw-r--r-- | arch/arm/plat-mxc/include/mach/mx5x.h | 5 |
7 files changed, 303 insertions, 43 deletions
diff --git a/arch/arm/mach-mx5/Makefile b/arch/arm/mach-mx5/Makefile index 5de332e2f9b3..453b96cbe5ed 100644 --- a/arch/arm/mach-mx5/Makefile +++ b/arch/arm/mach-mx5/Makefile @@ -6,11 +6,11 @@ obj-y := system.o iomux.o cpu.o mm.o devices.o serial.o dma.o lpmodes.o pm.o \ -sdram_autogating.o bus_freq.o usb_dr.o usb_h1.o usb_h2.o dummy_gpio.o wfi.o suspend.o early_setup.o +sdram_autogating.o bus_freq.o usb_dr.o usb_h1.o usb_h2.o dummy_gpio.o early_setup.o -obj-$(CONFIG_ARCH_MX51) += clock.o -obj-$(CONFIG_ARCH_MX53) += clock.o -obj-$(CONFIG_ARCH_MX50) += clock_mx50.o +obj-$(CONFIG_ARCH_MX51) += clock.o suspend.o +obj-$(CONFIG_ARCH_MX53) += clock.o suspend.o +obj-$(CONFIG_ARCH_MX50) += clock_mx50.o mx50_suspend.o obj-$(CONFIG_MACH_MX51_3DS) += mx51_3stack.o mx51_3stack_gpio.o mx51_3stack_pmic_mc13892.o obj-$(CONFIG_MACH_MX51_BABBAGE) += mx51_babbage.o mx51_babbage_gpio.o mx51_babbage_pmic_mc13892.o diff --git a/arch/arm/mach-mx5/clock_mx50.c b/arch/arm/mach-mx5/clock_mx50.c index ea7ba9da4ca4..d87ce481e550 100644 --- a/arch/arm/mach-mx5/clock_mx50.c +++ b/arch/arm/mach-mx5/clock_mx50.c @@ -3035,7 +3035,7 @@ int __init mx50_clocks_init(unsigned long ckil, unsigned long osc, unsigned long /* Turn off all possible clocks */ if (mxc_jtag_enabled) { __raw_writel(1 << MXC_CCM_CCGR0_CG0_OFFSET | - 1 << MXC_CCM_CCGR0_CG2_OFFSET | + 3 << MXC_CCM_CCGR0_CG2_OFFSET | 3 << MXC_CCM_CCGR0_CG3_OFFSET | 3 << MXC_CCM_CCGR0_CG4_OFFSET | 3 << MXC_CCM_CCGR0_CG8_OFFSET | @@ -3067,7 +3067,8 @@ int __init mx50_clocks_init(unsigned long ckil, unsigned long osc, unsigned long 3 << MXC_CCM_CCGR6_CG9_OFFSET | 3 << MXC_CCM_CCGR6_CG12_OFFSET | 3 << MXC_CCM_CCGR6_CG13_OFFSET | - 2 << MXC_CCM_CCGR6_CG14_OFFSET, MXC_CCM_CCGR6); + 2 << MXC_CCM_CCGR6_CG14_OFFSET | + 3 << MXC_CCM_CCGR6_CG15_OFFSET, MXC_CCM_CCGR6); __raw_writel(0, MXC_CCM_CCGR7); diff --git a/arch/arm/mach-mx5/mx50_arm2_pmic_mc13892.c b/arch/arm/mach-mx5/mx50_arm2_pmic_mc13892.c index d852fb0e016b..9b97fece73d8 100644 --- a/arch/arm/mach-mx5/mx50_arm2_pmic_mc13892.c +++ b/arch/arm/mach-mx5/mx50_arm2_pmic_mc13892.c @@ -328,9 +328,9 @@ static int mc13892_regulator_init(struct mc13892 *mc13892) { unsigned int value, register_mask; printk("Initializing regulators for mx50 arm2.\n"); - sw2_init.constraints.state_mem.uV = 1200000; - sw1_init.constraints.state_mem.uV = 1000000; + /* TBD later. */ +#if 0 /* enable standby controll for all regulators */ pmic_read_reg(REG_MODE_0, &value, 0xffffff); value |= REG_MODE_0_ALL_MASK; @@ -339,6 +339,7 @@ static int mc13892_regulator_init(struct mc13892 *mc13892) pmic_read_reg(REG_MODE_1, &value, 0xffffff); value |= REG_MODE_1_ALL_MASK; pmic_write_reg(REG_MODE_1, value, 0xffffff); +#endif /* enable switch audo mode */ pmic_read_reg(REG_IDENTIFICATION, &value, 0xffffff); @@ -360,7 +361,6 @@ static int mc13892_regulator_init(struct mc13892 *mc13892) (SWMODE_AUTO << SW4MODE_LSB); pmic_write_reg(REG_SW_5, value, 0xffffff); } - /* Enable coin cell charger */ value = BITFVAL(COINCHEN, 1) | BITFVAL(VCOIN, VCOIN_3_0V); register_mask = BITFMASK(COINCHEN) | BITFMASK(VCOIN); diff --git a/arch/arm/mach-mx5/mx50_suspend.S b/arch/arm/mach-mx5/mx50_suspend.S new file mode 100644 index 000000000000..25a49d44c4d9 --- /dev/null +++ b/arch/arm/mach-mx5/mx50_suspend.S @@ -0,0 +1,220 @@ +/* + * Copyright (C) 2010 Freescale Semiconductor, Inc. All Rights Reserved. + */ +/* + * 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> + +#define ARM_CTRL_DCACHE 1 << 2 +#define ARM_CTRL_ICACHE 1 << 12 +#define ARM_AUXCR_L2EN 1 << 1 + +/* + * mx50_suspend + * + * Suspend the processor (eg, wait for interrupt). + * Set the DDR into Self Refresh + * IRQs are already disabled. + */ +ENTRY(mx50_suspend) + stmfd sp!, {r4,r5,r6,r7,r9,r10,r11} @ Save registers + + mov r6, r0 @save databahn address + /* Disable L1 caches */ + mrc p15, 0, r0, c1, c0, 0 @ R0 = system control reg + bic r0, r0, #ARM_CTRL_ICACHE @ Disable ICache + bic r0, r0, #ARM_CTRL_DCACHE @ Disable DCache + mcr p15, 0, r0, c1, c0, 0 @ Update system control reg + + mrc p15, 1, r0, c0, c0, 1 @ Read CLIDR + ands r3, r0, #0x7000000 @ Isolate level of coherency + mov r3, r3, lsr #23 @ Cache level value (naturally aligned) + beq FinishedClean + mov r10, #0 +Loop1Clean: + add r2, r10, r10, lsr #1 @ Work out cache level + mov r1, r0, lsr r2 @ R0 bottom 3 bits = Cache Type + @ for this level + and r1, r1, #7 @ Get those 3 bits alone + cmp r1, #2 + blt SkipClean @ No cache or only instruction cache + @ at this level + mcr p15, 2, r10, c0, c0, 0 @ Write the Cache Size selection register + mov r1, #0 + .long 0xF57FF06F @ ISB + mrc p15, 1, r1, c0, c0, 0 @ Reads current Cache Size ID register + and r2, r1, #7 @ Extract the line length field + add r2, r2, #4 @ Add 4 for the line length offset + @ (log2 16 bytes) + ldr r4, =0x3FF + ands r4, r4, r1, lsr #3 @ R4 is the max number on the + @ way size (right aligned) + clz r5, r4 @ R5 is the bit position of the way + @ size increment + ldr r7, =0x00007FFF + ands r7, r7, r1, lsr #13 @ R7 is the max number of the index + @ size (right aligned) +Loop2Clean: + mov r9, r4 @ R9 working copy of the max way size + @ (right aligned) +Loop3Clean: + orr r11, r10, r9, lsl r5 @ Factor in the way number and cache + @number into R11 + orr r11, r11, r7, lsl r2 @ Factor in the index number + mcr p15, 0, r11, c7, c14, 2 @ Clean and invalidate by set/way + subs r9, r9, #1 @ Decrement the way number + bge Loop3Clean + subs r7, r7, #1 @ Decrement the index + bge Loop2Clean +SkipClean: + add r10, r10, #2 @ Increment the cache number + cmp r3, r10 + bgt Loop1Clean + +FinishedClean: + + /* Disable L2 cache */ + mrc p15, 0, r0, c1, c0, 1 @ R0 = auxiliary control reg + bic r0, r0, #ARM_AUXCR_L2EN @ Disable L2 cache + mcr p15, 0, r0, c1, c0, 1 @ Update aux control reg + +/* Wait for the databahn to idle + Meaning, no access to the databahn is + being made. +*/ +EnterWFI: + ldr r1, [r6, #0x8] + ldr r0,[r1] + and r0, r0, #0x100 + ldr r2, =0x100 + cmp r0, r2 + beq EnterWFI + /* Enter self-refresh mode */ + ldr r1,[r6, #0x4] + ldr r0,[r1] + orr r0,r0,#0x1 + str r0,[r1] + +LoopCKE0: + /* Wait for CKE = 0 */ + ldr r1,[r6, #0x8] + ldr r0,[r1] + and r0, r0, #0x10 + ldr r2, =0x10 + cmp r0, r2 + beq LoopCKE0 + /* Stop controller */ + ldr r1,[r6, #0] + ldr r0,[r1] + and r0,r0,#0xfffffffe + str r0,[r1] + + .long 0xe320f003 @ Opcode for WFI + + + /* Start controller */ + ldr r1,[r6, #0] + ldr r0,[r1] + orr r0,r0,#0x1 + str r0,[r1] + +LoopPHY: + /* Wait for PHY ready */ + ldr r1,[r6, #0xC] + ldr r0,[r1] + and r0, r0, #0xfffffffe + ldr r2, =0x0 + cmp r0, r2 + beq LoopPHY + /*Leave self-refresh mode */ + ldr r1,[r6, #4] + ldr r0,[r1] + and r0,r0,#0xfffffffe + str r0,[r1] + +LoopCKE1: + /*Wait for CKE = 1 */ + ldr r1,[r6, #0x8] + ldr r0,[r1] + and r0, r0, #0x10 + ldr r2, =0x0 + cmp r0, r2 + beq LoopCKE1 + + mov r0, #0 + mcr p15, 0, r0, c7, c5, 0 @ Invalidate inst cache + +/* Invalidate data caches */ + mrc p15, 1, r0, c0, c0, 1 @ Read CLIDR + ands r3, r0, #0x7000000 @ Isolate level of coherency + mov r3, r3, lsr #23 @ Cache level value (naturally aligned) + beq FinishedInvalidate + mov r10, #0 +Loop1Invalidate: + add r2, r10, r10, lsr #1 @ Work out cache level + mov r1, r0, lsr r2 @ R0 bottom 3 bits = Cache + @Type for this level + and r1, r1, #7 @ Get those 3 bits alone + cmp r1, #2 + blt SkipInvalidate @ No cache or only instruction cache + @at this level + mcr p15, 2, r10, c0, c0, 0 @ Write the Cache Size selection register + mov r1, #0 + .long 0xF57FF06F @ ISB + mrc p15, 1, r1, c0, c0, 0 @ Reads current Cache Size ID register + and r2, r1, #7 @ Extract the line length field + add r2, r2, #4 @ Add 4 for the line length offset + @(log2 16 bytes) + ldr r4, =0x3FF + ands r4, r4, r1, lsr #3 @ R4 is the max number on the way + @size (right aligned) + clz r5, r4 @ R5 is the bit position of the way + @ size increment + ldr r7, =0x00007FFF + ands r7, r7, r1, lsr #13 @ R7 is the max number of the + @ index size (right aligned) +Loop2Invalidate: + mov r9, r4 @ R9 working copy of the max way + @ size (right aligned) +Loop3Invalidate: + orr r11, r10, r9, lsl r5 @ Factor in the way number and cache + @ number into R11 + orr r11, r11, r7, lsl r2 @ Factor in the index number + mcr p15, 0, r11, c7, c6, 2 @ Invalidate by set/way + subs r9, r9, #1 @ Decrement the way number + bge Loop3Invalidate + subs r7, r7, #1 @ Decrement the index + bge Loop2Invalidate +SkipInvalidate: + add r10, r10, #2 @ Increment the cache number + cmp r3, r10 + bgt Loop1Invalidate + +FinishedInvalidate: + + /* Enable L2 cache */ + mrc p15, 0, r0, c1, c0, 1 @ R0 = auxiliary control reg + orr r0, r0, #ARM_AUXCR_L2EN @ Enable L2 cache + mcr p15, 0, r0, c1, c0, 1 @ Update aux control reg + + /* Enable L1 caches */ + mrc p15, 0, r0, c1, c0, 0 @ R0 = system control reg + orr r0, r0, #ARM_CTRL_ICACHE @ Enable ICache + orr r0, r0, #ARM_CTRL_DCACHE @ Enable DCache + mcr p15, 0, r0, c1, c0, 0 @ Update system control reg + + /* Restore registers */ + ldmfd sp!, {r4,r5,r6,r7,r9,r10,r11} + mov pc, lr + + .type mx50_do_suspend, #object +ENTRY(mx50_do_suspend) + .word mx50_suspend + .size mx50_suspend, . - mx50_suspend diff --git a/arch/arm/mach-mx5/pm.c b/arch/arm/mach-mx5/pm.c index 1cfb48aee96a..fb144b551f37 100644 --- a/arch/arm/mach-mx5/pm.c +++ b/arch/arm/mach-mx5/pm.c @@ -28,6 +28,10 @@ #define MXC_SRPG_EMPGC0_SRPGCR (IO_ADDRESS(GPC_BASE_ADDR) + 0x2C0) #define MXC_SRPG_EMPGC1_SRPGCR (IO_ADDRESS(GPC_BASE_ADDR) + 0x2D0) +#define DATABAHN_CTL_REG0 0 +#define DATABAHN_CTL_REG19 0x4c +#define DATABAHN_CTL_REG79 0x13c +#define DATABAHN_PHY_REG25 0x264 static struct cpu_wp *cpu_wp_tbl; static struct clk *cpu_clk; @@ -42,17 +46,17 @@ extern int set_cpu_freq(int wp); static struct device *pm_dev; struct clk *gpc_dvfs_clk; extern void cpu_do_suspend_workaround(u32 sdclk_iomux_addr); -extern void cpu_cortexa8_do_idle(void *); +extern void mx50_suspend(u32 databahn_addr); extern struct cpu_wp *(*get_cpu_wp)(int *wp); extern int iram_ready; void *suspend_iram_base; void (*suspend_in_iram)(void *sdclk_iomux_addr) = NULL; +void __iomem *suspend_param1; +u32 *suspend_param; -static int mx51_suspend_enter(suspend_state_t state) +static int mx5_suspend_enter(suspend_state_t state) { - void __iomem *sdclk_iomux_addr = IO_ADDRESS(IOMUXC_BASE_ADDR + 0x4b8); - if (gpc_dvfs_clk == NULL) gpc_dvfs_clk = clk_get(NULL, "gpc_dvfs_clk"); /* gpc clock is needed for SRPG */ @@ -75,12 +79,18 @@ static int mx51_suspend_enter(suspend_state_t state) local_flush_tlb_all(); flush_cache_all(); - /* Run the suspend code from iRAM. */ - suspend_in_iram(sdclk_iomux_addr); + if (cpu_is_mx51()) { + /* Run the suspend code from iRAM. */ + suspend_in_iram(suspend_param1); + + /*clear the EMPGC0/1 bits */ + __raw_writel(0, MXC_SRPG_EMPGC0_SRPGCR); + __raw_writel(0, MXC_SRPG_EMPGC1_SRPGCR); + } else + local_flush_tlb_all(); + flush_cache_all(); - /*clear the EMPGC0/1 bits */ - __raw_writel(0, MXC_SRPG_EMPGC0_SRPGCR); - __raw_writel(0, MXC_SRPG_EMPGC1_SRPGCR); + suspend_in_iram(suspend_param); } else { cpu_do_idle(); } @@ -92,7 +102,7 @@ static int mx51_suspend_enter(suspend_state_t state) /* * Called after processes are frozen, but before we shut down devices. */ -static int mx51_suspend_prepare(void) +static int mx5_suspend_prepare(void) { #if defined(CONFIG_CPU_FREQ) struct cpufreq_freqs freqs; @@ -115,7 +125,7 @@ static int mx51_suspend_prepare(void) /* * Called before devices are re-setup. */ -static void mx51_suspend_finish(void) +static void mx5_suspend_finish(void) { #if defined(CONFIG_CPU_FREQ) struct cpufreq_freqs freqs; @@ -138,55 +148,80 @@ static void mx51_suspend_finish(void) /* * Called after devices are re-setup, but before processes are thawed. */ -static void mx51_suspend_end(void) +static void mx5_suspend_end(void) { } -static int mx51_pm_valid(suspend_state_t state) +static int mx5_pm_valid(suspend_state_t state) { return (state > PM_SUSPEND_ON && state <= PM_SUSPEND_MAX); } -struct platform_suspend_ops mx51_suspend_ops = { - .valid = mx51_pm_valid, - .prepare = mx51_suspend_prepare, - .enter = mx51_suspend_enter, - .finish = mx51_suspend_finish, - .end = mx51_suspend_end, +struct platform_suspend_ops mx5_suspend_ops = { + .valid = mx5_pm_valid, + .prepare = mx5_suspend_prepare, + .enter = mx5_suspend_enter, + .finish = mx5_suspend_finish, + .end = mx5_suspend_end, }; -static int __devinit mx51_pm_probe(struct platform_device *pdev) +static int __devinit mx5_pm_probe(struct platform_device *pdev) { pm_dev = &pdev->dev; return 0; } -static struct platform_driver mx51_pm_driver = { +static struct platform_driver mx5_pm_driver = { .driver = { - .name = "mx51_pm", + .name = "mx5_pm", }, - .probe = mx51_pm_probe, + .probe = mx5_pm_probe, }; static int __init pm_init(void) { int cpu_wp_nr; - unsigned long iram_paddr; + unsigned long iram_paddr, iram_paddr1; - pr_info("Static Power Management for Freescale i.MX51\n"); - if (platform_driver_register(&mx51_pm_driver) != 0) { - printk(KERN_ERR "mx51_pm_driver register failed\n"); + pr_info("Static Power Management for Freescale i.MX5\n"); + if (platform_driver_register(&mx5_pm_driver) != 0) { + printk(KERN_ERR "mx5_pm_driver register failed\n"); return -ENODEV; } - suspend_set_ops(&mx51_suspend_ops); + suspend_set_ops(&mx5_suspend_ops); /* Move suspend routine into iRAM */ iram_alloc(SZ_4K, &iram_paddr); /* Need to remap the area here since we want the memory region to be executable. */ suspend_iram_base = __arm_ioremap(iram_paddr, SZ_4K, MT_HIGH_VECTORS); - memcpy(suspend_iram_base, cpu_do_suspend_workaround, SZ_4K); + suspend_param = (u32 *)iram_alloc(SZ_4K, &iram_paddr1); + + if (cpu_is_mx51()) { + suspend_param1 = IO_ADDRESS(IOMUXC_BASE_ADDR + 0x4b8); + memcpy(suspend_iram_base, cpu_do_suspend_workaround, + SZ_4K); + } else if (cpu_is_mx50()) { + /* + * Need to run the suspend code from IRAM as the DDR needs + * to be put into self refresh mode manually. + */ + memcpy(suspend_iram_base, mx50_suspend, SZ_4K); + suspend_param1 = ioremap(MX50_DATABAHN_BASE_ADDR, SZ_4K); + /* DDR Controller [0] */ + *suspend_param = (u32)suspend_param1 + + DATABAHN_CTL_REG0; + /* DDR Controller [19] */ + *(suspend_param + 1) = (u32)suspend_param1 + + DATABAHN_CTL_REG19; + /* DDR Controller [79] */ + *(suspend_param + 2) = (u32)suspend_param1 + + DATABAHN_CTL_REG79; + /* DDR Controller PHY [25] */ + *(suspend_param + 3) = (u32)suspend_param1 + + DATABAHN_PHY_REG25; + } suspend_in_iram = (void *)suspend_iram_base; cpu_wp_tbl = get_cpu_wp(&cpu_wp_nr); @@ -205,7 +240,7 @@ static int __init pm_init(void) static void __exit pm_cleanup(void) { /* Unregister the device structure */ - platform_driver_unregister(&mx51_pm_driver); + platform_driver_unregister(&mx5_pm_driver); } module_init(pm_init); diff --git a/arch/arm/mach-mx5/system.c b/arch/arm/mach-mx5/system.c index da27fc4605a3..1954e97b2367 100644 --- a/arch/arm/mach-mx5/system.c +++ b/arch/arm/mach-mx5/system.c @@ -35,9 +35,6 @@ extern int mxc_jtag_enabled; extern int iram_ready; static struct clk *gpc_dvfs_clk; -extern void cpu_cortexa8_do_idle(void *addr); - - /* set cpu low power mode before WFI instruction */ void mxc_cpu_lp_set(enum mxc_cpu_pwr_mode mode) { @@ -66,6 +63,7 @@ void mxc_cpu_lp_set(enum mxc_cpu_pwr_mode mode) if (mode == WAIT_UNCLOCKED_POWER_OFF) { ccm_clpcr |= (0x1 << MXC_CCM_CLPCR_LPM_OFFSET); ccm_clpcr &= ~MXC_CCM_CLPCR_VSTBY; + ccm_clpcr &= ~MXC_CCM_CLPCR_SBYOS; stop_mode = 0; } else { ccm_clpcr |= (0x2 << MXC_CCM_CLPCR_LPM_OFFSET); @@ -94,6 +92,7 @@ void mxc_cpu_lp_set(enum mxc_cpu_pwr_mode mode) __raw_writel(plat_lpc, MXC_CORTEXA8_PLAT_LPC); __raw_writel(ccm_clpcr, MXC_CCM_CLPCR); + /* Need to fix this for MX53 and MX508 */ if (cpu_is_mx51()) __raw_writel(arm_srpgcr, MXC_SRPG_ARM_SRPGCR); __raw_writel(arm_srpgcr, MXC_SRPG_NEON_SRPGCR); @@ -150,7 +149,7 @@ static int arch_idle_mode = WAIT_UNCLOCKED_POWER_OFF; */ void arch_idle(void) { - if (likely(!mxc_jtag_enabled)) { +/* if (likely(!mxc_jtag_enabled))*/ { if (gpc_dvfs_clk == NULL) gpc_dvfs_clk = clk_get(NULL, "gpc_dvfs_clk"); /* gpc clock is needed for SRPG */ diff --git a/arch/arm/plat-mxc/include/mach/mx5x.h b/arch/arm/plat-mxc/include/mach/mx5x.h index 879a0859beda..7c35b3ed9020 100644 --- a/arch/arm/plat-mxc/include/mach/mx5x.h +++ b/arch/arm/plat-mxc/include/mach/mx5x.h @@ -132,6 +132,11 @@ */ #define MX53_SATA_BASE_ADDR 0x10000000 +/* + * Databahn MX50 + */ + #define MX50_DATABAHN_BASE_ADDR 0x14000000 + #define DEBUG_BASE_ADDR 0x40000000 /*MX53 + 0x2000000 */ #define DEBUG_SIZE SZ_1M |