1/* SPDX-License-Identifier: GPL-2.0-only */
2/*
3 * Copyright (C) 2014-2015 Altera Corporation. All rights reserved.
4 */
5#include <linux/linkage.h>
6#include <asm/assembler.h>
7
8#define MAX_LOOP_COUNT 1000
9
10/* Register offset */
11#define SDR_CTRLGRP_LOWPWREQ_ADDR 0x54
12#define SDR_CTRLGRP_LOWPWRACK_ADDR 0x58
13
14/* Bitfield positions */
15#define SELFRSHREQ_POS 3
16#define SELFRSHREQ_MASK 0x8
17
18#define SELFRFSHACK_POS 1
19#define SELFRFSHACK_MASK 0x2
20
21 /*
22 * This code assumes that when the bootloader configured
23 * the sdram controller for the DDR on the board it
24 * configured the following fields depending on the DDR
25 * vendor/configuration:
26 *
27 * sdr.ctrlcfg.lowpwreq.selfrfshmask
28 * sdr.ctrlcfg.lowpwrtiming.clkdisablecycles
29 * sdr.ctrlcfg.dramtiming4.selfrfshexit
30 */
31
32 .arch armv7-a
33 .text
34 .align 3
35
36 /*
37 * socfpga_sdram_self_refresh
38 *
39 * r0 : sdr_ctl_base_addr
40 * r1 : temp storage of return value
41 * r2 : temp storage of register values
42 * r3 : loop counter
43 *
44 * return value: lower 16 bits: loop count going into self refresh
45 * upper 16 bits: loop count exiting self refresh
46 */
47ENTRY(socfpga_sdram_self_refresh)
48 /* Enable dynamic clock gating in the Power Control Register. */
49 mrc p15, 0, r2, c15, c0, 0
50 orr r2, r2, #1
51 mcr p15, 0, r2, c15, c0, 0
52
53 /* Enable self refresh: set sdr.ctrlgrp.lowpwreq.selfrshreq = 1 */
54 ldr r2, [r0, #SDR_CTRLGRP_LOWPWREQ_ADDR]
55 orr r2, r2, #SELFRSHREQ_MASK
56 str r2, [r0, #SDR_CTRLGRP_LOWPWREQ_ADDR]
57
58 /* Poll until sdr.ctrlgrp.lowpwrack.selfrfshack == 1 or hit max loops */
59 mov r3, #0
60while_ack_0:
61 ldr r2, [r0, #SDR_CTRLGRP_LOWPWRACK_ADDR]
62 and r2, r2, #SELFRFSHACK_MASK
63 cmp r2, #SELFRFSHACK_MASK
64 beq ack_1
65
66 add r3, #1
67 cmp r3, #MAX_LOOP_COUNT
68 bne while_ack_0
69
70ack_1:
71 mov r1, r3
72
73 /*
74 * Execute an ISB instruction to ensure that all of the
75 * CP15 register changes have been committed.
76 */
77 isb
78
79 /*
80 * Execute a barrier instruction to ensure that all cache,
81 * TLB and branch predictor maintenance operations issued
82 * by any CPU in the cluster have completed.
83 */
84 dsb
85 dmb
86
87 wfi
88
89 /* Disable self-refresh: set sdr.ctrlgrp.lowpwreq.selfrshreq = 0 */
90 ldr r2, [r0, #SDR_CTRLGRP_LOWPWREQ_ADDR]
91 bic r2, r2, #SELFRSHREQ_MASK
92 str r2, [r0, #SDR_CTRLGRP_LOWPWREQ_ADDR]
93
94 /* Poll until sdr.ctrlgrp.lowpwrack.selfrfshack == 0 or hit max loops */
95 mov r3, #0
96while_ack_1:
97 ldr r2, [r0, #SDR_CTRLGRP_LOWPWRACK_ADDR]
98 and r2, r2, #SELFRFSHACK_MASK
99 cmp r2, #SELFRFSHACK_MASK
100 bne ack_0
101
102 add r3, #1
103 cmp r3, #MAX_LOOP_COUNT
104 bne while_ack_1
105
106ack_0:
107 /*
108 * Prepare return value:
109 * Shift loop count for exiting self refresh into upper 16 bits.
110 * Leave loop count for requesting self refresh in lower 16 bits.
111 */
112 mov r3, r3, lsl #16
113 add r1, r1, r3
114
115 /* Disable dynamic clock gating in the Power Control Register. */
116 mrc p15, 0, r2, c15, c0, 0
117 bic r2, r2, #1
118 mcr p15, 0, r2, c15, c0, 0
119
120 mov r0, r1 @ return value
121 bx lr @ return
122
123ENDPROC(socfpga_sdram_self_refresh)
124ENTRY(socfpga_sdram_self_refresh_sz)
125 .word . - socfpga_sdram_self_refresh
126

source code of linux/arch/arm/mach-socfpga/self-refresh.S