summaryrefslogtreecommitdiff
path: root/linux/arch/arm/mach-omap2/sleep43xx.S
diff options
context:
space:
mode:
authorMichael J. Chudobiak <mjc@avtechpulse.com>2016-04-25 10:00:44 -0400
committerMichael J. Chudobiak <mjc@avtechpulse.com>2016-04-25 10:00:44 -0400
commita1df417e74aa6dae7352dc8cbb0ad471af5b7c69 (patch)
treec34b2311e37ea31db153c90cb8f4570374d05e78 /linux/arch/arm/mach-omap2/sleep43xx.S
initial Olimex linux tree from Daniel, originally Feb 3, 2016
Diffstat (limited to 'linux/arch/arm/mach-omap2/sleep43xx.S')
-rw-r--r--linux/arch/arm/mach-omap2/sleep43xx.S493
1 files changed, 493 insertions, 0 deletions
diff --git a/linux/arch/arm/mach-omap2/sleep43xx.S b/linux/arch/arm/mach-omap2/sleep43xx.S
new file mode 100644
index 00000000..19921344
--- /dev/null
+++ b/linux/arch/arm/mach-omap2/sleep43xx.S
@@ -0,0 +1,493 @@
+/*
+ * Low level suspend code for AM43XX SoCs
+ *
+ * Copyright (C) 2013 Texas Instruments Incorporated - http://www.ti.com/
+ * Dave Gerlach <d-gerlach@ti.com>
+ * Vaibhav Bedia <vaibhav.bedia@ti.com>
+ *
+ * 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 version 2.
+ *
+ * This program is distributed "as is" WITHOUT ANY WARRANTY of any
+ * kind, whether express or implied; 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/memory.h>
+#include <asm/assembler.h>
+
+#include "iomap.h"
+#include "cm33xx.h"
+#include "prm33xx.h"
+#include "prcm43xx.h"
+
+#include "common.h"
+#include "omap-secure.h"
+#include "omap44xx.h"
+#include <asm/hardware/cache-l2x0.h>
+
+#define EMIF_POWER_MGMT_SR_TIMER_MASK 0x00f0
+
+#define EMIF_POWER_MGMT_SELF_REFRESH_MODE 0x0200
+#define EMIF_POWER_MGMT_SELF_REFRESH_MODE_MASK 0x0700
+#define EMIF_POWER_MGMT_DELAY_PERIOD 0x1000
+
+#define EMIF_LPDDR2_AM43XX_REFRESH_EN_CS0 0x40000002
+#define EMIF_LPDDR2_AM43XX_REFRESH_EN_CS1 0xC0000002
+#define AM43XX_EMIF_PHY_CTRL_REG_COUNT 0x120
+
+#define AM33XX_CM_CLKCTRL_MODULEMODE_DISABLE 0x0003
+#define AM33XX_CM_CLKCTRL_MODULEMODE_ENABLE 0x0002
+
+#define AM43XX_EMIF_POWEROFF_ENABLE 0x1
+#define AM43XX_EMIF_POWEROFF_DISABLE 0x0
+
+#define AM43XX_CM_CLKSTCTRL_CLKTRCTRL_SW_SLEEP 0x1
+#define AM43XX_CM_CLKSTCTRL_CLKTRCTRL_HW_AUTO 0x3
+
+#define AM43XX_CM_BASE 0x44DF0000
+
+#define AM43XX_CTRL_CKE_OVERRIDE 0x44E1131C
+
+#define AM43XX_CM_REGADDR(inst, reg) \
+ AM33XX_L4_WK_IO_ADDRESS(AM43XX_CM_BASE + (inst) + (reg))
+
+#define AM43XX_PM_MPU_PWRSTCTRL AM43XX_CM_REGADDR(0x0300, 0x00)
+#define AM43XX_CM_MPU_CLKSTCTRL AM43XX_CM_REGADDR(0x8300, 0x00)
+#define AM43XX_CM_MPU_MPU_CLKCTRL AM43XX_CM_REGADDR(0x8300, 0x20)
+#define AM43XX_CM_PER_EMIF_CLKCTRL AM43XX_CM_REGADDR(0x8800, 0x0720)
+
+#define AM43XX_CM_PER_EMIF_CLKCTRL_OFFSET 0x0720
+#define AM43XX_PRM_EMIF_CTRL_OFFSET 0x30
+
+#define RTC_SECONDS_REG 0x0
+#define RTC_PMIC_REG 0x98
+#define RTC_PMIC_POWER_EN (1 << 16)
+#define RTC_PMIC_EXT_WAKEUP_STS (1 << 12)
+#define RTC_PMIC_EXT_WAKEUP_POL (1 << 4)
+#define RTC_PMIC_EXT_WAKEUP_EN (1 << 0)
+
+#define WFI_FLAG_RTC_ONLY (1 << 8)
+
+ .text
+ .align 3
+
+ENTRY(am43xx_do_wfi)
+ stmfd sp!, {r4 - r11, lr} @ save registers on stack
+
+ARM( str r0, wfi_flags )
+THUMB( adr r1, wfi_flags )
+THUMB( str r0, [r1] )
+
+ /* Retrieve l2 cache virt address BEFORE we shut off EMIF */
+ ldr r1, get_l2cache_base
+ blx r1
+ARM( str r0, l2_base_virt )
+THUMB( adr r1, l2_base_virt )
+THUMB( str r0, [r1] )
+
+ /*
+ * Flush all data from the L1 and L2 data cache before disabling
+ * SCTLR.C bit.
+ */
+ ldr r1, kernel_flush
+ blx r1
+
+ /*
+ * Clear the SCTLR.C bit to prevent further data cache
+ * allocation. Clearing SCTLR.C would make all the data accesses
+ * strongly ordered and would not hit the cache.
+ */
+ mrc p15, 0, r0, c1, c0, 0
+ bic r0, r0, #(1 << 2) @ Disable the C bit
+ mcr p15, 0, r0, c1, c0, 0
+ isb
+ dsb
+
+ /*
+ * Invalidate L1 and L2 data cache.
+ */
+ ldr r1, kernel_flush
+ blx r1
+
+ /*
+ * The kernel doesn't interwork: v7_flush_dcache_all in particluar will
+ * always return in Thumb state when CONFIG_THUMB2_KERNEL is enabled.
+ * This sequence switches back to ARM. Note that .align may insert a
+ * nop: bx pc needs to be word-aligned in order to work.
+ */
+ THUMB( .thumb )
+ THUMB( .align )
+ THUMB( bx pc )
+ THUMB( nop )
+ .arm
+
+#ifdef CONFIG_CACHE_L2X0
+ /*
+ * Clean and invalidate the L2 cache.
+ */
+#ifdef CONFIG_PL310_ERRATA_727915
+ mov r0, #0x03
+ mov r12, #OMAP4_MON_L2X0_DBG_CTRL_INDEX
+ dsb
+ smc #0
+ dsb
+#endif
+ ldr r0, l2_base_virt
+
+ mov r2, r0
+ ldr r0, [r2, #L2X0_AUX_CTRL]
+ str r0, l2_aux_ctrl_val
+ ldr r0, [r2, #L310_PREFETCH_CTRL]
+ str r0, l2_prefetch_ctrl_val
+
+ ldr r0, l2_val
+ str r0, [r2, #L2X0_CLEAN_INV_WAY]
+wait:
+ ldr r0, [r2, #L2X0_CLEAN_INV_WAY]
+ ldr r1, l2_val
+ ands r0, r0, r1
+ bne wait
+#ifdef CONFIG_PL310_ERRATA_727915
+ mov r0, #0x00
+ mov r12, #OMAP4_MON_L2X0_DBG_CTRL_INDEX
+ dsb
+ smc #0
+ dsb
+#endif
+l2x_sync:
+ ldr r0, l2_base_virt
+ mov r2, r0
+ mov r0, #0x0
+ str r0, [r2, #L2X0_CACHE_SYNC]
+sync:
+ ldr r0, [r2, #L2X0_CACHE_SYNC]
+ ands r0, r0, #0x1
+ bne sync
+#endif
+
+ mov r0, #0x1
+ ldr r1, ti_emif_save_context
+ blx r1
+
+ ldr r1, ti_emif_enter_sr
+ blx r1
+
+ mov r1, #EMIF_POWER_MGMT_DELAY_PERIOD
+wait_self_refresh:
+ subs r1, r1, #1
+ bne wait_self_refresh
+
+ /* Disable EMIF */
+ ldr r1, am43xx_virt_emif_clkctrl
+ ldr r2, [r1]
+ bic r2, r2, #AM33XX_CM_CLKCTRL_MODULEMODE_DISABLE
+ str r2, [r1]
+
+wait_emif_disable:
+ ldr r2, [r1]
+ ldr r3, module_disabled_val
+ cmp r2, r3
+ bne wait_emif_disable
+
+ ldr r1, wfi_flags
+
+ tst r1, #WFI_FLAG_RTC_ONLY
+ beq am43xx_deep_sleep_suspend
+
+ ldr r1, rtc_base_virt
+
+ ldr r0, [r1, #RTC_PMIC_REG]
+ orr r0, r0, #RTC_PMIC_POWER_EN
+ orr r0, r0, #RTC_PMIC_EXT_WAKEUP_STS
+ orr r0, r0, #RTC_PMIC_EXT_WAKEUP_EN
+ orr r0, r0, #RTC_PMIC_EXT_WAKEUP_POL
+ str r0, [r1, #RTC_PMIC_REG]
+ ldr r0, [r1, #RTC_PMIC_REG]
+ /* Wait for 2 seconds to lose power */
+ mov r3, #2
+ ldr r2, [r1, #RTC_SECONDS_REG]
+rtc_loop:
+ ldr r0, [r1, #RTC_SECONDS_REG]
+ cmp r0, r2
+ beq rtc_loop
+ mov r2, r0
+ subs r3, r3, #1
+ bne rtc_loop
+
+ b re_enable_emif
+
+am43xx_deep_sleep_suspend:
+ /*
+ * For the MPU WFI to be registered as an interrupt
+ * to WKUP_M3, MPU_CLKCTRL.MODULEMODE needs to be set
+ * to DISABLED
+ */
+ ldr r1, am43xx_virt_mpu_clkctrl
+ ldr r2, [r1]
+ bic r2, r2, #AM33XX_CM_CLKCTRL_MODULEMODE_DISABLE
+ str r2, [r1]
+
+ /*
+ * Put MPU CLKDM to SW_SLEEP
+ */
+ ldr r1, am43xx_virt_mpu_clkstctrl
+ mov r2, #AM43XX_CM_CLKSTCTRL_CLKTRCTRL_SW_SLEEP
+ str r2, [r1]
+
+ /*
+ * Execute a barrier instruction to ensure that all cache,
+ * TLB and branch predictor maintenance operations issued
+ * have completed.
+ */
+ dsb
+ dmb
+
+ /*
+ * Execute a WFI instruction and wait until the
+ * STANDBYWFI output is asserted to indicate that the
+ * CPU is in idle and low power state. CPU can specualatively
+ * prefetch the instructions so add NOPs after WFI. Sixteen
+ * NOPs as per Cortex-A9 pipeline.
+ */
+ wfi
+
+ nop
+ nop
+ nop
+ nop
+ nop
+ nop
+ nop
+ nop
+ nop
+ nop
+ nop
+ nop
+ nop
+ nop
+ nop
+ nop
+
+ /* We come here in case of an abort due to a late interrupt */
+ ldr r1, am43xx_virt_mpu_clkstctrl
+ mov r2, #AM43XX_CM_CLKSTCTRL_CLKTRCTRL_HW_AUTO
+ str r2, [r1]
+
+ /* Set MPU_CLKCTRL.MODULEMODE back to ENABLE */
+ ldr r1, am43xx_virt_mpu_clkctrl
+ mov r2, #AM33XX_CM_CLKCTRL_MODULEMODE_ENABLE
+ str r2, [r1]
+
+re_enable_emif:
+ /* Re-enable EMIF */
+ ldr r1, am43xx_virt_emif_clkctrl
+ mov r2, #AM33XX_CM_CLKCTRL_MODULEMODE_ENABLE
+ str r2, [r1]
+wait_emif_enable:
+ ldr r3, [r1]
+ cmp r2, r3
+ bne wait_emif_enable
+
+ /*
+ * Set SCTLR.C bit to allow data cache allocation
+ */
+ mrc p15, 0, r0, c1, c0, 0
+ orr r0, r0, #(1 << 2) @ Enable the C bit
+ mcr p15, 0, r0, c1, c0, 0
+ isb
+
+ ldr r1, ti_emif_abort_sr
+ blx r1
+
+ /* EMIF needs some time before read/write possible */
+ mov r0, #EMIF_POWER_MGMT_DELAY_PERIOD
+wait_abt:
+ subs r0, r0, #1
+ bne wait_abt
+
+ /* Let the suspend code know about the abort */
+ mov r0, #1
+ ldmfd sp!, {r4 - r11, pc} @ restore regs and return
+ENDPROC(am43xx_do_wfi)
+
+ .align
+ENTRY(am43xx_resume_offset)
+ .word . - am43xx_do_wfi
+
+ENTRY(am43xx_resume_from_deep_sleep)
+ /* Set MPU CLKSTCTRL to HW AUTO so that CPUidle works properly */
+ ldr r1, am43xx_virt_mpu_clkstctrl
+ mov r2, #AM43XX_CM_CLKSTCTRL_CLKTRCTRL_HW_AUTO
+ str r2, [r1]
+
+ /* For AM43xx, use EMIF power down until context is restored */
+ ldr r2, am43xx_phys_emif_poweroff
+ mov r1, #AM43XX_EMIF_POWEROFF_ENABLE
+ str r1, [r2, #0x0]
+
+ /* Re-enable EMIF */
+ ldr r1, am43xx_phys_emif_clkctrl
+ mov r2, #AM33XX_CM_CLKCTRL_MODULEMODE_ENABLE
+ str r2, [r1]
+wait_emif_enable1:
+ ldr r3, [r1]
+ cmp r2, r3
+ bne wait_emif_enable1
+
+ adr sp, temp_stack
+
+ mov r0, #0x1
+ ldr r1, ti_emif_restore_context
+ blx r1
+
+ ldr r1, ti_emif_exit_sr
+ blx r1
+
+ ldr r2, am43xx_phys_emif_poweroff
+ mov r1, #AM43XX_EMIF_POWEROFF_DISABLE
+ str r1, [r2, #0x0]
+
+#ifdef CONFIG_CACHE_L2X0
+ ldr r2, l2_cache_base
+ ldr r0, [r2, #L2X0_CTRL]
+ and r0, #0x0f
+ cmp r0, #1
+ beq skip_l2en @ Skip if already enabled
+ ldr r0, l2_prefetch_ctrl_val
+
+ ldr r12, l2_smc1
+ dsb
+ smc #0
+ dsb
+set_aux_ctrl:
+ ldr r0, l2_aux_ctrl_val
+ ldr r12, l2_smc2
+ dsb
+ smc #0
+ dsb
+
+ /* L2 invalidate on resume */
+ ldr r0, l2_val
+ ldr r2, l2_cache_base
+ str r0, [r2, #L2X0_INV_WAY]
+wait2:
+ ldr r0, [r2, #L2X0_INV_WAY]
+ ldr r1, l2_val
+ ands r0, r0, r1
+ bne wait2
+#ifdef CONFIG_PL310_ERRATA_727915
+ mov r0, #0x00
+ mov r12, #OMAP4_MON_L2X0_DBG_CTRL_INDEX
+ dsb
+ smc #0
+ dsb
+#endif
+l2x_sync2:
+ ldr r2, l2_cache_base
+ mov r0, #0x0
+ str r0, [r2, #L2X0_CACHE_SYNC]
+sync2:
+ ldr r0, [r2, #L2X0_CACHE_SYNC]
+ ands r0, r0, #0x1
+ bne sync2
+
+ mov r0, #0x1
+ ldr r12, l2_smc3
+ dsb
+ smc #0
+ dsb
+#endif
+skip_l2en:
+ /* We are back. Branch to the common CPU resume routine */
+ mov r0, #0
+ ldr pc, resume_addr
+ENDPROC(am43xx_resume_from_deep_sleep)
+
+/*
+ * Local variables
+ */
+ .align
+get_l2cache_base:
+ .word omap4_get_l2cache_base
+kernel_flush:
+ .word v7_flush_dcache_all
+ddr_start:
+ .word PAGE_OFFSET
+
+wfi_flags:
+ .word 0
+
+cke_override_virt:
+ .word 0xDEADBEEF
+cke_override_phys:
+ .word AM43XX_CTRL_CKE_OVERRIDE
+
+am43xx_phys_emif_poweroff:
+ .word (AM43XX_CM_BASE + AM43XX_PRM_DEVICE_INST + \
+ AM43XX_PRM_EMIF_CTRL_OFFSET)
+am43xx_virt_mpu_pwrstctrl:
+ .word (AM43XX_PM_MPU_PWRSTCTRL)
+am43xx_virt_mpu_clkstctrl:
+ .word (AM43XX_CM_MPU_CLKSTCTRL)
+am43xx_virt_mpu_clkctrl:
+ .word (AM43XX_CM_MPU_MPU_CLKCTRL)
+am43xx_virt_emif_clkctrl:
+ .word (AM43XX_CM_PER_EMIF_CLKCTRL)
+am43xx_phys_emif_clkctrl:
+ .word (AM43XX_CM_BASE + AM43XX_CM_PER_INST + \
+ AM43XX_CM_PER_EMIF_CLKCTRL_OFFSET)
+module_disabled_val:
+ .word 0x30000
+
+/* L2 cache related defines for AM437x */
+l2_base_virt:
+ .word 0xDEADBEEF
+l2_aux_ctrl_val:
+ .word 0xDEADBEEF
+l2_prefetch_ctrl_val:
+ .word 0xDEADBEEF
+l2_cache_base:
+ .word OMAP44XX_L2CACHE_BASE
+l2_val:
+ .word 0xffff
+l2_smc1:
+ .word OMAP4_MON_L2X0_PREFETCH_INDEX
+l2_smc2:
+ .word OMAP4_MON_L2X0_AUXCTRL_INDEX
+l2_smc3:
+ .word OMAP4_MON_L2X0_CTRL_INDEX
+
+
+/* DDR related defines */
+ENTRY(am43xx_emif_sram_table)
+ti_emif_save_context:
+ .word 0x00000000
+ti_emif_restore_context:
+ .word 0x00000000
+ti_emif_enter_sr:
+ .word 0x00000000
+ti_emif_exit_sr:
+ .word 0x00000000
+ti_emif_abort_sr:
+ .word 0x00000000
+ .align 3
+ .space 64
+temp_stack:
+ .align 3
+
+ENTRY(am43xx_pm_sram)
+.word am43xx_do_wfi
+.word am43xx_do_wfi_sz
+.word am43xx_resume_offset
+.word am43xx_emif_sram_table
+rtc_base_virt:
+.word 0xdeadbeef
+resume_addr:
+.word cpu_resume - PAGE_OFFSET + 0x80000000
+
+ENTRY(am43xx_do_wfi_sz)
+ .word . - am43xx_do_wfi