summaryrefslogtreecommitdiff
path: root/arch/arm/mach-imx/ddr3_freq_imx6.S
diff options
context:
space:
mode:
Diffstat (limited to 'arch/arm/mach-imx/ddr3_freq_imx6.S')
-rw-r--r--arch/arm/mach-imx/ddr3_freq_imx6.S1103
1 files changed, 1103 insertions, 0 deletions
diff --git a/arch/arm/mach-imx/ddr3_freq_imx6.S b/arch/arm/mach-imx/ddr3_freq_imx6.S
new file mode 100644
index 000000000000..e6f7f74f7d32
--- /dev/null
+++ b/arch/arm/mach-imx/ddr3_freq_imx6.S
@@ -0,0 +1,1103 @@
+/*
+ * Copyright (C) 2011-2015 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.
+ */
+
+#include <linux/linkage.h>
+#include <asm/smp_scu.h>
+#include "hardware.h"
+
+#define MMDC0_MDPDC 0x4
+#define MMDC0_MDCF0 0x0c
+#define MMDC0_MDCF1 0x10
+#define MMDC0_MDMISC 0x18
+#define MMDC0_MDSCR 0x1c
+#define MMDC0_MAARCR 0x400
+#define MMDC0_MAPSR 0x404
+#define MMDC0_MADPCR0 0x410
+#define MMDC0_MPZQHWCTRL 0x800
+#define MMDC1_MPZQHWCTRL 0x4800
+#define MMDC0_MPODTCTRL 0x818
+#define MMDC1_MPODTCTRL 0x4818
+#define MMDC0_MPDGCTRL0 0x83c
+#define MMDC1_MPDGCTRL0 0x483c
+#define MMDC0_MPMUR0 0x8b8
+#define MMDC1_MPMUR0 0x48b8
+
+#define CCM_CBCDR 0x14
+#define CCM_CBCMR 0x18
+#define CCM_CSCMR1 0x1c
+#define CCM_CDHIPR 0x48
+
+#define L2_CACHE_SYNC 0x730
+#define PL310_AUX_CTRL 0x104
+#define PL310_DCACHE_LOCKDOWN_BASE 0x900
+#define PL310_AUX_16WAY_BIT 0x10000
+#define PL310_LOCKDOWN_NBREGS 8
+#define PL310_LOCKDOWN_SZREG 4
+#define PL310_8WAYS_MASK 0x00FF
+#define PL310_16WAYS_UPPERMASK 0xFF00
+
+#define IMX6QP_REVISION_ID 0x630100
+#define ANADIG_DIGPROG 0x260
+
+.extern iram_tlb_phys_addr
+
+.globl mx6_ddr3_freq_change_start
+.globl mx6_ddr3_freq_change_end
+
+ .align 3
+
+ .macro is_mx6qp
+
+ /* check if the SOC is i.MX6QP */
+ ldr r0, =IMX_IO_P2V(MX6Q_ANATOP_BASE_ADDR)
+ ldr r1, [r0, #ANADIG_DIGPROG]
+ ldr r2, =IMX6QP_REVISION_ID
+ cmp r1, r2
+
+ .endm
+
+ .macro switch_to_528MHz
+
+ /* check if periph_clk_sel is already set */
+ ldr r0, [r6, #CCM_CBCDR]
+ and r0, r0, #(1 << 25)
+ cmp r0, #(1 << 25)
+ beq set_ahb_podf_before_switch
+
+ /* change periph_clk to be sourced from pll3_clk. */
+ ldr r0, [r6, #CCM_CBCMR]
+ bic r0, r0, #(3 << 12)
+ str r0, [r6, #CCM_CBCMR]
+
+ ldr r0, [r6, #CCM_CBCDR]
+ bic r0, r0, #(0x38 << 20)
+ str r0, [r6, #CCM_CBCDR]
+
+ /*
+ * set the AHB dividers before the switch,
+ * don't change AXI clock divider,
+ * set the MMDC_DIV=1, AXI_DIV = 2, AHB_DIV=4,
+ */
+ ldr r0, [r6, #CCM_CBCDR]
+ ldr r2, =0x3f1f00
+ bic r0, r0, r2
+ orr r0, r0, #0xd00
+ orr r0, r0, #(1 << 16)
+ str r0, [r6, #CCM_CBCDR]
+
+wait_div_update528:
+ ldr r0, [r6, #CCM_CDHIPR]
+ cmp r0, #0
+ bne wait_div_update528
+
+ /* now switch periph_clk to pll3_main_clk. */
+ ldr r0, [r6, #CCM_CBCDR]
+ orr r0, r0, #(1 << 25)
+ str r0, [r6, #CCM_CBCDR]
+
+periph_clk_switch3:
+ ldr r0, [r6, #CCM_CDHIPR]
+ cmp r0, #0
+ bne periph_clk_switch3
+
+ b switch_pre_periph_clk_528
+
+set_ahb_podf_before_switch:
+ /*
+ * set the MMDC_DIV=1, AXI_DIV = 2, AHB_DIV=4,
+ */
+ ldr r0, [r6, #CCM_CBCDR]
+ ldr r2, =0x3f1f00
+ bic r0, r0, r2
+ orr r0, r0, #0xd00
+ orr r0, r0, #(1 << 16)
+ str r0, [r6, #CCM_CBCDR]
+
+wait_div_update528_1:
+ ldr r0, [r6, #CCM_CDHIPR]
+ cmp r0, #0
+ bne wait_div_update528_1
+
+switch_pre_periph_clk_528:
+
+ /* now switch pre_periph_clk to PLL2_528MHz. */
+ ldr r0, [r6, #CCM_CBCMR]
+ bic r0, r0, #(0xc << 16)
+ str r0, [r6, #CCM_CBCMR]
+
+ /* now switch periph_clk back. */
+ ldr r0, [r6, #CCM_CBCDR]
+ bic r0, r0, #(1 << 25)
+ str r0, [r6, #CCM_CBCDR]
+
+periph_clk_switch4:
+ ldr r0, [r6, #CCM_CDHIPR]
+ cmp r0, #0
+ bne periph_clk_switch4
+
+ .endm
+
+ .macro switch_to_400MHz
+
+ /* check if periph_clk_sel is already set. */
+ ldr r0, [r6, #CCM_CBCDR]
+ and r0, r0, #(1 << 25)
+ cmp r0, #(1 << 25)
+ beq set_ahb_podf_before_switch1
+
+ /* change periph_clk to be sourced from pll3_clk. */
+ ldr r0, [r6, #CCM_CBCMR]
+ bic r0, r0, #(3 << 12)
+ str r0, [r6, #CCM_CBCMR]
+
+ ldr r0, [r6, #CCM_CBCDR]
+ bic r0, r0, #(0x38 << 24)
+ str r0, [r6, #CCM_CBCDR]
+
+ /* now switch periph_clk to pll3_main_clk. */
+ ldr r0, [r6, #CCM_CBCDR]
+ orr r0, r0, #(1 << 25)
+ str r0, [r6, #CCM_CBCDR]
+
+periph_clk_switch5:
+ ldr r0, [r6, #CCM_CDHIPR]
+ cmp r0, #0
+ bne periph_clk_switch5
+
+ b switch_pre_periph_clk_400
+
+set_ahb_podf_before_switch1:
+ /*
+ * set the MMDC_DIV=1, AXI_DIV = 2, AHB_DIV=4,
+ */
+ ldr r0, [r6, #CCM_CBCDR]
+ ldr r2, =0x3f1f00
+ bic r0, r0, r2
+ orr r0, r0, #(0x9 << 8)
+ orr r0, r0, #(1 << 16)
+ str r0, [r6, #CCM_CBCDR]
+
+wait_div_update400_1:
+ ldr r0, [r6, #CCM_CDHIPR]
+ cmp r0, #0
+ bne wait_div_update400_1
+
+switch_pre_periph_clk_400:
+
+ /* now switch pre_periph_clk to PFD_400MHz. */
+ ldr r0, [r6, #CCM_CBCMR]
+ bic r0, r0, #(0xc << 16)
+ orr r0, r0, #(0x4 << 16)
+ str r0, [r6, #CCM_CBCMR]
+
+ /* now switch periph_clk back. */
+ ldr r0, [r6, #CCM_CBCDR]
+ bic r0, r0, #(1 << 25)
+ str r0, [r6, #CCM_CBCDR]
+
+periph_clk_switch6:
+ ldr r0, [r6, #CCM_CDHIPR]
+ cmp r0, #0
+ bne periph_clk_switch6
+
+ /*
+ * change AHB divider so that we are at 400/3=133MHz.
+ * don't change AXI clock divider.
+ * set the MMDC_DIV=1, AXI_DIV=2, AHB_DIV=3,
+ */
+ ldr r0, [r6, #CCM_CBCDR]
+ ldr r2, =0x3f1f00
+ bic r0, r0, r2
+ orr r0, r0, #(0x9 << 8)
+ orr r0, r0, #(1 << 16)
+ str r0, [r6, #CCM_CBCDR]
+
+wait_div_update400_2:
+ ldr r0, [r6, #CCM_CDHIPR]
+ cmp r0, #0
+ bne wait_div_update400_2
+
+ .endm
+
+ .macro switch_to_50MHz
+
+ /* check if periph_clk_sel is already set. */
+ ldr r0, [r6, #CCM_CBCDR]
+ and r0, r0, #(1 << 25)
+ cmp r0, #(1 << 25)
+ beq switch_pre_periph_clk_50
+
+ /*
+ * set the periph_clk to be sourced from PLL2_PFD_200M
+ * change periph_clk to be sourced from pll3_clk.
+ * ensure PLL3 is the source and set the divider to 1.
+ */
+ ldr r0, [r6, #CCM_CBCMR]
+ bic r0, r0, #(0x3 << 12)
+ str r0, [r6, #CCM_CBCMR]
+
+ ldr r0, [r6, #CCM_CBCDR]
+ bic r0, r0, #(0x38 << 24)
+ str r0, [r6, #CCM_CBCDR]
+
+ /* now switch periph_clk to pll3_main_clk. */
+ ldr r0, [r6, #CCM_CBCDR]
+ orr r0, r0, #(1 << 25)
+ str r0, [r6, #CCM_CBCDR]
+
+periph_clk_switch_50:
+ ldr r0, [r6, #CCM_CDHIPR]
+ cmp r0, #0
+ bne periph_clk_switch_50
+
+switch_pre_periph_clk_50:
+
+ /* now switch pre_periph_clk to PFD_200MHz. */
+ ldr r0, [r6, #CCM_CBCMR]
+ orr r0, r0, #(0xc << 16)
+ str r0, [r6, #CCM_CBCMR]
+
+ /*
+ * set the MMDC_DIV=4, AXI_DIV = 4, AHB_DIV=8,
+ */
+ ldr r0, [r6, #CCM_CBCDR]
+ ldr r2, =0x3f1f00
+ bic r0, r0, r2
+ orr r0, r0, #(0x18 << 16)
+ orr r0, r0, #(0x3 << 16)
+
+ /*
+ * if changing AHB divider remember to change
+ * the IPGPER divider too below.
+ */
+ orr r0, r0, #0x1d00
+ str r0, [r6, #CCM_CBCDR]
+
+wait_div_update_50:
+ ldr r0, [r6, #CCM_CDHIPR]
+ cmp r0, #0
+ bne wait_div_update_50
+
+ /* now switch periph_clk back. */
+ ldr r0, [r6, #CCM_CBCDR]
+ bic r0, r0, #(1 << 25)
+ str r0, [r6, #CCM_CBCDR]
+
+periph_clk_switch2:
+ ldr r0, [r6, #CCM_CDHIPR]
+ cmp r0, #0
+ bne periph_clk_switch2
+
+ .endm
+
+ .macro switch_to_24MHz
+ /*
+ * change the freq now try setting DDR to 24MHz.
+ * source it from the periph_clk2 ensure the
+ * periph_clk2 is sourced from 24MHz and the
+ * divider is 1.
+ */
+
+ ldr r0, [r6, #CCM_CBCMR]
+ bic r0, r0, #(0x3 << 12)
+ orr r0, r0, #(1 << 12)
+ str r0, [r6, #CCM_CBCMR]
+
+ ldr r0, [r6, #CCM_CBCDR]
+ bic r0, r0, #(0x38 << 24)
+ str r0, [r6, #CCM_CBCDR]
+
+ /* now switch periph_clk to 24MHz. */
+ ldr r0, [r6, #CCM_CBCDR]
+ orr r0, r0, #(1 << 25)
+ str r0, [r6, #CCM_CBCDR]
+
+periph_clk_switch1:
+ ldr r0, [r6, #CCM_CDHIPR]
+ cmp r0, #0
+ bne periph_clk_switch1
+
+ /* change all the dividers to 1. */
+ ldr r0, [r6, #CCM_CBCDR]
+ ldr r2, =0x3f1f00
+ bic r0, r0, r2
+ orr r0, r0, #(1 << 8)
+ str r0, [r6, #CCM_CBCDR]
+
+ /* Wait for the divider to change. */
+wait_div_update:
+ ldr r0, [r6, #CCM_CDHIPR]
+ cmp r0, #0
+ bne wait_div_update
+
+ .endm
+
+ .macro disable_l1_dcache
+
+ /*
+ * Flush all data from the L1 data cache before disabling
+ * SCTLR.C bit.
+ */
+ push {r0 - r11, lr}
+
+ ldr r7, =v7_flush_kern_cache_all
+ mov lr, pc
+ mov pc, r7
+ pop {r0 - r11, lr}
+
+ /* disable d-cache */
+ mrc p15, 0, r6, c1, c0, 0
+ bic r6, r6, #0x4
+ mcr p15, 0, r6, c1, c0, 0
+ dsb
+ isb
+
+ push {r0 - r11, lr}
+
+ ldr r7, =v7_flush_kern_cache_all
+ mov lr, pc
+ mov pc, r7
+ pop {r0 - r11, lr}
+
+ .endm
+
+/*
+ * mx6_ddr3_freq_change
+ *
+ * idle the processor (eg, wait for interrupt).
+ * make sure DDR is in self-refresh.
+ * IRQs are already disabled.
+ */
+ENTRY(mx6_ddr3_freq_change)
+
+mx6_ddr3_freq_change_start:
+ stmfd sp!, {r4-r12}
+
+ /*
+ * r5 -> mmdc_base
+ * r6 -> ccm_base
+ * r7 -> iomux_base
+ * r12 -> l2_base
+ */
+ mov r4, r0
+ mov r8, r1
+ mov r9, r2
+ mov r11, r3
+
+ /* flush the TLB */
+ ldr r6, =0x0
+ mcr p15, 0, r6, c8, c3, 0
+
+ ldr r6, =iram_tlb_phys_addr
+ ldr r7, [r6]
+
+ /*
+ * Need to flush and disable L1 before
+ * disabling L2, we need data to
+ * coherent. Flushing L1 pushes
+ * everyhting to L2. We sync L2 later, but
+ * it can still have dirty lines.
+ * While exiting, we need to enable L2 first
+ * and then L1.
+ */
+ disable_l1_dcache
+
+#ifdef CONFIG_CACHE_L2X0
+ /*
+ * Make sure the L2 buffers are drained.
+ * Sync operation on L2 drains the buffers.
+ */
+ ldr r12, =IMX_IO_P2V(MX6Q_L2_BASE_ADDR)
+
+ /* Wait for background operations to complete. */
+wait_for_l2_to_idle:
+ ldr r1, [r12, #L2_CACHE_SYNC]
+ cmp r1, #0x0
+ bne wait_for_l2_to_idle
+
+ mov r1, #0x0
+ str r1, [r12, #L2_CACHE_SYNC]
+
+ dsb
+ isb
+
+ ldr r1, [r12, #PL310_AUX_CTRL]
+ tst r1, #PL310_AUX_16WAY_BIT
+ mov r1, #PL310_8WAYS_MASK
+ orrne r1, #PL310_16WAYS_UPPERMASK
+ mov r6, #PL310_LOCKDOWN_NBREGS
+ add r5, r12, #PL310_DCACHE_LOCKDOWN_BASE
+1: /* lock Dcache and Icache */
+ str r1, [r5], #PL310_LOCKDOWN_SZREG
+ str r1, [r5], #PL310_LOCKDOWN_SZREG
+ subs r6, r6, #1
+ bne 1b
+#endif
+
+ /*
+ * To ensure no page table walks occur in DDR, we
+ * have a another page table stored in IRAM that only
+ * contains entries pointing to IRAM, AIPS1 and AIPS2.
+ * We need to set the TTBR1 to the new IRAM TLB.
+ * Do the following steps:
+ * 1. Flush the Branch Target Address Cache (BTAC)
+ * 2. Set TTBR1 to point to IRAM page table.
+ * 3. Disable page table walks in TTBR0 (PD0 = 1)
+ * 4. Set TTBR0.N=1, implying 0-2G is translated by TTBR0
+ * and 2-4G is translated by TTBR1.
+ */
+
+
+ /* Now switch the TTBR. */
+ /* Disable Branch Prediction, Z bit in SCTLR. */
+ mrc p15, 0, r6, c1, c0, 0
+ bic r6, r6, #0x800
+ mcr p15, 0, r6, c1, c0, 0
+
+ /* Flush the Branch Target Address Cache (BTAC) */
+ ldr r6, =0x0
+ mcr p15, 0, r6, c7, c1, 6
+
+ dsb
+ isb
+
+ /* Store the IRAM table in TTBR1 */
+ mcr p15, 0, r7, c2, c0, 1
+
+ /* Read TTBCR and set PD0=1, N = 1 */
+ mrc p15, 0, r6, c2, c0, 2
+ orr r6, r6, #0x11
+ mcr p15, 0, r6, c2, c0, 2
+
+ dsb
+ isb
+
+ /* flush the TLB */
+ ldr r6, =0x0
+ mcr p15, 0, r6, c8, c3, 0
+
+ dsb
+ isb
+
+
+ ldr r5, =IMX_IO_P2V(MX6Q_MMDC_P0_BASE_ADDR)
+ ldr r6, =IMX_IO_P2V(MX6Q_CCM_BASE_ADDR)
+ ldr r7, =IMX_IO_P2V(MX6Q_IOMUXC_BASE_ADDR)
+
+ /* Read the Original MU delay value */
+ ldr r1, [r5, #MMDC0_MPMUR0]
+ mov r10, r1, lsr #16
+ ldr r1, =0x3ff
+ and r10, r10, r1
+
+ /* disable automatic power saving. */
+ ldr r0, [r5, #MMDC0_MAPSR]
+ orr r0, r0, #0x01
+ str r0, [r5, #MMDC0_MAPSR]
+
+ /* disable MMDC power down timer. */
+ ldr r0, [r5, #MMDC0_MDPDC]
+ bic r0, r0, #(0xff << 8)
+ str r0, [r5, #MMDC0_MDPDC]
+
+ /* delay for a while */
+ ldr r1, =4
+delay1:
+ ldr r2, =0
+cont1:
+ ldr r0, [r5, r2]
+ add r2, r2, #4
+ cmp r2, #16
+ bne cont1
+ sub r1, r1, #1
+ cmp r1, #0
+ bgt delay1
+
+ /* set CON_REG */
+ ldr r0, =0x8000
+ str r0, [r5, #MMDC0_MDSCR]
+poll_conreq_set_1:
+ ldr r0, [r5, #MMDC0_MDSCR]
+ and r0, r0, #(0x4 << 12)
+ cmp r0, #(0x4 << 12)
+ bne poll_conreq_set_1
+
+ /*
+ * if requested frequency is great than
+ * 300MHz, skip setting bypass adopt mode.
+ */
+ ldr r1, =300000000
+ cmp r4, r1
+ bge 1f
+
+ is_mx6qp
+ bne 1f
+ /* Switch to adopt mode, set MMDC0_MAARCR bit25~26 to 2b'01 */
+ ldr r0, [r5, #MMDC0_MAARCR]
+ bic r0, r0, #(0x3 << 25)
+ orr r0, #(0x01 << 25)
+ str r0 , [r5, #MMDC0_MAARCR]
+1:
+ ldr r0, =0x00008050
+ str r0, [r5, #MMDC0_MDSCR]
+ ldr r0, =0x00008058
+ str r0, [r5, #MMDC0_MDSCR]
+
+ /*
+ * if requested frequency is greater than
+ * 300MHz go to DLL on mode.
+ */
+ ldr r1, =300000000
+ cmp r4, r1
+ bge dll_on_mode
+
+dll_off_mode:
+
+ /* if DLL is currently on, turn it off. */
+ cmp r9, #1
+ beq continue_dll_off_1
+
+ ldr r0, =0x00018031
+ str r0, [r5, #MMDC0_MDSCR]
+
+ ldr r0, =0x00018039
+ str r0, [r5, #MMDC0_MDSCR]
+
+ ldr r1, =10
+delay1a:
+ ldr r2, =0
+cont1a:
+ ldr r0, [r5, r2]
+ add r2, r2, #4
+ cmp r2, #16
+ bne cont1a
+ sub r1, r1, #1
+ cmp r1, #0
+ bgt delay1a
+
+continue_dll_off_1:
+ /* set DVFS - enter self refresh mode */
+ ldr r0, [r5, #MMDC0_MAPSR]
+ orr r0, r0, #(1 << 21)
+ str r0, [r5, #MMDC0_MAPSR]
+
+ /* de-assert con_req */
+ mov r0, #0x0
+ str r0, [r5, #MMDC0_MDSCR]
+
+poll_dvfs_set_1:
+ ldr r0, [r5, #MMDC0_MAPSR]
+ and r0, r0, #(1 << 25)
+ cmp r0, #(1 << 25)
+ bne poll_dvfs_set_1
+
+ ldr r1, =24000000
+ cmp r4, r1
+ beq switch_freq_24
+
+ switch_to_50MHz
+ b continue_dll_off_2
+
+switch_freq_24:
+ switch_to_24MHz
+
+continue_dll_off_2:
+
+ /* set SBS - block ddr accesses */
+ ldr r0, [r5, #MMDC0_MADPCR0]
+ orr r0, r0, #(1 << 8)
+ str r0, [r5, #MMDC0_MADPCR0]
+
+ /* clear DVFS - exit from self refresh mode */
+ ldr r0, [r5, #MMDC0_MAPSR]
+ bic r0, r0, #(1 << 21)
+ str r0, [r5, #MMDC0_MAPSR]
+
+poll_dvfs_clear_1:
+ ldr r0, [r5, #MMDC0_MAPSR]
+ and r0, r0, #(1 << 25)
+ cmp r0, #(1 << 25)
+ beq poll_dvfs_clear_1
+
+ /* if DLL was previously on, continue DLL off routine. */
+ cmp r9, #1
+ beq continue_dll_off_3
+
+ ldr r0, =0x00018031
+ str r0, [r5, #MMDC0_MDSCR]
+
+ ldr r0, =0x00018039
+ str r0, [r5, #MMDC0_MDSCR]
+
+ ldr r0, =0x08208030
+ str r0, [r5, #MMDC0_MDSCR]
+
+ ldr r0, =0x08208038
+ str r0, [r5, #MMDC0_MDSCR]
+
+ ldr r0, =0x00088032
+ str r0, [r5, #MMDC0_MDSCR]
+
+ ldr r0, =0x0008803A
+ str r0, [r5, #MMDC0_MDSCR]
+
+ /* delay for a while. */
+ ldr r1, =4
+delay_1:
+ ldr r2, =0
+cont_1:
+ ldr r0, [r5, r2]
+ add r2, r2, #4
+ cmp r2, #16
+ bne cont_1
+ sub r1, r1, #1
+ cmp r1, #0
+ bgt delay_1
+
+ ldr r0, [r5, #MMDC0_MDCF0]
+ bic r0, r0, #0xf
+ orr r0, r0, #0x3
+ str r0, [r5, #MMDC0_MDCF0]
+
+ ldr r0, [r5, #MMDC0_MDCF1]
+ bic r0, r0, #0x7
+ orr r0, r0, #0x4
+ str r0, [r5, #MMDC0_MDCF1]
+
+ ldr r0, [r5, #MMDC0_MDMISC]
+ bic r0, r0, #(0x3 << 16) /* walat = 0x1 */
+ orr r0, r0, #(0x1 << 16)
+ bic r0, r0, #(0x7 << 6) /* ralat = 0x2 */
+ orr r0, r0, #(0x2 << 6)
+ str r0, [r5, #MMDC0_MDMISC]
+
+ /* enable dqs pull down in the IOMUX. */
+ ldr r1, [r11]
+ add r11, r11, #8
+ ldr r2, =0x3028
+update_iomux:
+ ldr r0, [r11, #0x0]
+ ldr r3, [r7, r0]
+ bic r3, r3, r2
+ orr r3, r3, #(0x3 << 12)
+ orr r3, r3, #0x28
+ str r3, [r7, r0]
+ add r11, r11, #8
+ sub r1, r1, #1
+ cmp r1, #0
+ bgt update_iomux
+
+ /* ODT disabled. */
+ ldr r0, =0x0
+ ldr r2, =MMDC0_MPODTCTRL
+ str r0, [r5, r2]
+ ldr r2, =MMDC1_MPODTCTRL
+ str r0, [r5, r2]
+
+ /* DQS gating disabled. */
+ ldr r2, =MMDC0_MPDGCTRL0
+ ldr r0, [r5, r2]
+ orr r0, r0, #(1 << 29)
+ str r0, [r5, r2]
+
+ ldr r2, =MMDC1_MPDGCTRL0
+ ldr r0, [r5, r2]
+ orr r0, r0, #(0x1 << 29)
+ str r0, [r5, r2]
+
+ /* Add workaround for ERR005778.*/
+ /* double the original MU_UNIT_DEL_NUM. */
+ lsl r10, r10, #1
+
+ /* Bypass the automatic MU by setting the mu_byp_en */
+ ldr r2, [r5, #MMDC0_MPMUR0]
+ orr r2, r2, #0x400
+ orr r2, r2, r10
+ str r2, [r5, #MMDC0_MPMUR0]
+ ldr r0, =MMDC1_MPMUR0
+ str r2, [r5, r0]
+
+ /* Now perform a force measure */
+ ldr r0, [r5, #MMDC0_MPMUR0]
+ orr r0, r0, #0x800
+ str r0, [r5, #MMDC0_MPMUR0]
+ ldr r2, =MMDC1_MPMUR0
+ str r0, [r5, r2]
+ /* Wait for FRC_MSR to clear. */
+1:
+ ldr r0, [r5, #MMDC0_MPMUR0]
+ and r0, r0, #0x800
+ ldr r1, [r5, r2]
+ and r1, r1, #0x800
+ orr r0, r0, r1
+ cmp r0, #0x0
+ bne 1b
+
+continue_dll_off_3:
+ /* clear SBS - unblock accesses to DDR. */
+ ldr r0, [r5, #MMDC0_MADPCR0]
+ bic r0, r0, #(0x1 << 8)
+ str r0, [r5, #MMDC0_MADPCR0]
+
+ mov r0, #0x0
+ str r0, [r5, #MMDC0_MDSCR]
+poll_conreq_clear_1:
+ ldr r0, [r5, #MMDC0_MDSCR]
+ and r0, r0, #(0x4 << 12)
+ cmp r0, #(0x4 << 12)
+ beq poll_conreq_clear_1
+
+ b done
+
+dll_on_mode:
+ /* assert DVFS - enter self refresh mode. */
+ ldr r0, [r5, #MMDC0_MAPSR]
+ orr r0, r0, #(1 << 21)
+ str r0, [r5, #MMDC0_MAPSR]
+
+ /* de-assert CON_REQ. */
+ mov r0, #0x0
+ str r0, [r5, #MMDC0_MDSCR]
+
+ /* poll DVFS ack. */
+poll_dvfs_set_2:
+ ldr r0, [r5, #MMDC0_MAPSR]
+ and r0, r0, #(1 << 25)
+ cmp r0, #(1 << 25)
+ bne poll_dvfs_set_2
+
+ ldr r1, =528000000
+ cmp r4, r1
+ beq switch_freq_528
+
+ switch_to_400MHz
+
+ b continue_dll_on
+
+switch_freq_528:
+ switch_to_528MHz
+
+continue_dll_on:
+
+ /* set SBS step-by-step mode. */
+ ldr r0, [r5, #MMDC0_MADPCR0]
+ orr r0, r0, #( 1 << 8)
+ str r0, [r5, #MMDC0_MADPCR0]
+
+ /* clear DVFS - exit self refresh mode. */
+ ldr r0, [r5, #MMDC0_MAPSR]
+ bic r0, r0, #(1 << 21)
+ str r0, [r5, #MMDC0_MAPSR]
+
+poll_dvfs_clear_2:
+ ldr r0, [r5, #MMDC0_MAPSR]
+ and r0, r0, #(1 << 25)
+ cmp r0, #(1 << 25)
+ beq poll_dvfs_clear_2
+
+ /* if DLL is currently off, turn it back on. */
+ cmp r9, #0
+ beq update_calibration_only
+
+ /* issue zq calibration command */
+ ldr r0, [r5, #MMDC0_MPZQHWCTRL]
+ orr r0, r0, #0x3
+ str r0, [r5, #MMDC0_MPZQHWCTRL]
+ ldr r2, =MMDC1_MPZQHWCTRL
+ str r0, [r5, r2]
+
+ /* enable DQS gating. */
+ ldr r2, =MMDC0_MPDGCTRL0
+ ldr r0, [r5, r2]
+ bic r0, r0, #(1 << 29)
+ str r0, [r5, r2]
+
+ ldr r2, =MMDC1_MPDGCTRL0
+ ldr r0, [r5, r2]
+ bic r0, r0, #(1 << 29)
+ str r0, [r5, r2]
+
+ /* force measure. */
+ ldr r0, =0x00000800
+ str r0, [r5, #MMDC0_MPMUR0]
+ ldr r2, =MMDC1_MPMUR0
+ str r0, [r5, r2]
+
+ /* Wait for FRC_MSR to clear. */
+1:
+ ldr r0, [r5, #MMDC0_MPMUR0]
+ and r0, r0, #0x800
+ ldr r1, [r5, r2]
+ and r1, r1, #0x800
+ orr r0, r0, r1
+ cmp r0, #0x0
+ bne 1b
+
+ /* disable dqs pull down in the IOMUX. */
+ ldr r1, [r11]
+ add r11, r11, #8
+update_iomux1:
+ ldr r0, [r11, #0x0]
+ ldr r3, [r11, #0x4]
+ str r3, [r7, r0]
+ add r11, r11, #8
+ sub r1, r1, #1
+ cmp r1, #0
+ bgt update_iomux1
+
+ /* config MMDC timings to 528MHz. */
+ ldr r9, [r8]
+ add r8, r8, #8
+ ldr r0, [r8, #0x0]
+ ldr r3, [r8, #0x4]
+ str r3, [r5, r0]
+ add r8, r8, #8
+
+ ldr r0, [r8, #0x0]
+ ldr r3, [r8, #0x4]
+ str r3, [r5, r0]
+ add r8, r8, #8
+
+ /* configure ddr devices to dll on, odt. */
+ ldr r0, =0x00048031
+ str r0, [r5, #MMDC0_MDSCR]
+
+ ldr r0, =0x00048039
+ str r0, [r5, #MMDC0_MDSCR]
+
+ /* delay for while. */
+ ldr r1, =4
+delay7:
+ ldr r2, =0
+cont7:
+ ldr r0, [r5, r2]
+ add r2, r2, #4
+ cmp r2, #16
+ bne cont7
+ sub r1, r1, #1
+ cmp r1, #0
+ bgt delay7
+
+ /* reset dll. */
+ ldr r0, =0x09408030
+ str r0, [r5, #MMDC0_MDSCR]
+
+ ldr r0, =0x09408038
+ str r0, [r5, #MMDC0_MDSCR]
+
+ /* delay for while. */
+ ldr r1, =100
+delay8:
+ ldr r2, =0
+cont8:
+ ldr r0, [r5, r2]
+ add r2, r2, #4
+ cmp r2, #16
+ bne cont8
+ sub r1, r1, #1
+ cmp r1, #0
+ bgt delay8
+
+ ldr r0, [r8, #0x0]
+ ldr r3, [r8, #0x4]
+ str r3, [r5, r0]
+ add r8, r8, #8
+
+ ldr r0, [r8, #0x0]
+ ldr r3, [r8, #0x4]
+ str r3, [r5, r0]
+ add r8, r8, #8
+
+ ldr r0, =0x00428031
+ str r0, [r5, #MMDC0_MDSCR]
+
+ ldr r0, =0x00428039
+ str r0, [r5, #MMDC0_MDSCR]
+
+ ldr r0, [r8, #0x0]
+ ldr r3, [r8, #0x4]
+ str r3, [r5, r0]
+ add r8, r8, #8
+
+ ldr r0, [r8, #0x0]
+ ldr r3, [r8, #0x4]
+ str r3, [r5, r0]
+ add r8, r8, #8
+
+ /* issue a zq command. */
+ ldr r0, =0x04008040
+ str r0, [r5, #MMDC0_MDSCR]
+
+ ldr r0, =0x04008048
+ str r0, [r5, #MMDC0_MDSCR]
+
+ /* MMDC ODT enable. */
+ ldr r0, [r8, #0x0]
+ ldr r3, [r8, #0x4]
+ str r3, [r5, r0]
+ add r8, r8, #8
+
+ ldr r2, =0x4818
+ str r3, [r5, r2]
+
+ /* delay for while. */
+ ldr r1, =40
+delay15:
+ ldr r2, =0
+cont15:
+ ldr r0, [r5, r2]
+ add r2, r2, #4
+ cmp r2, #16
+ bne cont15
+ sub r1, r1, #1
+ cmp r1, #0
+ bgt delay15
+
+ /* enable MMDC power down timer. */
+ ldr r0, [r5, #MMDC0_MDPDC]
+ orr r0, r0, #(0x55 << 8)
+ str r0, [r5, #MMDC0_MDPDC]
+
+ b update_calibration
+
+update_calibration_only:
+ ldr r1, [r8]
+ sub r1, r1, #7
+ add r8, r8, #64
+ b update_calib
+
+update_calibration:
+ /* write the new calibration values. */
+ mov r1, r9
+ sub r1, r1, #7
+
+update_calib:
+ ldr r0, [r8, #0x0]
+ ldr r3, [r8, #0x4]
+ str r3, [r5, r0]
+ add r8, r8, #8
+ sub r1, r1, #1
+ cmp r1, #0
+ bgt update_calib
+
+ /* perform a force measurement. */
+ ldr r0, =0x800
+ str r0, [r5, #MMDC0_MPMUR0]
+ ldr r2, =MMDC1_MPMUR0
+ str r0, [r5, r2]
+
+ /* Wait for FRC_MSR to clear. */
+1:
+ ldr r0, [r5, #MMDC0_MPMUR0]
+ and r0, r0, #0x800
+ ldr r1, [r5, r2]
+ and r1, r1, #0x800
+ orr r0, r0, r1
+ cmp r0, #0x0
+ bne 1b
+
+ /* clear SBS - unblock DDR accesses. */
+ ldr r0, [r5, #MMDC0_MADPCR0]
+ bic r0, r0, #(1 << 8)
+ str r0, [r5, #MMDC0_MADPCR0]
+
+ is_mx6qp
+ bne 3f
+ /*
+ * Switch back to adopt_bp mode, set MMDC0_MAARCR
+ * bit25~26 to 2b'10.
+ */
+ ldr r0, [r5, #MMDC0_MAARCR]
+ bic r0, r0, #(0x3 << 25)
+ orr r0, r0, #(0x2 << 25)
+ str r0, [r5, #MMDC0_MAARCR]
+3:
+ mov r0, #0x0
+ str r0, [r5, #MMDC0_MDSCR]
+poll_conreq_clear_2:
+ ldr r0, [r5, #MMDC0_MDSCR]
+ and r0, r0, #(0x4 << 12)
+ cmp r0, #(0x4 << 12)
+ beq poll_conreq_clear_2
+
+done:
+ /* MMDC0_MAPSR adopt power down enable. */
+ ldr r0, [r5, #MMDC0_MAPSR]
+ bic r0, r0, #0x01
+ str r0, [r5, #MMDC0_MAPSR]
+
+#ifdef CONFIG_CACHE_L2X0
+ ldr r1, [r12, #PL310_AUX_CTRL]
+ tst r1, #PL310_AUX_16WAY_BIT
+ mov r6, #PL310_LOCKDOWN_NBREGS
+ mov r1, #0x00 /* 8 ways mask */
+ orrne r1, #0x0000 /* 16 ways mask */
+ add r5, r12, #PL310_DCACHE_LOCKDOWN_BASE
+1: /* lock Dcache and Icache */
+ str r1, [r5], #PL310_LOCKDOWN_SZREG
+ str r1, [r5], #PL310_LOCKDOWN_SZREG
+ subs r6, r6, #1
+ bne 1b
+
+ isb
+ dsb
+#endif
+
+ /* Enable L1 data cache. */
+ mrc p15, 0, r6, c1, c0, 0
+ orr r6, r6, #0x4
+ mcr p15, 0, r6, c1, c0, 0
+
+ /* Restore the TTBCR */
+ dsb
+ isb
+
+ /* Read TTBCR and set PD0=0, N = 0 */
+ mrc p15, 0, r6, c2, c0, 2
+ bic r6, r6, #0x11
+ mcr p15, 0, r6, c2, c0, 2
+ dsb
+ isb
+
+ /* flush the TLB */
+ ldr r6, =0x0
+ mcr p15, 0, r6, c8, c3, 0
+
+ dsb
+ isb
+
+ /* Enable Branch Prediction, Z bit in SCTLR. */
+ mrc p15, 0, r6, c1, c0, 0
+ orr r6, r6, #0x800
+ mcr p15, 0, r6, c1, c0, 0
+
+ isb
+
+ /* Flush the Branch Target Address Cache (BTAC) */
+ ldr r6, =0x0
+ mcr p15, 0, r6, c7, c1, 6
+ isb
+ dsb
+
+ /* restore registers */
+ ldmfd sp!, {r4-r12}
+ mov pc, lr
+
+ /*
+ * Add ltorg here to ensure that all
+ * literals are stored here and are
+ * within the text space.
+ */
+ .ltorg
+mx6_ddr3_freq_change_end: