mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git
synced 2024-09-05 10:29:37 +00:00
![Claudiu Beznea](/assets/img/avatar_default.png)
The SAMA5D2 NRST input signal is resynchronized with the SLCK clock and it can take up to 2 SLCK cycles (about 90us) for the internal reset to be effective. During this delay, the VDDCORE current consumption may still be high (application-dependent) with the VDDCORE regulator already OFF. Under such conditions, VDDCORE may operate below its operating range leading to potential register corruption. To prevent such situation, it is recommended to decrease significantly the power consumption of the device once the voltage regulator is turned-off. This can be achieved by operating the device at a much lower low frequency. To solve this switch the master clock to slock clock just before writing shutdown command to shutdown controller. Signed-off-by: Claudiu Beznea <claudiu.beznea@microchip.com> Suggested-by: Patrice Vilchez <patrice.vilchez@microchip.com> Acked-by: Nicolas Ferre <nicolas.ferre@microchip.com> Signed-off-by: Sebastian Reichel <sebastian.reichel@collabora.com>
480 lines
9.6 KiB
ArmAsm
480 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
|