linux/arch/arm/mach-zynq/suspend.S
<<
>>
Prefs
   1/*
   2 * Suspend support for Zynq
   3 *
   4 *  Copyright (C) 2012 Xilinx
   5 *
   6 *  Soren Brinkmann <soren.brinkmann@xilinx.com>
   7 *
   8 * This software is licensed under the terms of the GNU General Public
   9 * License version 2, as published by the Free Software Foundation, and
  10 * may be copied, distributed, and modified under those terms.
  11 *
  12 * This program is distributed in the hope that it will be useful,
  13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
  14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  15 * GNU General Public License for more details.
  16 */
  17
  18#include <linux/linkage.h>
  19
  20#define ARMPLL_CTRL_OFFS        0x100
  21#define DDRPLL_CTRL_OFFS        0x104
  22#define PLLSTATUS_OFFS          0x10c
  23#define DDR_CLK_CTRL_OFFS       0x124
  24#define DCI_CLK_CTRL_OFFS       0x128
  25#define MODE_STS_OFFS           0x54
  26
  27#define PLL_RESET_MASK          1
  28#define PLL_PWRDWN_MASK         (1 << 1)
  29#define PLL_BYPASS_MASK         (1 << 4)
  30#define DCICLK_ENABLE_MASK      1
  31#define DDRCLK_ENABLE_MASK      3
  32#define ARM_LOCK_MASK           (1 << 0)
  33#define DDR_LOCK_MASK           (1 << 1)
  34#define DDRC_STATUS_MASK        7
  35
  36#define DDRC_OPMODE_SR          3
  37#define MAXTRIES                100
  38
  39        .text
  40        .align 3
  41
  42/**
  43 * zynq_sys_suspend - Enter suspend
  44 * @ddrc_base:  Base address of the DDRC
  45 * @slcr_base:  Base address of the SLCR
  46 * Returns -1 if DRAM subsystem is not gated off, 0 otherwise.
  47 *
  48 * This function is moved into OCM and finishes the suspend operation. I.e. DDR
  49 * related clocks are gated off and the DDR PLL is bypassed.
  50 */
  51ENTRY(zynq_sys_suspend)
  52        push    {r4 - r7}
  53
  54        /* Check DDRC is in self-refresh mode */
  55        ldr     r2, [r0, #MODE_STS_OFFS]
  56        and     r2, #DDRC_STATUS_MASK
  57        cmp     r2, #DDRC_OPMODE_SR
  58        movweq  r3, #0xff00
  59        bne     suspend
  60
  61        mov     r3, #MAXTRIES
  62        movw    r4, #0xfff0
  63        movt    r4, #0x1f
  64        /* Wait for command queue empty */
  651:      subs    r3, #1
  66        movweq  r3, #0xff00
  67        beq     suspend
  68        dsb     sy
  69        ldr     r2, [r0, #MODE_STS_OFFS]
  70        ands    r2, r4
  71        bne     1b
  72
  73        dsb     sy
  74
  75        /*
  76         * Wait for DDRC pipeline/queues to drain.
  77         * We should wait ~40 DDR cycles. DDR is still at full speed while the
  78         * CPU might already run in PLL bypass mode. The fastest speed the CPU
  79         * runs at is ~1 GHz ~ 2 * DDR speed.
  80         */
  81        mov     r3, #160
  821:      nop
  83        subs    r3, #1
  84        bne     1b
  85
  86        dsb     sy
  87
  88        /* read back CAM status once more */
  89        ldr     r2, [r0, #MODE_STS_OFFS]
  90        ands    r2, r4
  91        movwne  r3, #0xff00
  92        bne     suspend
  93
  94        /* Stop DDR clocks */
  95        ldr     r2, [r1, #DDR_CLK_CTRL_OFFS]
  96        bic     r2, #DDRCLK_ENABLE_MASK
  97        str     r2, [r1, #DDR_CLK_CTRL_OFFS]
  98
  99        dmb     st
 100
 101        ldr     r2, [r1, #DCI_CLK_CTRL_OFFS]
 102        bic     r2, #DCICLK_ENABLE_MASK
 103        str     r2, [r1, #DCI_CLK_CTRL_OFFS]
 104
 105        dmb     st
 106
 107        /* Bypass and powerdown DDR PLL */
 108        ldr     r2, [r1, #DDRPLL_CTRL_OFFS]
 109        orr     r2, #PLL_BYPASS_MASK
 110        str     r2, [r1, #DDRPLL_CTRL_OFFS]
 111        orr     r2, #(PLL_PWRDWN_MASK | PLL_RESET_MASK)
 112        str     r2, [r1, #DDRPLL_CTRL_OFFS]
 113
 114        /* Bypass and powerdown ARM PLL */
 115        ldr     r2, [r1, #ARMPLL_CTRL_OFFS]
 116        orr     r2, #PLL_BYPASS_MASK
 117        str     r2, [r1, #ARMPLL_CTRL_OFFS]
 118        orr     r2, #(PLL_PWRDWN_MASK | PLL_RESET_MASK)
 119        str     r2, [r1, #ARMPLL_CTRL_OFFS]
 120
 121suspend:
 122        dsb     sy
 123        wfi
 124        dsb     sy
 125        cmp     r3, #0xff00
 126        moveq   r0, #-1
 127        beq     exit
 128
 129        /* Power up ARM PLL */
 130        ldr     r2, [r1, #ARMPLL_CTRL_OFFS]
 131        bic     r2, #(PLL_PWRDWN_MASK | PLL_RESET_MASK)
 132        str     r2, [r1, #ARMPLL_CTRL_OFFS]
 133        /* wait for lock */
 1341:      ldr     r2, [r1, #PLLSTATUS_OFFS]
 135        ands    r2, #ARM_LOCK_MASK
 136        beq     1b
 137
 138        dsb     sy
 139
 140        /* Disable ARM PLL bypass */
 141        ldr     r2, [r1, #ARMPLL_CTRL_OFFS]
 142        bic     r2, #PLL_BYPASS_MASK
 143        str     r2, [r1, #ARMPLL_CTRL_OFFS]
 144
 145        dmb     st
 146
 147        /* Power up DDR PLL */
 148        ldr     r2, [r1, #DDRPLL_CTRL_OFFS]
 149        bic     r2, #(PLL_PWRDWN_MASK | PLL_RESET_MASK)
 150        str     r2, [r1, #DDRPLL_CTRL_OFFS]
 151        /* wait for lock */
 1521:      ldr     r2, [r1, #PLLSTATUS_OFFS]
 153        ands    r2, #DDR_LOCK_MASK
 154        beq     1b
 155
 156        dsb     sy
 157
 158        /* Disable DDR PLL bypass */
 159        ldr     r2, [r1, #DDRPLL_CTRL_OFFS]
 160        bic     r2, #PLL_BYPASS_MASK
 161        str     r2, [r1, #DDRPLL_CTRL_OFFS]
 162
 163        dmb     st
 164
 165        /* Start DDR clocks */
 166        ldr     r2, [r1, #DCI_CLK_CTRL_OFFS]
 167        orr     r2, #DCICLK_ENABLE_MASK
 168        str     r2, [r1, #DCI_CLK_CTRL_OFFS]
 169
 170        dmb     st
 171
 172        ldr     r2, [r1, #DDR_CLK_CTRL_OFFS]
 173        orr     r2, #DDRCLK_ENABLE_MASK
 174        str     r2, [r1, #DDR_CLK_CTRL_OFFS]
 175
 176        dsb     sy
 177
 178        mov     r0, #0
 179exit:   pop     {r4 - r7}
 180        bx      lr
 181
 182ENTRY(zynq_sys_suspend_sz)
 183        .word   . - zynq_sys_suspend
 184
 185        ENDPROC(zynq_sys_suspend)
 186