xref: /linux/arch/arm/mach-socfpga/self-refresh.S (revision 75bf465f0bc33e9b776a46d6a1b9b990f5fb7c37)
1*9952f691SThomas Gleixner/* SPDX-License-Identifier: GPL-2.0-only */
244fd8c7dSAlan Tull/*
344fd8c7dSAlan Tull * Copyright (C) 2014-2015 Altera Corporation. All rights reserved.
444fd8c7dSAlan Tull */
544fd8c7dSAlan Tull#include <linux/linkage.h>
644fd8c7dSAlan Tull#include <asm/assembler.h>
744fd8c7dSAlan Tull
844fd8c7dSAlan Tull#define MAX_LOOP_COUNT		1000
944fd8c7dSAlan Tull
1044fd8c7dSAlan Tull/* Register offset */
1144fd8c7dSAlan Tull#define SDR_CTRLGRP_LOWPWREQ_ADDR       0x54
1244fd8c7dSAlan Tull#define SDR_CTRLGRP_LOWPWRACK_ADDR      0x58
1344fd8c7dSAlan Tull
1444fd8c7dSAlan Tull/* Bitfield positions */
1544fd8c7dSAlan Tull#define SELFRSHREQ_POS                  3
1644fd8c7dSAlan Tull#define SELFRSHREQ_MASK                 0x8
1744fd8c7dSAlan Tull
1844fd8c7dSAlan Tull#define SELFRFSHACK_POS                 1
1944fd8c7dSAlan Tull#define SELFRFSHACK_MASK                0x2
2044fd8c7dSAlan Tull
2144fd8c7dSAlan Tull	/*
2244fd8c7dSAlan Tull	 * This code assumes that when the bootloader configured
2344fd8c7dSAlan Tull	 * the sdram controller for the DDR on the board it
2444fd8c7dSAlan Tull	 * configured the following fields depending on the DDR
2544fd8c7dSAlan Tull	 * vendor/configuration:
2644fd8c7dSAlan Tull	 *
2744fd8c7dSAlan Tull	 * sdr.ctrlcfg.lowpwreq.selfrfshmask
2844fd8c7dSAlan Tull	 * sdr.ctrlcfg.lowpwrtiming.clkdisablecycles
2944fd8c7dSAlan Tull	 * sdr.ctrlcfg.dramtiming4.selfrfshexit
3044fd8c7dSAlan Tull	 */
3144fd8c7dSAlan Tull
3244fd8c7dSAlan Tull	.arch   armv7-a
3344fd8c7dSAlan Tull	.text
3444fd8c7dSAlan Tull	.align 3
3544fd8c7dSAlan Tull
3644fd8c7dSAlan Tull	/*
3744fd8c7dSAlan Tull	 * socfpga_sdram_self_refresh
3844fd8c7dSAlan Tull	 *
3944fd8c7dSAlan Tull	 *  r0 : sdr_ctl_base_addr
4044fd8c7dSAlan Tull	 *  r1 : temp storage of return value
4144fd8c7dSAlan Tull	 *  r2 : temp storage of register values
4244fd8c7dSAlan Tull	 *  r3 : loop counter
4344fd8c7dSAlan Tull	 *
4444fd8c7dSAlan Tull	 *  return value: lower 16 bits: loop count going into self refresh
4544fd8c7dSAlan Tull	 *                upper 16 bits: loop count exiting self refresh
4644fd8c7dSAlan Tull	 */
4744fd8c7dSAlan TullENTRY(socfpga_sdram_self_refresh)
4844fd8c7dSAlan Tull	/* Enable dynamic clock gating in the Power Control Register. */
4944fd8c7dSAlan Tull	mrc	p15, 0, r2, c15, c0, 0
5044fd8c7dSAlan Tull	orr	r2, r2, #1
5144fd8c7dSAlan Tull	mcr	p15, 0, r2, c15, c0, 0
5244fd8c7dSAlan Tull
5344fd8c7dSAlan Tull	/* Enable self refresh: set sdr.ctrlgrp.lowpwreq.selfrshreq = 1 */
5444fd8c7dSAlan Tull	ldr	r2, [r0, #SDR_CTRLGRP_LOWPWREQ_ADDR]
5544fd8c7dSAlan Tull	orr	r2, r2, #SELFRSHREQ_MASK
5644fd8c7dSAlan Tull	str	r2, [r0, #SDR_CTRLGRP_LOWPWREQ_ADDR]
5744fd8c7dSAlan Tull
5844fd8c7dSAlan Tull	/* Poll until sdr.ctrlgrp.lowpwrack.selfrfshack == 1 or hit max loops */
5944fd8c7dSAlan Tull	mov	r3, #0
6044fd8c7dSAlan Tullwhile_ack_0:
6144fd8c7dSAlan Tull	ldr	r2, [r0, #SDR_CTRLGRP_LOWPWRACK_ADDR]
6244fd8c7dSAlan Tull	and	r2, r2, #SELFRFSHACK_MASK
6344fd8c7dSAlan Tull	cmp	r2, #SELFRFSHACK_MASK
6444fd8c7dSAlan Tull	beq	ack_1
6544fd8c7dSAlan Tull
6644fd8c7dSAlan Tull	add	r3, #1
6744fd8c7dSAlan Tull	cmp	r3, #MAX_LOOP_COUNT
6844fd8c7dSAlan Tull	bne	while_ack_0
6944fd8c7dSAlan Tull
7044fd8c7dSAlan Tullack_1:
7144fd8c7dSAlan Tull	mov	r1, r3
7244fd8c7dSAlan Tull
7344fd8c7dSAlan Tull	/*
7444fd8c7dSAlan Tull	 * Execute an ISB instruction to ensure that all of the
7544fd8c7dSAlan Tull	 * CP15 register changes have been committed.
7644fd8c7dSAlan Tull	 */
7744fd8c7dSAlan Tull	isb
7844fd8c7dSAlan Tull
7944fd8c7dSAlan Tull	/*
8044fd8c7dSAlan Tull	 * Execute a barrier instruction to ensure that all cache,
8144fd8c7dSAlan Tull	 * TLB and branch predictor maintenance operations issued
8244fd8c7dSAlan Tull	 * by any CPU in the cluster have completed.
8344fd8c7dSAlan Tull	 */
8444fd8c7dSAlan Tull	dsb
8544fd8c7dSAlan Tull	dmb
8644fd8c7dSAlan Tull
8744fd8c7dSAlan Tull	wfi
8844fd8c7dSAlan Tull
8944fd8c7dSAlan Tull	/* Disable self-refresh: set sdr.ctrlgrp.lowpwreq.selfrshreq = 0 */
9044fd8c7dSAlan Tull	ldr	r2, [r0, #SDR_CTRLGRP_LOWPWREQ_ADDR]
9144fd8c7dSAlan Tull	bic	r2, r2, #SELFRSHREQ_MASK
9244fd8c7dSAlan Tull	str	r2, [r0, #SDR_CTRLGRP_LOWPWREQ_ADDR]
9344fd8c7dSAlan Tull
9444fd8c7dSAlan Tull	/* Poll until sdr.ctrlgrp.lowpwrack.selfrfshack == 0 or hit max loops */
9544fd8c7dSAlan Tull	mov	r3, #0
9644fd8c7dSAlan Tullwhile_ack_1:
9744fd8c7dSAlan Tull	ldr	r2, [r0, #SDR_CTRLGRP_LOWPWRACK_ADDR]
9844fd8c7dSAlan Tull	and	r2, r2, #SELFRFSHACK_MASK
9944fd8c7dSAlan Tull	cmp	r2, #SELFRFSHACK_MASK
10044fd8c7dSAlan Tull	bne	ack_0
10144fd8c7dSAlan Tull
10244fd8c7dSAlan Tull	add	r3, #1
10344fd8c7dSAlan Tull	cmp	r3, #MAX_LOOP_COUNT
10444fd8c7dSAlan Tull	bne	while_ack_1
10544fd8c7dSAlan Tull
10644fd8c7dSAlan Tullack_0:
10744fd8c7dSAlan Tull	/*
10844fd8c7dSAlan Tull	 * Prepare return value:
10944fd8c7dSAlan Tull	 * Shift loop count for exiting self refresh into upper 16 bits.
11044fd8c7dSAlan Tull	 * Leave loop count for requesting self refresh in lower 16 bits.
11144fd8c7dSAlan Tull	 */
11244fd8c7dSAlan Tull	mov	r3, r3, lsl #16
11344fd8c7dSAlan Tull	add	r1, r1, r3
11444fd8c7dSAlan Tull
11544fd8c7dSAlan Tull	/* Disable dynamic clock gating in the Power Control Register. */
11644fd8c7dSAlan Tull	mrc	p15, 0, r2, c15, c0, 0
11744fd8c7dSAlan Tull	bic	r2, r2, #1
11844fd8c7dSAlan Tull	mcr	p15, 0, r2, c15, c0, 0
11944fd8c7dSAlan Tull
12044fd8c7dSAlan Tull	mov     r0, r1                  @ return value
12144fd8c7dSAlan Tull	bx	lr			@ return
12244fd8c7dSAlan Tull
12344fd8c7dSAlan TullENDPROC(socfpga_sdram_self_refresh)
12444fd8c7dSAlan TullENTRY(socfpga_sdram_self_refresh_sz)
12544fd8c7dSAlan Tull	.word	. - socfpga_sdram_self_refresh
126