/*
 * Copyright 2017 NXP
 *
 * SPDX-License-Identifier:	GPL-2.0+
 */

#include <common.h>
#include <errno.h>
#include <asm/io.h>
#include <asm/arch/ddr.h>
#include <asm/arch/clock.h>
#include "../ddr.h"

#ifdef CONFIG_ENABLE_DDR_TRAINING_DEBUG
#define ddr_printf(args...) printf(args)
#else
#define ddr_printf(args...)
#endif

#include "../wait_ddrphy_training_complete.c"

volatile unsigned int tmp, tmp_t;
void umctl2_cfg(void){
	reg32_write(DDRC_DBG1(0), 0x00000001);
	reg32_write(DDRC_PWRCTL(0), 0x00000001);
	tmp = reg32_read(DDRC_STAT(0));
	while (tmp_t == 0x00000001){
		tmp = reg32_read(DDRC_STAT(0));
		tmp_t = tmp && 0x00000001;
	}

	reg32_write(DDRC_MSTR(0), 0x83040010); /* Two ranks */

	reg32_write(DDRC_MRCTRL0(0), 0x40007030);
	reg32_write(DDRC_MRCTRL1(0), 0x000170df);
	reg32_write(DDRC_MRCTRL2(0), 0x97d37be3);
	reg32_write(DDRC_DERATEEN(0), 0x00000302);
	reg32_write(DDRC_DERATEINT(0), 0xbc808cc7);
	reg32_write(DDRC_MSTR2(0), 0x00000001);
	reg32_write(DDRC_PWRCTL(0), 0x000001ae);
	reg32_write(DDRC_PWRTMG(0), 0x000d2800);
	reg32_write(DDRC_HWLPCTL(0), 0x000c0000);
	reg32_write(DDRC_HWFFCCTL(0), 0x00000010);
	reg32_write(DDRC_RFSHCTL0(0), 0x007090b0);
	reg32_write(DDRC_RFSHCTL1(0), 0x00420019);
	reg32_write(DDRC_RFSHCTL3(0), 0x00000010);
	reg32_write(DDRC_RFSHTMG(0), 0x0049009d);
	reg32_write(DDRC_CRCPARCTL0(0), 0x00000000);
	reg32_write(DDRC_CRCPARCTL1(0), 0x00001011);
	reg32_write(DDRC_INIT0(0), 0xc0030002);
	reg32_write(DDRC_INIT1(0), 0x00030006);
	reg32_write(DDRC_INIT2(0), 0x00000305);
	reg32_write(DDRC_INIT3(0), 0x0a300001);
	reg32_write(DDRC_INIT4(0), 0x10180240);
	reg32_write(DDRC_INIT5(0), 0x0011008a);
	reg32_write(DDRC_INIT6(0), 0x0a000042);
	reg32_write(DDRC_INIT7(0), 0x00000800);
	reg32_write(DDRC_DIMMCTL(0), 0x00000032); /* [1] dimm_addr_mirr_en, it will effect the MRS if use umctl2 to initi dram. */
	reg32_write(DDRC_RANKCTL(0), 0x00000530);
	reg32_write(DDRC_DRAMTMG0(0), 0x14132813);
	reg32_write(DDRC_DRAMTMG1(0), 0x0007051b);
	reg32_write(DDRC_DRAMTMG2(0), 0x090a050f);
	reg32_write(DDRC_DRAMTMG3(0), 0x0000f00f);
	reg32_write(DDRC_DRAMTMG4(0), 0x08030409);
	reg32_write(DDRC_DRAMTMG5(0), 0x0c0d0504);
	reg32_write(DDRC_DRAMTMG6(0), 0x00000003);
	reg32_write(DDRC_DRAMTMG7(0), 0x00000d0c);
	reg32_write(DDRC_DRAMTMG8(0), 0x05051f09);
	reg32_write(DDRC_DRAMTMG9(0), 0x0002040c);
	reg32_write(DDRC_DRAMTMG10(0), 0x000e0d0b);
	reg32_write(DDRC_DRAMTMG11(0), 0x1409011e);
	reg32_write(DDRC_DRAMTMG12(0), 0x0000000d);
	reg32_write(DDRC_DRAMTMG13(0), 0x09000000);
	reg32_write(DDRC_DRAMTMG14(0), 0x00000371);
	reg32_write(DDRC_DRAMTMG15(0), 0x80000000);
	reg32_write(DDRC_DRAMTMG17(0), 0x0076006e);
	reg32_write(DDRC_ZQCTL0(0), 0x51000040);
	reg32_write(DDRC_ZQCTL1(0), 0x00000070);
	reg32_write(DDRC_ZQCTL2(0), 0x00000000);
	reg32_write(DDRC_DFITMG0(0), 0x038f820c);
	reg32_write(DDRC_DFITMG1(0), 0x00020103);
	reg32_write(DDRC_DFILPCFG0(0), 0x07e1b011);
	reg32_write(DDRC_DFILPCFG1(0), 0x00000030);
	reg32_write(DDRC_DFIUPD0(0), 0xe0400018);
	reg32_write(DDRC_DFIUPD1(0), 0x004e00c3);
	reg32_write(DDRC_DFIUPD2(0), 0x00000000);
	reg32_write(DDRC_DFIMISC(0), 0x00000001);
	reg32_write(DDRC_DFITMG2(0), 0x00000f0c);
	reg32_write(DDRC_DFITMG3(0), 0x00000001);
	reg32_write(DDRC_DBICTL(0), 0x00000000);
	reg32_write(DDRC_DFIPHYMSTR(0), 0x00000000);

	reg32_write(DDRC_ADDRMAP0(0), 0x00001F17); /* [4:0]cs0: 6+23 */
	reg32_write(DDRC_ADDRMAP1(0), 0x003F0808); /* [5:0] bank b0: 2+8; [13:8] b1: P3+8 ; [21:16] b2: 4+, unused */
	reg32_write(DDRC_ADDRMAP2(0), 0x00000000); /* [3:0] col-b2: 2;  [11:8] col-b3: 3+0; [19:16] col-b4: 4+0 ; [27:24] col-b5: 5+0 */
	reg32_write(DDRC_ADDRMAP3(0), 0x00000000); /* [3:0] col-b6: 6+0;  [11:8] col-b7: 7+0; [19:16] col-b8: 8+0 ; [27:24] col-b9: 9+0 */
	reg32_write(DDRC_ADDRMAP4(0), 0x00001f1f); /* col-b10, col-b11 not used */
	reg32_write(DDRC_ADDRMAP5(0), 0x07070707); /* [3:0] row-b0: 6+7;  [11:8] row-b1: 7+7; [19:16] row-b2_b10: 8~16+7; [27:24] row-b11: 17+7 */
	reg32_write(DDRC_ADDRMAP6(0), 0x07070707); /* [3:0] row-b12:18+7; [11:8] row-b13: 19+7; [19:16] row-b14:20+7 */
	reg32_write(DDRC_ADDRMAP7(0), 0x00000f0f); /* col-b10, col-b11 not used */
	reg32_write(DDRC_ADDRMAP8(0), 0x00003F0A); /* [5:0] bg-b0: 2+10; [13:8]bg-b1:3+, unused */
	reg32_write(DDRC_ADDRMAP9(0), 0x00000000); /*  it's valid only when ADDRMAP5.addrmap_row_b2_10 is set to value 15 */
	reg32_write(DDRC_ADDRMAP10(0), 0x00000000);/*  it's valid only when ADDRMAP5.addrmap_row_b2_10 is set to value 15 */
	reg32_write(DDRC_ADDRMAP11(0), 0x00000000);


	reg32_write(DDRC_ODTCFG(0), 0x05170558);
	reg32_write(DDRC_ODTMAP(0), 0x00002113);
	reg32_write(DDRC_SCHED(0), 0x0d6f0705);
	reg32_write(DDRC_SCHED1(0), 0x00000000);
	reg32_write(DDRC_PERFHPR1(0), 0xe500558b);
	reg32_write(DDRC_PERFLPR1(0), 0x75001fea);
	reg32_write(DDRC_PERFWR1(0), 0x880026c7);
	reg32_write(DDRC_DBG0(0), 0x00000011);
	reg32_write(DDRC_DBG1(0), 0x00000000);
	reg32_write(DDRC_DBGCMD(0), 0x00000000);
	reg32_write(DDRC_SWCTL(0), 0x00000001);
	reg32_write(DDRC_POISONCFG(0), 0x00100011);
	reg32_write(DDRC_PCCFG(0), 0x00000100);
	reg32_write(DDRC_PCFGR_0(0), 0x00015313);
	reg32_write(DDRC_PCFGW_0(0), 0x000050dc);
	reg32_write(DDRC_PCTRL_0(0), 0x00000001);
	reg32_write(DDRC_PCFGQOS0_0(0), 0x01100200);
	reg32_write(DDRC_PCFGQOS1_0(0), 0x01ba023a);
	reg32_write(DDRC_PCFGWQOS0_0(0), 0x00110000);
	reg32_write(DDRC_PCFGWQOS1_0(0), 0x0000001e);
}

void ddr_init(void)
{
	/* change the clock source of dram_apb_clk_root  */
	reg32_write(CCM_IP_CLK_ROOT_GEN_TAGET_CLR(1),(0x7<<24)|(0x7<<16));
	reg32_write(CCM_IP_CLK_ROOT_GEN_TAGET_SET(1),(0x4<<24)|(0x3<<16));

	/* disable the clock gating */
	reg32_write(0x303A00EC,0x0000ffff);
	reg32setbit(0x303A00F8,5);
	reg32_write(SRC_DDRC_RCR_ADDR + 0x04, 0x8F000000);

	dram_pll_init(DRAM_PLL_OUT_600M);

	reg32_write(SRC_DDRC_RCR_ADDR, 0x8F000006);

	/* Configure uMCTL2's registers */
	umctl2_cfg();

	tmp = reg32_read(DDRC_RFSHCTL3(0));
	reg32_write(DDRC_RFSHCTL3(0), 0x00000011);

	reg32_write(SRC_DDRC_RCR_ADDR, 0x8F000000);

	ddr_load_train_code(FW_1D_IMAGE);

	reg32_write(DDRC_DBG1(0), 0x00000000);
	tmp = reg32_read(DDRC_PWRCTL(0));
	reg32_write(DDRC_PWRCTL(0), 0x000001ae);
	tmp = reg32_read(DDRC_PWRCTL(0));
	reg32_write(DDRC_PWRCTL(0), 0x000001ac);
	reg32_write(DDRC_SWCTL(0), 0x00000000);
	tmp = reg32_read(DDRC_CRCPARSTAT(0));

	reg32_write(DDRC_DFIMISC(0), 0x00000000);
	reg32_write(DDRC_DFIMISC(0), 0x00000000);

	tmp = reg32_read(DDRC_DBICTL(0));
	tmp = reg32_read(DDRC_MSTR(0));
	tmp = reg32_read(DDRC_INIT3(0));
	tmp = reg32_read(DDRC_INIT4(0));
	tmp = reg32_read(DDRC_INIT6(0));
	tmp = reg32_read(DDRC_INIT7(0));
	tmp = reg32_read(DDRC_INIT0(0));

	ddr4_phyinit_train_2400mts();

	do {
		tmp_t = reg32_read(IP2APB_DDRPHY_IPS_BASE_ADDR(0) + 4*0x00020097);
	} while (tmp_t != 0);


	reg32_write(DDRC_DFIMISC(0), 0x00000020);

	/*  wait DFISTAT.dfi_init_complete to 1 */
	tmp_t = 0;
	while(tmp_t == 0){
		tmp  = reg32_read(DDRC_DFISTAT(0));
		tmp_t = tmp & 0x01;
	}

	/*  clear DFIMISC.dfi_init_complete_en */
	reg32_write(DDRC_DFIMISC(0), 0x00000000);
	/*  set DFIMISC.dfi_init_complete_en again */
	reg32_write(DDRC_DFIMISC(0), 0x00000001);
	reg32_write(DDRC_PWRCTL(0), 0x0000018c);

	/*  set SWCTL.sw_done to enable quasi-dynamic register programming outside reset .*/
	reg32_write(DDRC_SWCTL(0), 0x00000001);

	/* wait SWSTAT.sw_done_ack to 1 */
	tmp_t = 0;
	while(tmp_t==0){
		tmp  = reg32_read(DDRC_SWSTAT(0));
		tmp_t = tmp & 0x01;
	}

	/* wait STAT to normal state */
	tmp_t = 0;
	while(tmp_t==0){
		tmp  = reg32_read(DDRC_STAT(0));
		tmp_t = tmp & 0x01;
	}

	tmp = reg32_read(DDRC_CRCPARSTAT(0));

	reg32_write(DDRC_PWRCTL(0), 0x0000018c);

	reg32_write(DDRC_DERATEEN(0), 0x00000302);

	reg32_write(DDRC_PCTRL_0(0), 0x00000001);

	reg32_write(DDRC_RFSHCTL3(0), 0x00000010); /*  dis_auto-refresh is set to 0 */

	reg32_write(IP2APB_DDRPHY_IPS_BASE_ADDR(0) + 4*(0xd0000), 0);
	tmp = reg32_read(IP2APB_DDRPHY_IPS_BASE_ADDR(0) + 4*(0x54030));
	tmp = reg32_read(IP2APB_DDRPHY_IPS_BASE_ADDR(0) + 4*(0x54035));
	reg32_write(IP2APB_DDRPHY_IPS_BASE_ADDR(0) + 4*(0xd0000), 1);
}
