summaryrefslogtreecommitdiff
path: root/arch
diff options
context:
space:
mode:
Diffstat (limited to 'arch')
-rw-r--r--arch/arm/mach-mx6/Makefile2
-rw-r--r--arch/arm/mach-mx6/bus_freq.c24
-rwxr-xr-xarch/arm/mach-mx6/clock_mx6sl.c6
-rw-r--r--arch/arm/mach-mx6/mx6sl_wfi.S348
-rw-r--r--arch/arm/mach-mx6/system.c51
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