//
//  Copyright (c) 2012, ARM Limited. All rights reserved.
//
//  This program and the accompanying materials
//  are licensed and made available under the terms and conditions of the BSD License
//  which accompanies this distribution.  The full text of the license may be found at
//  http://opensource.org/licenses/bsd-license.php
//
//  THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
//  WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
//
//

#include <AsmMacroIoLib.h>
#include <Base.h>
#include <AutoGen.h>
#include <ArmDdr3PhyHw.h>

  INCLUDE AsmMacroIoLib.inc

  EXPORT  ArmVeCtxA15A7_DmcInit
    
// Register definitions for the DMC400

#define  DMC400_STATUS                            0x00           // Status register
#define  DMC400_CONFIG                            0x04           // Config register
#define  DMC400_COMMAND                           0x08           // Command register
#define  DMC400_ADD_CTRL                          0x10           // Address control regsiter
#define  DMC400_DEC_CTRL                          0x14           // Decode control register
#define  DMC400_FORMAT_CTRL                       0x18           // Format control register
#define  DMC400_POWER_CTRL                        0x20           // Low Power control register

#define  DMC400_DIRECT_CMD                        0x108          // Direct command register
#define  DMC400_REFRESH_CTRL                      0x120          // Refresh control regsiter
#define  DMC400_ECC_CTRL                          0x148          // EEC control register

#define  DMC400_T_REFI                            0x200          // Refresh interval timing
#define  DMC400_T_RFC                             0x204          // Delay after AUTOREFRESH command
#define  DMC400_T_MRR                             0x208          // Delay after Mode register read command
#define  DMC400_T_MRW                             0x20C          // Delay after Mode register write command
#define  DMC400_T_RCD                             0x218          // Delay after ACTIVATE command before read or write
#define  DMC400_T_RAS                             0x21C          // Delay after ACTIVATE command before PRECHARGE
#define  DMC400_T_RP                              0x220          // Delay after PRECHARGE
#define  DMC400_T_RPALL                           0x224          // Delay after PRECHARGEALL
#define  DMC400_T_RRD                             0x228          // Delay after ACTIVATE command before another ACTIVATE
#define  DMC400_T_FAW                             0x22C          // Configures the tFAW timing parameter
#define  DMC400_RD_LAT                            0x230          // Configures the read latency timing parameter
#define  DMC400_T_RTR                             0x234          // Configures the read to read timing parameter
#define  DMC400_T_RTW                             0x238          // Configures the read to write timing parameter
#define  DMC400_T_RTP                             0x23C          // Configures the read to precharge timing parameter
#define  DMC400_WR_LAT                            0x240          // Configures the write latency timing parameter
#define  DMC400_T_WR                              0x244          // Configures the write recovery timing parameter
#define  DMC400_T_WTR                             0x248          // Configures the write to read timing parameter
#define  DMC400_T_WTW                             0x24C          // Configures the write to write timing parameter
#define  DMC400_T_ECKD                            0x250          // Configures the enter DRAM clock disable timing parameter
#define  DMC400_T_XCKD                            0x254          // Configures the exit DRAM clock disable timing parameter
#define  DMC400_T_EP                              0x258          // Configures the enter power down timing parameter
#define  DMC400_T_XP                              0x25C          // Configures the exit power down timing parameter
#define  DMC400_T_ESR                             0x260          // Configures the enter self-refresh timing parameter
#define  DMC400_T_XSR                             0x264          // Configures the exit self-refresh timing parameter
#define  DMC400_T_SRCKD                           0x268          // Configures the self-refresh to DRAM clock disable timing parameter
#define  DMC400_T_CKSRD                           0x26C          // Configures the DRAM clock enable to exist self-refresh timing parameter
#define  DMC400_T_RDDATA_EN                       0x300          // Configures the time between a READ command and assertion of dfi_read_en signal
#define  DMC400_T_PHY_WRLAT                       0x304          // Configures the time between a WRITE command and assertion of dfi_wrdata_en and dfi_wrdata signals
#define  DMC400_RDLVL_CTRL                        0x308          // Determines the DMC behavior during read training operations

#define  DMC_PERIPH_ID_0                          0xFE0          // Peripheral ID register
#define  DMC_PERIPH_ID_1                          0xFE4          // Peripheral ID register


  PRESERVE8
  AREA    ArmDdr3PhyHelper, CODE, READONLY

//VOID
//ArmVeCtxA15A7_DmcInit (
//  IN UINTN                 DmcBaseAddress, // r0 = Base Address of DMC controller
//  IN UINTN                 DummyVariable,  // r1 = Not Used
//  IN UINTN                 PhyBaseAddress  // r2 = Base Address of PHY DMC controller
//  )
//
// Initializes dynamic memory controller for the Versatile Express TC2 board
//

ArmVeCtxA15A7_DmcInit
        
    push    {r3-r7}
        
// Save the DMC base address to r6, r7
    MOV     r6, r0       //base address of DMC
    MOV     r7, r2       //base address of PHY
    
//
// Configure ARM 40nm CLN40G DDR2/3 PHY registers
//
//
// r2 = PHY base address
// r3 = register value
//

//
// Set DFI input clock range
//
// b000 = <= 200MHz
// b011 = <= 334 - 400 MHz
//
    LDR     r3, =PHY_PTM_DFI_CLK_RANGE_VAL                 // = 3
    STR     r3, [r2, #PHY_PTM_DFI_CLK_RANGE_REG]           // = 0xE00
    
//
// Set PLL settings
//
    LDR     r3, =PHY_PTM_PLL_RANGE_200_400MHz              // Refclock range = 0
    STR     r3, [r2, #PHY_PTM_PLL_RANGE_REG]               // = 0xE18
    
    LDR     r3, =PHY_PTM_FEEBACK_DIV_200_400MHz            // Feedback divider = 0
    STR     r3, [r2, #PHY_PTM_FEEBACK_DIV_REG]             // = 0xE1C

    LDR     r3, =PHY_PTM_REFCLK_DIV_200_400MHz             // Ref clock divider = 0
    STR     r3, [r2, #PHY_PTM_RCLK_DIV_REG]                // = 0xE20

//
// Enable PLL
//
    MOV     r3, #0x1
    STR     r3, [r2, #PHY_PTM_PLL_EN_REG]

//
// Wait for PHY to lock
//
101 
    LDR     r3, [r2, #PHY_PTM_LOCK_STATUS_REG]
    AND     r3, #0xff
    CMP     r3, #0x1
    BNE     %B101

//
// Set Global IO termination
//
    LDR     r3, =0x5
    STR     r3, [r2, #PHY_PTM_IOTERM_REG]

//
// Take the PHY out of H/W reset via SCC
//
    LDR     r0, = A_SCC_BASE                               // Load the address of the reg
    LDR     r3, [r0, #SCC_PHY_RST_REG_OFF]                 // Read the current PHY reset settings
    
    ORR     r3, r3, #0x40000000
    STR     r3, [r0, #SCC_PHY_RST_REG_OFF]

    LDR     r3, = SCC_APB_CTRL_VALUE                       // Revert control of RST_REG back to the DCC
    ORR     r3, r3, #SCC_PHY_RST_REG_OFF
    STR     r3, [r0, #SCC_APB_CTRL_CLR]

//
// Wait for PHY to report "ready"
//
103 
    LDR     r3, [r2, #PHY_PTM_INIT_DONE_REG]
    AND     r3, #0x1
    TST     r3, #0x1
    BEQ     %B103

//
// Configure the DMC400 Memory Controller Registers
//

// Move the DMC address back to R0
    MOV     r0, r6         

    LDR     r2, =0x000000C3                                // Done - TC1 = 0x186
    STR     r2, [r0, #DMC400_T_REFI] 
    
    LDR     r2, =0x004F004F                                // Done
    STR     r2, [r0, #DMC400_T_RFC] 
    
    MOV     r2, #0x02                                      // Done
    STR     r2, [r0, #DMC400_T_MRR] 
    
    MOV     r2, #0x02                                      // Done
    STR     r2, [r0, #DMC400_T_MRW]   

    MOV     r2, #0x06                                      // Done
    STR     r2, [r0, #DMC400_T_RCD] 
    
    MOV     r2, #0x12                                      // Done - Validation = 0x10
    STR     r2, [r0, #DMC400_T_RAS] 
    
    MOV     r2, #0x06                                      // Done
    STR     r2, [r0, #DMC400_T_RP] 
    
    MOV     r2, #0x07                                      // Done - TC1 = 0x03
    STR     r2, [r0, #DMC400_T_RPALL] 
    
    MOV     r2, #0x04                                      // Done
    STR     r2, [r0, #DMC400_T_RRD] 
    
    LDR     r2, =0x00000012                                // Done - TC1 = 0x0E
    STR     r2, [r0, #DMC400_T_FAW]  
    
    MOV     r2, #0x04                                      // Done
    STR     r2, [r0, #DMC400_T_RTR] 
    
    MOV     r2, #0x07                                      // Done
    STR     r2, [r0, #DMC400_T_RTW] 
    
    MOV     r2, #0x03                                      // Done - Validation = 0x04
    STR     r2, [r0, #DMC400_T_RTP]
    
    MOV     r2, #0x0F                                      // Done - Validation = 0x05
    STR     r2, [r0, #DMC400_T_WR] 
    
    LDR     r2, =0x000C000C                                // Done - Validation = 0x30002
    STR     r2, [r0, #DMC400_T_WTR] 
    
// LDR     r2, =0x00040000                                // Done
// STR     r2, [r0, #DMC400_T_WTW]
    
    MOV     r2, #0x06                                      // Done
    STR     r2, [r0, #DMC400_RD_LAT]
    
    MOV     r2, #0x05                                      // Done
    STR     r2, [r0, #DMC400_WR_LAT] 
    
    MOV     r2, #0x04                                      // Done - TC1 = 0x03
    STR     r2, [r0, #DMC400_T_RDDATA_EN]
    
    LDR     r2, =0x00000103                                // Done
    STR     r2, [r0, #DMC400_T_PHY_WRLAT] 
    
// MOV     r2, #0x03                                      // Done
// STR     r2, [r0, #DMC400_T_EP]
    
    LDR     r2, =0x00080002                                // Done - Validation = 0x60002
    STR     r2, [r0, #DMC400_T_XP] 
    
    MOV     r2, #0x37                                      // Done - TC1 = 0xC8
    STR     r2, [r0, #DMC400_T_ESR] 
    
    LDR     r2, =0x00C800C8                                // Done - TC1 = 0x52
    STR     r2, [r0, #DMC400_T_XSR] 
    
// MOV     r2, #0x03                                      // Done
// STR     r2, [r0, #DMC400_T_SRCKD]
    
// MOV     r2, #0x03                                      // Done
// STR     r2, [r0, #DMC400_T_CKSRD]
    
// MOV     r2, #0x03                                      // Done
// STR     r2, [r0, #DMC400_T_ECKD]
    
// MOV     r2, #0x03                                      // Done
// STR     r2, [r0, #DMC400_T_XCKD]
     
    MOV     r2, #0x00                                      // Done
    STR     r2, [r0, #DMC400_ECC_CTRL]
                                                                                
    LDR     r2, =0x01030402                                // Done
    STR     r2, [r0, #DMC400_ADD_CTRL]

    LDR     r2, =0x11000102                                // Done
    STR     r2, [r0, #DMC400_FORMAT_CTRL] 

    MOV     r2, #0x02                                      // Done
    STR     r2, [r0, #DMC400_DEC_CTRL]

    LDR     r2, =0x00001001                                // Done
    STR     r2, [r0, #DMC400_RDLVL_CTRL]

//
// config memories
//
    
// send nop
    LDR     r2, =0x00000000                                // NOP
    STR     r2, [r0, #DMC400_DIRECT_CMD]  
    LDR     r2, =0x00100000                                // NOP
    STR     r2, [r0, #DMC400_DIRECT_CMD]  
// pre-charge all
    LDR     r2, =0x20000000                                // PRECHARGE ALL
    STR     r2, [r0, #DMC400_DIRECT_CMD]  
    LDR     r2, =0x20100000                                // PRECHARGE ALL
    STR     r2, [r0, #DMC400_DIRECT_CMD]  

// delay
    LDR     r2, =20
1
    SUB     r2, r2, #1
    CMP     r2, #0
    BNE     %B1
    
    LDR     r2, =0x10020000                                // EMR2 chip 0 - Clear
    STR     r2, [r0, #DMC400_DIRECT_CMD]  
    LDR     r2, =0x10120000                                // EMR2 chip 1 - Clear
    STR     r2, [r0, #DMC400_DIRECT_CMD]
    NOP
    LDR     r2, =0x10030000                                // EMR3 chip 0 - Clear
    STR     r2, [r0, #DMC400_DIRECT_CMD]
    LDR     r2, =0x10130000                                // EMR3 chip 1 - Clear
    STR     r2, [r0, #DMC400_DIRECT_CMD]
    NOP
    LDR     r2, =0x10010000                                // EMR1 chip 0 - Clear
    STR     r2, [r0, #DMC400_DIRECT_CMD]
    LDR     r2, =0x10110000                                // EMR1 chip 1 - Clear
    STR     r2, [r0, #DMC400_DIRECT_CMD]
    NOP
    LDR     r2, =0x10000B62                                // MR chip 0 - DLL reset
    STR     r2, [r0, #DMC400_DIRECT_CMD]
    LDR     r2, =0x10100B62                                // MR chip 1 - DLL reset
    STR     r2, [r0, #DMC400_DIRECT_CMD]
    NOP
    LDR     r2, =0x20000000                                // PRECHARGE ALL
    STR     r2, [r0, #DMC400_DIRECT_CMD]
    LDR     r2, =0x20100000                                // PRECHARGE ALL
    STR     r2, [r0, #DMC400_DIRECT_CMD]
    NOP
    LDR     r2, =0x30000000                                // AUTO REFRESH
    STR     r2, [r0, #DMC400_DIRECT_CMD]
    LDR     r2, =0x30100000                                // AUTO REFRESH
    STR     r2, [r0, #DMC400_DIRECT_CMD]
    NOP
    NOP
    LDR     r2, =0x30000000                                // AUTO REFRESH
    STR     r2, [r0, #DMC400_DIRECT_CMD]
    LDR     r2, =0x30100000                                // AUTO REFRESH
    STR     r2, [r0, #DMC400_DIRECT_CMD]
    NOP
    LDR     r2, =0x10000A62                                // MR chip 0 - DLL ON
    STR     r2, [r0, #DMC400_DIRECT_CMD]
    LDR     r2, =0x10100A62                                // MR chip 1 - DLL ON
    STR     r2, [r0, #DMC400_DIRECT_CMD]

// delay
    LDR     r2, =20
2
    SUB     r2, r2, #1
    CMP     r2, #0
    BNE     %B2

    LDR     r2, =0x10010384                                // EMR1 chip 0 - OCD = Full,75R
    STR     r2, [r0, #DMC400_DIRECT_CMD]
    LDR     r2, =0x10110384                                // EMR1 chip 1 - OCD = Full,75R
    STR     r2, [r0, #DMC400_DIRECT_CMD]

// delay
    LDR     r2, =20
3
    SUB     r2, r2, #1
    CMP     r2, #0
    BNE     %B3

    LDR     r2, =0x10010004                                // EMR1 chip 0 - OCD Exit
    STR     r2, [r0, #DMC400_DIRECT_CMD]
    LDR     r2, =0x10110004                                // EMR1 chip 1 - OCD Exit
    STR     r2, [r0, #DMC400_DIRECT_CMD]
    NOP

// wait for t_DLLK and t_ZQINIT  (1us)
    LDR     r2, =10
4
    SUB     r2, r2, #1
    CMP     r2, #0
    BNE     %B4

// go command
    MOV     r2, #0x03
    STR     r2, [r0, #DMC400_COMMAND]
    
// wait for ready
5
    LDR     r2, [r0,#DMC400_STATUS]
    ANDS    r2, r2, #0x03
    CMP     r2,#3
    BNE     %B5
    
//
// PHY Squelch Training
//

    MOV     r2, r7                                         // Restore address of PHY
    
    LDR     r3, =0x1
    STR     r3, [r2, #PHY_PTM_SQU_TRAINING_REG]            // Enable Training

    LDR     r5, =0x80000000
113
    MOV     r4, #0
114 
    LDR     r3, [r5, #0]                                   // Dummy read
    ADD     r4, #1
    CMP     r4, #200
    BNE     %B114      

// wait for ready
    LDR     r3, [r2,#PHY_PTM_SQU_STAT_REG]
    TST     r3,#1
    BEQ     %B113        

    MOV     r3, #0
    STR     r3, [r2, #PHY_PTM_SQU_TRAINING_REG]            // Disable Training
    
    pop     {r3-r7}
    BX      lr        

    END
