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