123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262 |
- /* SPDX-License-Identifier: GPL-2.0 */
- /*
- * Low level suspend code for AM33XX SoCs
- *
- * Copyright (C) 2012-2018 Texas Instruments Incorporated - https://www.ti.com/
- * Dave Gerlach, Vaibhav Bedia
- */
- #include <linux/linkage.h>
- #include <linux/platform_data/pm33xx.h>
- #include <linux/ti-emif-sram.h>
- #include <asm/assembler.h>
- #include <asm/memory.h>
- #include "iomap.h"
- #include "cm33xx.h"
- #include "pm-asm-offsets.h"
- #define AM33XX_CM_CLKCTRL_MODULESTATE_DISABLED 0x00030000
- #define AM33XX_CM_CLKCTRL_MODULEMODE_DISABLE 0x0003
- #define AM33XX_CM_CLKCTRL_MODULEMODE_ENABLE 0x0002
- /* replicated define because linux/bitops.h cannot be included in assembly */
- #define BIT(nr) (1 << (nr))
- .arm
- .arch armv7-a
- .align 3
- ENTRY(am33xx_do_wfi)
- stmfd sp!, {r4 - r11, lr} @ save registers on stack
- /* Save wfi_flags arg to data space */
- mov r4, r0
- adr r3, am33xx_pm_ro_sram_data
- ldr r2, [r3, #AMX3_PM_RO_SRAM_DATA_VIRT_OFFSET]
- str r4, [r2, #AMX3_PM_WFI_FLAGS_OFFSET]
- /* Only flush cache is we know we are losing MPU context */
- tst r4, #WFI_FLAG_FLUSH_CACHE
- beq cache_skip_flush
- /*
- * 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
- /*
- * Invalidate L1 and L2 data cache.
- */
- ldr r1, kernel_flush
- blx r1
- adr r3, am33xx_pm_ro_sram_data
- ldr r2, [r3, #AMX3_PM_RO_SRAM_DATA_VIRT_OFFSET]
- ldr r4, [r2, #AMX3_PM_WFI_FLAGS_OFFSET]
- cache_skip_flush:
- /* Check if we want self refresh */
- tst r4, #WFI_FLAG_SELF_REFRESH
- beq emif_skip_enter_sr
- adr r9, am33xx_emif_sram_table
- ldr r3, [r9, #EMIF_PM_ENTER_SR_OFFSET]
- blx r3
- emif_skip_enter_sr:
- /* Only necessary if PER is losing context */
- tst r4, #WFI_FLAG_SAVE_EMIF
- beq emif_skip_save
- ldr r3, [r9, #EMIF_PM_SAVE_CONTEXT_OFFSET]
- blx r3
- emif_skip_save:
- /* Only can disable EMIF if we have entered self refresh */
- tst r4, #WFI_FLAG_SELF_REFRESH
- beq emif_skip_disable
- /* Disable EMIF */
- ldr r1, virt_emif_clkctrl
- ldr r2, [r1]
- bic r2, r2, #AM33XX_CM_CLKCTRL_MODULEMODE_DISABLE
- str r2, [r1]
- ldr r1, virt_emif_clkctrl
- wait_emif_disable:
- ldr r2, [r1]
- mov r3, #AM33XX_CM_CLKCTRL_MODULESTATE_DISABLED
- cmp r2, r3
- bne wait_emif_disable
- emif_skip_disable:
- tst r4, #WFI_FLAG_WAKE_M3
- beq wkup_m3_skip
- /*
- * For the MPU WFI to be registered as an interrupt
- * to WKUP_M3, MPU_CLKCTRL.MODULEMODE needs to be set
- * to DISABLED
- */
- ldr r1, virt_mpu_clkctrl
- ldr r2, [r1]
- bic r2, r2, #AM33XX_CM_CLKCTRL_MODULEMODE_DISABLE
- str r2, [r1]
- wkup_m3_skip:
- /*
- * Execute an ISB instruction to ensure that all of the
- * CP15 register changes have been committed.
- */
- isb
- /*
- * 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. Thirteen
- * NOPs as per Cortex-A8 pipeline.
- */
- wfi
- 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 */
- /* Set MPU_CLKCTRL.MODULEMODE back to ENABLE */
- ldr r1, virt_mpu_clkctrl
- mov r2, #AM33XX_CM_CLKCTRL_MODULEMODE_ENABLE
- str r2, [r1]
- /* Re-enable EMIF */
- ldr r1, 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
- /* Only necessary if PER is losing context */
- tst r4, #WFI_FLAG_SELF_REFRESH
- beq emif_skip_exit_sr_abt
- adr r9, am33xx_emif_sram_table
- ldr r1, [r9, #EMIF_PM_ABORT_SR_OFFSET]
- blx r1
- emif_skip_exit_sr_abt:
- tst r4, #WFI_FLAG_FLUSH_CACHE
- beq cache_skip_restore
- /*
- * 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
- cache_skip_restore:
- /* Let the suspend code know about the abort */
- mov r0, #1
- ldmfd sp!, {r4 - r11, pc} @ restore regs and return
- ENDPROC(am33xx_do_wfi)
- .align
- ENTRY(am33xx_resume_offset)
- .word . - am33xx_do_wfi
- ENTRY(am33xx_resume_from_deep_sleep)
- /* Re-enable EMIF */
- ldr r0, phys_emif_clkctrl
- mov r1, #AM33XX_CM_CLKCTRL_MODULEMODE_ENABLE
- str r1, [r0]
- wait_emif_enable1:
- ldr r2, [r0]
- cmp r1, r2
- bne wait_emif_enable1
- adr r9, am33xx_emif_sram_table
- ldr r1, [r9, #EMIF_PM_RESTORE_CONTEXT_OFFSET]
- blx r1
- ldr r1, [r9, #EMIF_PM_EXIT_SR_OFFSET]
- blx r1
- resume_to_ddr:
- /* We are back. Branch to the common CPU resume routine */
- mov r0, #0
- ldr pc, resume_addr
- ENDPROC(am33xx_resume_from_deep_sleep)
- /*
- * Local variables
- */
- .align
- kernel_flush:
- .word v7_flush_dcache_all
- virt_mpu_clkctrl:
- .word AM33XX_CM_MPU_MPU_CLKCTRL
- virt_emif_clkctrl:
- .word AM33XX_CM_PER_EMIF_CLKCTRL
- phys_emif_clkctrl:
- .word (AM33XX_CM_BASE + AM33XX_CM_PER_MOD + \
- AM33XX_CM_PER_EMIF_CLKCTRL_OFFSET)
- .align 3
- /* DDR related defines */
- am33xx_emif_sram_table:
- .space EMIF_PM_FUNCTIONS_SIZE
- ENTRY(am33xx_pm_sram)
- .word am33xx_do_wfi
- .word am33xx_do_wfi_sz
- .word am33xx_resume_offset
- .word am33xx_emif_sram_table
- .word am33xx_pm_ro_sram_data
- resume_addr:
- .word cpu_resume - PAGE_OFFSET + 0x80000000
- .align 3
- ENTRY(am33xx_pm_ro_sram_data)
- .space AMX3_PM_RO_SRAM_DATA_SIZE
- ENTRY(am33xx_do_wfi_sz)
- .word . - am33xx_do_wfi
|