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