/* * arch/arm/mach-at91/pm_slow_clock.S * * Copyright (C) 2006 Savin Zlobec * * AT91SAM9 support: * Copyright (C) 2007 Anti Sullin #include #include #include /* * When SLOWDOWN_MASTER_CLOCK is defined we will also slow down the Master * clock during suspend by adjusting its prescalar and divisor. * NOTE: This hasn't been shown to be stable on SAM9s; and on the RM9200 there * are errata regarding adjusting the prescalar and divisor. */ #undef SLOWDOWN_MASTER_CLOCK #define MCKRDY_TIMEOUT 1000 #define MOSCRDY_TIMEOUT 1000 #define PLLALOCK_TIMEOUT 1000 #define PLLBLOCK_TIMEOUT 1000 pmc .req r0 sdramc .req r1 ramc1 .req r2 memctrl .req r3 tmp1 .req r4 tmp2 .req r5 /* * Wait until master clock is ready (after switching master clock source) */ .macro wait_mckrdy mov tmp2, #MCKRDY_TIMEOUT 1: sub tmp2, tmp2, #1 cmp tmp2, #0 beq 2f ldr tmp1, [pmc, #AT91_PMC_SR] tst tmp1, #AT91_PMC_MCKRDY beq 1b 2: .endm /* * Wait until master oscillator has stabilized. */ .macro wait_moscrdy mov tmp2, #MOSCRDY_TIMEOUT 1: sub tmp2, tmp2, #1 cmp tmp2, #0 beq 2f ldr tmp1, [pmc, #AT91_PMC_SR] tst tmp1, #AT91_PMC_MOSCS beq 1b 2: .endm /* * Wait until PLLA has locked. */ .macro wait_pllalock mov tmp2, #PLLALOCK_TIMEOUT 1: sub tmp2, tmp2, #1 cmp tmp2, #0 beq 2f ldr tmp1, [pmc, #AT91_PMC_SR] tst tmp1, #AT91_PMC_LOCKA beq 1b 2: .endm /* * Wait until PLLB has locked. */ .macro wait_pllblock mov tmp2, #PLLBLOCK_TIMEOUT 1: sub tmp2, tmp2, #1 cmp tmp2, #0 beq 2f ldr tmp1, [pmc, #AT91_PMC_SR] tst tmp1, #AT91_PMC_LOCKB beq 1b 2: .endm .text /* void at91_slow_clock(void __iomem *pmc, void __iomem *sdramc, * void __iomem *ramc1, int memctrl) */ ENTRY(at91_slow_clock) /* Save registers on stack */ stmfd sp!, {r4 - r12, lr} /* * Register usage: * R0 = Base address of AT91_PMC * R1 = Base address of RAM Controller (SDRAM, DDRSDR, or AT91_SYS) * R2 = Base address of second RAM Controller or 0 if not present * R3 = Memory controller * R4 = temporary register * R5 = temporary register */ /* Drain write buffer */ mov tmp1, #0 mcr p15, 0, tmp1, c7, c10, 4 cmp memctrl, #AT91_MEMCTRL_MC bne ddr_sr_enable /* * at91rm9200 Memory controller */ /* Put SDRAM in self-refresh mode */ mov tmp1, #1 str tmp1, [sdramc, #AT91RM9200_SDRAMC_SRR] b sdr_sr_done /* * DDRSDR Memory controller */ ddr_sr_enable: cmp memctrl, #AT91_MEMCTRL_DDRSDR bne sdr_sr_enable /* prepare for DDRAM self-refresh mode */ ldr tmp1, [sdramc, #AT91_DDRSDRC_LPR] str tmp1, .saved_sam9_lpr bic tmp1, #AT91_DDRSDRC_LPCB orr tmp1, #AT91_DDRSDRC_LPCB_SELF_REFRESH /* figure out if we use the second ram controller */ cmp ramc1, #0 ldrne tmp2, [ramc1, #AT91_DDRSDRC_LPR] strne tmp2, .saved_sam9_lpr1 bicne tmp2, #AT91_DDRSDRC_LPCB orrne tmp2, #AT91_DDRSDRC_LPCB_SELF_REFRESH /* Enable DDRAM self-refresh mode */ str tmp1, [sdramc, #AT91_DDRSDRC_LPR] strne tmp2, [ramc1, #AT91_DDRSDRC_LPR] b sdr_sr_done /* * SDRAMC Memory controller */ sdr_sr_enable: /* Enable SDRAM self-refresh mode */ ldr tmp1, [sdramc, #AT91_SDRAMC_LPR] str tmp1, .saved_sam9_lpr bic tmp1, #AT91_SDRAMC_LPCB orr tmp1, #AT91_SDRAMC_LPCB_SELF_REFRESH str tmp1, [sdramc, #AT91_SDRAMC_LPR] sdr_sr_done: /* 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 #ifdef SLOWDOWN_MASTER_CLOCK /* * Set the Master Clock PRES and MDIV fields. * * See AT91RM9200 errata #27 and #28 for details. */ mov tmp1, #0 str tmp1, [pmc, #AT91_PMC_MCKR] wait_mckrdy #endif /* 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] /* Save PLLB setting and disable it */ ldr tmp1, [pmc, #AT91_CKGR_PLLBR] str tmp1, .saved_pllbr mov tmp1, #AT91_PMC_PLLCOUNT str tmp1, [pmc, #AT91_CKGR_PLLBR] /* Turn off the main oscillator */ ldr tmp1, [pmc, #AT91_CKGR_MOR] bic tmp1, tmp1, #AT91_PMC_MOSCEN str tmp1, [pmc, #AT91_CKGR_MOR] /* Wait for interrupt */ mcr p15, 0, tmp1, c7, c0, 4 /* Turn on the main oscillator */ ldr tmp1, [pmc, #AT91_CKGR_MOR] orr tmp1, tmp1, #AT91_PMC_MOSCEN str tmp1, [pmc, #AT91_CKGR_MOR] wait_moscrdy /* Restore PLLB setting */ ldr tmp1, .saved_pllbr str tmp1, [pmc, #AT91_CKGR_PLLBR] tst tmp1, #(AT91_PMC_MUL & 0xff0000) bne 1f tst tmp1, #(AT91_PMC_MUL & ~0xff0000) beq 2f 1: wait_pllblock 2: /* 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: #ifdef SLOWDOWN_MASTER_CLOCK /* * First set PRES if it was not 0, * than set CSS and MDIV fields. * * See AT91RM9200 errata #27 and #28 for details. */ ldr tmp1, .saved_mckr tst tmp1, #AT91_PMC_PRES beq 2f and tmp1, tmp1, #AT91_PMC_PRES str tmp1, [pmc, #AT91_PMC_MCKR] wait_mckrdy #endif /* * Restore master clock setting */ 2: ldr tmp1, .saved_mckr str tmp1, [pmc, #AT91_PMC_MCKR] wait_mckrdy /* * at91rm9200 Memory controller * Do nothing - self-refresh is automatically disabled. */ cmp memctrl, #AT91_MEMCTRL_MC beq ram_restored /* * DDRSDR Memory controller */ cmp memctrl, #AT91_MEMCTRL_DDRSDR bne sdr_en_restore /* Restore LPR on AT91 with DDRAM */ ldr tmp1, .saved_sam9_lpr str tmp1, [sdramc, #AT91_DDRSDRC_LPR] /* if we use the second ram controller */ cmp ramc1, #0 ldrne tmp2, .saved_sam9_lpr1 strne tmp2, [ramc1, #AT91_DDRSDRC_LPR] b ram_restored /* * SDRAMC Memory controller */ sdr_en_restore: /* Restore LPR on AT91 with SDRAM */ ldr tmp1, .saved_sam9_lpr str tmp1, [sdramc, #AT91_SDRAMC_LPR] ram_restored: /* Restore registers, and return */ ldmfd sp!, {r4 - r12, pc} .saved_mckr: .word 0 .saved_pllar: .word 0 .saved_pllbr: .word 0 .saved_sam9_lpr: .word 0 .saved_sam9_lpr1: .word 0 ENTRY(at91_slow_clock_sz) .word .-at91_slow_clock