| /* SPDX-License-Identifier: GPL-2.0-only */ | 
 | /* | 
 |  * Copyright (C) 2014-2015 Altera Corporation. All rights reserved. | 
 |  */ | 
 | #include <linux/linkage.h> | 
 | #include <asm/assembler.h> | 
 |  | 
 | #define MAX_LOOP_COUNT		1000 | 
 |  | 
 | /* Register offset */ | 
 | #define SDR_CTRLGRP_LOWPWREQ_ADDR       0x54 | 
 | #define SDR_CTRLGRP_LOWPWRACK_ADDR      0x58 | 
 |  | 
 | /* Bitfield positions */ | 
 | #define SELFRSHREQ_POS                  3 | 
 | #define SELFRSHREQ_MASK                 0x8 | 
 |  | 
 | #define SELFRFSHACK_POS                 1 | 
 | #define SELFRFSHACK_MASK                0x2 | 
 |  | 
 | 	/* | 
 | 	 * This code assumes that when the bootloader configured | 
 | 	 * the sdram controller for the DDR on the board it | 
 | 	 * configured the following fields depending on the DDR | 
 | 	 * vendor/configuration: | 
 | 	 * | 
 | 	 * sdr.ctrlcfg.lowpwreq.selfrfshmask | 
 | 	 * sdr.ctrlcfg.lowpwrtiming.clkdisablecycles | 
 | 	 * sdr.ctrlcfg.dramtiming4.selfrfshexit | 
 | 	 */ | 
 |  | 
 | 	.arch   armv7-a | 
 | 	.text | 
 | 	.align 3 | 
 |  | 
 | 	/* | 
 | 	 * socfpga_sdram_self_refresh | 
 | 	 * | 
 | 	 *  r0 : sdr_ctl_base_addr | 
 | 	 *  r1 : temp storage of return value | 
 | 	 *  r2 : temp storage of register values | 
 | 	 *  r3 : loop counter | 
 | 	 * | 
 | 	 *  return value: lower 16 bits: loop count going into self refresh | 
 | 	 *                upper 16 bits: loop count exiting self refresh | 
 | 	 */ | 
 | ENTRY(socfpga_sdram_self_refresh) | 
 | 	/* Enable dynamic clock gating in the Power Control Register. */ | 
 | 	mrc	p15, 0, r2, c15, c0, 0 | 
 | 	orr	r2, r2, #1 | 
 | 	mcr	p15, 0, r2, c15, c0, 0 | 
 |  | 
 | 	/* Enable self refresh: set sdr.ctrlgrp.lowpwreq.selfrshreq = 1 */ | 
 | 	ldr	r2, [r0, #SDR_CTRLGRP_LOWPWREQ_ADDR] | 
 | 	orr	r2, r2, #SELFRSHREQ_MASK | 
 | 	str	r2, [r0, #SDR_CTRLGRP_LOWPWREQ_ADDR] | 
 |  | 
 | 	/* Poll until sdr.ctrlgrp.lowpwrack.selfrfshack == 1 or hit max loops */ | 
 | 	mov	r3, #0 | 
 | while_ack_0: | 
 | 	ldr	r2, [r0, #SDR_CTRLGRP_LOWPWRACK_ADDR] | 
 | 	and	r2, r2, #SELFRFSHACK_MASK | 
 | 	cmp	r2, #SELFRFSHACK_MASK | 
 | 	beq	ack_1 | 
 |  | 
 | 	add	r3, #1 | 
 | 	cmp	r3, #MAX_LOOP_COUNT | 
 | 	bne	while_ack_0 | 
 |  | 
 | ack_1: | 
 | 	mov	r1, r3 | 
 |  | 
 | 	/* | 
 | 	 * Execute an ISB instruction to ensure that all of the | 
 | 	 * CP15 register changes have been committed. | 
 | 	 */ | 
 | 	isb | 
 |  | 
 | 	/* | 
 | 	 * Execute a barrier instruction to ensure that all cache, | 
 | 	 * TLB and branch predictor maintenance operations issued | 
 | 	 * by any CPU in the cluster have completed. | 
 | 	 */ | 
 | 	dsb | 
 | 	dmb | 
 |  | 
 | 	wfi | 
 |  | 
 | 	/* Disable self-refresh: set sdr.ctrlgrp.lowpwreq.selfrshreq = 0 */ | 
 | 	ldr	r2, [r0, #SDR_CTRLGRP_LOWPWREQ_ADDR] | 
 | 	bic	r2, r2, #SELFRSHREQ_MASK | 
 | 	str	r2, [r0, #SDR_CTRLGRP_LOWPWREQ_ADDR] | 
 |  | 
 | 	/* Poll until sdr.ctrlgrp.lowpwrack.selfrfshack == 0 or hit max loops */ | 
 | 	mov	r3, #0 | 
 | while_ack_1: | 
 | 	ldr	r2, [r0, #SDR_CTRLGRP_LOWPWRACK_ADDR] | 
 | 	and	r2, r2, #SELFRFSHACK_MASK | 
 | 	cmp	r2, #SELFRFSHACK_MASK | 
 | 	bne	ack_0 | 
 |  | 
 | 	add	r3, #1 | 
 | 	cmp	r3, #MAX_LOOP_COUNT | 
 | 	bne	while_ack_1 | 
 |  | 
 | ack_0: | 
 | 	/* | 
 | 	 * Prepare return value: | 
 | 	 * Shift loop count for exiting self refresh into upper 16 bits. | 
 | 	 * Leave loop count for requesting self refresh in lower 16 bits. | 
 | 	 */ | 
 | 	mov	r3, r3, lsl #16 | 
 | 	add	r1, r1, r3 | 
 |  | 
 | 	/* Disable dynamic clock gating in the Power Control Register. */ | 
 | 	mrc	p15, 0, r2, c15, c0, 0 | 
 | 	bic	r2, r2, #1 | 
 | 	mcr	p15, 0, r2, c15, c0, 0 | 
 |  | 
 | 	mov     r0, r1                  @ return value | 
 | 	bx	lr			@ return | 
 |  | 
 | ENDPROC(socfpga_sdram_self_refresh) | 
 | ENTRY(socfpga_sdram_self_refresh_sz) | 
 | 	.word	. - socfpga_sdram_self_refresh |