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