| /* SPDX-License-Identifier: GPL-2.0-only */ |
| /* |
| * (C) Copyright 2009, Texas Instruments, Inc. http://www.ti.com/ |
| */ |
| |
| /* replicated define because linux/bitops.h cannot be included in assembly */ |
| #define BIT(nr) (1 << (nr)) |
| |
| #include <linux/linkage.h> |
| #include <asm/assembler.h> |
| #include "psc.h" |
| #include "ddr2.h" |
| |
| #include "clock.h" |
| |
| /* Arbitrary, hardware currently does not update PHYRDY correctly */ |
| #define PHYRDY_CYCLES 0x1000 |
| |
| /* Assume 25 MHz speed for the cycle conversions since PLLs are bypassed */ |
| #define PLL_BYPASS_CYCLES (PLL_BYPASS_TIME * 25) |
| #define PLL_RESET_CYCLES (PLL_RESET_TIME * 25) |
| #define PLL_LOCK_CYCLES (PLL_LOCK_TIME * 25) |
| |
| #define DEEPSLEEP_SLEEPENABLE_BIT BIT(31) |
| |
| .text |
| /* |
| * Move DaVinci into deep sleep state |
| * |
| * Note: This code is copied to internal SRAM by PM code. When the DaVinci |
| * wakes up it continues execution at the point it went to sleep. |
| * Register Usage: |
| * r0: contains virtual base for DDR2 controller |
| * r1: contains virtual base for DDR2 Power and Sleep controller (PSC) |
| * r2: contains PSC number for DDR2 |
| * r3: contains virtual base DDR2 PLL controller |
| * r4: contains virtual address of the DEEPSLEEP register |
| */ |
| ENTRY(davinci_cpu_suspend) |
| stmfd sp!, {r0-r12, lr} @ save registers on stack |
| |
| ldr ip, CACHE_FLUSH |
| blx ip |
| |
| ldmia r0, {r0-r4} |
| |
| /* |
| * Switch DDR to self-refresh mode. |
| */ |
| |
| /* calculate SDRCR address */ |
| ldr ip, [r0, #DDR2_SDRCR_OFFSET] |
| bic ip, ip, #DDR2_SRPD_BIT |
| orr ip, ip, #DDR2_LPMODEN_BIT |
| str ip, [r0, #DDR2_SDRCR_OFFSET] |
| |
| ldr ip, [r0, #DDR2_SDRCR_OFFSET] |
| orr ip, ip, #DDR2_MCLKSTOPEN_BIT |
| str ip, [r0, #DDR2_SDRCR_OFFSET] |
| |
| mov ip, #PHYRDY_CYCLES |
| 1: subs ip, ip, #0x1 |
| bne 1b |
| |
| /* Disable DDR2 LPSC */ |
| mov r7, r0 |
| mov r0, #0x2 |
| bl davinci_ddr_psc_config |
| mov r0, r7 |
| |
| /* Disable clock to DDR PHY */ |
| ldr ip, [r3, #PLLDIV1] |
| bic ip, ip, #PLLDIV_EN |
| str ip, [r3, #PLLDIV1] |
| |
| /* Put the DDR PLL in bypass and power down */ |
| ldr ip, [r3, #PLLCTL] |
| bic ip, ip, #PLLCTL_PLLENSRC |
| bic ip, ip, #PLLCTL_PLLEN |
| str ip, [r3, #PLLCTL] |
| |
| /* Wait for PLL to switch to bypass */ |
| mov ip, #PLL_BYPASS_CYCLES |
| 2: subs ip, ip, #0x1 |
| bne 2b |
| |
| /* Power down the PLL */ |
| ldr ip, [r3, #PLLCTL] |
| orr ip, ip, #PLLCTL_PLLPWRDN |
| str ip, [r3, #PLLCTL] |
| |
| /* Go to deep sleep */ |
| ldr ip, [r4] |
| orr ip, ip, #DEEPSLEEP_SLEEPENABLE_BIT |
| /* System goes to sleep beyond after this instruction */ |
| str ip, [r4] |
| |
| /* Wake up from sleep */ |
| |
| /* Clear sleep enable */ |
| ldr ip, [r4] |
| bic ip, ip, #DEEPSLEEP_SLEEPENABLE_BIT |
| str ip, [r4] |
| |
| /* initialize the DDR PLL controller */ |
| |
| /* Put PLL in reset */ |
| ldr ip, [r3, #PLLCTL] |
| bic ip, ip, #PLLCTL_PLLRST |
| str ip, [r3, #PLLCTL] |
| |
| /* Clear PLL power down */ |
| ldr ip, [r3, #PLLCTL] |
| bic ip, ip, #PLLCTL_PLLPWRDN |
| str ip, [r3, #PLLCTL] |
| |
| mov ip, #PLL_RESET_CYCLES |
| 3: subs ip, ip, #0x1 |
| bne 3b |
| |
| /* Bring PLL out of reset */ |
| ldr ip, [r3, #PLLCTL] |
| orr ip, ip, #PLLCTL_PLLRST |
| str ip, [r3, #PLLCTL] |
| |
| /* Wait for PLL to lock (assume prediv = 1, 25MHz OSCIN) */ |
| mov ip, #PLL_LOCK_CYCLES |
| 4: subs ip, ip, #0x1 |
| bne 4b |
| |
| /* Remove PLL from bypass mode */ |
| ldr ip, [r3, #PLLCTL] |
| bic ip, ip, #PLLCTL_PLLENSRC |
| orr ip, ip, #PLLCTL_PLLEN |
| str ip, [r3, #PLLCTL] |
| |
| /* Start 2x clock to DDR2 */ |
| |
| ldr ip, [r3, #PLLDIV1] |
| orr ip, ip, #PLLDIV_EN |
| str ip, [r3, #PLLDIV1] |
| |
| /* Enable VCLK */ |
| |
| /* Enable DDR2 LPSC */ |
| mov r7, r0 |
| mov r0, #0x3 |
| bl davinci_ddr_psc_config |
| mov r0, r7 |
| |
| /* clear MCLKSTOPEN */ |
| |
| ldr ip, [r0, #DDR2_SDRCR_OFFSET] |
| bic ip, ip, #DDR2_MCLKSTOPEN_BIT |
| str ip, [r0, #DDR2_SDRCR_OFFSET] |
| |
| ldr ip, [r0, #DDR2_SDRCR_OFFSET] |
| bic ip, ip, #DDR2_LPMODEN_BIT |
| str ip, [r0, #DDR2_SDRCR_OFFSET] |
| |
| /* Restore registers and return */ |
| ldmfd sp!, {r0-r12, pc} |
| |
| ENDPROC(davinci_cpu_suspend) |
| |
| /* |
| * Disables or Enables DDR2 LPSC |
| * Register Usage: |
| * r0: Enable or Disable LPSC r0 = 0x3 => Enable, r0 = 0x2 => Disable LPSC |
| * r1: contains virtual base for DDR2 Power and Sleep controller (PSC) |
| * r2: contains PSC number for DDR2 |
| */ |
| ENTRY(davinci_ddr_psc_config) |
| /* Set next state in mdctl for DDR2 */ |
| mov r6, #MDCTL |
| add r6, r6, r2, lsl #2 |
| ldr ip, [r1, r6] |
| bic ip, ip, #MDSTAT_STATE_MASK |
| orr ip, ip, r0 |
| str ip, [r1, r6] |
| |
| /* Enable the Power Domain Transition Command */ |
| ldr ip, [r1, #PTCMD] |
| orr ip, ip, #0x1 |
| str ip, [r1, #PTCMD] |
| |
| /* Check for Transition Complete (PTSTAT) */ |
| ptstat_done: |
| ldr ip, [r1, #PTSTAT] |
| and ip, ip, #0x1 |
| cmp ip, #0x0 |
| bne ptstat_done |
| |
| /* Check for DDR2 clock disable completion; */ |
| mov r6, #MDSTAT |
| add r6, r6, r2, lsl #2 |
| ddr2clk_stop_done: |
| ldr ip, [r1, r6] |
| and ip, ip, #MDSTAT_STATE_MASK |
| cmp ip, r0 |
| bne ddr2clk_stop_done |
| |
| ret lr |
| ENDPROC(davinci_ddr_psc_config) |
| |
| CACHE_FLUSH: |
| #ifdef CONFIG_CPU_V6 |
| .word v6_flush_kern_cache_all |
| #else |
| .word arm926_flush_kern_cache_all |
| #endif |
| |
| ENTRY(davinci_cpu_suspend_sz) |
| .word . - davinci_cpu_suspend |
| ENDPROC(davinci_cpu_suspend_sz) |