linux/arch/arm/mach-at91/pm_suspend.S
Claudiu Beznea a02875c4cb ARM: at91: pm: fix self-refresh for sama7g5
It has been discovered that on some parts, from time to time, self-refresh
procedure doesn't work as expected. Debugging and investigating it proved
that disabling AC DLL introduce glitches in RAM controllers which
leads to unexpected behavior. This is confirmed as a hardware bug. DLL
bypass disables 3 DLLs: 2 DX DLLs and AC DLL. Thus, keep only DX DLLs
disabled. This introduce 6mA extra current consumption on VDDCORE when
switching to any ULP mode or standby mode but the self-refresh procedure
still works.

Fixes: f0bbf17958 ("ARM: at91: pm: add self-refresh support for sama7g5")
Suggested-by: Frederic Schumacher <frederic.schumacher@microchip.com>
Signed-off-by: Claudiu Beznea <claudiu.beznea@microchip.com>
Tested-by: Cristian Birsan <cristian.birsan@microchip.com>
Link: https://lore.kernel.org/r/20220826083927.3107272-3-claudiu.beznea@microchip.com
2022-08-31 10:27:57 +03:00

1153 lines
24 KiB
ArmAsm

/* SPDX-License-Identifier: GPL-2.0-only */
/*
* arch/arm/mach-at91/pm_slow_clock.S
*
* Copyright (C) 2006 Savin Zlobec
*
* AT91SAM9 support:
* Copyright (C) 2007 Anti Sullin <anti.sullin@artecdesign.ee>
*/
#include <linux/linkage.h>
#include <linux/clk/at91_pmc.h>
#include "pm.h"
#include "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
tmp3 .req r6
/*
* Wait until master clock is ready (after switching master clock source)
*
* @r_mckid: register holding master clock identifier
*
* Side effects: overwrites r7, r8
*/
.macro wait_mckrdy r_mckid
#ifdef CONFIG_SOC_SAMA7
cmp \r_mckid, #0
beq 1f
mov r7, #AT91_PMC_MCKXRDY
b 2f
#endif
1: mov r7, #AT91_PMC_MCKRDY
2: ldr r8, [pmc, #AT91_PMC_SR]
and r8, r7
cmp r8, r7
bne 2b
.endm
/*
* Wait until master oscillator has stabilized.
*
* Side effects: overwrites r7
*/
.macro wait_moscrdy
1: ldr r7, [pmc, #AT91_PMC_SR]
tst r7, #AT91_PMC_MOSCS
beq 1b
.endm
/*
* Wait for main oscillator selection is done
*
* Side effects: overwrites r7
*/
.macro wait_moscsels
1: ldr r7, [pmc, #AT91_PMC_SR]
tst r7, #AT91_PMC_MOSCSELS
beq 1b
.endm
/*
* Put the processor to enter the idle state
*
* Side effects: overwrites r7
*/
.macro at91_cpu_idle
#if defined(CONFIG_CPU_V7)
mov r7, #AT91_PMC_PCK
str r7, [pmc, #AT91_PMC_SCDR]
dsb
wfi @ Wait For Interrupt
#else
mcr p15, 0, tmp1, c7, c0, 4
#endif
.endm
/**
* Set state for 2.5V low power regulator
* @ena: 0 - disable regulator
* 1 - enable regulator
*
* Side effects: overwrites r7, r8, r9, r10
*/
.macro at91_2_5V_reg_set_low_power ena
#ifdef CONFIG_SOC_SAMA7
ldr r7, .sfrbu
mov r8, #\ena
ldr r9, [r7, #AT91_SFRBU_25LDOCR]
orr r9, r9, #AT91_SFRBU_25LDOCR_LP
cmp r8, #1
beq lp_done_\ena
bic r9, r9, #AT91_SFRBU_25LDOCR_LP
lp_done_\ena:
ldr r10, =AT91_SFRBU_25LDOCR_LDOANAKEY
orr r9, r9, r10
str r9, [r7, #AT91_SFRBU_25LDOCR]
#endif
.endm
.macro at91_backup_set_lpm reg
#ifdef CONFIG_SOC_SAMA7
orr \reg, \reg, #0x200000
#endif
.endm
.text
.arm
#ifdef CONFIG_SOC_SAMA7
/**
* Enable self-refresh
*
* Side effects: overwrites r2, r3, tmp1, tmp2, tmp3, r7
*/
.macro at91_sramc_self_refresh_ena
ldr r2, .sramc_base
ldr r3, .sramc_phy_base
ldr r7, .pm_mode
dsb
/* Disable all AXI ports. */
ldr tmp1, [r2, #UDDRC_PCTRL_0]
bic tmp1, tmp1, #0x1
str tmp1, [r2, #UDDRC_PCTRL_0]
ldr tmp1, [r2, #UDDRC_PCTRL_1]
bic tmp1, tmp1, #0x1
str tmp1, [r2, #UDDRC_PCTRL_1]
ldr tmp1, [r2, #UDDRC_PCTRL_2]
bic tmp1, tmp1, #0x1
str tmp1, [r2, #UDDRC_PCTRL_2]
ldr tmp1, [r2, #UDDRC_PCTRL_3]
bic tmp1, tmp1, #0x1
str tmp1, [r2, #UDDRC_PCTRL_3]
ldr tmp1, [r2, #UDDRC_PCTRL_4]
bic tmp1, tmp1, #0x1
str tmp1, [r2, #UDDRC_PCTRL_4]
sr_ena_1:
/* Wait for all ports to disable. */
ldr tmp1, [r2, #UDDRC_PSTAT]
ldr tmp2, =UDDRC_PSTAT_ALL_PORTS
tst tmp1, tmp2
bne sr_ena_1
/* Switch to self-refresh. */
ldr tmp1, [r2, #UDDRC_PWRCTL]
orr tmp1, tmp1, #UDDRC_PWRCTL_SELFREF_SW
str tmp1, [r2, #UDDRC_PWRCTL]
sr_ena_2:
/* Wait for self-refresh enter. */
ldr tmp1, [r2, #UDDRC_STAT]
bic tmp1, tmp1, #~UDDRC_STAT_SELFREF_TYPE_MSK
cmp tmp1, #UDDRC_STAT_SELFREF_TYPE_SW
bne sr_ena_2
/* Put DDR PHY's DLL in bypass mode for non-backup modes. */
cmp r7, #AT91_PM_BACKUP
beq sr_ena_3
/* Disable DX DLLs. */
ldr tmp1, [r3, #DDR3PHY_DX0DLLCR]
orr tmp1, tmp1, #DDR3PHY_DXDLLCR_DLLDIS
str tmp1, [r3, #DDR3PHY_DX0DLLCR]
ldr tmp1, [r3, #DDR3PHY_DX1DLLCR]
orr tmp1, tmp1, #DDR3PHY_DXDLLCR_DLLDIS
str tmp1, [r3, #DDR3PHY_DX1DLLCR]
sr_ena_3:
/* Power down DDR PHY data receivers. */
ldr tmp1, [r3, #DDR3PHY_DXCCR]
orr tmp1, tmp1, #DDR3PHY_DXCCR_DXPDR
str tmp1, [r3, #DDR3PHY_DXCCR]
/* Power down ADDR/CMD IO. */
ldr tmp1, [r3, #DDR3PHY_ACIOCR]
orr tmp1, tmp1, #DDR3PHY_ACIORC_ACPDD
orr tmp1, tmp1, #DDR3PHY_ACIOCR_CKPDD_CK0
orr tmp1, tmp1, #DDR3PHY_ACIOCR_CSPDD_CS0
str tmp1, [r3, #DDR3PHY_ACIOCR]
/* Power down ODT. */
ldr tmp1, [r3, #DDR3PHY_DSGCR]
orr tmp1, tmp1, #DDR3PHY_DSGCR_ODTPDD_ODT0
str tmp1, [r3, #DDR3PHY_DSGCR]
.endm
/**
* Disable self-refresh
*
* Side effects: overwrites r2, r3, tmp1, tmp2, tmp3
*/
.macro at91_sramc_self_refresh_dis
ldr r2, .sramc_base
ldr r3, .sramc_phy_base
/* Power up DDR PHY data receivers. */
ldr tmp1, [r3, #DDR3PHY_DXCCR]
bic tmp1, tmp1, #DDR3PHY_DXCCR_DXPDR
str tmp1, [r3, #DDR3PHY_DXCCR]
/* Power up the output of CK and CS pins. */
ldr tmp1, [r3, #DDR3PHY_ACIOCR]
bic tmp1, tmp1, #DDR3PHY_ACIORC_ACPDD
bic tmp1, tmp1, #DDR3PHY_ACIOCR_CKPDD_CK0
bic tmp1, tmp1, #DDR3PHY_ACIOCR_CSPDD_CS0
str tmp1, [r3, #DDR3PHY_ACIOCR]
/* Power up ODT. */
ldr tmp1, [r3, #DDR3PHY_DSGCR]
bic tmp1, tmp1, #DDR3PHY_DSGCR_ODTPDD_ODT0
str tmp1, [r3, #DDR3PHY_DSGCR]
/* Enable DX DLLs. */
ldr tmp1, [r3, #DDR3PHY_DX0DLLCR]
bic tmp1, tmp1, #DDR3PHY_DXDLLCR_DLLDIS
str tmp1, [r3, #DDR3PHY_DX0DLLCR]
ldr tmp1, [r3, #DDR3PHY_DX1DLLCR]
bic tmp1, tmp1, #DDR3PHY_DXDLLCR_DLLDIS
str tmp1, [r3, #DDR3PHY_DX1DLLCR]
/* Enable quasi-dynamic programming. */
mov tmp1, #0
str tmp1, [r2, #UDDRC_SWCTRL]
/* De-assert SDRAM initialization. */
ldr tmp1, [r2, #UDDRC_DFIMISC]
bic tmp1, tmp1, #UDDRC_DFIMISC_DFI_INIT_COMPLETE_EN
str tmp1, [r2, #UDDRC_DFIMISC]
/* Quasi-dynamic programming done. */
mov tmp1, #UDDRC_SWCTRL_SW_DONE
str tmp1, [r2, #UDDRC_SWCTRL]
sr_dis_1:
ldr tmp1, [r2, #UDDRC_SWSTAT]
tst tmp1, #UDDRC_SWSTAT_SW_DONE_ACK
beq sr_dis_1
/* DLL soft-reset + DLL lock wait + ITM reset */
mov tmp1, #(DDR3PHY_PIR_INIT | DDR3PHY_PIR_DLLSRST | \
DDR3PHY_PIR_DLLLOCK | DDR3PHY_PIR_ITMSRST)
str tmp1, [r3, #DDR3PHY_PIR]
sr_dis_4:
/* Wait for it. */
ldr tmp1, [r3, #DDR3PHY_PGSR]
tst tmp1, #DDR3PHY_PGSR_IDONE
beq sr_dis_4
/* Enable quasi-dynamic programming. */
mov tmp1, #0
str tmp1, [r2, #UDDRC_SWCTRL]
/* Assert PHY init complete enable signal. */
ldr tmp1, [r2, #UDDRC_DFIMISC]
orr tmp1, tmp1, #UDDRC_DFIMISC_DFI_INIT_COMPLETE_EN
str tmp1, [r2, #UDDRC_DFIMISC]
/* Programming is done. Set sw_done. */
mov tmp1, #UDDRC_SWCTRL_SW_DONE
str tmp1, [r2, #UDDRC_SWCTRL]
sr_dis_5:
/* Wait for it. */
ldr tmp1, [r2, #UDDRC_SWSTAT]
tst tmp1, #UDDRC_SWSTAT_SW_DONE_ACK
beq sr_dis_5
/* Trigger self-refresh exit. */
ldr tmp1, [r2, #UDDRC_PWRCTL]
bic tmp1, tmp1, #UDDRC_PWRCTL_SELFREF_SW
str tmp1, [r2, #UDDRC_PWRCTL]
sr_dis_6:
/* Wait for self-refresh exit done. */
ldr tmp1, [r2, #UDDRC_STAT]
bic tmp1, tmp1, #~UDDRC_STAT_OPMODE_MSK
cmp tmp1, #UDDRC_STAT_OPMODE_NORMAL
bne sr_dis_6
/* Enable all AXI ports. */
ldr tmp1, [r2, #UDDRC_PCTRL_0]
orr tmp1, tmp1, #0x1
str tmp1, [r2, #UDDRC_PCTRL_0]
ldr tmp1, [r2, #UDDRC_PCTRL_1]
orr tmp1, tmp1, #0x1
str tmp1, [r2, #UDDRC_PCTRL_1]
ldr tmp1, [r2, #UDDRC_PCTRL_2]
orr tmp1, tmp1, #0x1
str tmp1, [r2, #UDDRC_PCTRL_2]
ldr tmp1, [r2, #UDDRC_PCTRL_3]
orr tmp1, tmp1, #0x1
str tmp1, [r2, #UDDRC_PCTRL_3]
ldr tmp1, [r2, #UDDRC_PCTRL_4]
orr tmp1, tmp1, #0x1
str tmp1, [r2, #UDDRC_PCTRL_4]
dsb
.endm
#else
/**
* Enable self-refresh
*
* register usage:
* @r1: memory type
* @r2: base address of the sram controller
* @r3: temporary
*/
.macro at91_sramc_self_refresh_ena
ldr r1, .memtype
ldr r2, .sramc_base
cmp r1, #AT91_MEMCTRL_MC
bne sr_ena_ddrc_sf
/* Active SDRAM self-refresh mode */
mov r3, #1
str r3, [r2, #AT91_MC_SDRAMC_SRR]
b sr_ena_exit
sr_ena_ddrc_sf:
cmp r1, #AT91_MEMCTRL_DDRSDR
bne sr_ena_sdramc_sf
/*
* DDR Memory controller
*/
/* 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 sr_ena_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]
sr_ena_no_2nd_ddrc:
b sr_ena_exit
/*
* SDRAMC Memory controller
*/
sr_ena_sdramc_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]
ldr r3, .saved_sam9_lpr
str r3, [r2, #AT91_SDRAMC_LPR]
sr_ena_exit:
.endm
/**
* Disable self-refresh
*
* register usage:
* @r1: memory type
* @r2: base address of the sram controller
* @r3: temporary
*/
.macro at91_sramc_self_refresh_dis
ldr r1, .memtype
ldr r2, .sramc_base
cmp r1, #AT91_MEMCTRL_MC
bne sr_dis_ddrc_exit_sf
/*
* at91rm9200 Memory controller
*/
/*
* For exiting the self-refresh mode, do nothing,
* automatically exit the self-refresh mode.
*/
b sr_dis_exit
sr_dis_ddrc_exit_sf:
cmp r1, #AT91_MEMCTRL_DDRSDR
bne sdramc_exit_sf
/* DDR Memory controller */
/* 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 sr_dis_exit
sdramc_exit_sf:
/* SDRAMC Memory controller */
ldr r3, .saved_sam9_lpr
str r3, [r2, #AT91_SDRAMC_LPR]
sr_dis_exit:
.endm
#endif
.macro at91_pm_ulp0_mode
ldr pmc, .pmc_base
ldr tmp2, .pm_mode
ldr tmp3, .mckr_offset
/* Check if ULP0 fast variant has been requested. */
cmp tmp2, #AT91_PM_ULP0_FAST
bne 0f
/* Set highest prescaler for power saving */
ldr tmp1, [pmc, tmp3]
bic tmp1, tmp1, #AT91_PMC_PRES
orr tmp1, tmp1, #AT91_PMC_PRES_64
str tmp1, [pmc, tmp3]
mov tmp3, #0
wait_mckrdy tmp3
b 1f
0:
/* 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]
/* Save RC oscillator state */
ldr tmp1, [pmc, #AT91_PMC_SR]
str tmp1, .saved_osc_status
tst tmp1, #AT91_PMC_MOSCRCS
bne 1f
/* Turn off RC oscillator */
ldr tmp1, [pmc, #AT91_CKGR_MOR]
bic tmp1, tmp1, #AT91_PMC_MOSCRCEN
bic tmp1, tmp1, #AT91_PMC_KEY_MASK
orr tmp1, tmp1, #AT91_PMC_KEY
str tmp1, [pmc, #AT91_CKGR_MOR]
/* Wait main RC disabled done */
2: ldr tmp1, [pmc, #AT91_PMC_SR]
tst tmp1, #AT91_PMC_MOSCRCS
bne 2b
/* Wait for interrupt */
1: at91_cpu_idle
/* Check if ULP0 fast variant has been requested. */
cmp tmp2, #AT91_PM_ULP0_FAST
bne 5f
/* Set lowest prescaler for fast resume. */
ldr tmp3, .mckr_offset
ldr tmp1, [pmc, tmp3]
bic tmp1, tmp1, #AT91_PMC_PRES
str tmp1, [pmc, tmp3]
mov tmp3, #0
wait_mckrdy tmp3
b 6f
5: /* Restore RC oscillator state */
ldr tmp1, .saved_osc_status
tst tmp1, #AT91_PMC_MOSCRCS
beq 4f
/* Turn on RC oscillator */
ldr tmp1, [pmc, #AT91_CKGR_MOR]
orr tmp1, tmp1, #AT91_PMC_MOSCRCEN
bic tmp1, tmp1, #AT91_PMC_KEY_MASK
orr tmp1, tmp1, #AT91_PMC_KEY
str tmp1, [pmc, #AT91_CKGR_MOR]
/* Wait main RC stabilization */
3: ldr tmp1, [pmc, #AT91_PMC_SR]
tst tmp1, #AT91_PMC_MOSCRCS
beq 3b
/* Turn on the crystal oscillator */
4: 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
6:
.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
ldr tmp2, .mckr_offset
mov tmp3, #0
/* Save RC oscillator state and check if it is enabled. */
ldr tmp1, [pmc, #AT91_PMC_SR]
str tmp1, .saved_osc_status
tst tmp1, #AT91_PMC_MOSCRCS
bne 2f
/* Enable RC oscillator */
ldr tmp1, [pmc, #AT91_CKGR_MOR]
orr tmp1, tmp1, #AT91_PMC_MOSCRCEN
bic tmp1, tmp1, #AT91_PMC_KEY_MASK
orr tmp1, tmp1, #AT91_PMC_KEY
str tmp1, [pmc, #AT91_CKGR_MOR]
/* Wait main RC stabilization */
1: ldr tmp1, [pmc, #AT91_PMC_SR]
tst tmp1, #AT91_PMC_MOSCRCS
beq 1b
/* Switch the main clock source to 12-MHz RC oscillator */
2: 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, tmp2]
bic tmp1, tmp1, #AT91_PMC_CSS
orr tmp1, tmp1, #AT91_PMC_CSS_MAIN
str tmp1, [pmc, tmp2]
wait_mckrdy tmp3
/* 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]
/* Quirk for SAM9X60's PMC */
nop
nop
wait_mckrdy tmp3
/* 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, tmp2]
bic tmp1, tmp1, #AT91_PMC_CSS
str tmp1, [pmc, tmp2]
wait_mckrdy tmp3
/* 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, tmp2]
bic tmp1, tmp1, #AT91_PMC_CSS
orr tmp1, tmp1, #AT91_PMC_CSS_MAIN
str tmp1, [pmc, tmp2]
wait_mckrdy tmp3
/* Restore RC oscillator state */
ldr tmp1, .saved_osc_status
tst tmp1, #AT91_PMC_MOSCRCS
bne 3f
/* Disable RC oscillator */
ldr tmp1, [pmc, #AT91_CKGR_MOR]
bic tmp1, tmp1, #AT91_PMC_MOSCRCEN
bic tmp1, tmp1, #AT91_PMC_KEY_MASK
orr tmp1, tmp1, #AT91_PMC_KEY
str tmp1, [pmc, #AT91_CKGR_MOR]
/* Wait RC oscillator disable done */
4: ldr tmp1, [pmc, #AT91_PMC_SR]
tst tmp1, #AT91_PMC_MOSCRCS
bne 4b
3:
.endm
.macro at91_plla_disable
/* Save PLLA setting and disable it */
ldr tmp1, .pmc_version
cmp tmp1, #AT91_PMC_V1
beq 1f
#ifdef CONFIG_HAVE_AT91_SAM9X60_PLL
/* Save PLLA settings. */
ldr tmp2, [pmc, #AT91_PMC_PLL_UPDT]
bic tmp2, tmp2, #AT91_PMC_PLL_UPDT_ID
str tmp2, [pmc, #AT91_PMC_PLL_UPDT]
/* save div. */
mov tmp1, #0
ldr tmp2, [pmc, #AT91_PMC_PLL_CTRL0]
bic tmp2, tmp2, #0xffffff00
orr tmp1, tmp1, tmp2
/* save mul. */
ldr tmp2, [pmc, #AT91_PMC_PLL_CTRL1]
bic tmp2, tmp2, #0xffffff
orr tmp1, tmp1, tmp2
str tmp1, .saved_pllar
/* step 2. */
ldr tmp1, [pmc, #AT91_PMC_PLL_UPDT]
bic tmp1, tmp1, #AT91_PMC_PLL_UPDT_UPDATE
bic tmp1, tmp1, #AT91_PMC_PLL_UPDT_ID
str tmp1, [pmc, #AT91_PMC_PLL_UPDT]
/* step 3. */
ldr tmp1, [pmc, #AT91_PMC_PLL_CTRL0]
bic tmp1, tmp1, #AT91_PMC_PLL_CTRL0_ENPLLCK
orr tmp1, tmp1, #AT91_PMC_PLL_CTRL0_ENPLL
str tmp1, [pmc, #AT91_PMC_PLL_CTRL0]
/* step 4. */
ldr tmp1, [pmc, #AT91_PMC_PLL_UPDT]
orr tmp1, tmp1, #AT91_PMC_PLL_UPDT_UPDATE
bic tmp1, tmp1, #AT91_PMC_PLL_UPDT_ID
str tmp1, [pmc, #AT91_PMC_PLL_UPDT]
/* step 5. */
ldr tmp1, [pmc, #AT91_PMC_PLL_CTRL0]
bic tmp1, tmp1, #AT91_PMC_PLL_CTRL0_ENPLL
str tmp1, [pmc, #AT91_PMC_PLL_CTRL0]
/* step 7. */
ldr tmp1, [pmc, #AT91_PMC_PLL_UPDT]
orr tmp1, tmp1, #AT91_PMC_PLL_UPDT_UPDATE
bic tmp1, tmp1, #AT91_PMC_PLL_UPDT_ID
str tmp1, [pmc, #AT91_PMC_PLL_UPDT]
b 2f
#endif
1: /* Save PLLA setting and disable it */
ldr tmp1, [pmc, #AT91_CKGR_PLLAR]
str tmp1, .saved_pllar
/* Disable PLLA. */
mov tmp1, #AT91_PMC_PLLCOUNT
orr tmp1, tmp1, #(1 << 29) /* bit 29 always set */
str tmp1, [pmc, #AT91_CKGR_PLLAR]
2:
.endm
.macro at91_plla_enable
ldr tmp2, .saved_pllar
ldr tmp3, .pmc_version
cmp tmp3, #AT91_PMC_V1
beq 4f
#ifdef CONFIG_HAVE_AT91_SAM9X60_PLL
/* step 1. */
ldr tmp1, [pmc, #AT91_PMC_PLL_UPDT]
bic tmp1, tmp1, #AT91_PMC_PLL_UPDT_ID
bic tmp1, tmp1, #AT91_PMC_PLL_UPDT_UPDATE
str tmp1, [pmc, #AT91_PMC_PLL_UPDT]
/* step 2. */
ldr tmp1, =AT91_PMC_PLL_ACR_DEFAULT_PLLA
str tmp1, [pmc, #AT91_PMC_PLL_ACR]
/* step 3. */
ldr tmp1, [pmc, #AT91_PMC_PLL_CTRL1]
mov tmp3, tmp2
bic tmp3, tmp3, #0xffffff
orr tmp1, tmp1, tmp3
str tmp1, [pmc, #AT91_PMC_PLL_CTRL1]
/* step 8. */
ldr tmp1, [pmc, #AT91_PMC_PLL_UPDT]
bic tmp1, tmp1, #AT91_PMC_PLL_UPDT_ID
orr tmp1, tmp1, #AT91_PMC_PLL_UPDT_UPDATE
str tmp1, [pmc, #AT91_PMC_PLL_UPDT]
/* step 9. */
ldr tmp1, [pmc, #AT91_PMC_PLL_CTRL0]
orr tmp1, tmp1, #AT91_PMC_PLL_CTRL0_ENLOCK
orr tmp1, tmp1, #AT91_PMC_PLL_CTRL0_ENPLL
orr tmp1, tmp1, #AT91_PMC_PLL_CTRL0_ENPLLCK
bic tmp1, tmp1, #0xff
mov tmp3, tmp2
bic tmp3, tmp3, #0xffffff00
orr tmp1, tmp1, tmp3
str tmp1, [pmc, #AT91_PMC_PLL_CTRL0]
/* step 10. */
ldr tmp1, [pmc, #AT91_PMC_PLL_UPDT]
orr tmp1, tmp1, #AT91_PMC_PLL_UPDT_UPDATE
bic tmp1, tmp1, #AT91_PMC_PLL_UPDT_ID
str tmp1, [pmc, #AT91_PMC_PLL_UPDT]
/* step 11. */
3: ldr tmp1, [pmc, #AT91_PMC_PLL_ISR0]
tst tmp1, #0x1
beq 3b
b 2f
#endif
/* Restore PLLA setting */
4: str tmp2, [pmc, #AT91_CKGR_PLLAR]
/* Enable PLLA. */
tst tmp2, #(AT91_PMC_MUL & 0xff0000)
bne 1f
tst tmp2, #(AT91_PMC_MUL & ~0xff0000)
beq 2f
1: ldr tmp1, [pmc, #AT91_PMC_SR]
tst tmp1, #AT91_PMC_LOCKA
beq 1b
2:
.endm
/**
* at91_mckx_ps_enable: save MCK1..4 settings and switch it to main clock
*
* Side effects: overwrites tmp1, tmp2
*/
.macro at91_mckx_ps_enable
#ifdef CONFIG_SOC_SAMA7
ldr pmc, .pmc_base
/* There are 4 MCKs we need to handle: MCK1..4 */
mov tmp1, #1
e_loop: cmp tmp1, #5
beq e_done
/* Write MCK ID to retrieve the settings. */
str tmp1, [pmc, #AT91_PMC_MCR_V2]
ldr tmp2, [pmc, #AT91_PMC_MCR_V2]
e_save_mck1:
cmp tmp1, #1
bne e_save_mck2
str tmp2, .saved_mck1
b e_ps
e_save_mck2:
cmp tmp1, #2
bne e_save_mck3
str tmp2, .saved_mck2
b e_ps
e_save_mck3:
cmp tmp1, #3
bne e_save_mck4
str tmp2, .saved_mck3
b e_ps
e_save_mck4:
str tmp2, .saved_mck4
e_ps:
/* Use CSS=MAINCK and DIV=1. */
bic tmp2, tmp2, #AT91_PMC_MCR_V2_CSS
bic tmp2, tmp2, #AT91_PMC_MCR_V2_DIV
orr tmp2, tmp2, #AT91_PMC_MCR_V2_CSS_MAINCK
orr tmp2, tmp2, #AT91_PMC_MCR_V2_DIV1
str tmp2, [pmc, #AT91_PMC_MCR_V2]
wait_mckrdy tmp1
add tmp1, tmp1, #1
b e_loop
e_done:
#endif
.endm
/**
* at91_mckx_ps_restore: restore MCK1..4 settings
*
* Side effects: overwrites tmp1, tmp2
*/
.macro at91_mckx_ps_restore
#ifdef CONFIG_SOC_SAMA7
ldr pmc, .pmc_base
/* There are 4 MCKs we need to handle: MCK1..4 */
mov tmp1, #1
r_loop: cmp tmp1, #5
beq r_done
r_save_mck1:
cmp tmp1, #1
bne r_save_mck2
ldr tmp2, .saved_mck1
b r_ps
r_save_mck2:
cmp tmp1, #2
bne r_save_mck3
ldr tmp2, .saved_mck2
b r_ps
r_save_mck3:
cmp tmp1, #3
bne r_save_mck4
ldr tmp2, .saved_mck3
b r_ps
r_save_mck4:
ldr tmp2, .saved_mck4
r_ps:
/* Write MCK ID to retrieve the settings. */
str tmp1, [pmc, #AT91_PMC_MCR_V2]
ldr tmp3, [pmc, #AT91_PMC_MCR_V2]
/* We need to restore CSS and DIV. */
bic tmp3, tmp3, #AT91_PMC_MCR_V2_CSS
bic tmp3, tmp3, #AT91_PMC_MCR_V2_DIV
orr tmp3, tmp3, tmp2
bic tmp3, tmp3, #AT91_PMC_MCR_V2_ID_MSK
orr tmp3, tmp3, tmp1
orr tmp3, tmp3, #AT91_PMC_MCR_V2_CMD
str tmp2, [pmc, #AT91_PMC_MCR_V2]
wait_mckrdy tmp1
add tmp1, tmp1, #1
b r_loop
r_done:
#endif
.endm
.macro at91_ulp_mode
at91_mckx_ps_enable
ldr pmc, .pmc_base
ldr tmp2, .mckr_offset
ldr tmp3, .pm_mode
/* Save Master clock setting */
ldr tmp1, [pmc, tmp2]
str tmp1, .saved_mckr
/*
* Set master clock source to:
* - MAINCK if using ULP0 fast variant
* - slow clock, otherwise
*/
bic tmp1, tmp1, #AT91_PMC_CSS
cmp tmp3, #AT91_PM_ULP0_FAST
bne save_mck
orr tmp1, tmp1, #AT91_PMC_CSS_MAIN
save_mck:
str tmp1, [pmc, tmp2]
mov tmp3, #0
wait_mckrdy tmp3
at91_plla_disable
/* Enable low power mode for 2.5V regulator. */
at91_2_5V_reg_set_low_power 1
ldr tmp3, .pm_mode
cmp tmp3, #AT91_PM_ULP1
beq ulp1_mode
at91_pm_ulp0_mode
b ulp_exit
ulp1_mode:
at91_pm_ulp1_mode
b ulp_exit
ulp_exit:
/* Disable low power mode for 2.5V regulator. */
at91_2_5V_reg_set_low_power 0
ldr pmc, .pmc_base
at91_plla_enable
/*
* Restore master clock setting
*/
ldr tmp1, .mckr_offset
ldr tmp2, .saved_mckr
str tmp2, [pmc, tmp1]
mov tmp3, #0
wait_mckrdy tmp3
at91_mckx_ps_restore
.endm
.macro at91_backup_mode
/* Switch the master clock source to slow clock. */
ldr pmc, .pmc_base
ldr tmp2, .mckr_offset
ldr tmp1, [pmc, tmp2]
bic tmp1, tmp1, #AT91_PMC_CSS
str tmp1, [pmc, tmp2]
mov tmp3, #0
wait_mckrdy tmp3
/*BUMEN*/
ldr r0, .sfrbu
mov tmp1, #0x1
str tmp1, [r0, #0x10]
/* Wait for it. */
1: ldr tmp1, [r0, #0x10]
tst tmp1, #0x1
beq 1b
/* Shutdown */
ldr r0, .shdwc
mov tmp1, #0xA5000000
add tmp1, tmp1, #0x1
at91_backup_set_lpm tmp1
str tmp1, [r0, #0]
.endm
/*
* 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
/* Flush tlb. */
mov r4, #0
mcr p15, 0, r4, c8, c7, 0
ldr tmp1, [r0, #PM_DATA_PMC_MCKR_OFFSET]
str tmp1, .mckr_offset
ldr tmp1, [r0, #PM_DATA_PMC_VERSION]
str tmp1, .pmc_version
ldr tmp1, [r0, #PM_DATA_MEMCTRL]
str tmp1, .memtype
ldr tmp1, [r0, #PM_DATA_MODE]
str tmp1, .pm_mode
/*
* ldrne below are here to preload their address in the TLB as access
* to RAM may be limited while in self-refresh.
*/
ldr tmp1, [r0, #PM_DATA_PMC]
str tmp1, .pmc_base
cmp tmp1, #0
ldrne tmp2, [tmp1, #0]
ldr tmp1, [r0, #PM_DATA_RAMC0]
str tmp1, .sramc_base
cmp tmp1, #0
ldrne tmp2, [tmp1, #0]
ldr tmp1, [r0, #PM_DATA_RAMC1]
str tmp1, .sramc1_base
cmp tmp1, #0
ldrne tmp2, [tmp1, #0]
#ifndef CONFIG_SOC_SAM_V4_V5
/* ldrne below are here to preload their address in the TLB */
ldr tmp1, [r0, #PM_DATA_RAMC_PHY]
str tmp1, .sramc_phy_base
cmp tmp1, #0
ldrne tmp2, [tmp1, #0]
ldr tmp1, [r0, #PM_DATA_SHDWC]
str tmp1, .shdwc
cmp tmp1, #0
ldrne tmp2, [tmp1, #0]
ldr tmp1, [r0, #PM_DATA_SFRBU]
str tmp1, .sfrbu
cmp tmp1, #0
ldrne tmp2, [tmp1, #0x10]
#endif
/* Active the self-refresh mode */
at91_sramc_self_refresh_ena
ldr r0, .pm_mode
cmp r0, #AT91_PM_STANDBY
beq standby
cmp r0, #AT91_PM_BACKUP
beq backup_mode
at91_ulp_mode
b exit_suspend
standby:
/* Wait for interrupt */
ldr pmc, .pmc_base
at91_cpu_idle
b exit_suspend
backup_mode:
at91_backup_mode
exit_suspend:
/* Exit the self-refresh mode */
at91_sramc_self_refresh_dis
/* Restore registers, and return */
ldmfd sp!, {r4 - r12, pc}
ENDPROC(at91_pm_suspend_in_sram)
.pmc_base:
.word 0
.sramc_base:
.word 0
.sramc1_base:
.word 0
.sramc_phy_base:
.word 0
.shdwc:
.word 0
.sfrbu:
.word 0
.memtype:
.word 0
.pm_mode:
.word 0
.mckr_offset:
.word 0
.pmc_version:
.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
.saved_osc_status:
.word 0
#ifdef CONFIG_SOC_SAMA7
.saved_mck1:
.word 0
.saved_mck2:
.word 0
.saved_mck3:
.word 0
.saved_mck4:
.word 0
#endif
ENTRY(at91_pm_suspend_in_sram_sz)
.word .-at91_pm_suspend_in_sram