/*
 * BCM47XX Denali based memory controller initialization
 *
 * Copyright (C) 2010, Broadcom Corporation. All Rights Reserved.
 * 
 * Permission to use, copy, modify, and/or distribute this software for any
 * purpose with or without fee is hereby granted, provided that the above
 * copyright notice and this permission notice appear in all copies.
 * 
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
 * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
 * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
 * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 *
 * $Id: aisdram.S,v 1.26.10.9 2011-01-20 04:28:14 Exp $
 */

#include <hndsoc.h>
#include <bcmdevs.h>
#include <bcmnvram.h>
#include <sbsdram.h>
#include <sbmemc.h>
#include <sbsocram.h>
#include <dmemc_core.h>
#include <sbchipc.h>

#if	defined(NFLASH_SUPPORT)
#include <nflash.h>
#endif

#include <mipsinc.h>


/* Debug macro - write a number to a chipc reg - use it with caution,
 *  it changes k0 and k1 registers.
 */
#define TRACEINIT(x)
#define TRACE(x)
#define TRACE2(x)
	.text

dmemc_init:
	.word	0,	0x00000101
	.word	1,	0x01030100
	.word	2,	0x01030101
	.word	3,	0x01010101
	.word	5,	0x00000100
	.word	7,	0x01010000
	.word	8,	0x00000100
	.word	9,	0x00000100
	.word	10,	0x01010101
	.word	12,	0x00010200
	.word	16,	0x0f00000a
	.word	20,	0x64000000
	.word	27,	0x00000001
	.word	28,	0x0039000a
	.word	29,	0x081b002b
	.word	30,	0x00000000
	.word	31,	0x00000000
	.word	33,	0x000200c8
	.word	35,	0x000000c8
	.word	36,	0x00000000
	.word	125,	0x01010000
	.word	126,	0x02000100
	.word	129,	0x02000200
	.word	132,	0x00000000
	.word	138,	0x00000000
	.word	139,	0x00000000
	.word	151,	0x00000005
	.word	DMEMC_TABLE_END

sdr_init:
	.word	4,	0x00000100
	.word	6,	0x00000000
	.word	11,	0x00030001
	.word	14,	0x00020000
	.word	15,	0x0f000102
	.word	17,	0x05000002
	.word	18,	0x00000003
	.word	19,	0x00090200
	.word	21,	0x70006400
	.word	22,	0x7f000070
	.word	23,	0x00400000
	.word	24,	0x09030600
	.word	25,	0x00170017
	.word	32,	0x00320000
	.word	34,	0x000a4186
	.word	DMEMC_TABLE_END

ddr1_init:
	.word	4,	0x00000100
	.word	6,	0x00000100
	.word	11,	0x00030000
	.word	14,	0x02030200
	.word	15,	0x0f010203
	.word	17,	0x06000002
	.word	18,	0x00000006
	.word	19,	0x0010010e
	.word	21,	0x20006400
	.word	22,	0x5700002a
	.word	23,	0x00340000
	.word	24,	0x0e030800
	.word	25,	0x000f000f
	.word	26,	0x000f000f
	.word	32,	0x00000000
	.word	34,	0x000d2d89
	.word	127,	0x00000001
	.word	128,	0x07000300
	.word	130,	0x00010103
	.word	131,	0x00000200
	.word	133,	0x06120000
	.word	134,	0x06120612
	.word	135,	0x06120612
	.word	136,	0x00000612
	.word	137,	0x00000032
	.word	140,	0x001f4008
	.word	141,	0x001f4008
	.word	142,	0x002ee004
	.word	143,	0x002ee004
	.word	146,	0x000f0033
	.word	147,	0xf4113c17
	.word	148,	0xf4113c17
	.word	149,	0x26c00300
	.word	150,	0x26c00300
	.word	DMEMC_TABLE_END

ddr2_init:
	.word	4,	0x00010100
	.word	6,	0x00000000
	.word	11,	0x00030000
	.word	14,	0x02030203
	.word	15,	0x0f030204
	.word	17,	0x08000002
	.word	18,	0x00000004
	.word	19,	0x000f020e
	.word	21,	0x16006400
	.word	22,	0x6026162a
	.word	23,	0x00340000
	.word	24,	0x35060c00
	.word	25,	0x00200020
	.word	26,	0x00200020
	.word	32,	0x006b0000
	.word	34,	0x003848e1
	.word	127,	0x00000002
	.word	128,	0x07000404
	.word	130,	0x03020304
	.word	131,	0x00000400
	.word	133,	0x081b0000
	.word	134,	0x081b081b
	.word	135,	0x081b081b
	.word	136,	0x0000081b
	.word	137,	0x00400642
	.word	140,	0x00164008
	.word	141,	0x00164008
	.word	142,	0x00236004
	.word	143,	0x00236004
	.word	146,	0x000f0133
	.word	147,	0xf4112c17
	.word	148,	0xf4112c17
	.word	149,	0x26c00300
	.word	150,	0x26c00300
	.word	DMEMC_TABLE_END

#if	defined(NFLASH_SUPPORT)
nfl_pagesz_map:
	/* page size mapping */
	.word	0x200, 0x800, 0x1000, 0x2000
	
nfl_blksz_map:
	/* block size mapping */
	.word	0x4000, 0x20000, 0x2000, 0x80000, 0x40000, 0, 0, 0
#endif

	/* Register conventions.
	 *	Inherited from sisdram.S:
	 *		s2 = SI_ENUM_BASE
	 *		s5 = Relocation factor
	 *		s6 = ChipId reg
	 *		s7 = ChipType
	 *	Local:
	 *		s0 = sdram_config + sdram_refresh values
	 *		s1 = package opt
	 *		s3 = Controller corerev
	 *		s4 = Controller coreid
	 *		s8 = config_ncdl
	 *		a1 = dmemc regs
	 *		a2 = dmemc DMP regs
	 *		a3 = memory type (sdr,ddr1,ddr2)
	 */

LEAF(ai_draminit)
	.set	mips32
	.set	noreorder

	TRACE(0x415301)

	move	t6,ra

	/* Scan for a Denali DDR controller (a0) */
	lw	a0,CC_EROMPTR(s2)
	li	t0,KSEG1			# t0 = KSEG1
	or	a0,a0,t0			# a0 points to the EROM
	TRACE(0x415302)
1:
#if	defined(IL_BIGENDIAN) && defined(BCMHND74K)
	xor	t0,a0,4
	lw	t0,0(t0)
	add	t3,a0,4
	xor	t3,t3,4
	lw	t3,0(t3)
#else
	lw	t0,0(a0)			# t0 = CIA
	lw	t3,4(a0)			# t3 = CIB
#endif
	and	t1,t0,ER_TAG
	TRACE(0x415303)
	beq	t1,ER_END,noctrl
	nop
	TRACE(0x415304)
	beq	t1,ER_CI,2f
	nop
	TRACE(0x415305)
	b	1b
	addi	a0,4

2:	TRACE(0x415306)
	and	s4,t0,CIA_CID_MASK
	srl	s4,s4,CIA_CID_SHIFT		# s4 has controler coreid

	beq	s4,DMEMC_CORE_ID,founddmemc
	nop

	beq	s4,DMEMS_CORE_ID,founddmemc
	nop

	beq	s4,AMEMC_CORE_ID,foundamemc
	nop

	b	1b
	addi	a0,8				# Skip CIB too

	/* No DMEMC controller found */
noctrl:	TRACE(0x415307)
	jr	t6
	li	v0,-1

foundamemc:
	beqz	s5,1f				# Running from RAM, simply return
	nop

	la      t2,ai_amemcinit
	add     t2,t2,s5
	jalr    t2
	nop
1:
	jr	t6
	move	v0,zero

founddmemc:
	TRACE(0x415308)
	/* If we found the controller, but we are already in RAM, there is nothing
	 * to do. This will change if/when we have an AI chip with MIPS and
	 * SOCRAM only.
	 */
	bnez	s5,1f				# Not running from RAM, go ahead
	nop
	
	jr	t6				# Return with 0 rc.
	move	v0,zero
	
	/* We'll cheat a little: memory controllers don't have master ports, so
	 * the EROM entry right after the CIDs is the slave port for the registers
	 */
1:
#if	defined(IL_BIGENDIAN) && defined(BCMHND74K)
	add	a1,a0,8
	xor	a1,a1,4
	lw	a1,0(a1)
#else
	lw	a1,8(a0)
#endif
	li	t2,AD_ADDR_MASK
	and	a1,a1,t2
	li	t0,KSEG1			# t0 = KSEG1
	or	a1,a1,t0			# a1: dmemc regs
	/* after that, the first slave wrapper will be its DMP registers */
	addi	a0,12
1:
#if	defined(IL_BIGENDIAN) && defined(BCMHND74K)
	xor	t0,a0,4
	lw	t0,0(t0)
#else
	lw	t0,0(a0)
#endif
	and	t1,t0,ER_TAG
	beq	t1,ER_ADD,addesc
	nop
	b	1b
	addi	a0,4

addesc:	and	t1,t0,AD_ST_MASK
	beq	t1,AD_ST_SWRAP,swrap
	nop
	b	1b
	addi	a0,4

swrap:	and	a2,t0,t2
	li	t0,KSEG1			# t0 = KSEG1
	or	a2,a2,t0			# a2: dmemc DMP regs

	/* Got our core, reset it */
	TRACE(0x415309)
	bal	ai_core_reset
	nop

	/* Get package option for later */
	TRACE(0x41530a)
	li	t0,CID_PKG_MASK
	and	t0,t0,s6
	srl	s1,t0,CID_PKG_SHIFT		# s1 = package opt

	/* Find out the type of memory from the straps */
	/* Corerevs 0 & 1 did not have this register, so we have to
	 * check the corerev and use chipstatus for those two.
	 */
	and	t3,t3,CIB_REV_MASK
	srl	s3,t3,CIB_REV_SHIFT		# s3 = core revision
	beq     s4,DMEMS_CORE_ID,1f
	nop
	ble	s3,1,is16x
	nop

1:	/* Not a 4716/47162 (a0?) read the stat register */
	lw	t0,DMEMC_STAT(a1)
	li	t1,DM_STAT_MASK
	and	a3,t0,t1			# a3 == 4 if ddr2, 2 if ddr1, 1 if sdr.
	b	find_nvram
	nop

	/* Check chipc:	chipstatus for the ddr1/ddr2 strapping option */
is16x:	TRACE(0x41530b)
	lw	t0,CC_CHIPST(s2)
	li	t1,0x200
	and	t0,t0,t1
	beqz	t0,find_nvram
	li	a3,DM_STAT_DDR2

	li	a3,DM_STAT_DDR1

	/* Read sdram_config from nvram */
find_nvram:
	TRACE(0x41530c)
	li	t0,KSEG1ADDR(SI_FLASH2 - NVRAM_SPACE)
	li	t1,FLASH_MIN 
	li	t2,SI_FLASH2_SZ
	li	t3,NVRAM_MAGIC

#if	defined(NFLASH_SUPPORT)
	/* Take care of 5357 NAND boot */
	li	t4,CID_ID_MASK
	and	t4,t4,s6
	bne	t4,BCM5357_CHIP_ID,1f
	nop
	lw	t5,CC_CHIPST(s2)
	li	t4,0x10	
	and	t4,t4,t5
	beqz	t4,1f
	nop

	la	t4,nfl_size_block
	add	t4,t4,s5
	jalr	t4
	nop
	beqz	v0,embedded_nv
	nop

	/* skip bad blocks and locate nvram */
	move	t1,v0					# block size
	move	t2,t1
check_badb:
	move	a0,t2
	move	t0,a1					# save a1
	move	a1,v1					# page size
	la	t4,nfl_check_badb
	add	t4,t4,s5
	jalr	t4
	nop
	move	a1,t0					# restore a1
	bnez	v0,skip_badb
	nop

	li	t0,KSEG1ADDR(SI_FLASH1)
	add	t4,t0,t2
	lw	t5,0(t4)
	beq	t3,t5,read_config
	nop
skip_badb:
	add	t2,t2,t1
	li	t5,SI_FLASH1_SZ
	blt	t2,t5,check_badb
	nop
	b	embedded_nv
	nop
#endif

1:	add	t4,t0,t1
	lw	t5,0(t4)
	beq	t3,t5,read_config
	nop

	sll	t1,t1,1
	ble	t1,t2,1b
	nop

#if	defined(NFLASH_SUPPORT)
embedded_nv:
#endif
	/* Try embedded NVRAM at 4 KB and 1 KB as last resorts */
	TRACE(0x41530d)
	li	t4,KSEG1ADDR(SI_FLASH1 + 0x1000)
	lw	t5,0(t4)
	beq	t3,t5,read_config
	nop

	TRACE(0x41530e)
	li	t4,KSEG1ADDR(SI_FLASH1 + 0x400)
	lw	t5,0(t4)
	beq	t3,t5,read_config
	nop

	/* No nvram, pick sone defaults */

	/* assume DDRM16MX16 if ddr1 */
	TRACE(0x41530f)
	bne	a3,DM_STAT_DDR1,1f
	nop

	beq	s4,DMEMC_CORE_ID,init_dmemc
	li	s0,0x283		# Value for dmemc

	/* Use DDR1M16MX16 if QT and DDR1M32MX16 otherwise */
	beq	s1,HWSIM_PKG_ID,init_dmemc
	li	s0,0x103		# Value for dmems

	b	init_dmemc
	li	s0,0x003		# Value for dmems at 200MHz

1:	beq	a3,DM_STAT_SDR,init_dmemc
	li	s0,0x002		# Value for SDR

	/* For ddr2, use DDR2M32X16X2 if QT and DDR2M128X16X2 otherwise */
	beq	s1,HWSIM_PKG_ID,init_dmemc
	li	s0,0x104

	b	init_dmemc
	li	s0,0x144
	
read_config:
	/* sdram_config is a 16bit value 12 bytes inside the nvram hdr.
	 * Currently it is defined as:
	 *	10:8	column_size as per control13
	 *	7	reduc i.e. memory is half the width of the controller
	 *	6	8 banks
	 *	5	bypass
	 *	2:0	cas latency
	 *
	 * sdram_refresh:
	 *	15:8	delay_dqs_0
	 *	7:0	clk_wr_delay, or clk_wr_delay_0 (for corerev >= 2)
	 *
	 * sdram_ncdl:	control22
	 *	31:24	clk_dqs_delay, or clk_wr_delay_1 (for corerev >= 2)
	 *	23:16	delay_dqs_3
	 *	15:8	delay_dqs_2
	 *	7:0	delay_dqs_1
	 */
	TRACE(0x415310)
	lw	s0,12(t4)	# Pick up sdram_config & sdram_refresh
	lw	s8,16(t4)	# Pick up sdram_ncdl

	/* Initialize DMEMC/DMEMS */
init_dmemc:
	/* For DDR2, init pvt controller */
	bne	a3,DM_STAT_DDR2,init_regs
	nop

	TRACE(0x415311)
	li	t0,1
	sw	t0,DMEMC_PVTGROUPJ(a1)
	sw	zero,DMEMC_PVTGROUPA(a1)
	sw	t0,DMEMC_PVTGROUPA(a1)

	/* Init the common regs */
init_regs:
	TRACE(0x415312)
	la	a0,dmemc_init
	bal	dmemc_init_regs			# dmemc_init_regs(a0): Inits from the table @ a0
	nop

	la	a0,sdr_init
	beq	a3,DM_STAT_SDR,1f
	nop

	TRACE(0x415313)
	la	a0,ddr1_init
	beq	a3,DM_STAT_DDR1,1f
	nop

	TRACE(0x415314)
	la	a0,ddr2_init
1:	bal	dmemc_init_regs
	nop

	/* Fixup differences between dmems & dmemc */
fixs:	bne	s4,DMEMS_CORE_ID,fix2
	nop

	li	t0,0x02020002
	beq	a3,DM_STAT_SDR,1f
	nop

	li	t0,0x18006400
	sw	t0,DMEMC_CONTROL21(a1)
	li	t0,0x7f000018			# DLL DQS delay for DDR1
	sw	t0,DMEMC_CONTROL22(a1)
	li	t0,0x00500000
	sw	t0,DMEMC_CONTROL23(a1)
	li	t0,0x00320000			# Change MRS data for DDR1
	sw	t0,DMEMC_CONTROL32(a1)

	li	t0,0x02000002

1:	sw	t0,DMEMC_CONTROL52(a1)
	li	t0,0x00000001
	sw	t0,DMEMC_CONTROL53(a1)

	/* Fixup differences in 47162 */
fix2:	li	t0,CID_ID_MASK
	and	t0,t0,s6
	bne	t0,BCM47162_CHIP_ID,nvover
	nop

	li	t0,0x16006400
	sw	t0,DMEMC_CONTROL21(a1)
	li	t0,0x00480000
	sw	t0,DMEMC_CONTROL23(a1)

	/* Presumaby this is for DDR1 only? */
	li	t0,0x61161616
	sw	t0,DMEMC_CONTROL22(a1)

	/* Override the dll delays from nvram if provided */
nvover:	beqz	s8,chhalf
	nop

	# Check for dmems (any rev)
	beq	s4,DMEMS_CORE_ID,old_ncdl
	nop

	# Dmemc rev < 2?
	ble	s3,1,old_ncdl
	nop

	li	t0,0x7f000000
	and	t0,t0,s0		# delay_dqs_0
	srl	t0,t0,15
	lw	t1,DMEMC_CONTROL140(a1)
	li	t2,~0x0000fe00
	and	t1,t1,t2
	or	t0,t0,t1
	sw	t0,DMEMC_CONTROL140(a1)

	li	t0,0x7f
	and	t0,t0,s8		# delay_dqs_1
	sll	t0,t0,9
	lw	t1,DMEMC_CONTROL141(a1)
	and	t1,t1,t2
	or	t0,t0,t1
	sw	t0,DMEMC_CONTROL141(a1)
	
	li	t0,0x7f0000
	and	t0,t0,s0		# clk_wr_delay_0
	srl	t0,t0,8
	lw	t1,DMEMC_CONTROL142(a1)
	li	t2,~0x00007f00
	and	t1,t1,t2
	or	t0,t0,t1
	sw	t0,DMEMC_CONTROL142(a1)

	li	t0,0x7f000000
	and	t0,t0,s8		# clk_wr_delay_1
	srl	t0,t0,16
	lw	t1,DMEMC_CONTROL143(a1)
	and	t1,t1,t2
	or	t0,t0,t1
	sw	t0,DMEMC_CONTROL143(a1)

	b	chhalf
	nop

old_ncdl:
	li	t0,0x7f000000
	and	t0,t0,s0		# delay_dqs_0
	or	t0,t0,0x6400
	sw	t0,DMEMC_CONTROL21(a1)
	li	t0,0x007f0000
	and	t0,t0,s0		# clk_wr_delay
	sw	t0,DMEMC_CONTROL23(a1)

	sw	s8,DMEMC_CONTROL22(a1)
	
	/* Check for half-width */
chhalf:	li	t0,0x80
	and	t0,t0,s0
	beqz	t0,ch8banks
	nop

setreduc:
	/* Set reduc bit if half-wide */
	TRACE(0x415315)
	lw	t0,DMEMC_CONTROL08(a1)
	li	t1,0x01000000
	or	t0,t0,t1
	sw	t0,DMEMC_CONTROL08(a1)

	/* Check for 8-bank DDRs */
ch8banks:
	li	t0,0x40
	and	t0,t0,s0
	beqz	t0,docaslat
	nop

	/* Change regs for 8-bank DDRs */
do8banks:	
	lw	t0,DMEMC_CONTROL05(a1)
	li	t1,0x00010000
	or	t0,t0,t1
	sw	t0,DMEMC_CONTROL05(a1)

	lw	t0,DMEMC_CONTROL19(a1)
	li	t1,0x0000000e
	or	t0,t0,t1
	sw	t0,DMEMC_CONTROL19(a1)

	lw	t0,DMEMC_CONTROL24(a1)
	li	t1,~0xff000000
	li	t2,0x22000000
	and	t0,t0,t1
	or	t0,t0,t2
	sw	t0,DMEMC_CONTROL24(a1)

	lw	t0,DMEMC_CONTROL34(a1)
	li	t1,~0x00ff0000
	li	t2,0x00250000
	and	t0,t0,t1
	or	t0,t0,t2
	sw	t0,DMEMC_CONTROL34(a1)

	/* Set the right value for column size and CAS latency */
docaslat:
	TRACE(0x415316)
	li	t0,0x0707
	and	t0,t0,s0
	sw	t0,DMEMC_CONTROL13(a1)
	andi	t0,s0,7			# Isolate cas
	beq	s4,DMEMS_CORE_ID,setcaslin
	nop
	ble	s3,1,setcaslin
	nop
	/* Additional settings required for dmemc rev >= 2 */
	sll	t1,t0,8
	and	t1,0xf00
	bne	a3,DM_STAT_DDR1,1f
	nop
	and	t1,0x300
1:	lw	t2,DMEMC_CONTROL128(a1)
	and	t2,~0xf00
	or	t2,t2,t1
	sw	t2,DMEMC_CONTROL128(a1)
	sll	t1,t0,4
	lw	t2,DMEMC_CONTROL137(a1)
	and	t2,~0x70
	or	t2,t2,t1
	sw	t2,DMEMC_CONTROL137(a1)
	bne	a3,DM_STAT_DDR2,setcaslin
	nop
	sub	t1,t0,1
	sll	t1,8
	lw	t2,DMEMC_CONTROL130(a1)
	and	t2,~0xf00
	or	t2,t2,t1
	sw	t2,DMEMC_CONTROL130(a1)
	sll	t1,8
	lw	t2,DMEMC_CONTROL15(a1)
	and	t2,~0xf0000
	or	t2,t2,t1
	sw	t2,DMEMC_CONTROL15(a1)
setcaslin:
	/* Set caslat_lin and caslat_lin_gate */
	lw	t2,DMEMC_CONTROL16(a1)
	/* Take care of fractional CAS latencies for DDR1 */
	li	t3,0
	bne	a3,DM_STAT_DDR1,1f
	nop
	andi	t1,t0,4
	beqz	t1,1f
	nop
	andi	t0,t0,3			# take off half bit
	li	t3,1
1:	sll	t0,t0,1			#  * 2
	add	t0,t0,t3
	addi	t1,t0,1			#  + 1 => caslin
	sll	t1,t1,8
	or	t2,t2,t1
	addi	t0,t0,-1		# and -1 => caslin_gate
	sll	t0,t0,16
	or	t2,t0,t2
	sw	t2,DMEMC_CONTROL16(a1)

	/* Finally set bypass mode if needed, but always for quickturn */
ckbyp:	beq	s1,HWSIM_PKG_ID,dobypass
	nop
	li	t0,0x20
	and	t0,t0,s0
	beqz	t0,ckvsim
	nop

dobypass:
	TRACE(0x415317)

	beq	s4,DMEMS_CORE_ID,1f
	nop

	bgt	s3,1,4f
	nop
	
1:	li	t0,0x00170017
	beq	a3,DM_STAT_SDR,2f
	nop
	
	li	t0,0x000f000f
	beq	a3,DM_STAT_DDR1,2f
	nop

	li	t0,0x00200020
	
2:	sw	t0,DMEMC_CONTROL25(a1)
	beq	s4,DMEMS_CORE_ID,3f
	nop
	sw	t0,DMEMC_CONTROL26(a1)

3:	lw	t0,DMEMC_CONTROL28(a1)
	li	t1,0x00ff0000
	or	t0,t0,t1
	sw	t0,DMEMC_CONTROL28(a1)

4:	lw	t0,DMEMC_CONTROL29(a1)
	li	t1,~0x000000ff
	li	t2,0x0000005f
	and	t0,t0,t1
	or	t0,t0,t2
	sw	t0,DMEMC_CONTROL29(a1)

	lw	t0,DMEMC_CONTROL05(a1)
	li	t1,0x00000001
	or	t0,t0,t1
	sw	t0,DMEMC_CONTROL05(a1)

	beq	s4,DMEMS_CORE_ID,ckvsim
	nop

	ble	s3,1,ckvsim
	nop
	
	lw	t0,DMEMC_CONTROL140(a1)
	li	t1,0x10000000
	or	t0,t0,t1
	sw	t0,DMEMC_CONTROL140(a1)

	lw	t0,DMEMC_CONTROL141(a1)
	or	t0,t0,t1
	sw	t0,DMEMC_CONTROL141(a1)

	/* For vsim change tinit so sims run faster */
ckvsim:	bne	s1,HDLSIM_PKG_ID,turnon
	nop

	TRACE(0x415318)
	li	t0,0x36
	sw	t0,DMEMC_CONTROL36(a1)

turnon:
	/* We are ready, turn controller on */
	TRACE(0x415319)
	lw	t0,DMEMC_CONTROL09(a1)		# Read current control09 reg
	or	t0,t0,DMC09_START		# Add start bit
	sw	t0,DMEMC_CONTROL09(a1)		# Start the controller

	beq	s4,DMEMS_CORE_ID,2f
	nop

	ble	s3,1,2f
	nop

1:	lw	t0,DMEMC_CONTROL133(a1)		# Poll for INT_INIT_DONE (dmemc >=2)
	and	t1,t0,DM_INT_INIT_DONE
	beqz	t1,1b
	nop
	/* Bypass mode programming */
	lw	t0,DMEMC_CONTROL05(a1)
	li	t1,0x00000001
	and	t0,t0,t1
	beqz	t0,ack_ints
	nop
	lw	t1,DMEMC_CONTROL144(a1)
	srl	t1,t1,3
	lw	t2,DMEMC_CONTROL140(a1)
	li	t0,~0x03ff0000
	and	t2,t2,t0
	sll	t0,t1,16
	or	t2,t2,t0
	sw	t2,DMEMC_CONTROL140(a1)
	
	lw	t2,DMEMC_CONTROL142(a1)
	li	t0,~0x01ff8000
	and	t2,t2,t0
	sll	t0,t1,1
	add	t0,t0,t1
	li	t1,0x3ff
	and	t0,t0,t1
	sll	t0,t0,15
	or	t2,t2,t0
	sw	t2,DMEMC_CONTROL142(a1)
	
	lw	t1,DMEMC_CONTROL145(a1)
	srl	t1,t1,3
	lw	t2,DMEMC_CONTROL141(a1)
	li	t0,~0x03ff0000
	and	t2,t2,t0
	sll	t0,t1,16
	or	t2,t2,t0
	sw	t2,DMEMC_CONTROL141(a1)
	
	lw	t2,DMEMC_CONTROL143(a1)
	li	t0,~0x01ff8000
	and	t2,t2,t0
	sll	t0,t1,1
	add	t0,t0,t1
	li	t1,0x3ff
	and	t0,t0,t1
	sll	t0,t0,15
	or	t2,t2,t0
	sw	t2,DMEMC_CONTROL143(a1)
ack_ints:
	/* Clear any pending interrupts from dmemc */
	li	t1,DMC132_INTACK_MASK
	and	t0,t0,t1			# t0 = control133 & mask
	lw	t2,DMEMC_CONTROL132(a1)
	not	t1
	and	t2,t1,t2			# t2 = control132 & ~mask
	or	t0,t0,t2			# Or them and ...
	sw	t0,DMEMC_CONTROL132(a1)		# Ack all ints
	b	3f
	nop

2:	lw	t0,DMEMC_CONTROL24(a1)		# Poll for INT_INIT_DONE (dmems & dmemc<2)
	and	t1,t0,DM_INT_INIT_DONE
	beqz	t1,2b
	nop
	/* Clear any pending interrupts from dmemc */
	li	t1,DMC23_INTACK_MASK
	and	t0,t0,t1			# t0 = control24 & mask
	lw	t2,DMEMC_CONTROL23(a1)
	not	t1
	and	t2,t1,t2			# t2 = control23 & ~mask
	or	t0,t0,t2			# Or them and ...
	sw	t0,DMEMC_CONTROL23(a1)		# Ack all ints

3:	jr	t6
	li	v0,0

	/* Reset core using DMP regs at (a2) */
ai_core_reset:
	/* Set reset while enabling the clock */
	li	t9,(SICF_FGC | SICF_CLOCK_EN)
	li	t8,AIRC_RESET
	sw	t9,AI_IOCTRLSET(a2)
	sw	t8,AI_RESETCTRL(a2)

	/* Read back and delay */
	lw	t8,AI_RESETCTRL(a2)
	lw	t8,AI_RESETCTRL(a2)
	lw	t8,AI_RESETCTRL(a2)

	/* Clear reset */
	li	t8,0
	sw	t8,AI_RESETCTRL(a2)

	/* Read back and delay */
	lw	t8,AI_RESETCTRL(a2)
	lw	t8,AI_RESETCTRL(a2)
	lw	t8,AI_RESETCTRL(a2)

	/* Clear Force Gated Clock */
	li	t9,SICF_FGC
	sw	t9,AI_IOCTRLCLEAR(a2)

	/* Read back and delay */
	lw	t9,AI_IOCTRL(a2)
	lw	t9,AI_IOCTRL(a2)
	lw	t9,AI_IOCTRL(a2)

	jr	ra
	nop

	/* Use table at (a0) to init dmemc regs.
	 * Assumes (a1) points to the regs.
	 */
dmemc_init_regs:
	beq     s4,DMEMS_CORE_ID,loop_regs
	li	t3,128
	
	ble	s3,1,loop_regs
	nop

	li	t3,256
	
loop_regs:
	add	a0,a0,s5			# Relocate address
	li	t0,DMEMC_TABLE_END
1:	lw	t1,0(a0)			# Pick up reg num
	bge	t1,t3,2f			# Return if the reg num >= num of supported regs
	nop
	beq	t1,t0,2f			# Return if done
	nop

	lw	t2,4(a0)			# Get reg value
	sll	t1,2				# Reg num * 4 is reg offset
	addu	t1,a1,t1
#if	defined(IL_BIGENDIAN) && defined(BCMHND74K)
	xor	t1,t1,4
#endif
	sw	t2,0(t1)			# Write reg
	b	1b
	addi	a0,8

2:	jr	ra
	nop

END(ai_draminit)

#if	defined(NFLASH_SUPPORT)
LEAF(nfl_size_block)
	lw	t9,CC_NAND_CONFIG(s2)
	li	t8,0x3
	sll	t8,20
	and	t8,t8,t9
	srl	t8,18				# index = (ncf & (0x3 << 20) >> 18)
	la	a0,nfl_pagesz_map
	add	a0,a0,s5			# Relocate address
	add	t8,t8,a0
	lw	v1,0(t8)
	
	li	t8,0x7
	sll	t8,28
	and	t8,t8,t9
	srl	t8,26				# index = (ncf & (0x7 << 28) >> 26)
	la	a0,nfl_blksz_map
	add	a0,a0,s5			# Relocate address
	add	t8,t8,a0
	lw	v0,0(t8)
	jr	ra
	nop
END(nfl_size_block)


LEAF(nfl_check_badb)
	add	t9,a0,zero
	move	t5,zero
check_spare:
	sw	t9,CC_NAND_CMD_ADDR(s2)
	lw	t9,CC_NAND_CMD_ADDR(s2)		# read back
	li	t9,NCMD_SPARE_RD
	sw	t9,CC_NAND_CMD_START(s2)
	lw	t9,CC_NAND_CMD_START(s2)	# read back
	/* Polling */
	li	t9,NIST_CTRL_READY
	li	t8,NIST_FLASH_READY
	or	t9,t9,t8
1:	lw	t8,CC_NAND_INTFC_STATUS(s2)
	and	t7,t8,t9
	bne	t7,t9,1b
	nop
	/* Check spare valid */
	li	t9,NIST_SPARE_VALID
	and	t7,t8,t9
	beqz	t7,badb
	nop
	/* Check spare byte */
	lw	t9,CC_NAND_SPARE_RD_0(s2)
	li	t8,0xff
	and	t7,t8,t9
	bne	t7,t8,badb
	nop
	/* Read next page */
	add	t9,a0,a1
	add	t5,t5,1
	beq	t5,1,check_spare
	nop

	jr	ra
	li	v0,0
badb:
	jr	ra
	li	v0,1
END(nfl_check_badb)
#endif

/*  *********************************************************************
    *  AI_AMEMCINIT
    *  
    *  AI version of DDR / memory controller initialization
    *
    *  This routine deals with DDR23_PHY and PL341 MEMC.
    *  
    ********************************************************************* */
#define MEMC_BURST_LENGTH       (4)    
/* Convenient macro for writing registers (use t0 for base) */
#define ddr_write(offset, value)   \
    li      a0, (value);           \
    sw      a0, offset(t0);

#define AI_DDRPHY_BASE       (0x1800f000)
#define AI_MEMC_BASE         (0x18006000)
#define AI_MEMC_SLAVE_BASE   (0x18106000)

/* From bsp_config.h of _BCM953003RSP_ */
/* (required) PLL clock */
#define CFG_DDR_PLL_CLOCK       (331250)	/* KHz */

/* (required) Row bits: 0=11bits, 1=12bits, ..., 5=16bits */
#define CFG_DDR_ROW_BITS        2   /* 13 bits */

/* (required) Column bits: 1=9bits, 2=10bits, 3=11bits */
#define CFG_DDR_COLUMN_BITS     2   /* 10 bits */

/* (required) Bank bits: 0=2bits, 3=3bits */
#define CFG_DDR_BANK_BITS       0  

/* (required) Memory width: 0=16bits, 1=32bits, 2=64bits */
#define CFG_DDR_MEM_WIDTH       1

/* (required) CAS Latency (NOTE: could be affected by PLL clock) */
#define CFG_DDR_CAS_LATENCY     5

/* (required) t_wr (picoseconds) */
#define CFG_DDR_T_WR            15000

/* (optional) Refresh period t_refi (picoseconds) */
#define CFG_DDR_REFRESH_PRD     7800000

/* (optional) t_rfc (picoseconds) */
#define CFG_DDR_T_RFC           105000

/*
 * Convenient macros
 */
#define MEMCYCLES(psec)   (((psec) * (CFG_DDR_PLL_CLOCK) + 999999999) / 1000000000)

/*
 * Convenient macros (minimum requirement and truncated decimal)
 */
#define MEMCYCLES_MIN(psec)   ((psec) * (CFG_DDR_PLL_CLOCK) / 1000000000)

/*
 * PLL clock configuration
 */
#define PLL_NDIV_INT_VAL    (16 * (CFG_DDR_PLL_CLOCK) / 100000)

/*
 * DDR23PHY registers
 */
#define DDR23PHY_PLL_STATUS             0x010
#define DDR23PHY_PLL_CONFIG             0x014
#define DDR23PHY_PLL_PRE_DIVIDER        0x018
#define DDR23PHY_PLL_DIVIDER            0x01c
#define DDR23PHY_STATIC_VDL_OVERRIDE    0x030
#define DDR23PHY_ZQ_PVT_COMP_CTL        0x03c
#define DDR23PHY_BL3_VDL_CALIBRATE      0x104
#define DDR23PHY_BL3_VDL_STATUS         0x108
#define DDR23PHY_BL3_READ_CONTROL       0x130
#define DDR23PHY_BL3_WR_PREAMBLE_MODE   0x148
#define DDR23PHY_BL2_VDL_CALIBRATE      0x204
#define DDR23PHY_BL2_VDL_STATUS         0x208
#define DDR23PHY_BL2_READ_CONTROL       0x230
#define DDR23PHY_BL2_WR_PREAMBLE_MODE   0x248
#define DDR23PHY_BL1_VDL_CALIBRATE      0x304
#define DDR23PHY_BL1_VDL_STATUS         0x308
#define DDR23PHY_BL1_READ_CONTROL       0x330
#define DDR23PHY_BL1_WR_PREAMBLE_MODE   0x348
#define DDR23PHY_BL0_VDL_CALIBRATE      0x404
#define DDR23PHY_BL0_VDL_STATUS         0x408
#define DDR23PHY_BL0_READ_CONTROL       0x430
#define DDR23PHY_BL0_WR_PREAMBLE_MODE   0x448

/*
 * PL341 registers
 */
#define PL341_memc_status               0x000
#define PL341_memc_cmd                  0x004
#define PL341_direct_cmd                0x008
#define PL341_memory_cfg                0x00c
#define PL341_refresh_prd               0x010
#define PL341_cas_latency               0x014
#define PL341_write_latency             0x018
#define PL341_t_mrd                     0x01c
#define PL341_t_ras                     0x020
#define PL341_t_rc                      0x024
#define PL341_t_rcd                     0x028
#define PL341_t_rfc                     0x02c
#define PL341_t_rp                      0x030
#define PL341_t_rrd                     0x034
#define PL341_t_wr                      0x038
#define PL341_t_wtr                     0x03c
#define PL341_t_xp                      0x040
#define PL341_t_xsr                     0x044
#define PL341_t_esr                     0x048
#define PL341_memory_cfg2               0x04c
#define PL341_memory_cfg3               0x050
#define PL341_t_faw                     0x054
#define PL341_chip_0_cfg                0x200
#define PL341_user_config0              0x304


/*
 * Values for PL341 Direct Command Register
 */
#define MCHIP_CMD_PRECHARGE_ALL         (0x0 << 18)
#define MCHIP_CMD_AUTO_REFRESH          (0x1 << 18)
#define MCHIP_CMD_MODE_REG              (0x2 << 18)
#define MCHIP_CMD_NOP                   (0x3 << 18)
#define MCHIP_MODEREG_SEL(x)            ((x) << 16)
#define MCHIP_MR_WRITE_RECOVERY(x)      (((x) - 1) << 9)
#define MCHIP_MR_DLL_RESET(x)           ((x) << 8)
#define MCHIP_MR_CAS_LATENCY(x)         ((x) << 4)
#if MEMC_BURST_LENGTH == 4
#define MCHIP_MR_BURST_LENGTH           (2)
#else
#define MCHIP_MR_BURST_LENGTH           (3)
#endif
#define MCHIP_EMR1_DLL_DISABLE(x)       ((x) << 0)
#define MCHIP_EMR1_RTT_ODT_DISABLED     (0)
#define MCHIP_EMR1_RTT_75_OHM           (1 << 2)
#define MCHIP_EMR1_RTT_150_OHM          (1 << 6)
#define MCHIP_EMR1_RTT_50_OHM           ((1 << 6) | (1 << 2))
#define MCHIP_EMR1_OCD_CALI_EXIT        (0x0 << 7)
#define MCHIP_EMR1_OCD_CALI_DEFAULT     (0x3 << 7)

LEAF(ai_amemcinit)
        .set    noreorder
    
        move    t7,ra
    
        /* 
         * ddr23_phy_init
         */

        li      t0, KSEG1ADDR(AI_DDRPHY_BASE)
        

#ifndef CFG_QUICKTURN

#define PVT_MAX_RETRY       (120)
#define PVT_MATCHED_COUNT   (3)
        li      t2, 0x10000000
        li      t3, 0
        li      t5, 0
2:
        /* Do recalibration */
        beq     t5, PVT_MAX_RETRY, 9f
        lw      a1, DDR23PHY_ZQ_PVT_COMP_CTL(t0)
        ddr_write(DDR23PHY_ZQ_PVT_COMP_CTL, 0x04000000);
1:      
        /* Wait until sample_done == 1 */
        lw      t1, DDR23PHY_ZQ_PVT_COMP_CTL(t0)
        and     t1, t1, t2;
        beq     t1, zero, 1b
        nop

        /* Increase total retry count */
        add     t5, t5, 1
        
        /* Check if the result is the same as previous one */
        lw      t1, DDR23PHY_ZQ_PVT_COMP_CTL(t0)
        beq     t1, a1, 3f
        nop
        
        /* If not, clear matched count */
        b       2b
        li      t3, 0
3:
        /* If so, increase matched count; continue if matched count == 3 */
        add     t3, t3, 1
        bne     t3, PVT_MATCHED_COUNT, 2b
        nop
9:
       
        /* setup PLL */
        ddr_write(DDR23PHY_PLL_CONFIG,      0x8000000c);
        ddr_write(DDR23PHY_PLL_PRE_DIVIDER, 
                0x00000011 + (PLL_NDIV_INT_VAL << 8));
        ddr_write(DDR23PHY_PLL_DIVIDER,     0x02000000);
        ddr_write(DDR23PHY_PLL_CONFIG,      0x80000008);
        
        /* Wait for PLL locked */
        li      t2, 1;
1:      /* Wait until lock == 1 */
        lw      t1, DDR23PHY_PLL_STATUS(t0)
        and     t1, t1, t2;
        beq     t1, zero, 1b
        nop

        /* De-assert PLL reset */
        ddr_write(DDR23PHY_PLL_CONFIG,      0x80000000);
        ddr_write(DDR23PHY_PLL_CONFIG,      0x00000000);

        /* 
         * Calibrate VDL
         */
        
        /* calib_once + calib_fast (for all BL) */
        ddr_write(DDR23PHY_BL3_VDL_CALIBRATE, 0x00000003);
        ddr_write(DDR23PHY_BL2_VDL_CALIBRATE, 0x00000003);
        ddr_write(DDR23PHY_BL1_VDL_CALIBRATE, 0x00000003);
        ddr_write(DDR23PHY_BL0_VDL_CALIBRATE, 0x00000003);
    
        li      t2, 0x0000003;
        li      t0, KSEG1ADDR(AI_DDRPHY_BASE)
1:      /* Wait until calib_idle == 1 and locked for all BL */
        lw      t1, DDR23PHY_BL3_VDL_STATUS(t0)
        and     t1, t1, t2;
        beq     t1, zero, 1b
        nop
        lw      t1, DDR23PHY_BL2_VDL_STATUS(t0)
        and     t1, t1, t2;
        beq     t1, zero, 1b
        nop
        lw      t1, DDR23PHY_BL1_VDL_STATUS(t0)
        and     t1, t1, t2;
        beq     t1, zero, 1b
        nop
        lw      t1, DDR23PHY_BL0_VDL_STATUS(t0)
        and     t1, t1, t2;
        beq     t1, zero, 1b
        nop
    
        /* VDL override */
        lw      t1, DDR23PHY_BL0_VDL_STATUS(t0)
        srl     t1, t1, 8
        andi    t2, t1, 0x3f                        /* VDL step size */
        li      t1, 1
        sll     t1, 16
        or      t2, t2, t1                          /* ovr_en */
        li      t1, 1
        sll     t1, 20
        or      t2, t2, t1                          /* ovr_force */
        sw      t2, DDR23PHY_STATIC_VDL_OVERRIDE(t0)
    
#endif /* !CFG_QUICKTURN */

        /*
         * Memory controller PL341 initialization
         */ 
        
        /* De-assert core reset */
        li      t0, KSEG1ADDR(AI_MEMC_SLAVE_BASE);
        ddr_write(AI_RESETCTRL, 0x00000000)
    
        li      t0, KSEG1ADDR(AI_MEMC_BASE);
        
#ifdef CFG_DDR_REFRESH_PRD
        /* refresh_prd */
        ddr_write(PL341_refresh_prd, MEMCYCLES_MIN(CFG_DDR_REFRESH_PRD));
#endif
        
#ifdef CFG_DDR_CAS_LATENCY
        /* cas_latency */
        ddr_write(PL341_cas_latency, (CFG_DDR_CAS_LATENCY << 1));
#endif
    
#ifdef CFG_DDR_WRITE_LATENCY
        /* write_latency */
        ddr_write(PL341_write_latency, CFG_DDR_WRITE_LATENCY);
#endif
    
#ifdef CFG_DDR_T_MRD
        /* MRD time [6:0] */
        ddr_write(PL341_t_mrd, MEMCYCLES(CFG_DDR_T_MRD));
#endif
    
#ifdef CFG_DDR_T_RAS
        /* ras time [4:0] */
        ddr_write(PL341_t_ras, MEMCYCLES(CFG_DDR_T_RAS));
#endif
    
#ifdef CFG_DDR_T_RC
        /* t_rc [4:0] */
        ddr_write(PL341_t_rc, MEMCYCLES(CFG_DDR_T_RC));
#endif
    
#ifdef CFG_DDR_T_RCD
        /* t_rcd [4:0] */
        ddr_write(PL341_t_rcd, MEMCYCLES(CFG_DDR_T_RCD));
#endif
    
#ifdef CFG_DDR_T_RFC
        /* t_rfc [6:0]  schedule_rfc[14:8] */
        ddr_write(PL341_t_rfc,
            ((MEMCYCLES(CFG_DDR_T_RFC) - 3) << 8) | MEMCYCLES(CFG_DDR_T_RFC));
#endif
    
#ifdef CFG_DDR_T_RP
        /* t_rp [3:0] */
        ddr_write(PL341_t_rp, 
            ((MEMCYCLES(CFG_DDR_T_RP) - 3) << 8) | MEMCYCLES(CFG_DDR_T_RP));
#endif
    
#ifdef CFG_DDR_T_RRD
        /* t_rrd [2:0] */
        ddr_write(PL341_t_rrd, MEMCYCLES(CFG_DDR_T_RRD));
#endif
    
#ifdef CFG_DDR_T_WR
        /* t_wr */
        ddr_write(PL341_t_wr, MEMCYCLES(CFG_DDR_T_WR));
#endif
    
#ifdef CFG_DDR_T_WTR
        /* t_wtr */
        ddr_write(PL341_t_wtr, MEMCYCLES(CFG_DDR_T_WTR));
#endif
    
#ifdef CFG_DDR_T_XP
        /* t_xp[7:0] */
        ddr_write(PL341_t_xp, MEMCYCLES(CFG_DDR_T_XP));
#endif
    
#ifdef CFG_DDR_T_XSR
        /* t_xsr[7:0] */
        ddr_write(PL341_t_xsr, MEMCYCLES(CFG_DDR_T_XSR));
#endif
    
#ifdef CFG_DDR_T_ESR
        /* t_esr[7:0] */
        ddr_write(PL341_t_esr, MEMCYCLES(CFG_DDR_T_ESR));
#endif
    
#ifdef CFG_DDR_T_FAW
        /* t_faw */
        ddr_write(PL341_t_faw, 
            ((MEMCYCLES(CFG_DDR_T_FAW) - 3) << 8) | MEMCYCLES(CFG_DDR_T_FAW));
#endif
    
        /* memory_cfg register */
        ddr_write(PL341_memory_cfg, 
            (2 << 15) | (CFG_DDR_ROW_BITS << 3) | CFG_DDR_COLUMN_BITS
            );
            
        /* Figure out if it's a low cost package */
        #li      s2,PHYS_TO_K1(SI_ENUM_BASE)         # s2 = SI_ENUM_BASE
        lw      t1,CC_CHIPID(s2)
        li      s3,0x00300000
        and     t1,t1,s3
    
        /* memory_cfg2 register */
        li      t2, (CFG_DDR_MEM_WIDTH << 6) | (CFG_DDR_BANK_BITS << 4)
        beq     t1, 0, 1f
        nop
        and     t2, t2, 0xffffff3f /* Set to 16bit for low cost package */
1:
        sw      t2, PL341_memory_cfg2(t0);
    
        /* chip0 configuration */
        ddr_write(PL341_chip_0_cfg, 0x00000000);
        
        /* user_config0 */
        ddr_write(PL341_user_config0, 0x00000003);


        /*
         * DDR2 chip initialization
         */

        /* Issue NOP */
        ddr_write(PL341_direct_cmd, MCHIP_CMD_NOP);
    
        /* issue precharge all */
        ddr_write(PL341_direct_cmd, MCHIP_CMD_PRECHARGE_ALL);
    
        /* Set EMR2 */
        ddr_write(PL341_direct_cmd, MCHIP_CMD_MODE_REG | MCHIP_MODEREG_SEL(2)); 
        
        /* Set EMR3 */
        ddr_write(PL341_direct_cmd, MCHIP_CMD_MODE_REG | MCHIP_MODEREG_SEL(3)); 
        
        /* DLL Enable */
        ddr_write(PL341_direct_cmd, 
            MCHIP_CMD_MODE_REG | MCHIP_MODEREG_SEL(1) | 
            MCHIP_EMR1_DLL_DISABLE(0)
            );
    
        /* DLL Reset */
        ddr_write(PL341_direct_cmd, 
            MCHIP_CMD_MODE_REG | MCHIP_MODEREG_SEL(0) |
            MCHIP_MR_WRITE_RECOVERY(MEMCYCLES(CFG_DDR_T_WR)) |
            MCHIP_MR_DLL_RESET(1) |
            MCHIP_MR_CAS_LATENCY(CFG_DDR_CAS_LATENCY) |
            MCHIP_MR_BURST_LENGTH
            );
    
        /* issue precharge all */
        ddr_write(PL341_direct_cmd, MCHIP_CMD_PRECHARGE_ALL);
    
        /* auto-refresh 2 times */
        ddr_write(PL341_direct_cmd, MCHIP_CMD_AUTO_REFRESH);
        ddr_write(PL341_direct_cmd, MCHIP_CMD_AUTO_REFRESH);
        
        /* DLL Reset=0 (Un-Reset DLL) */
        ddr_write(PL341_direct_cmd, 
            MCHIP_CMD_MODE_REG | MCHIP_MODEREG_SEL(0) |
            MCHIP_MR_WRITE_RECOVERY(MEMCYCLES(CFG_DDR_T_WR)) |
            MCHIP_MR_DLL_RESET(0) | 
            MCHIP_MR_CAS_LATENCY(CFG_DDR_CAS_LATENCY) | 
            MCHIP_MR_BURST_LENGTH
            );
        
        /* DLL Enable & RTT 75ohm */
        ddr_write(PL341_direct_cmd, 
            MCHIP_CMD_MODE_REG | MCHIP_MODEREG_SEL(1) | 
            MCHIP_EMR1_DLL_DISABLE(0) | MCHIP_EMR1_RTT_75_OHM |
            MCHIP_EMR1_OCD_CALI_EXIT
            );
    
        /* OCD Defaults */
        ddr_write(PL341_direct_cmd, 
            MCHIP_CMD_MODE_REG | MCHIP_MODEREG_SEL(1) | 
            MCHIP_EMR1_DLL_DISABLE(0) | MCHIP_EMR1_RTT_75_OHM |
            MCHIP_EMR1_OCD_CALI_DEFAULT
            );
    
        /* OCD Exit */
        ddr_write(PL341_direct_cmd, 
            MCHIP_CMD_MODE_REG | MCHIP_MODEREG_SEL(1) | 
            MCHIP_EMR1_DLL_DISABLE(0) | MCHIP_EMR1_RTT_75_OHM |
            MCHIP_EMR1_OCD_CALI_EXIT
            );
    
        /* set MEMC to GO */
        ddr_write(PL341_memc_cmd, 0);
        nop
        nop
    
        jr      t7
        nop
    
        .set    reorder
END(ai_amemcinit)
