hc
2024-12-19 9370bb92b2d16684ee45cf24e879c93c509162da
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
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
/* SPDX-License-Identifier: GPL-2.0-or-later */
/*
 * Copyright 2014 Freescale Semiconductor, Inc.
 */
 
#include <linux/linkage.h>
#include <asm/assembler.h>
#include <asm/asm-offsets.h>
#include <asm/hardware/cache-l2x0.h>
#include "hardware.h"
 
/*
 * ==================== low level suspend ====================
 *
 * Better to follow below rules to use ARM registers:
 * r0: pm_info structure address;
 * r1 ~ r4: for saving pm_info members;
 * r5 ~ r10: free registers;
 * r11: io base address.
 *
 * suspend ocram space layout:
 * ======================== high address ======================
 *                              .
 *                              .
 *                              .
 *                              ^
 *                              ^
 *                              ^
 *                      imx6_suspend code
 *              PM_INFO structure(imx6_cpu_pm_info)
 * ======================== low address =======================
 */
 
/*
 * Below offsets are based on struct imx6_cpu_pm_info
 * which defined in arch/arm/mach-imx/pm-imx6q.c, this
 * structure contains necessary pm info for low level
 * suspend related code.
 */
#define PM_INFO_PBASE_OFFSET            0x0
#define PM_INFO_RESUME_ADDR_OFFSET        0x4
#define PM_INFO_DDR_TYPE_OFFSET            0x8
#define PM_INFO_PM_INFO_SIZE_OFFSET        0xC
#define PM_INFO_MX6Q_MMDC_P_OFFSET        0x10
#define PM_INFO_MX6Q_MMDC_V_OFFSET        0x14
#define PM_INFO_MX6Q_SRC_P_OFFSET        0x18
#define PM_INFO_MX6Q_SRC_V_OFFSET        0x1C
#define PM_INFO_MX6Q_IOMUXC_P_OFFSET        0x20
#define PM_INFO_MX6Q_IOMUXC_V_OFFSET        0x24
#define PM_INFO_MX6Q_CCM_P_OFFSET        0x28
#define PM_INFO_MX6Q_CCM_V_OFFSET        0x2C
#define PM_INFO_MX6Q_GPC_P_OFFSET        0x30
#define PM_INFO_MX6Q_GPC_V_OFFSET        0x34
#define PM_INFO_MX6Q_L2_P_OFFSET        0x38
#define PM_INFO_MX6Q_L2_V_OFFSET        0x3C
#define PM_INFO_MMDC_IO_NUM_OFFSET        0x40
#define PM_INFO_MMDC_IO_VAL_OFFSET        0x44
 
#define MX6Q_SRC_GPR1    0x20
#define MX6Q_SRC_GPR2    0x24
#define MX6Q_MMDC_MAPSR    0x404
#define MX6Q_MMDC_MPDGCTRL0    0x83c
#define MX6Q_GPC_IMR1    0x08
#define MX6Q_GPC_IMR2    0x0c
#define MX6Q_GPC_IMR3    0x10
#define MX6Q_GPC_IMR4    0x14
#define MX6Q_CCM_CCR    0x0
 
   .align 3
   .arm
 
   .macro  sync_l2_cache
 
   /* sync L2 cache to drain L2's buffers to DRAM. */
#ifdef CONFIG_CACHE_L2X0
   ldr    r11, [r0, #PM_INFO_MX6Q_L2_V_OFFSET]
   teq    r11, #0
   beq    6f
   mov    r6, #0x0
   str    r6, [r11, #L2X0_CACHE_SYNC]
1:
   ldr    r6, [r11, #L2X0_CACHE_SYNC]
   ands    r6, r6, #0x1
   bne    1b
6:
#endif
 
   .endm
 
   .macro    resume_mmdc
 
   /* restore MMDC IO */
   cmp    r5, #0x0
   ldreq    r11, [r0, #PM_INFO_MX6Q_IOMUXC_V_OFFSET]
   ldrne    r11, [r0, #PM_INFO_MX6Q_IOMUXC_P_OFFSET]
 
   ldr    r6, [r0, #PM_INFO_MMDC_IO_NUM_OFFSET]
   ldr    r7, =PM_INFO_MMDC_IO_VAL_OFFSET
   add    r7, r7, r0
1:
   ldr    r8, [r7], #0x4
   ldr    r9, [r7], #0x4
   str    r9, [r11, r8]
   subs    r6, r6, #0x1
   bne    1b
 
   cmp    r5, #0x0
   ldreq    r11, [r0, #PM_INFO_MX6Q_MMDC_V_OFFSET]
   ldrne    r11, [r0, #PM_INFO_MX6Q_MMDC_P_OFFSET]
 
   cmp    r3, #IMX_DDR_TYPE_LPDDR2
   bne    4f
 
   /* reset read FIFO, RST_RD_FIFO */
   ldr    r7, =MX6Q_MMDC_MPDGCTRL0
   ldr    r6, [r11, r7]
   orr     r6, r6, #(1 << 31)
   str    r6, [r11, r7]
2:
   ldr    r6, [r11, r7]
   ands    r6, r6, #(1 << 31)
   bne    2b
 
   /* reset FIFO a second time */
   ldr    r6, [r11, r7]
   orr     r6, r6, #(1 << 31)
   str    r6, [r11, r7]
3:
   ldr    r6, [r11, r7]
   ands    r6, r6, #(1 << 31)
   bne    3b
4:
   /* let DDR out of self-refresh */
   ldr    r7, [r11, #MX6Q_MMDC_MAPSR]
   bic    r7, r7, #(1 << 21)
   str    r7, [r11, #MX6Q_MMDC_MAPSR]
5:
   ldr    r7, [r11, #MX6Q_MMDC_MAPSR]
   ands    r7, r7, #(1 << 25)
   bne    5b
 
   /* enable DDR auto power saving */
   ldr    r7, [r11, #MX6Q_MMDC_MAPSR]
   bic    r7, r7, #0x1
   str    r7, [r11, #MX6Q_MMDC_MAPSR]
 
   .endm
 
ENTRY(imx6_suspend)
   ldr    r1, [r0, #PM_INFO_PBASE_OFFSET]
   ldr    r2, [r0, #PM_INFO_RESUME_ADDR_OFFSET]
   ldr    r3, [r0, #PM_INFO_DDR_TYPE_OFFSET]
   ldr    r4, [r0, #PM_INFO_PM_INFO_SIZE_OFFSET]
 
   /*
    * counting the resume address in iram
    * to set it in SRC register.
    */
   ldr    r6, =imx6_suspend
   ldr    r7, =resume
   sub    r7, r7, r6
   add    r8, r1, r4
   add    r9, r8, r7
 
   /*
    * make sure TLB contain the addr we want,
    * as we will access them after MMDC IO floated.
    */
 
   ldr    r11, [r0, #PM_INFO_MX6Q_CCM_V_OFFSET]
   ldr    r6, [r11, #0x0]
   ldr    r11, [r0, #PM_INFO_MX6Q_GPC_V_OFFSET]
   ldr    r6, [r11, #0x0]
   ldr    r11, [r0, #PM_INFO_MX6Q_IOMUXC_V_OFFSET]
   ldr    r6, [r11, #0x0]
 
   /* use r11 to store the IO address */
   ldr    r11, [r0, #PM_INFO_MX6Q_SRC_V_OFFSET]
   /* store physical resume addr and pm_info address. */
   str    r9, [r11, #MX6Q_SRC_GPR1]
   str    r1, [r11, #MX6Q_SRC_GPR2]
 
   /* need to sync L2 cache before DSM. */
   sync_l2_cache
 
   ldr    r11, [r0, #PM_INFO_MX6Q_MMDC_V_OFFSET]
   /*
    * put DDR explicitly into self-refresh and
    * disable automatic power savings.
    */
   ldr    r7, [r11, #MX6Q_MMDC_MAPSR]
   orr    r7, r7, #0x1
   str    r7, [r11, #MX6Q_MMDC_MAPSR]
 
   /* make the DDR explicitly enter self-refresh. */
   ldr    r7, [r11, #MX6Q_MMDC_MAPSR]
   orr    r7, r7, #(1 << 21)
   str    r7, [r11, #MX6Q_MMDC_MAPSR]
 
poll_dvfs_set:
   ldr    r7, [r11, #MX6Q_MMDC_MAPSR]
   ands    r7, r7, #(1 << 25)
   beq    poll_dvfs_set
 
   ldr    r11, [r0, #PM_INFO_MX6Q_IOMUXC_V_OFFSET]
   ldr    r6, =0x0
   ldr    r7, [r0, #PM_INFO_MMDC_IO_NUM_OFFSET]
   ldr    r8, =PM_INFO_MMDC_IO_VAL_OFFSET
   add    r8, r8, r0
   /* LPDDR2's last 3 IOs need special setting */
   cmp    r3, #IMX_DDR_TYPE_LPDDR2
   subeq    r7, r7, #0x3
set_mmdc_io_lpm:
   ldr    r9, [r8], #0x8
   str    r6, [r11, r9]
   subs    r7, r7, #0x1
   bne    set_mmdc_io_lpm
 
   cmp     r3, #IMX_DDR_TYPE_LPDDR2
   bne    set_mmdc_io_lpm_done
   ldr    r6, =0x1000
   ldr    r9, [r8], #0x8
   str    r6, [r11, r9]
   ldr    r9, [r8], #0x8
   str    r6, [r11, r9]
   ldr    r6, =0x80000
   ldr    r9, [r8]
   str    r6, [r11, r9]
set_mmdc_io_lpm_done:
 
   /*
    * mask all GPC interrupts before
    * enabling the RBC counters to
    * avoid the counter starting too
    * early if an interupt is already
    * pending.
    */
   ldr    r11, [r0, #PM_INFO_MX6Q_GPC_V_OFFSET]
   ldr    r6, [r11, #MX6Q_GPC_IMR1]
   ldr    r7, [r11, #MX6Q_GPC_IMR2]
   ldr    r8, [r11, #MX6Q_GPC_IMR3]
   ldr    r9, [r11, #MX6Q_GPC_IMR4]
 
   ldr    r10, =0xffffffff
   str    r10, [r11, #MX6Q_GPC_IMR1]
   str    r10, [r11, #MX6Q_GPC_IMR2]
   str    r10, [r11, #MX6Q_GPC_IMR3]
   str    r10, [r11, #MX6Q_GPC_IMR4]
 
   /*
    * enable the RBC bypass counter here
    * to hold off the interrupts. RBC counter
    * = 32 (1ms), Minimum RBC delay should be
    * 400us for the analog LDOs to power down.
    */
   ldr    r11, [r0, #PM_INFO_MX6Q_CCM_V_OFFSET]
   ldr    r10, [r11, #MX6Q_CCM_CCR]
   bic    r10, r10, #(0x3f << 21)
   orr    r10, r10, #(0x20 << 21)
   str    r10, [r11, #MX6Q_CCM_CCR]
 
   /* enable the counter. */
   ldr    r10, [r11, #MX6Q_CCM_CCR]
   orr    r10, r10, #(0x1 << 27)
   str    r10, [r11, #MX6Q_CCM_CCR]
 
   /* unmask all the GPC interrupts. */
   ldr    r11, [r0, #PM_INFO_MX6Q_GPC_V_OFFSET]
   str    r6, [r11, #MX6Q_GPC_IMR1]
   str    r7, [r11, #MX6Q_GPC_IMR2]
   str    r8, [r11, #MX6Q_GPC_IMR3]
   str    r9, [r11, #MX6Q_GPC_IMR4]
 
   /*
    * now delay for a short while (3usec)
    * ARM is at 1GHz at this point
    * so a short loop should be enough.
    * this delay is required to ensure that
    * the RBC counter can start counting in
    * case an interrupt is already pending
    * or in case an interrupt arrives just
    * as ARM is about to assert DSM_request.
    */
   ldr    r6, =2000
rbc_loop:
   subs    r6, r6, #0x1
   bne    rbc_loop
 
   /* Zzz, enter stop mode */
   wfi
   nop
   nop
   nop
   nop
 
   /*
    * run to here means there is pending
    * wakeup source, system should auto
    * resume, we need to restore MMDC IO first
    */
   mov    r5, #0x0
   resume_mmdc
 
   /* return to suspend finish */
   ret    lr
 
resume:
   /* invalidate L1 I-cache first */
   mov     r6, #0x0
   mcr     p15, 0, r6, c7, c5, 0
   mcr     p15, 0, r6, c7, c5, 6
   /* enable the Icache and branch prediction */
   mov     r6, #0x1800
   mcr     p15, 0, r6, c1, c0, 0
   isb
 
   /* get physical resume address from pm_info. */
   ldr    lr, [r0, #PM_INFO_RESUME_ADDR_OFFSET]
   /* clear core0's entry and parameter */
   ldr    r11, [r0, #PM_INFO_MX6Q_SRC_P_OFFSET]
   mov    r7, #0x0
   str    r7, [r11, #MX6Q_SRC_GPR1]
   str    r7, [r11, #MX6Q_SRC_GPR2]
 
   ldr    r3, [r0, #PM_INFO_DDR_TYPE_OFFSET]
   mov    r5, #0x1
   resume_mmdc
 
   ret    lr
ENDPROC(imx6_suspend)