| /* |
| * 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: |