summaryrefslogtreecommitdiff
path: root/cleopatre/linux-2.6.25.10-spc300/arch/arm/mach-spc300/sram.S
blob: 5fb6e31ba8d6771b30399c2ceff981ae31ce517b (plain)
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
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
/*
 * arch/arm/mach-spc300/sram.S
 *
 * Copyright (C) 2012 SPiDCOM Technologies
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License as
 * published by the Free Software Foundation; either version 2 of
 * the License, or (at your option) any later version.
 *
 * 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.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
 * MA 02111-1307 USA
 */
#include <asm/arch/hardware.h>
#include <asm/arch/irqs.h>
#include <asm/arch/ips/wdt.h>
#include <asm/arch/ips/sram.h>

.file       "sram.S"

.section .sram
.arm                        @ This is ARM code; performs the same action as .code 32
.align      2               @ Align to word boundary; "2" means the number of bits that must be zero
.globl      pm_process
.type       pm_process, %function

/* Counter to repeat group of NB_INSTR instructions to reach a defined amount
 * of time : (((time_to_wait * current clock) / 1000000) / cpu_cycles_nb) */
#define MIU_WAIT_TIME   99      /* (((1 * 492000000) / 1000000) / 5) */
#define PLL_WAIT_TIME   2880    /* (((600 * 24000000) / 1000000) / 5) */

/* Gmac IP and MII registers and bits offsets. */
#define MDIO_REQUEST_OFFSET           0x10
#define MDIO_REQUEST_BUSY_SHIFT       0
#define MDIO_REQUEST_REG_SHIFT        6
#define MDIO_REQUEST_PHY_ADDR_SHIFT   11
/*
 * MDIO_REQUEST_CMD sets 3 parameters of the MDIO request :
 *     - clock speed (to adapt to 12 Mhz)
 *     - read request
 *     - busy bit
*/
#define MDIO_REQUEST_CMD              0x25
#define MDIO_DATA_OFFSET              0x14
#define MII_STATUS_REG                1
#define MII_LINK_STATUS_SHIFT         2


/* This macro is used to clear bits in an IP register. */
.macro regbitclear, base, offset, shift, mask, tmp1, tmp2
    ldr \tmp2, [\base, #\offset]
    mov \tmp1, #\mask
    mov \tmp1, \tmp1, lsl #\shift
    bic \tmp1, \tmp2, \tmp1
    str \tmp1, [\base, #\offset]
.endm

/* This macro is used to set bits in an IP register. */
.macro regbitset, base, offset, shift, mask, tmp1, tmp2
    ldr \tmp2, [\base, #\offset]
    mov \tmp1, #\mask
    mov \tmp1, \tmp1, lsl #\shift
    orr \tmp1, \tmp2, \tmp1
    str \tmp1, [\base, #\offset]
.endm

.macro waitstatus, base, offset, statval, tmp
1:
    ldr     \tmp, [\base, #\offset]
    cmp     \tmp, #\statval
    bne     1b
.endm

pm_process:
    /* Save registers that we will use. */
    push {r1, r2, r3}

    /* Flush TLB. */
    mov r0, #0
    mcr p15, 0, r0, c8, c7, 0

    /* Dummy reads to fill TLB with the pages we will use after SDRAM
     * deactivation. This is needed to ensure that there will not be any
     * access to SDRAM once it is in self-refresh mode. */
    ldr r0, =IO_ADDRESS(ARM_TIMER1_BASE)
    ldr r1, [r0, #TIMEREOIOFF_1]
    ldr r0, =IO_ADDRESS(MARIA_REGBANK_BASE)
    ldr r1, [r0]
    ldr r0, =IO_ADDRESS(ARM_WDT_BASE)
    ldr r1, [r0]
    ldr r0, =IO_ADDRESS(ARM_ICTL_BASE)
    ldr r1, [r0]
    ldr r0, =SRAM_DATA_BASE_VA + SRAM_DATA_GMAC_VA_OFFSET
    ldr r1, [r0]
    ldr r0, [r1]
    ldr r0, =SRAM_DATA_BASE_VA + SRAM_DATA_SPLL_ADDR_OFFSET
    ldr r1, [r0]
    ldr r0, [r1]
    ldr r0, =SRAM_DATA_BASE_VA + SRAM_DATA_DSPPLL_ADDR_OFFSET
    ldr r1, [r0]
    ldr r0, [r1]

    /* Put MIU into self-refresh mode. */
    ldr r0, =IO_ADDRESS(MIU_BASE)
    mov r1, #0x0
    str r1, [r0, #0x1d0]
    ldr r1, =0xfffe
    str r1, [r0, #0x8c]
    ldr r1, =0xffff
    str r1, [r0, #0xcc]
    str r1, [r0, #0x10c]
    str r1, [r0, #0x14c]
    ldr r2, =MIU_WAIT_TIME
1:
    sub r2, r2, #1
    cmp r2, #0
    bne 1b
    ldr r1, =0x400
    str r1, [r0, #0x30]
    ldr r1, =0x2f
    str r1, [r0, #0x0]
    ldr r1, =0x1420
    str r1, [r0, #0xc]
    ldr r1, =0x52e
    str r1, [r0, #0x0]
    ldr r1, =0x2e
    str r1, [r0, #0x0]
    ldr r1, =0x32e
    str r1, [r0, #0x0]
    ldr r1, =0x2e
    str r1, [r0, #0x0]
    ldr r2, =MIU_WAIT_TIME
1:
    sub r2, r2, #1
    cmp r2, #0
    bne 1b
    ldr r1, =0xffff
    str r1, [r0, #0x8c]
    ldr r1, =0x202e
    str r1, [r0, #0x0]
    ldr r1, =0xe2a2
    str r1, [r0, #0x4]

    /*
     * Bypass PLLs. We keep Peripheral PLL alive for ETH PHY and GMAC.
     */
    ldr r0, =IO_ADDRESS(MARIA_REGBANK_BASE)
    ldr r1, =PLL_CMD_BYPASS
    /* System PLL */
    str r1, [r0, #RB_SPLL_BYPASS_OFFSET]
    waitstatus r0, RB_SPLL_BYPASS_STAT_OFFSET, PLL_IS_BYPASS, r2
    /* DSP PLL */
    str r1, [r0, #RB_DPLL_BYPASS_OFFSET]
    waitstatus r0, RB_DPLL_BYPASS_STAT_OFFSET, PLL_IS_BYPASS, r2

    /*
     * Switch OFF PLLs.
     */
    /* System PLL */
    ldr r1, =SRAM_DATA_BASE_VA + SRAM_DATA_SPLL_ADDR_OFFSET
    ldr r0, [r1]
    regbitset r0, SPLL_CTRL_OFFSET, MSEPLL_SPPLL_CTRL_PD_SHIFT, \
              MSEPLL_SPPLL_CTRL_PD_MASK, r1, r2
    /* DSP PLL */
    ldr r1, =SRAM_DATA_BASE_VA + SRAM_DATA_DSPPLL_ADDR_OFFSET
    ldr r0, [r1]
    regbitset r0, DSPPLL_CONF_OFFSET, MSEAFE_DPLL_CONF_PD_SHIFT, \
              MSEAFE_DPLL_CONF_PD_MASK, r1, r2
    regbitset r0, DSPPLL_CTRL_OFFSET, MSEAFE_DPLL_CTRL_PD_CLK_SHIFT, \
              MSEAFE_DPLL_CTRL_PD_CLK_MASK, r1, r2
    regbitset r0, DSPPLL_CTRL_OFFSET, MSEAFE_DPLL_CTRL_PD_DAC_CLK_OUT_SHIFT, \
              MSEAFE_DPLL_CTRL_PD_DAC_CLK_OUT_MASK, r1, r2
    regbitset r0, DSPPLL_CTRL_OFFSET, MSEAFE_DPLL_CTRL_PD_DAC_CLK_SHIFT, \
              MSEAFE_DPLL_CTRL_PD_DAC_CLK_MASK, r1, r2
    regbitset r0, DSPPLL_CTRL_OFFSET, MSEAFE_DPLL_CTRL_PD_ADC_CLK_SHIFT, \
              MSEAFE_DPLL_CTRL_PD_ADC_CLK_MASK, r1, r2

    /* Set WDT timer to 1.4s (at 12MHz) */
    mov r1, #8
    ldr r0, =IO_ADDRESS(ARM_WDT_BASE) + WDTTimeoutRangeReg_Offset
    /* Save old value of the timeout */
    ldr r3, [r0]
    str r1, [r0]

2:
    /* Refresh WDT. */
    ldr r0, =IO_ADDRESS(ARM_WDT_BASE) + WDTCounterResetReg_Offset
    mov r1, #WDT_CR_VAL
    str r1, [r0]

    /* WDT IP runs twice slower than ARM, and need a few cycles to
     * clear the IRQ line in GIC, so we need to wait to make sure that it
     * will be cleared before entering wait for interrupt mode. */
    nop
    nop
    nop
    nop

    /* Check link status. We need to read the status register twice on some
     * PHYs. */
    ldr r0, =SRAM_DATA_BASE_VA + SRAM_DATA_GMAC_VA_OFFSET
    ldr r1, [r0]
    add r0, r1, #MDIO_REQUEST_OFFSET
    ldr r2, =SRAM_DATA_BASE_VA + SRAM_DATA_PHY_ADDR_OFFSET
    ldr r1, [r2]
    lsl r1, r1, #MDIO_REQUEST_PHY_ADDR_SHIFT
    add r1, r1, #(MII_STATUS_REG << MDIO_REQUEST_REG_SHIFT \
                  + MDIO_REQUEST_CMD)
    str r1, [r0]

1:
    ldr r1, [r0]
    tst r1, #(1 << MDIO_REQUEST_BUSY_SHIFT)
    bne 1b

    ldr r1, [r2]
    lsl r1, r1, #MDIO_REQUEST_PHY_ADDR_SHIFT
    add r1, r1, #(MII_STATUS_REG << MDIO_REQUEST_REG_SHIFT \
                  + MDIO_REQUEST_CMD)
    str r1, [r0]

1:
    ldr r1, [r0]
    tst r1, #(1 << MDIO_REQUEST_BUSY_SHIFT)
    bne 1b

    ldr r0, =SRAM_DATA_BASE_VA + SRAM_DATA_GMAC_VA_OFFSET
    ldr r1, [r0]
    add r0, r1, #MDIO_DATA_OFFSET
    ldr r1, [r0]

    tst r1, #(1 << MII_LINK_STATUS_SHIFT)
    bne exit_sram

wait_for_irq:
    /* Put ARM in wait for interrupt mode. */
    mov r0, #0
    mcr p15, 0, r0, c7, c0, 4

    /* Test if we are awaken by timer tick. If so, we need to inc the
       counter in sram */
    ldr r0, =IO_ADDRESS(ARM_ICTL_BASE) + IRQ_FINALSTATUS_OFFSET_L
    ldr r1, [r0]
    tst r1, #INTMASK_TIMER_1
    beq 1f
    /* Inc tick count */
    ldr r0, =SRAM_DATA_BASE_VA + SRAM_DATA_TICK_CNT_OFFSET
    ldr r1, [r0]
    add r1, r1, #1
    str r1, [r0]
    /* Clear IRQ */
    ldr r0, =IO_ADDRESS(ARM_TIMER1_BASE)
    ldr r1, [r0, #TIMEREOIOFF_1]
    nop
    nop
    /* Go back in wait for IRQ */
    b wait_for_irq

1:
    /* Test if we are awaken by WDT. If so, we need to refresh it
       and go back to wait for interrupt mode. */
    ldr r0, =IO_ADDRESS(ARM_ICTL_BASE) + IRQ_FINALSTATUS_OFFSET_L
    ldr r1, [r0]
    tst r1, #INTMASK_WDT
    bne 2b

exit_sram:
    /* Restore WDT timeout value */
    ldr r0, =IO_ADDRESS(ARM_WDT_BASE) + WDTTimeoutRangeReg_Offset
    /* Save old value of the timeout */
    str r3, [r0]

    /*
     * Switch ON PLLs and wait until they stabilize
     */
    /* System PLL */
    ldr r1, =SRAM_DATA_BASE_VA + SRAM_DATA_SPLL_ADDR_OFFSET
    ldr r0, [r1]
    regbitclear r0, SPLL_CTRL_OFFSET, MSEPLL_SPPLL_CTRL_PD_SHIFT, \
                MSEPLL_SPPLL_CTRL_PD_MASK, r1, r2
    /* DSP PLL */
    ldr r1, =SRAM_DATA_BASE_VA + SRAM_DATA_DSPPLL_ADDR_OFFSET
    ldr r0, [r1]
    regbitclear r0, DSPPLL_CTRL_OFFSET, MSEAFE_DPLL_CTRL_PD_ADC_CLK_SHIFT, \
                MSEAFE_DPLL_CTRL_PD_ADC_CLK_MASK, r1, r2
    regbitclear r0, DSPPLL_CTRL_OFFSET, MSEAFE_DPLL_CTRL_PD_DAC_CLK_SHIFT, \
                MSEAFE_DPLL_CTRL_PD_DAC_CLK_MASK, r1, r2
    regbitclear r0, DSPPLL_CTRL_OFFSET, MSEAFE_DPLL_CTRL_PD_DAC_CLK_OUT_SHIFT, \
                MSEAFE_DPLL_CTRL_PD_DAC_CLK_OUT_MASK, r1, r2
    regbitclear r0, DSPPLL_CTRL_OFFSET, MSEAFE_DPLL_CTRL_PD_CLK_SHIFT, \
                MSEAFE_DPLL_CTRL_PD_CLK_MASK, r1, r2
    regbitclear r0, DSPPLL_CONF_OFFSET, MSEAFE_DPLL_CONF_PD_SHIFT, \
                MSEAFE_DPLL_CONF_PD_MASK, r1, r2

    /* Active wait */
    ldr r2, =PLL_WAIT_TIME
1:
    sub r2, r2, #1
    cmp r2, #0
    bne 1b

    /*
     * Switch to PLL clock
     */
    ldr r0, =IO_ADDRESS(MARIA_REGBANK_BASE)
    ldr r1, =PLL_CMD_PLL

    /* System PLL */
    str r1, [r0, #RB_SPLL_BYPASS_OFFSET]
    waitstatus r0, RB_SPLL_BYPASS_STAT_OFFSET, PLL_IS_PLL, r2

    /* DSP PLL */
    str r1, [r0, #RB_DPLL_BYPASS_OFFSET]
    waitstatus r0, RB_DPLL_BYPASS_STAT_OFFSET, PLL_IS_PLL, r2

    /* Put MIU back into auto-refresh mode. */
    ldr r0, =IO_ADDRESS(MIU_BASE)
    ldr r1, =0x2a2
    str r1, [r0, #0x4]
    ldr r1, =0x2e
    str r1, [r0, #0x0]
    ldr r1, =0xfffe
    str r1, [r0, #0x8c]
    ldr r2, =MIU_WAIT_TIME
1:
    sub r2, r2, #1
    cmp r2, #0
    bne 1b
    ldr r1, =0x32e
    str r1, [r0, #0x0]
    ldr r1, =0x2e
    str r1, [r0, #0x0]
    ldr r1, =0xe
    str r1, [r0, #0x0]
    ldr r2, =MIU_WAIT_TIME
1:
    sub r2, r2, #1
    cmp r2, #0
    bne 1b
    ldr r1, =0x420
    str r1, [r0, #0xc]
    ldr r1, =0x0
    str r1, [r0, #0x8c]
    str r1, [r0, #0xcc]
    str r1, [r0, #0x10c]
    str r1, [r0, #0x14c]

    /* Restore registers. */
    pop {r1, r2, r3}

    /* Back to my caller. */
    mov pc, lr

.size pm_process, . - pm_process