summaryrefslogtreecommitdiff
path: root/arch/arm/mach-davinci/sleep.S
blob: d5affab4396d7b060369425f7ceeb613fc712650 (plain) (blame)
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
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
/* SPDX-License-Identifier: GPL-2.0-only */
/*
 * (C) Copyright 2009, Texas Instruments, Inc. https://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
	.arch	armv5te
/*
 * 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)