blob: 2a6756663ea7cf58f692ad6f4cf26384bbdd327a [file] [log] [blame]
/*
* Copyright (C) 2012-2014 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 "hardware.h"
.extern iram_tlb_phys_addr
.globl mx6sl_lpm_wfi_start
.globl mx6sl_lpm_wfi_end
.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, #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 */
stmfd r9!, {r4-r6}
.endm
.macro sl_ddr_io_restore
/*
* r9 points to IRAM stack.
* r1 points to IOMUX base address.
* r8 points to MMDC base address.
*/
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, #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-r6}
str r4, [r1, #0x330] /* DRAM_SDCKE0 */
str r5, [r1, #0x334] /* DRAM_SDCKE1 */
str r6, [r1, #0x320] /* DRAM_RESET */
/*
* Need to reset the FIFO to avoid MMDC lockup
* caused because of floating/changing the
* configuration of many DDR IO pads.
*/
ldr r7, =0x83c
ldr r6, [r8, r7]
orr r6, r6, #0x80000000
str r6, [r8, r7]
fifo_reset1_wait:
ldr r6, [r8, r7]
and r6, r6, #0x80000000
cmp r6, #0
bne fifo_reset1_wait
/* reset FIFO a second time */
ldr r6, [r8, r7]
orr r6, r6, #0x80000000
str r6, [r8, r7]
fifo_reset2_wait:
ldr r6, [r8, r7]
and r6, r6, #0x80000000
cmp r6, #0
bne fifo_reset2_wait
.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, #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*/
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
/*
* imx6sl_low_power_wfi
*
* Idle the processor (eg, wait for interrupt).
* Make sure DDR is in self-refresh.
* IRQs are already disabled.
* r0: WFI IRAMcode base address.
* r1: 1 if vbus_ldo (ldo2p5) cannot be disabled
* r2: 1 if in audio_bus_freq_mode
*/
.align 3
ENTRY(imx6sl_low_power_wfi)
mx6sl_lpm_wfi_start:
push {r4-r12}
/* Can LDO2p5 be disabled */
mov r12, r1
/* Store audio_bus_freq_mode */
mov r11, r2
/* Get the IRAM data storage address. */
mov r10, r0
mov r9, r0 /* get wfi_iram_base */
adrl r4, mx6sl_lpm_wfi_end
add r9, r4, #MX6SL_WFI_IRAM_DATA_SIZE
/*
* 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.
*/
ldr r6, =iram_tlb_phys_addr
ldr r7, [r6]
/* 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 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
/* Disable L1 data cache. */
mrc p15, 0, r6, c1, c0, 0
bic r6, r6, #0x4
mcr p15, 0, r6, c1, c0, 0
dsb
isb
#ifdef CONFIG_CACHE_L2X0
/*
* Need to make sure the buffers in L2 are drained.
* Performing a sync operation does this.
*/
ldr r7, =IMX_IO_P2V(MX6Q_L2_BASE_ADDR)
/* Wait for background operations to complete. */
wait_for_l2_to_idle:
ldr r6, [r7, #0x730]
cmp r6, #0x0
bne wait_for_l2_to_idle
mov r6, #0x0
str r6, [r7, #0x730]
/* Disable L2. */
str r6, [r7, #0x100]
/*
* The second dsb might be needed to keep cache sync (device write)
* ordering with the memory accesses before it.
*/
dsb
isb
#endif
ldr r1, =IMX_IO_P2V(MX6Q_IOMUXC_BASE_ADDR)
ldr r3, =IMX_IO_P2V(MX6Q_ANATOP_BASE_ADDR)
ldr r2, =IMX_IO_P2V(MX6Q_CCM_BASE_ADDR)
ldr r8, =IMX_IO_P2V(MX6Q_MMDC_P0_BASE_ADDR)
/* Store the original ARM PODF. */
ldr r0, [r2, #0x10]
/* Save the DDR IO state. */
sl_ddr_io_save
/* 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
/* set SBS step-by-step mode */
ldr r6, [r8, #0x410]
orr r6, r6, #0x100
str r6, [r8, #0x410]
cmp r11, #1
beq audio_mode
/*
* 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]
b mmdc_podf
audio_mode:
/* MMDC is from PLL2_200M.
* Set the mmdc_podf to div by 8.
*/
ldr r6, [r2, #0x14]
orr r6, r6, #0x38
str r6, [r2, #0x14]
/* Loop till podf is accepted. */
mmdc_podf:
ldr r6, [r2, #0x48]
cmp r6, #0x0
bne mmdc_podf
/* Set the DDR IO in LPM state. */
sl_ddr_io_set_lpm
cmp r11, #1
beq do_audio_arm_clk
/*
* Check if none of the PLLs are
* locked, except PLL1 which will get
* bypassed below.
* We should not be here if PLL2 is not
* bypassed.
*/
ldr r7, =1
/* USB1 PLL3 */
ldr r6, [r3, #0x10]
and r6, r6, #0x80000000
cmp r6, #0x80000000
beq no_analog_saving
/* USB2 PLL7 */
ldr r6, [r3, #0x20]
and r6, r6, #0x80000000
cmp r6, #0x80000000
beq no_analog_saving
/* Audio PLL4 */
ldr r6, [r3, #0x70]
and r6, r6, #0x80000000
cmp r6, #0x80000000
beq no_analog_saving
/* Video PLL5 */
ldr r6, [r3, #0xA0]
and r6, r6, #0x80000000
cmp r6, #0x80000000
beq no_analog_saving
/* ENET PLL8 */
ldr r6, [r3, #0xE0]
and r6, r6, #0x80000000
cmp r6, #0x80000000
beq no_analog_saving
b cont
no_analog_saving:
ldr r7, =0
cont:
/* Set the AHB to 3MHz. AXI to 3MHz. */
ldr r9, [r2, #0x14]
mov r6, r9
orr r6, r6, #0x1c00
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 1.5MHz 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
/*
* Check if we can save some
* power in the Analog section.
*/
cmp r7, #0x1
bne do_wfi
/* Disable 1p1 brown out. */
ldr r6, [r3, #0x110]
bic r6, r6, #0x2
str r6, [r3, #0x110]
/*
* Set the OSC bias current to -37.5%
* to drop the power on VDDHIGH.
*/
ldr r6, [r3, #0x150]
orr r6, r6, #0xC000
str r6, [r3, #0x150]
cmp r12, #0x1
beq leave_2p5_on
/* Enable the weak 2P5 */
ldr r6, [r3, #0x130]
orr r6, r6, #0x40000
str r6, [r3, #0x130]
/* Disable main 2p5. */
ldr r6, [r3, #0x130]
bic r6, r6, #0x1
str r6, [r3, #0x130]
/*
* Cannot disable regular bandgap
* in LDO-enabled mode. The bandgap
* is required for ARM-LDO to regulate
* the voltage.
*/
ldr r6, [r3, #0x140]
and r6, r6, #0x1f
cmp r6, #0x1f
bne leave_bandgap_enabled
/* Enable low power bandgap */
ldr r6, [r3, #0x260]
orr r6, r6, #0x20
str r6, [r3, #0x260]
/*
* Turn off the bias current
* from the regular bandgap.
*/
ldr r6, [r3, #0x260]
orr r6, r6, #0x80
str r6, [r3, #0x260]
/*
* Clear the REFTOP_SELFBIASOFF,
* self-bias circuit of the band gap.
* Per RM, should be cleared when
* band gap is powered down.
*/
ldr r6, [r3, #0x150]
bic r6, r6, #0x8
str r6, [r3, #0x150]
/* Power down the regular bandgap. */
ldr r6, [r3, #0x150]
orr r6, r6, #0x1
str r6, [r3, #0x150]
leave_bandgap_enabled:
leave_2p5_on:
b do_wfi
do_audio_arm_clk:
/*
* ARM is from PLL2_PFD2_400M here.
* Switch ARM to bypassed PLL1.
*/
ldr r6, [r2, #0xC]
bic r6, r6, #0x4
str r6, [r2, #0xC]
/*
* Set the ARM_PODF to divide by 2
* as IPG is at 4MHz, we cannot run
* ARM_CLK above 9.6MHz when
* system enters WAIT mode.
*/
ldr r6, =0x2
str r6, [r2, #0x10]
/* Loop till podf is accepted. */
podf_loop_audio:
ldr r6, [r2, #0x48]
cmp r6, #0x0
bne podf_loop_audio
do_wfi:
/* Now do WFI. */
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
cmp r11, #1
beq audio_arm_clk_restore
/*
* Check if powered down
* analog components.
*/
cmp r7, #0x1
bne skip_analog_restore
cmp r12, #1
beq ldo2p5_not_disabled
/*
* Regular bandgap will not be disabled
* in LDO-enabled mode as it is required
* for ARM-LDO to regulate the voltage.
*/
ldr r6, [r3, #0x140]
and r6, r6, #0x1f
cmp r6, #0x1f
bne skip_bandgap_restore
/* Power up the regular bandgap. */
ldr r6, [r3, #0x150]
bic r6, r6, #0x1
str r6, [r3, #0x150]
/* Wait for bandgap to stabilize. */
bg_not_stable:
ldr r6, [r3, #0x150]
and r6, r6, #0x80
cmp r6, #0x80
bne bg_not_stable
/* Now disable bandgap self-bias circuit. */
ldr r6, [r3, #0x150]
orr r6, r6, #0x8
str r6, [r3, #0x150]
/*
* Turn on the bias current
* from the regular bandgap.
*/
ldr r6, [r3, #0x260]
bic r6, r6, #0x80
str r6, [r3, #0x260]
/* Disable the low power bandgap */
ldr r6, [r3, #0x260]
bic r6, r6, #0x20
str r6, [r3, #0x260]
skip_bandgap_restore:
/* Enable main 2p5. */
ldr r6, [r3, #0x130]
orr r6, r6, #0x1
str r6, [r3, #0x130]
/* Ensure the 2P5 is up. */
loop_2p5:
ldr r6, [r3, #0x130]
and r6, r6, #0x20000
cmp r6, #0x20000
bne loop_2p5
/* Disable the weak 2P5 */
ldr r6, [r3, #0x130]
bic r6, r6, #0x40000
str r6, [r3, #0x130]
ldo2p5_not_disabled:
/*
* Set the OSC bias current to max
* value for normal operation.
*/
ldr r6, [r3, #0x150]
bic r6, r6, #0xC000
str r6, [r3, #0x150]
/* Enable 1p1 brown out. */
ldr r6, [r3, #0x110]
orr r6, r6, #0x2
str r6, [r3, #0x110]
skip_analog_restore:
/* 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
b wfi_restore
audio_arm_clk_restore:
/* Move ARM back to PLL2_PFD2_400M */
ldr r6, [r2, #0xC]
orr r6, r6, #0x4
str r6, [r2, #0xC]
wfi_restore:
/* get wfi_iram_base */
adrl r9, mx6sl_lpm_wfi_end
add r9, r9, #MX6SL_WFI_IRAM_DATA_SIZE
/* Restore the DDR IO before exiting self-refresh. */
sl_ddr_io_restore
/* Restore the MMDC PODF back */
cmp r11, #0x1
beq audio_mmdc_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]
b mmdc_podf1
/*
* In audio busfreq mode, when entering the low
* power code the original MMDC podf is setted to 0x1,
* means the DIV is 2. So before exiting lower power idle,
* need to restore this value back.
*/
audio_mmdc_restore:
ldr r6, [r2, #0x14]
bic r6, r6, #0x3f
orr r6, #0x8
str r6, [r2, #0x14]
mmdc_podf1:
ldr r6, [r2, #0x48]
cmp r6, #0x0
bne mmdc_podf1
/* 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]
/* clear SBS - unblock DDR accesses */
ldr r6, [r8, #0x410]
bic r6, r6, #0x100
str r6, [r8, #0x410]
#ifdef CONFIG_CACHE_L2X0
/* Enable L2. */
ldr r7, =IMX_IO_P2V(MX6Q_L2_BASE_ADDR)
ldr r6, =0x1
str r6, [r7, #0x100]
#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
/* Flush the Branch Target Address Cache (BTAC) */
ldr r6, =0x0
mcr p15, 0, r6, c7, c1, 6
/*
* Add these nops so that the
* prefetcher will not try to get
* any instructions from DDR.
* The prefetch depth is about 23
* on A9, so adding 25 nops.
*/
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
pop {r4-r12}
/* Restore registers */
mov pc, lr
/*
* Add ltorg here to ensure that all
* literals are stored here and are
* within the text space.
*/
.ltorg
mx6sl_lpm_wfi_end: