| /* 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" |
| |
| #ifdef CONFIG_CPU_V7 |
| .arch armv7-a |
| #endif |
| |
| #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 |
| |
| /* Disable DX DLLs for non-backup modes. */ |
| cmp r7, #AT91_PM_BACKUP |
| beq sr_ena_3 |
| |
| /* Do not soft reset the AC DLL. */ |
| ldr tmp1, [r3, DDR3PHY_ACDLLCR] |
| bic tmp1, tmp1, DDR3PHY_ACDLLCR_DLLSRST |
| str tmp1, [r3, DDR3PHY_ACDLLCR] |
| |
| /* 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 |