blob: f2d7f883e33deb896d577d43d6af9989617c6caf [file] [log] [blame]
Alan Tull44fd8c72015-06-05 08:24:52 -05001/*
2 * Copyright (C) 2014-2015 Altera Corporation. All rights reserved.
3 *
4 * This program is free software; you can redistribute it and/or modify it
5 * under the terms and conditions of the GNU General Public License,
6 * version 2, as published by the Free Software Foundation.
7 *
8 * This program is distributed in the hope it will be useful, but WITHOUT
9 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
10 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
11 * more details.
12 *
13 * You should have received a copy of the GNU General Public License along with
14 * this program. If not, see <http://www.gnu.org/licenses/>.
15 */
16#include <linux/linkage.h>
17#include <asm/assembler.h>
18
19#define MAX_LOOP_COUNT 1000
20
21/* Register offset */
22#define SDR_CTRLGRP_LOWPWREQ_ADDR 0x54
23#define SDR_CTRLGRP_LOWPWRACK_ADDR 0x58
24
25/* Bitfield positions */
26#define SELFRSHREQ_POS 3
27#define SELFRSHREQ_MASK 0x8
28
29#define SELFRFSHACK_POS 1
30#define SELFRFSHACK_MASK 0x2
31
32 /*
33 * This code assumes that when the bootloader configured
34 * the sdram controller for the DDR on the board it
35 * configured the following fields depending on the DDR
36 * vendor/configuration:
37 *
38 * sdr.ctrlcfg.lowpwreq.selfrfshmask
39 * sdr.ctrlcfg.lowpwrtiming.clkdisablecycles
40 * sdr.ctrlcfg.dramtiming4.selfrfshexit
41 */
42
43 .arch armv7-a
44 .text
45 .align 3
46
47 /*
48 * socfpga_sdram_self_refresh
49 *
50 * r0 : sdr_ctl_base_addr
51 * r1 : temp storage of return value
52 * r2 : temp storage of register values
53 * r3 : loop counter
54 *
55 * return value: lower 16 bits: loop count going into self refresh
56 * upper 16 bits: loop count exiting self refresh
57 */
58ENTRY(socfpga_sdram_self_refresh)
59 /* Enable dynamic clock gating in the Power Control Register. */
60 mrc p15, 0, r2, c15, c0, 0
61 orr r2, r2, #1
62 mcr p15, 0, r2, c15, c0, 0
63
64 /* Enable self refresh: set sdr.ctrlgrp.lowpwreq.selfrshreq = 1 */
65 ldr r2, [r0, #SDR_CTRLGRP_LOWPWREQ_ADDR]
66 orr r2, r2, #SELFRSHREQ_MASK
67 str r2, [r0, #SDR_CTRLGRP_LOWPWREQ_ADDR]
68
69 /* Poll until sdr.ctrlgrp.lowpwrack.selfrfshack == 1 or hit max loops */
70 mov r3, #0
71while_ack_0:
72 ldr r2, [r0, #SDR_CTRLGRP_LOWPWRACK_ADDR]
73 and r2, r2, #SELFRFSHACK_MASK
74 cmp r2, #SELFRFSHACK_MASK
75 beq ack_1
76
77 add r3, #1
78 cmp r3, #MAX_LOOP_COUNT
79 bne while_ack_0
80
81ack_1:
82 mov r1, r3
83
84 /*
85 * Execute an ISB instruction to ensure that all of the
86 * CP15 register changes have been committed.
87 */
88 isb
89
90 /*
91 * Execute a barrier instruction to ensure that all cache,
92 * TLB and branch predictor maintenance operations issued
93 * by any CPU in the cluster have completed.
94 */
95 dsb
96 dmb
97
98 wfi
99
100 /* Disable self-refresh: set sdr.ctrlgrp.lowpwreq.selfrshreq = 0 */
101 ldr r2, [r0, #SDR_CTRLGRP_LOWPWREQ_ADDR]
102 bic r2, r2, #SELFRSHREQ_MASK
103 str r2, [r0, #SDR_CTRLGRP_LOWPWREQ_ADDR]
104
105 /* Poll until sdr.ctrlgrp.lowpwrack.selfrfshack == 0 or hit max loops */
106 mov r3, #0
107while_ack_1:
108 ldr r2, [r0, #SDR_CTRLGRP_LOWPWRACK_ADDR]
109 and r2, r2, #SELFRFSHACK_MASK
110 cmp r2, #SELFRFSHACK_MASK
111 bne ack_0
112
113 add r3, #1
114 cmp r3, #MAX_LOOP_COUNT
115 bne while_ack_1
116
117ack_0:
118 /*
119 * Prepare return value:
120 * Shift loop count for exiting self refresh into upper 16 bits.
121 * Leave loop count for requesting self refresh in lower 16 bits.
122 */
123 mov r3, r3, lsl #16
124 add r1, r1, r3
125
126 /* Disable dynamic clock gating in the Power Control Register. */
127 mrc p15, 0, r2, c15, c0, 0
128 bic r2, r2, #1
129 mcr p15, 0, r2, c15, c0, 0
130
131 mov r0, r1 @ return value
132 bx lr @ return
133
134ENDPROC(socfpga_sdram_self_refresh)
135ENTRY(socfpga_sdram_self_refresh_sz)
136 .word . - socfpga_sdram_self_refresh