blob: 978f8d1cc234fa66756707802da116b31a4f7297 [file] [log] [blame]
/*
* Copyright (C) 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 teh 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>
#define PM_INFO_PM_INFO_SIZE_OFFSET 0x0
#define PM_INFO_TTBR_OFFSET 0x4
#define PM_INFO_MMDC_V_OFFSET 0x8
#define PM_INFO_IOMUXC_V_OFFSET 0xc
#define PM_INFO_CCM_V_OFFSET 0x10
#define PM_INFO_L2_V_OFFSET 0x14
#define PM_INFO_ANATOP_V_OFFSET 0x18
#define PM_INFO_IO_NUM_OFFSET 0x1c
#define PM_INFO_IO_VAL_OFFSET 0x20
#define MX6Q_MMDC_MAPSR 0x404
#define MX6Q_MMDC_MPDGCTRL0 0x83c
.global mx6sl_lpm_wfi_start
.global mx6sl_lpm_wfi_end
.macro pll_do_wait_lock
1:
ldr r7, [r10, r8]
ands r7, #0x80000000
beq 1b
.endm
.macro ccm_do_wait
2:
ldr r7, [r10, #0x48]
cmp r7, #0x0
bne 2b
.endm
.macro ccm_enter_idle
ldr r10, [r0, #PM_INFO_CCM_V_OFFSET]
/*
* if in audio_bus_freq_mode, skip to
* audio_mode low power setting.
*/
cmp r1, #0x1
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, [r10, #0x14]
orr r6, r6, #0x07
str r6, [r10, #0x14]
/* Now set MMDC PODF to divide by 3. */
ldr r6, [r10, #0x14]
bic r6, r6, #0x38
orr r6, r6, #0x10
str r6, [r10, #0x14]
ccm_do_wait
/* Set the AHB to 3MHz. AXI to 3MHz. */
ldr r6, [r10, #0x14]
/*r12 stores the origin AHB podf value */
mov r12, r6
orr r6, r6, #0x1c00
orr r6, r6, #0x70000
str r6, [r10, #0x14]
ccm_do_wait
/* Now set ARM to 24MHz.
* Move ARM to be sourced from step_clk
* after setting step_clk to 24MHz.
*/
ldr r6, [r10, #0x0c]
bic r6, r6, #0x100
str r6, [r10, #0xc]
/*Now pll1_sw_clk to step_clk */
ldr r6, [r10, #0x0c]
orr r6, r6, #0x4
str r6, [r10, #0x0c]
/* Bypass PLL1 and power it down */
ldr r10, [r0, #PM_INFO_ANATOP_V_OFFSET]
ldr r6, =(1 << 16)
orr r6, r6, #0x1000
str r6, [r10, #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 r10, [r0, #PM_INFO_CCM_V_OFFSET]
ldr r11, [r10, #0x10]
ldr r6, =0x07
str r6, [r10, #0x10]
ccm_do_wait
b ccm_idle_done
audio_mode:
/*
* MMDC is sourced from pll2_200M.
* Set the mmdc_podf to div by 8
*/
ldr r10, [r0, #PM_INFO_CCM_V_OFFSET]
ldr r6, [r10, #0x14]
orr r6, r6, #0x38
str r6, [r10, #0x14]
ccm_do_wait
/*
* ARM is sourced from pll2_pfd2_400M here.
* switch ARM to bypassed PLL1
*/
ldr r10, [r0, #PM_INFO_CCM_V_OFFSET]
ldr r6, [r10, #0x0c]
bic r6, r6, #0x4
str r6, [r10, #0xc]
/*
* set the arm_podf to divide by 3
* as IPG is at 4MHz, we cannot run
* arm clk above 9.6MHz when system
* enter WAIT mode
*/
ldr r11, [r10, #0x10]
ldr r6, =0x2
str r6, [r10, #0x10]
ccm_do_wait
ccm_idle_done:
.endm
.macro ccm_exit_idle
/*
* If in audio_bus_freq_mode, skip to
* audio_mode ccm restore.
*/
cmp r1, #0x1
beq audio_ccm_restore
ldr r10, [r0, #PM_INFO_ANATOP_V_OFFSET]
/* Power up PLL1 and un-bypass it. */
ldr r6, =(1 << 12)
str r6, [r10, #0x08]
/* Wait for PLL1 to relock */
ldr r8, =0x0
pll_do_wait_lock
ldr r6, =(1 << 16)
str r6, [r10, #0x08]
ldr r10, [r0, #PM_INFO_CCM_V_OFFSET]
/* Set PLL1_sw_clk back to PLL1 */
ldr r6, [r10, #0x0c]
bic r6, r6, #0x4
str r6, [r10, #0x0c]
/* Restore AHB/AXI back */
str r12, [r10, #0x14]
ccm_do_wait
/* restore mmdc back to 24MHz*/
ldr r6, [r10, #0x14]
bic r6, r6, #0x3f
str r6, [r10, #0x14]
ccm_do_wait
b ccm_exit_done
audio_ccm_restore:
/* move arm clk back to pll2_pfd2_400M */
ldr r6, [r10, #0xc]
orr r6, r6, #0x4
str r6, [r10, #0xc]
/* restore mmdc podf */
ldr r10, [r0, #PM_INFO_CCM_V_OFFSET]
ldr r6, [r10, #0x14]
bic r6, r6, #0x38
orr r6, #0x8
str r6, [r10, #0x14]
ccm_do_wait
ccm_exit_done:
.endm
.macro check_pll_state
ldr r10, [r0, #PM_INFO_ANATOP_V_OFFSET]
/*
* Check whether any PLL is enabled, as only when
* there is no PLLs enabled, 2p5 can be off and
* only enable the weak one. PLL1 will be powered
* down late, so no need to check PLL1 state.
*/
/* sys PLL2 */
ldr r6, [r10, #0x30]
ands r6, r6, #(1 << 31)
bne 1f
/* usb PLL3 */
ldr r6, [r10, #0x10]
ands r6, r6, #(1 << 31)
bne 1f
/* audio PLL4 */
ldr r6, [r10, #0x70]
ands r6, r6, #(1 << 31)
bne 1f
/* video PLL5 */
ldr r6, [r10, #0xa0]
ands r6, r6, #(1 << 31)
bne 1f
/* enet PLL6 */
ldr r6, [r10, #0xe0]
ands r6, r6, #(1 << 31)
bne 1f
/* usb host PLL7 */
ldr r6, [r10, #0x20]
ands r6, r6, #(1 << 31)
bne 1f
ldr r4, =0x1
b check_done
1:
ldr r4, =0x0
check_done:
.endm
.macro anatop_enter_idle
ldr r10, [r0, #PM_INFO_ANATOP_V_OFFSET]
cmp r4, #0x0
beq anatop_enter_done
/* Disable 1p1 brown out. */
ldr r10, [r0, #PM_INFO_ANATOP_V_OFFSET]
ldr r6, [r10, #0x110]
bic r6, r6, #0x2
str r6, [r10, #0x110]
/*
* Set the OSC bias current to -37.5%
* to drop the power on VDDHIGH.
*/
ldr r6, [r10, #0x150]
orr r6, r6, #0xc000
str r6, [r10, #0x150]
/*
* if the usb VBUS wakeup is enabled, skip
* disable main 2p5.
*/
cmp r2, #0x1
beq anatop_enter_done
/* Enable the week 2p5 */
ldr r6, [r10, #0x130]
orr r6, r6, #0x40000
str r6, [r10, #0x130]
/* Disable main 2p5. */
ldr r6, [r10, #0x130]
bic r6, r6, #0x1
str r6, [r10, #0x130]
/*
* Cannot diable regular bandgap
* in LDO-enable mode. The bandgap
* is required for ARM-LDO to regulate
* the voltage.
*/
ldr r6, [r10, #0x140]
and r6, r6, #0x1f
cmp r6, #0x1f
bne anatop_enter_done
/* Enable low power bandgap */
ldr r6, [r10, #0x260]
orr r6, r6, #0x20
str r6, [r10, #0x260]
/*
* Turn off the bias current
* from the regular bandgap.
*/
ldr r6, [r10, #0x260]
orr r6, r6, #0x80
str r6, [r10, #0x260]
/*
* Clear the REFTTOP+SELFBIASOFF,
* self_bais circuit of the band gap.
* Per RM, should be cleared when
* band gap is powered down.
*/
ldr r6, [r10, #0x150]
bic r6, r6, #0x8
str r6, [r10, #0x150]
/* Power down the regular bandgap */
ldr r6, [r10, #0x150]
orr r6, r6, #0x1
str r6, [r10, #0x150]
anatop_enter_done:
.endm
.macro anatop_exit_idle
ldr r10, [r0, #PM_INFO_ANATOP_V_OFFSET]
cmp r4, #0x0
beq skip_anatop_restore
cmp r2, #0x1
beq ldo2p5_not_disabled
/*
* Regular bandgap will not be disabled
* in LDO-enabled mode as it is required
* for ARM-LDO to reguulate the voltage.
*/
ldr r6, [r10, #0x140]
and r6, r6, #0x1f
cmp r6, #0x1f
bne skip_bandgap_restore
/* Power up the regular bandgap */
ldr r6, [r10, #0x150]
bic r6, r6, #0x1
str r6, [r10, #0x150]
/* wait for bandgap stable */
3:
ldr r6, [r10, #0x150]
and r6, r6, #0x80
cmp r6, #0x80
bne 3b
/* now disable bandgap self-bias circuit */
ldr r6, [r10, #0x150]
orr r6, r6, #0x8
str r6, [r10, #0x150]
/* Turn on the bias current
* from the regular bandgap.
*/
ldr r6, [r10, #0x260]
bic r6, r6, #0x80
str r6, [r10, #0x260]
/* Disable the low power bandgap */
ldr r6, [r10, #0x260]
bic r6, r6, #0x20
str r6, [r10, #0x260]
skip_bandgap_restore:
/* Enable main 2p5. */
ldr r6, [r10, #0x130]
orr r6, r6, #0x1
str r6, [r10, #0x130]
/* Ensure the 2p5 is up */
5:
ldr r6, [r10, #0x130]
and r6, r6, #0x20000
cmp r6, #0x20000
bne 5b
/* Disable the weak 2p5 */
ldr r6, [r10, #0x130]
bic r6, r6, #0x40000
str r6, [r10, #0x130]
ldo2p5_not_disabled:
/*
* Set the OSC bias current to max
* value for normal operation.
*/
ldr r6, [r10, #0x150]
bic r6, r6, #0xc000
str r6, [r10, #0x150]
/* Enable 1p1 brown out, */
ldr r6, [r10, #0x110]
orr r6, r6, #0x2
str r6, [r10, #0x110]
skip_anatop_restore:
.endm
.macro disable_l1_dcache
/* disable d-cache */
mrc p15, 0, r7, c1, c0, 0
bic r7, r7, #(1 << 2)
mcr p15, 0, r7, c1, c0, 0
dsb
isb
.endm
.macro mmdc_enter_dvfs_mode
/* disable automatic power saving. */
ldr r7, [r10, #MX6Q_MMDC_MAPSR]
orr r7, r7, #0x1
str r7, [r10, #MX6Q_MMDC_MAPSR]
/* disable power down timer */
ldr r7, [r10, #0x04]
bic r7, r7, #0xff00
str r7, [r10, #0x04]
/* Make the DDR explicitly enter self-refresh. */
ldr r7, [r10, #MX6Q_MMDC_MAPSR]
orr r7, r7, #(1 << 21)
str r7, [r10, #MX6Q_MMDC_MAPSR]
poll_dvfs_set:
ldr r7, [r10, #MX6Q_MMDC_MAPSR]
ands r7, r7, #(1 << 25)
beq poll_dvfs_set
/* set SBS step-by step mode */
ldr r7, [r10, #0x410]
orr r7, r7, #0x100
str r7, [r10, #0x410]
.endm
.macro resume_mmdc
/* restore MMDC IO */
ldr r10, [r0, #PM_INFO_IOMUXC_V_OFFSET]
ldr r6, [r0, #PM_INFO_IO_NUM_OFFSET]
ldr r7, =PM_INFO_IO_VAL_OFFSET
add r7, r7, r0
6:
ldr r8, [r7], #0x4
ldr r9, [r7], #0x4
str r9, [r10, r8]
subs r6, r6, #0x1
bne 6b
/*
* Need to reset the FIFO to avoid MMDC lockup
* caused because of floating/changing the
* configuration of many DDR IO pads.
*/
ldr r10, [r0, #PM_INFO_MMDC_V_OFFSET]
/* reset read FIFO, RST_RD_FIFO */
ldr r7, =MX6Q_MMDC_MPDGCTRL0
ldr r6, [r10, r7]
orr r6, r6, #(1 << 31)
str r6, [r10, r7]
7:
ldr r6, [r10, r7]
ands r6, r6, #(1 << 31)
bne 7b
/* reset FIFO a second time */
ldr r7, =MX6Q_MMDC_MPDGCTRL0
ldr r6, [r10, r7]
orr r6, r6, #(1 << 31)
str r6, [r10, r7]
8:
ldr r6, [r10, r7]
ands r6, r6, #(1 <<31)
bne 8b
ldr r10, [r0, #PM_INFO_MMDC_V_OFFSET]
/* Let DDR out of self-refresh */
ldr r7, [r10, #MX6Q_MMDC_MAPSR]
bic r7, r7, #(1 << 21)
str r7, [r10, #MX6Q_MMDC_MAPSR]
9:
ldr r7, [r10, #MX6Q_MMDC_MAPSR]
ands r7, r7, #(1 << 25)
bne 9b
/* enable power down timer */
ldr r7, [r10, #0x04]
orr r7, r7, #0x5500
str r7, [r10, #0x04]
/* enable DDR auto power saving */
ldr r7, [r10, #MX6Q_MMDC_MAPSR]
bic r7, r7, #0x1
str r7, [r10, #MX6Q_MMDC_MAPSR]
/* Clear SBS - unblock DDR accesses */
ldr r7, [r10, #0x410]
bic r7, r7, #0x100
str r7, [r10, #0x410]
.endm
.macro tlb_set_to_ocram
/* save ttbr */
mrc p15, 0, r7, c2, c0, 1
str r7, [r0, #PM_INFO_TTBR_OFFSET]
/*
* 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 the IRAM page table.
* 3. Disable page table walks in TTBR0 (PD0 = 1)
* 4. Set TTBR0.N=1, implying 0-2G is transslated 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
.endm
.macro tlb_back_to_ddr
/* 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
/* 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
/* Restore ttbr */
ldr r7, [r0, #PM_INFO_TTBR_OFFSET]
mcr p15, 0, r7, c2, c0, 1
.endm
.extern iram_tlb_phys_addr
/*
* imx6sl_low_power_wfi code
* r0: wfi code base address
* r1: audio_bus_freq mode stat
* r2: vbus_ldo status
* r4: used for store the PLLs state
* r11: used for saving the ARM_PODF origin value
* r12: used for saving AHB_PODF origin value
*/
.align 3
ENTRY(imx6sl_low_power_idle)
mx6sl_lpm_wfi_start:
push {r4-r12}
tlb_set_to_ocram
disable_l1_dcache
#ifdef CONFIG_CACHE_L2X0
/* sync L2 */
ldr r10, [r0, #PM_INFO_L2_V_OFFSET]
/* Wait for background operations to complete. */
wait_for_l2_idle:
ldr r6, [r10, #0x730]
cmp r6, #0x0
bne wait_for_l2_idle
mov r6, #0x0
str r6, [r10, #0x730]
/* disable L2 */
str r6, [r10, #0x100]
dsb
isb
#endif
/* make sure MMDC in self-refresh */
ldr r10, [r0, #PM_INFO_MMDC_V_OFFSET]
mmdc_enter_dvfs_mode
/* save DDR IO settings and set to LPM mode*/
ldr r10, [r0, #PM_INFO_IOMUXC_V_OFFSET]
ldr r6, =0x0
ldr r7, [r0, #PM_INFO_IO_NUM_OFFSET]
ldr r8, =PM_INFO_IO_VAL_OFFSET
add r8, r8, r0
/* imx6sl's last 3 IOs need special setting */
sub r7, r7, #0x3
save_and_set_mmdc_io_lpm:
ldr r9, [r8], #0x4
ldr r5, [r10, r9]
str r6, [r10, r9]
str r5, [r8], #0x4
subs r7, r7, #0x1
bne save_and_set_mmdc_io_lpm
ldr r6, =0x1000
ldr r9, [r8], #0x4
ldr r5, [r10, r9]
str r5, [r8], #0x4
str r6, [r10, r9]
ldr r9, [r8], #0x4
ldr r5, [r10, r9]
str r6, [r10, r9]
str r5, [r8], #0x4
ldr r6, =0x80000
ldr r9, [r8], #0x4
ldr r5, [r10, r9]
str r6, [r10, r9]
str r5, [r8], #0x4
/* check the PLLs lock state */
check_pll_state
ccm_enter_idle
/* if in audio low power mode, no
* need to do anatop setting.
*/
cmp r1, #0x1
beq do_wfi
anatop_enter_idle
do_wfi:
wfi
/*
* Add these nops so that the
* prefetcher will not try to get
* any instrutions 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
/*
* restore the ARM PODF first to speed
* up the restore procedure
*/
ldr r10, [r0, #PM_INFO_CCM_V_OFFSET]
/* Restore arm_clk_podf */
str r11, [r10, #0x10]
ccm_do_wait
/*
* if in audio low power mode, skip
* restore the anatop setting.
*/
cmp r1, #0x1
beq skip_analog_restore
anatop_exit_idle
skip_analog_restore:
ccm_exit_idle
resume_mmdc
/* enable d-cache */
mrc p15, 0, r7, c1, c0, 0
orr r7, r7, #(1 << 2)
mcr p15, 0, r7, c1, c0, 0
#ifdef CONFIG_CACHE_L2X0
ldr r10, [r0, #PM_INFO_L2_V_OFFSET]
mov r7, #0x1
/* enable L2 */
str r7, [r10, #0x100]
#endif
tlb_back_to_ddr
/* Restore register */
pop {r4 - r12}
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: