linux/arch/arm/mach-at91/pm_suspend.S

481 lines
9.6 KiB
ArmAsm

/*
* arch/arm/mach-at91/pm_slow_clock.S
*
* Copyright (C) 2006 Savin Zlobec
*
* AT91SAM9 support:
* Copyright (C) 2007 Anti Sullin <anti.sullin@artecdesign.ee>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
*/
#include <linux/linkage.h>
#include <linux/clk/at91_pmc.h>
#include "pm.h"
#include "generated/at91_pm_data-offsets.h"
#define SRAMC_SELF_FRESH_ACTIVE 0x01
#define SRAMC_SELF_FRESH_EXIT 0x00
pmc .req r0
tmp1 .req r4
tmp2 .req r5
/*
* Wait until master clock is ready (after switching master clock source)
*/
.macro wait_mckrdy
1: ldr tmp1, [pmc, #AT91_PMC_SR]
tst tmp1, #AT91_PMC_MCKRDY
beq 1b
.endm
/*
* Wait until master oscillator has stabilized.
*/
.macro wait_moscrdy
1: ldr tmp1, [pmc, #AT91_PMC_SR]
tst tmp1, #AT91_PMC_MOSCS
beq 1b
.endm
/*
* Wait for main oscillator selection is done
*/
.macro wait_moscsels
1: ldr tmp1, [pmc, #AT91_PMC_SR]
tst tmp1, #AT91_PMC_MOSCSELS
beq 1b
.endm
/*
* Wait until PLLA has locked.
*/
.macro wait_pllalock
1: ldr tmp1, [pmc, #AT91_PMC_SR]
tst tmp1, #AT91_PMC_LOCKA
beq 1b
.endm
/*
* Put the processor to enter the idle state
*/
.macro at91_cpu_idle
#if defined(CONFIG_CPU_V7)
mov tmp1, #AT91_PMC_PCK
str tmp1, [pmc, #AT91_PMC_SCDR]
dsb
wfi @ Wait For Interrupt
#else
mcr p15, 0, tmp1, c7, c0, 4
#endif
.endm
.text
.arm
/*
* void at91_suspend_sram_fn(struct at91_pm_data*)
* @input param:
* @r0: base address of struct at91_pm_data
*/
/* at91_pm_suspend_in_sram must be 8-byte aligned per the requirements of fncpy() */
.align 3
ENTRY(at91_pm_suspend_in_sram)
/* Save registers on stack */
stmfd sp!, {r4 - r12, lr}
/* Drain write buffer */
mov tmp1, #0
mcr p15, 0, tmp1, c7, c10, 4
ldr tmp1, [r0, #PM_DATA_PMC]
str tmp1, .pmc_base
ldr tmp1, [r0, #PM_DATA_RAMC0]
str tmp1, .sramc_base
ldr tmp1, [r0, #PM_DATA_RAMC1]
str tmp1, .sramc1_base
ldr tmp1, [r0, #PM_DATA_MEMCTRL]
str tmp1, .memtype
ldr tmp1, [r0, #PM_DATA_MODE]
str tmp1, .pm_mode
/* Both ldrne below are here to preload their address in the TLB */
ldr tmp1, [r0, #PM_DATA_SHDWC]
str tmp1, .shdwc
cmp tmp1, #0
ldrne tmp2, [tmp1, #0]
ldr tmp1, [r0, #PM_DATA_SFRBU]
str tmp1, .sfr
cmp tmp1, #0
ldrne tmp2, [tmp1, #0x10]
/* Active the self-refresh mode */
mov r0, #SRAMC_SELF_FRESH_ACTIVE
bl at91_sramc_self_refresh
ldr r0, .pm_mode
cmp r0, #AT91_PM_STANDBY
beq standby
cmp r0, #AT91_PM_BACKUP
beq backup_mode
bl at91_ulp_mode
b exit_suspend
standby:
/* Wait for interrupt */
ldr pmc, .pmc_base
at91_cpu_idle
b exit_suspend
backup_mode:
bl at91_backup_mode
b exit_suspend
exit_suspend:
/* Exit the self-refresh mode */
mov r0, #SRAMC_SELF_FRESH_EXIT
bl at91_sramc_self_refresh
/* Restore registers, and return */
ldmfd sp!, {r4 - r12, pc}
ENDPROC(at91_pm_suspend_in_sram)
ENTRY(at91_backup_mode)
/* Switch the master clock source to slow clock. */
ldr pmc, .pmc_base
ldr tmp1, [pmc, #AT91_PMC_MCKR]
bic tmp1, tmp1, #AT91_PMC_CSS
str tmp1, [pmc, #AT91_PMC_MCKR]
wait_mckrdy
/*BUMEN*/
ldr r0, .sfr
mov tmp1, #0x1
str tmp1, [r0, #0x10]
/* Shutdown */
ldr r0, .shdwc
mov tmp1, #0xA5000000
add tmp1, tmp1, #0x1
str tmp1, [r0, #0]
ENDPROC(at91_backup_mode)
.macro at91_pm_ulp0_mode
ldr pmc, .pmc_base
/* Turn off the crystal oscillator */
ldr tmp1, [pmc, #AT91_CKGR_MOR]
bic tmp1, tmp1, #AT91_PMC_MOSCEN
orr tmp1, tmp1, #AT91_PMC_KEY
str tmp1, [pmc, #AT91_CKGR_MOR]
/* Wait for interrupt */
at91_cpu_idle
/* Turn on the crystal oscillator */
ldr tmp1, [pmc, #AT91_CKGR_MOR]
orr tmp1, tmp1, #AT91_PMC_MOSCEN
orr tmp1, tmp1, #AT91_PMC_KEY
str tmp1, [pmc, #AT91_CKGR_MOR]
wait_moscrdy
.endm
/**
* Note: This procedure only applies on the platform which uses
* the external crystal oscillator as a main clock source.
*/
.macro at91_pm_ulp1_mode
ldr pmc, .pmc_base
/* Switch the main clock source to 12-MHz RC oscillator */
ldr tmp1, [pmc, #AT91_CKGR_MOR]
bic tmp1, tmp1, #AT91_PMC_MOSCSEL
bic tmp1, tmp1, #AT91_PMC_KEY_MASK
orr tmp1, tmp1, #AT91_PMC_KEY
str tmp1, [pmc, #AT91_CKGR_MOR]
wait_moscsels
/* Disable the crystal oscillator */
ldr tmp1, [pmc, #AT91_CKGR_MOR]
bic tmp1, tmp1, #AT91_PMC_MOSCEN
bic tmp1, tmp1, #AT91_PMC_KEY_MASK
orr tmp1, tmp1, #AT91_PMC_KEY
str tmp1, [pmc, #AT91_CKGR_MOR]
/* Switch the master clock source to main clock */
ldr tmp1, [pmc, #AT91_PMC_MCKR]
bic tmp1, tmp1, #AT91_PMC_CSS
orr tmp1, tmp1, #AT91_PMC_CSS_MAIN
str tmp1, [pmc, #AT91_PMC_MCKR]
wait_mckrdy
/* Enter the ULP1 mode by set WAITMODE bit in CKGR_MOR */
ldr tmp1, [pmc, #AT91_CKGR_MOR]
orr tmp1, tmp1, #AT91_PMC_WAITMODE
bic tmp1, tmp1, #AT91_PMC_KEY_MASK
orr tmp1, tmp1, #AT91_PMC_KEY
str tmp1, [pmc, #AT91_CKGR_MOR]
wait_mckrdy
/* Enable the crystal oscillator */
ldr tmp1, [pmc, #AT91_CKGR_MOR]
orr tmp1, tmp1, #AT91_PMC_MOSCEN
bic tmp1, tmp1, #AT91_PMC_KEY_MASK
orr tmp1, tmp1, #AT91_PMC_KEY
str tmp1, [pmc, #AT91_CKGR_MOR]
wait_moscrdy
/* Switch the master clock source to slow clock */
ldr tmp1, [pmc, #AT91_PMC_MCKR]
bic tmp1, tmp1, #AT91_PMC_CSS
str tmp1, [pmc, #AT91_PMC_MCKR]
wait_mckrdy
/* Switch main clock source to crystal oscillator */
ldr tmp1, [pmc, #AT91_CKGR_MOR]
orr tmp1, tmp1, #AT91_PMC_MOSCSEL
bic tmp1, tmp1, #AT91_PMC_KEY_MASK
orr tmp1, tmp1, #AT91_PMC_KEY
str tmp1, [pmc, #AT91_CKGR_MOR]
wait_moscsels
/* Switch the master clock source to main clock */
ldr tmp1, [pmc, #AT91_PMC_MCKR]
bic tmp1, tmp1, #AT91_PMC_CSS
orr tmp1, tmp1, #AT91_PMC_CSS_MAIN
str tmp1, [pmc, #AT91_PMC_MCKR]
wait_mckrdy
.endm
ENTRY(at91_ulp_mode)
ldr pmc, .pmc_base
/* Save Master clock setting */
ldr tmp1, [pmc, #AT91_PMC_MCKR]
str tmp1, .saved_mckr
/*
* Set the Master clock source to slow clock
*/
bic tmp1, tmp1, #AT91_PMC_CSS
str tmp1, [pmc, #AT91_PMC_MCKR]
wait_mckrdy
/* Save PLLA setting and disable it */
ldr tmp1, [pmc, #AT91_CKGR_PLLAR]
str tmp1, .saved_pllar
mov tmp1, #AT91_PMC_PLLCOUNT
orr tmp1, tmp1, #(1 << 29) /* bit 29 always set */
str tmp1, [pmc, #AT91_CKGR_PLLAR]
ldr r0, .pm_mode
cmp r0, #AT91_PM_ULP1
beq ulp1_mode
at91_pm_ulp0_mode
b ulp_exit
ulp1_mode:
at91_pm_ulp1_mode
b ulp_exit
ulp_exit:
ldr pmc, .pmc_base
/* Restore PLLA setting */
ldr tmp1, .saved_pllar
str tmp1, [pmc, #AT91_CKGR_PLLAR]
tst tmp1, #(AT91_PMC_MUL & 0xff0000)
bne 3f
tst tmp1, #(AT91_PMC_MUL & ~0xff0000)
beq 4f
3:
wait_pllalock
4:
/*
* Restore master clock setting
*/
ldr tmp1, .saved_mckr
str tmp1, [pmc, #AT91_PMC_MCKR]
wait_mckrdy
mov pc, lr
ENDPROC(at91_ulp_mode)
/*
* void at91_sramc_self_refresh(unsigned int is_active)
*
* @input param:
* @r0: 1 - active self-refresh mode
* 0 - exit self-refresh mode
* register usage:
* @r1: memory type
* @r2: base address of the sram controller
*/
ENTRY(at91_sramc_self_refresh)
ldr r1, .memtype
ldr r2, .sramc_base
cmp r1, #AT91_MEMCTRL_MC
bne ddrc_sf
/*
* at91rm9200 Memory controller
*/
/*
* For exiting the self-refresh mode, do nothing,
* automatically exit the self-refresh mode.
*/
tst r0, #SRAMC_SELF_FRESH_ACTIVE
beq exit_sramc_sf
/* Active SDRAM self-refresh mode */
mov r3, #1
str r3, [r2, #AT91_MC_SDRAMC_SRR]
b exit_sramc_sf
ddrc_sf:
cmp r1, #AT91_MEMCTRL_DDRSDR
bne sdramc_sf
/*
* DDR Memory controller
*/
tst r0, #SRAMC_SELF_FRESH_ACTIVE
beq ddrc_exit_sf
/* LPDDR1 --> force DDR2 mode during self-refresh */
ldr r3, [r2, #AT91_DDRSDRC_MDR]
str r3, .saved_sam9_mdr
bic r3, r3, #~AT91_DDRSDRC_MD
cmp r3, #AT91_DDRSDRC_MD_LOW_POWER_DDR
ldreq r3, [r2, #AT91_DDRSDRC_MDR]
biceq r3, r3, #AT91_DDRSDRC_MD
orreq r3, r3, #AT91_DDRSDRC_MD_DDR2
streq r3, [r2, #AT91_DDRSDRC_MDR]
/* Active DDRC self-refresh mode */
ldr r3, [r2, #AT91_DDRSDRC_LPR]
str r3, .saved_sam9_lpr
bic r3, r3, #AT91_DDRSDRC_LPCB
orr r3, r3, #AT91_DDRSDRC_LPCB_SELF_REFRESH
str r3, [r2, #AT91_DDRSDRC_LPR]
/* If using the 2nd ddr controller */
ldr r2, .sramc1_base
cmp r2, #0
beq no_2nd_ddrc
ldr r3, [r2, #AT91_DDRSDRC_MDR]
str r3, .saved_sam9_mdr1
bic r3, r3, #~AT91_DDRSDRC_MD
cmp r3, #AT91_DDRSDRC_MD_LOW_POWER_DDR
ldreq r3, [r2, #AT91_DDRSDRC_MDR]
biceq r3, r3, #AT91_DDRSDRC_MD
orreq r3, r3, #AT91_DDRSDRC_MD_DDR2
streq r3, [r2, #AT91_DDRSDRC_MDR]
/* Active DDRC self-refresh mode */
ldr r3, [r2, #AT91_DDRSDRC_LPR]
str r3, .saved_sam9_lpr1
bic r3, r3, #AT91_DDRSDRC_LPCB
orr r3, r3, #AT91_DDRSDRC_LPCB_SELF_REFRESH
str r3, [r2, #AT91_DDRSDRC_LPR]
no_2nd_ddrc:
b exit_sramc_sf
ddrc_exit_sf:
/* Restore MDR in case of LPDDR1 */
ldr r3, .saved_sam9_mdr
str r3, [r2, #AT91_DDRSDRC_MDR]
/* Restore LPR on AT91 with DDRAM */
ldr r3, .saved_sam9_lpr
str r3, [r2, #AT91_DDRSDRC_LPR]
/* If using the 2nd ddr controller */
ldr r2, .sramc1_base
cmp r2, #0
ldrne r3, .saved_sam9_mdr1
strne r3, [r2, #AT91_DDRSDRC_MDR]
ldrne r3, .saved_sam9_lpr1
strne r3, [r2, #AT91_DDRSDRC_LPR]
b exit_sramc_sf
/*
* SDRAMC Memory controller
*/
sdramc_sf:
tst r0, #SRAMC_SELF_FRESH_ACTIVE
beq sdramc_exit_sf
/* Active SDRAMC self-refresh mode */
ldr r3, [r2, #AT91_SDRAMC_LPR]
str r3, .saved_sam9_lpr
bic r3, r3, #AT91_SDRAMC_LPCB
orr r3, r3, #AT91_SDRAMC_LPCB_SELF_REFRESH
str r3, [r2, #AT91_SDRAMC_LPR]
sdramc_exit_sf:
ldr r3, .saved_sam9_lpr
str r3, [r2, #AT91_SDRAMC_LPR]
exit_sramc_sf:
mov pc, lr
ENDPROC(at91_sramc_self_refresh)
.pmc_base:
.word 0
.sramc_base:
.word 0
.sramc1_base:
.word 0
.shdwc:
.word 0
.sfr:
.word 0
.memtype:
.word 0
.pm_mode:
.word 0
.saved_mckr:
.word 0
.saved_pllar:
.word 0
.saved_sam9_lpr:
.word 0
.saved_sam9_lpr1:
.word 0
.saved_sam9_mdr:
.word 0
.saved_sam9_mdr1:
.word 0
ENTRY(at91_pm_suspend_in_sram_sz)
.word .-at91_pm_suspend_in_sram