diff options
Diffstat (limited to 'arch')
-rw-r--r-- | arch/arm/mach-mx6/Makefile | 2 | ||||
-rw-r--r-- | arch/arm/mach-mx6/bus_freq.c | 24 | ||||
-rwxr-xr-x | arch/arm/mach-mx6/clock_mx6sl.c | 6 | ||||
-rw-r--r-- | arch/arm/mach-mx6/mx6sl_wfi.S | 348 | ||||
-rw-r--r-- | arch/arm/mach-mx6/system.c | 51 |
5 files changed, 401 insertions, 30 deletions
diff --git a/arch/arm/mach-mx6/Makefile b/arch/arm/mach-mx6/Makefile index 91fc841e9529..49b5f86a6920 100644 --- a/arch/arm/mach-mx6/Makefile +++ b/arch/arm/mach-mx6/Makefile @@ -5,7 +5,7 @@ # Object file lists. obj-y := cpu.o mm.o system.o devices.o dummy_gpio.o irq.o bus_freq.o usb_dr.o usb_h2.o usb_h3.o\ pm.o cpu_op-mx6.o mx6_wfi.o mx6_fec.o mx6_anatop_regulator.o cpu_regulator-mx6.o \ -mx6_mmdc.o mx6_ddr_freq.o mx6sl_ddr.o +mx6_mmdc.o mx6_ddr_freq.o mx6sl_ddr.o mx6sl_wfi.o obj-$(CONFIG_ARCH_MX6) += clock.o mx6_suspend.o clock_mx6sl.o obj-$(CONFIG_MACH_MX6Q_ARM2) += board-mx6q_arm2.o diff --git a/arch/arm/mach-mx6/bus_freq.c b/arch/arm/mach-mx6/bus_freq.c index b85c8ec8fd7c..fa11d89e1191 100644 --- a/arch/arm/mach-mx6/bus_freq.c +++ b/arch/arm/mach-mx6/bus_freq.c @@ -75,6 +75,9 @@ unsigned int ddr_normal_rate; int low_freq_bus_used(void); void set_ddr_freq(int ddr_freq); +void *mx6sl_wfi_iram_base; +void (*mx6sl_wfi_iram)(int arm_podf, unsigned long wfi_iram_addr) = NULL; +extern void mx6sl_wait (int arm_podf, unsigned long wfi_iram_addr); void *mx6sl_ddr_freq_base; void (*mx6sl_ddr_freq_change_iram)(int ddr_freq) = NULL; @@ -84,7 +87,6 @@ extern int init_mmdc_settings(void); extern struct cpu_op *(*get_cpu_op)(int *op); extern int update_ddr_freq(int ddr_rate); extern int chip_rev; -extern bool arm_mem_clked_in_wait; DEFINE_MUTEX(bus_freq_mutex); @@ -153,8 +155,6 @@ static void reduce_bus_freq_handler(struct work_struct *work) u32 div; unsigned long flags; - arm_mem_clked_in_wait = true; - spin_lock_irqsave(&freq_lock, flags); /* Set periph_clk to be sourced from OSC_CLK */ @@ -177,7 +177,7 @@ static void reduce_bus_freq_handler(struct work_struct *work) ; clk_set_parent(pll1_sw_clk, pll1); - /* Now change DDR freq in IRAM. */ + /* Now change DDR freq while running from IRAM. */ mx6sl_ddr_freq_change_iram(LPAPM_CLK); low_bus_freq_mode = 1; @@ -308,9 +308,6 @@ int set_high_bus_freq(int high_bus_freq) clk_disable(pll3); } - if (cpu_is_mx6sl()) - arm_mem_clked_in_wait = false; - mutex_unlock(&bus_freq_mutex); return 0; } @@ -618,7 +615,6 @@ static int __devinit busfreq_probe(struct platform_device *pdev) if (!cpu_is_mx6sl()) init_mmdc_settings(); else { -#if 1 unsigned long iram_paddr; /* Allocate IRAM for WFI code when system is @@ -628,12 +624,22 @@ static int __devinit busfreq_probe(struct platform_device *pdev) /* Need to remap the area here since we want * the memory region to be executable. */ + mx6sl_wfi_iram_base = __arm_ioremap(iram_paddr, + SZ_4K, MT_MEMORY_NONCACHED); + memcpy(mx6sl_wfi_iram_base, mx6sl_wait, SZ_4K); + mx6sl_wfi_iram = (void *)mx6sl_wfi_iram_base; + + /* Allocate IRAM for WFI code when system is + *in low freq mode. + */ + iram_alloc(SZ_4K, &iram_paddr); + /* Need to remap the area here since we want the memory region + to be executable. */ mx6sl_ddr_freq_base = __arm_ioremap(iram_paddr, SZ_4K, MT_MEMORY_NONCACHED); memcpy(mx6sl_ddr_freq_base, mx6sl_ddr_iram, SZ_4K); mx6sl_ddr_freq_change_iram = (void *)mx6sl_ddr_freq_base; -#endif } return 0; diff --git a/arch/arm/mach-mx6/clock_mx6sl.c b/arch/arm/mach-mx6/clock_mx6sl.c index 8fab0f287ecd..33721a4e212f 100755 --- a/arch/arm/mach-mx6/clock_mx6sl.c +++ b/arch/arm/mach-mx6/clock_mx6sl.c @@ -4000,6 +4000,12 @@ int __init mx6sl_clocks_init(unsigned long ckil, unsigned long osc, * should be from OSC24M */ clk_set_parent(&ipg_perclk, &osc_clk); + /* Need to set IPG_PERCLK to 3MHz, so that we can + * satisfy the 2.5:1 AHB:IPG_PERCLK ratio. Since AHB + * can be dropped to as low as 8MHz in low power mode. + */ + clk_set_rate(&ipg_perclk, 3000000); + gpt_clk[0].parent = &ipg_perclk; gpt_clk[0].get_rate = NULL; diff --git a/arch/arm/mach-mx6/mx6sl_wfi.S b/arch/arm/mach-mx6/mx6sl_wfi.S new file mode 100644 index 000000000000..610a57f13730 --- /dev/null +++ b/arch/arm/mach-mx6/mx6sl_wfi.S @@ -0,0 +1,348 @@ +/* + * Copyright (C) 2012 Freescale Semiconductor, Inc. All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include <linux/linkage.h> +#include <mach/hardware.h> +#define IRAM_WAIT_SIZE (1 << 11) + + + .macro sl_ddr_io_save + + ldr r4, [r1, #0x30c] /* DRAM_DQM0 */ + ldr r5, [r1, #0x310] /* DRAM_DQM1 */ + ldr r6, [r1, #0x314] /* DRAM_DQM2 */ + ldr r7, [r1, #0x318] /* DRAM_DQM3 */ + stmfd r9!, {r4-r7} + + ldr r4, [r1, #0x344] /* DRAM_SDQS0 */ + ldr r5, [r1, #0x348] /* DRAM_SDQS1 */ + ldr r6, [r1, #0x34c] /* DRAM_SDQS2 */ + ldr r7, [r1, #0x350] /* DRAM_SDQS3 */ + stmfd r9!, {r4-r7} + + ldr r4, [r1, #0x5c4] /* GPR_B0DS */ + ldr r5, [r1, #0x5cc] /* GPR_B1DS */ + ldr r6, [r1, #0x5d4] /* GPR_B2DS */ + ldr r7, [r1, #0x5d8] /* GPR_B3DS */ + stmfd r9!, {r4-r7} + + ldr r4, [r1, #0x300] /* DRAM_CAS */ + ldr r5, [r1, #0x31c] /* DRAM_RAS */ + ldr r6, [r1, #0x338] /* DRAM_SDCLK_0 */ + ldr r7, [r1, #0x5ac] /* GPR_ADDS*/ + stmfd r9!, {r4-r7} + + ldr r4, [r1, #0x5b0] /* DDRMODE_CTL */ + ldr r5, [r1, #0x5c0] /* DDRMODE */ + ldr r6, [r1, #0x33c] /* DRAM_SODT0*/ + ldr r7, [r1, #0x340] /* DRAM_SODT1*/ + stmfd r9!, {r4-r7} + + ldr r4, [r1, #0x330] /* DRAM_SDCKE0 */ + ldr r5, [r1, #0x334] /* DRAM_SDCKE1 */ + ldr r6, [r1, #0x320] /* DRAM_RESET */ + ldr r7, [r1, #0x5c8] /* GPR_CTLDS */ + stmfd r9!, {r4-r7} + + .endm + + .macro sl_ddr_io_restore + + ldmea r9!, {r4-r7} + str r4, [r1, #0x30c] /* DRAM_DQM0 */ + str r5, [r1, #0x310] /* DRAM_DQM1 */ + str r6, [r1, #0x314] /* DRAM_DQM2 */ + str r7, [r1, #0x318] /* DRAM_DQM3 */ + + ldmea r9!, {r4-r7} + str r4, [r1, #0x344] /* DRAM_SDQS0 */ + str r5, [r1, #0x348] /* DRAM_SDQS1 */ + str r6, [r1, #0x34c] /* DRAM_SDQS2 */ + str r7, [r1, #0x350] /* DRAM_SDQS3 */ + + ldmea r9!, {r4-r7} + str r4, [r1, #0x5c4] /* GPR_B0DS */ + str r5, [r1, #0x5cc] /* GPR_B1DS */ + str r6, [r1, #0x5d4] /* GPR_B2DS */ + str r7, [r1, #0x5d8] /* GPR_B3DS */ + + ldmea r9!, {r4-r7} + str r4, [r1, #0x300] /* DRAM_CAS */ + str r5, [r1, #0x31c] /* DRAM_RAS */ + str r6, [r1, #0x338] /* DRAM_SDCLK_0 */ + str r7, [r1, #0x5ac] /* GPR_ADDS*/ + + ldmea r9!, {r4-r7} + str r4, [r1, #0x5b0] /* DDRMODE_CTL */ + str r5, [r1, #0x5c0] /* DDRMODE */ + str r6, [r1, #0x33c] /* DRAM_SODT0*/ + str r7, [r1, #0x340] /* DRAM_SODT1*/ + + ldmea r9!, {r4-r7} + str r4, [r1, #0x330] /* DRAM_SDCKE0 */ + str r5, [r1, #0x334] /* DRAM_SDCKE1 */ + str r6, [r1, #0x320] /* DRAM_RESET */ + str r7, [r1, #0x5c8] /* GPR_CTLDS */ + + .endm + + .macro sl_ddr_io_set_lpm + + mov r4, #0 + str r4, [r1, #0x30c] /* DRAM_DQM0 */ + str r4, [r1, #0x310] /* DRAM_DQM1 */ + str r4, [r1, #0x314] /* DRAM_DQM2 */ + str r4, [r1, #0x318] /* DRAM_DQM3 */ + + str r4, [r1, #0x344] /* DRAM_SDQS0 */ + str r4, [r1, #0x348] /* DRAM_SDQS1 */ + str r4, [r1, #0x34c] /* DRAM_SDQS2 */ + str r4, [r1, #0x350] /* DRAM_SDQS3 */ + + str r4, [r1, #0x5c4] /* GPR_B0DS */ + str r4, [r1, #0x5cc] /* GPR_B1DS */ + str r4, [r1, #0x5d4] /* GPR_B2DS */ + str r4, [r1, #0x5d8] /* GPR_B3DS */ + + str r4, [r1, #0x300] /* DRAM_CAS */ + str r4, [r1, #0x31c] /* DRAM_RAS */ + str r4, [r1, #0x338] /* DRAM_SDCLK_0 */ + str r4, [r1, #0x5ac] /* GPR_ADDS*/ + + str r4, [r1, #0x5b0] /* DDRMODE_CTL */ + str r4, [r1, #0x5c0] /* DDRMODE */ + str r4, [r1, #0x33c] /* DRAM_SODT0*/ + str r4, [r1, #0x340] /* DRAM_SODT1*/ + + str r4, [r1, #0x5c8] /* GPR_CTLDS */ + mov r4, #0x80000 + str r4, [r1, #0x320] /* DRAM_RESET */ + mov r4, #0x1000 + str r4, [r1, #0x330] /* DRAM_SDCKE0 */ + str r4, [r1, #0x334] /* DRAM_SDCKE1 */ + + .endm + +/* + * mx6sl_wait + * + * Idle the processor (eg, wait for interrupt). + * Make sure DDR is in self-refresh. + * IRQs are already disabled. + * r0 : arm_podf before WFI is entered + * r1: WFI IRAMcode base address. + */ +ENTRY(mx6sl_wait) + + push {r4, r5, r6, r7, r8, r9, r10} + +mx6sl_lpm_wfi: + /* Get the IRAM data storage address. */ + mov r10, r1 + mov r9, r1 /* get suspend_iram_base */ + add r9, r9, #IRAM_WAIT_SIZE /* 4K */ + + ldr r1, =MX6Q_IOMUXC_BASE_ADDR + add r1, r1, #PERIPBASE_VIRT + + /* Save the DDR IO state. */ + sl_ddr_io_save + + ldr r3, =ANATOP_BASE_ADDR + add r3, r3, #PERIPBASE_VIRT + + ldr r2, =CCM_BASE_ADDR + add r2, r2, #PERIPBASE_VIRT + + ldr r8, =MMDC_P0_BASE_ADDR + add r8, r8, #PERIPBASE_VIRT + + + /* Prime all TLB entries. */ + adr r7, mx6sl_lpm_wfi @Address in this function. + + ldr r6, [r7] + + ldr r6, [r8] + ldr r6, [r3] + ldr r6, [r2] + ldr r6, [r1] + + dsb + + /* Disable Automatic power savings. */ + ldr r6, [r8, #0x404] + orr r6, r6, #0x01 + str r6, [r8, #0x404] + + /* Make the DDR explicitly enter self-refresh. */ + ldr r6, [r8, #0x404] + orr r6, r6, #0x200000 + str r6, [r8, #0x404] + +poll_dvfs_set_1: + ldr r6, [r8, #0x404] + and r6, r6, #0x2000000 + cmp r6, #0x2000000 + bne poll_dvfs_set_1 + + /* Now set DDR rate to 1MHz. */ + /* DDR is from bypassed PLL2 on periph2_clk2 path. + * Set the periph2_clk2_podf to divide by 8. + */ + ldr r6, [r2, #0x14] + orr r6, r6, #0x07 + str r6, [r2, #0x14] + + /* Now set MMDC PODF to divide by 3. */ + ldr r6, [r2, #0x14] + bic r6, r6, #0x38 + orr r6, r6, #0x10 + str r6, [r2, #0x14] + + /* Set the DDR IO in LPM state. */ + sl_ddr_io_set_lpm + + /* Set AHB to 8MHz., AXI to 3MHz */ + /* We want to ensure IPG_PERCLK to AHB + * clk ratio is 1:2.5 + */ + /* Store the AXI/AHB podfs. */ + ldr r9, [r2, #0x14] + mov r6, r9 + bic r6, r6, #0x1c00 + orr r6, r6, #0x800 + orr r6, r6, #0x70000 + str r6, [r2, #0x14] + + /* Loop till podf is accepted. */ +ahb_podf: + ldr r6, [r2, #0x48] + cmp r6, #0x0 + bne podf_loop + + /* Now set ARM to 24MHz. */ + /* Move ARM to be sourced from STEP_CLK + * after setting STEP_CLK to 24MHz. + */ + ldr r6, [r2, #0xc] + bic r6, r6, #0x100 + str r6, [r2, #0x0c] + /* Now PLL1_SW_CLK to step_clk. */ + ldr r6, [r2, #0x0c] + orr r6, r6, #0x4 + str r6, [r2, #0x0c] + + /* Bypass PLL1 and power it down. */ + ldr r6, =(1 << 16) + orr r6, r6, #0x1000 + str r6, [r3, #0x04] + + /* Set the ARM PODF to divide by 8. */ + /* IPG is at 4MHz here, we need ARM to + * run at the 12:5 ratio (WAIT mode issue). + */ + ldr r6, =0x7 + str r6, [r2, #0x10] + + /* Loop till podf is accepted. */ +podf_loop: + ldr r6, [r2, #0x48] + cmp r6, #0x0 + bne podf_loop + + /* Now do WFI. */ + dsb + + wfi + + /* Set original ARM PODF back. */ + str r0, [r2, #0x10] + + /* Loop till podf is accepted. */ +podf_loop1: + ldr r6, [r2, #0x48] + cmp r6, #0x0 + bne podf_loop1 + + /* Power up PLL1 and un-bypass it. */ + ldr r6, =(1 << 12) + str r6, [r3, #0x08] + + /* Wait for PLL1 to relock. */ +wait_for_pll_lock: + ldr r6, [r3, #0x0] + and r6, r6, #0x80000000 + cmp r6, #0x80000000 + bne wait_for_pll_lock + + ldr r6, =(1 << 16) + str r6, [r3, #0x08] + + /* Set PLL1_sw_clk back to PLL1. */ + ldr r6, [r2, #0x0c] + bic r6, r6, #0x4 + str r6, [r2, #0xc] + + /* Restore AHB/AXI back. */ + str r9, [r2, #0x14] + + /* Loop till podf is accepted. */ +ahb_podf1: + ldr r6, [r2, #0x48] + cmp r6, #0x0 + bne podf_loop1 + + mov r9, r10 /* get suspend_iram_base */ + add r9, r9, #IRAM_WAIT_SIZE /* 4K */ + + /* Restore the DDR IO before exiting self-refresh. */ + sl_ddr_io_restore + + /* Set MMDC back to 24MHz. */ + /* Set periph2_clk2_podf to divide by 1. */ + /* Now set MMDC PODF to divide by 1. */ + ldr r6, [r2, #0x14] + bic r6, r6, #0x3f + str r6, [r2, #0x14] + + /* clear DVFS - exit from self refresh mode */ + ldr r6, [r8, #0x404] + bic r6, r6, #0x200000 + str r6, [r8, #0x404] + +poll_dvfs_clear_1: + ldr r6, [r8, #0x404] + and r6, r6, #0x2000000 + cmp r6, #0x2000000 + beq poll_dvfs_clear_1 + + /* Enable Automatic power savings. */ + ldr r6, [r8, #0x404] + bic r6, r6, #0x01 + str r6, [r8, #0x404] + + pop {r4,r5, r6, r7, r8, r9, r10} + + /* Restore registers */ + mov pc, lr + + .type mx6sl_do_wait, #object +ENTRY(mx6sl_do_wait) + .word mx6sl_wait + .size mx6sl_wait, . - mx6sl_wait diff --git a/arch/arm/mach-mx6/system.c b/arch/arm/mach-mx6/system.c index 57dee5f4c4c0..09940ffd49aa 100644 --- a/arch/arm/mach-mx6/system.c +++ b/arch/arm/mach-mx6/system.c @@ -57,9 +57,10 @@ volatile unsigned int num_cpu_idle; volatile unsigned int num_cpu_idle_lock = 0x0; int wait_mode_arm_podf; int cur_arm_podf; -bool arm_mem_clked_in_wait; void arch_idle_with_workaround(int cpu); +extern void *mx6sl_wfi_iram_base; +extern void (*mx6sl_wfi_iram)(int arm_podf, unsigned long wfi_iram_addr); extern void mx6_wait(void *num_cpu_idle_lock, void *num_cpu_idle, \ int wait_arm_podf, int cur_arm_podf); extern bool enable_wait_mode; @@ -78,6 +79,7 @@ void gpc_set_wakeup(unsigned int irq[4]) return; } + /* set cpu low power mode before WFI instruction */ void mxc_cpu_lp_set(enum mxc_cpu_pwr_mode mode) { @@ -251,25 +253,34 @@ void arch_idle_single_core(void) ca9_do_idle(); } else { - /* - * Implement the 12:5 ARM:IPG_CLK ratio - * workaround for the WAIT mode issue. - * We can directly use the divider to drop the ARM - * core freq in a single core environment. - * Set the ARM_PODF to get the max freq possible - * to avoid the WAIT mode issue when IPG is at 66MHz. - */ - if (cpu_is_mx6sl()) { - reg = __raw_readl(MXC_CCM_CGPR); - reg |= MXC_CCM_CGPR_MEM_IPG_STOP_MASK; - __raw_writel(reg, MXC_CCM_CGPR); - } - __raw_writel(wait_mode_arm_podf, MXC_CCM_CACRR); - while (__raw_readl(MXC_CCM_CDHIPR)) - ; - ca9_do_idle(); + if (low_bus_freq_mode && cpu_is_mx6sl()) { + u32 org_arm_podf = __raw_readl(MXC_CCM_CACRR); - __raw_writel(cur_arm_podf - 1, MXC_CCM_CACRR); + /* Need to run WFI code from IRAM so that + * we can lower DDR freq. + */ + mx6sl_wfi_iram(org_arm_podf, + (unsigned long)mx6sl_wfi_iram_base); + + /* Clear the chicken bit to allow memories + * to be powered down + */ + } else { + /* + * Implement the 12:5 ARM:IPG_CLK ratio + * workaround for the WAIT mode issue. + * We can directly use the divider to drop the ARM + * core freq in a single core environment. + * Set the ARM_PODF to get the max freq possible + * to avoid the WAIT mode issue when IPG is at 66MHz. + */ + __raw_writel(wait_mode_arm_podf, MXC_CCM_CACRR); + while (__raw_readl(MXC_CCM_CDHIPR)) + ; + ca9_do_idle(); + + __raw_writel(cur_arm_podf - 1, MXC_CCM_CACRR); + } } } @@ -331,7 +342,7 @@ void arch_idle(void) { if (enable_wait_mode) { mxc_cpu_lp_set(WAIT_UNCLOCKED_POWER_OFF); - if ((mem_clk_on_in_wait || arm_mem_clked_in_wait)) { + if (mem_clk_on_in_wait) { u32 reg; /* * MX6SL, MX6Q (TO1.2 or later) and |