summaryrefslogblamecommitdiff
path: root/arch/arm/mach-at91/pm_suspend.S
blob: abe4ced33edafbbf5f91b4a69e4a6f76ce978ff1 (plain) (tree)
1
2
3
4
5
6
7
8
9
                                           





                                     
                                                               
   
                          
                               
               
                            
 


                                            
                  

                  
                  


                                                                         



                                                        
   











                                       



                                               

                              

                           

                                       
                  


             
                                             

                              

                            

                                       



                  
                                            

                              



                            

                                         









                                            






















                                                  





                                      

             

            










































                                                        
                                                    



















































































































                                                                    
                                                    
































                                                   






                                                
   


                                  
 

                                    
 



                                             
 


                                        
 


                                
 








                                                            
 





                                                       
 



                                             
 







                                                  
 





                                                       
 

                           
 









                                                      
 

                                          
 

























































                                                          
      
 

                              











                                                            


                        
                  
 
  





                                             

















                                              
                                
                     
 




                                                            
                                  


                                          


                        


                                         




                                       
                                           











                                              




                                            
  







                                                               
                                  
                        
 






                                                                  
                                           











                                                                  














                                                          
                                 

                                              
                                 
 
                        







                                                                 



                                     
                        










                                                          
                                 
                                         
                                 
 
                        










                                                            
                                 

                                              
                                 
 
                        


















                                              

     

                                              



                                  
                                   

















































                                                       






                                                                       
  


                       




                                  
                                   






                                                     
                                                    






































                                                       
                                  
                                             

                          
                                                 
                  
                                                 







                                         
















































































































                                                                         
                    

                           
                              
                                  
                              
 
                                       
                                 
                                 

          


                                              
           
                                         



                                              
                                 
 

                        
 
                         
 


                                                       
                              
                                   
                         
 

                         
 


                         
 
         


                                                        
                              
 
                        
 


                                       


                                  
 

                        
 
                            
     
 






                                                           
 

                        
 



                                 
 




                                 



                                 
                                

                              
 









                                                                                    
 


                                        
 



                                     








                                                    



                                                                             

                                        


                                

                                          


                                

                                          




                                                                      

                                             


                                



                                          
 



                                          
      
 

                                          
 




                                    
 

                            
 




                                
 

                        
 


                                        
 


                                           






               

                

               
       
               

               

               

               

               

               

               

                

                 

                

                 

                  









                       
 

                                       
/* 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
	ldr	tmp1, [r3, #DDR3PHY_PIR]
	orr	tmp1, tmp1, #DDR3PHY_PIR_DLLBYP
	str	tmp1, [r3, #DDR3PHY_PIR]

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]

	/* Take DDR PHY's DLL out of bypass mode. */
	ldr	tmp1, [r3, #DDR3PHY_PIR]
	bic	tmp1, tmp1, #DDR3PHY_PIR_DLLBYP
	str	tmp1, [r3, #DDR3PHY_PIR]

	/* 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