/*
 * esdctl-v4.c - i.MX sdram controller functions for i.MX53
 *
 * Copyright (c) 2012 Sascha Hauer <s.hauer@pengutronix.de>, Pengutronix
 *
 * See file CREDITS for list of people who contributed to this
 * project.
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2
 * as published by the Free Software Foundation.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 */

#include <common.h>
#include <io.h>
#include <mach/esdctl-v4.h>
#include <mach/imx53-regs.h>
#include <asm/system.h>

void imx_esdctlv4_do_write_leveling(void)
{
	u32 val;
	void __iomem *base = IOMEM(MX53_ESDCTL_BASE_ADDR);

	/* switch RAMs to write-leveling mode */

	val = ESDCTL_V4_DDR3_MR1_ODIC_RZQ7 | ESDCTL_V4_DDR3_MR1_RTTN_DIS |
		ESDCTL_V4_DDR3_MR1_AL_DISABLE | ESDCTL_V4_DDR3_MR1_WL |
		ESDCTL_V4_ESDSCR_CON_REQ | ESDCTL_V4_ESDSCR_CMD_LMR |
		ESDCTL_V4_ESDSCR_CMD_CS0 | ESDCTL_V4_ESDSCR_WL_EN |
		ESDCTL_V4_DDR3_REG_MR1;
	writel(val, base + ESDCTL_V4_ESDSCR);

	val = ESDCTL_V4_DDR3_MR1_ODIC_RZQ7 | ESDCTL_V4_DDR3_MR1_RTTN_DIS |
		ESDCTL_V4_DDR3_MR1_AL_DISABLE |	ESDCTL_V4_DDR3_MR1_WL |
		ESDCTL_V4_ESDSCR_CON_REQ | ESDCTL_V4_ESDSCR_CMD_LMR |
		ESDCTL_V4_ESDSCR_CMD_CS1 | ESDCTL_V4_ESDSCR_WL_EN |
		ESDCTL_V4_DDR3_REG_MR1;
	writel(val, base + ESDCTL_V4_ESDSCR);

	/* start write leveling */
	writel(ESDCTL_V4_WLGCR_HW_WL_EN, base + ESDCTL_V4_WLGCR);

	do {
		val = readl(base + ESDCTL_V4_WLGCR);
	} while (val & ESDCTL_V4_WLGCR_HW_WL_EN);

	val &= ESDCTL_V4_WLGCR_WL_HW_ERR3 | ESDCTL_V4_WLGCR_WL_HW_ERR2 |
		ESDCTL_V4_WLGCR_WL_HW_ERR1 | ESDCTL_V4_WLGCR_WL_HW_ERR0;

	if (val)
		hang();

	/*
	 * manual still talks about HW_WLx_CYC fields that
	 * must be updated from HW_WLx_RES field by SW, but none
	 * of these fields seem to exist. Closest equivalents
	 * are WL_CYC_DELx-fields but no RES-fields can be found.
	 */

	/* configure RAMs back to normal operation */
	val = ESDCTL_V4_DDR3_MR1_ODIC_RZQ7 | ESDCTL_V4_DDR3_MR1_RTTN_RZQ2 |
		ESDCTL_V4_DDR3_MR1_AL_DISABLE | ESDCTL_V4_ESDSCR_CON_REQ |
		ESDCTL_V4_ESDSCR_CMD_LMR | ESDCTL_V4_ESDSCR_CMD_CS0 |
		ESDCTL_V4_DDR3_REG_MR1;
	writel(val, base + ESDCTL_V4_ESDSCR);

	val = ESDCTL_V4_DDR3_MR1_ODIC_RZQ7 | ESDCTL_V4_DDR3_MR1_RTTN_RZQ2 |
		ESDCTL_V4_DDR3_MR1_AL_DISABLE | ESDCTL_V4_ESDSCR_CON_REQ |
		ESDCTL_V4_ESDSCR_CMD_LMR | ESDCTL_V4_ESDSCR_CMD_CS1 |
		ESDCTL_V4_DDR3_REG_MR1;
	writel(val, base + ESDCTL_V4_ESDSCR);
}

void imx_esdctlv4_do_dqs_gating(void)
{
	u32 val;
	void __iomem *base = IOMEM(MX53_ESDCTL_BASE_ADDR);

	/* configure ESDCTL comparator to use MPR pattern */
	writel(ESDCTL_V4_PDCMPR2_MPR_FULL_CMP | ESDCTL_V4_PDCMPR2_MPR_CMP,
			base + ESDCTL_V4_PDCMPR2);

	/* pre-charge all RAM banks */
	writel(ESDCTL_V4_ESDSCR_CON_REQ | ESDCTL_V4_ESDSCR_CMD_CS0 |
			ESDCTL_V4_ESDSCR_CMD_PRE_ALL, base + ESDCTL_V4_ESDSCR);
	writel(ESDCTL_V4_ESDSCR_CON_REQ | ESDCTL_V4_ESDSCR_CMD_CS1 |
			ESDCTL_V4_ESDSCR_CMD_PRE_ALL, base + ESDCTL_V4_ESDSCR);

	/* configure RAMs to generate MPR pattern */
	writel(ESDCTL_V4_DDR3_MR3_MPR_ENABLE | ESDCTL_V4_DDR3_MR3_MPR_PATTERN |
			ESDCTL_V4_ESDSCR_CMD_CS0 | ESDCTL_V4_ESDSCR_CON_REQ |
			ESDCTL_V4_ESDSCR_CMD_LMR | ESDCTL_V4_DDR3_REG_MR3,
			base + ESDCTL_V4_ESDSCR);
	writel(ESDCTL_V4_DDR3_MR3_MPR_ENABLE | ESDCTL_V4_DDR3_MR3_MPR_PATTERN |
			ESDCTL_V4_ESDSCR_CMD_CS1 | ESDCTL_V4_ESDSCR_CON_REQ |
			ESDCTL_V4_ESDSCR_CMD_LMR | ESDCTL_V4_DDR3_REG_MR3,
			base + ESDCTL_V4_ESDSCR);

	/* start HW DQS gating */
	writel(ESDCTL_V4_ESDDGCTRL0_HW_DG_EN, base + ESDCTL_V4_DGCTRL0);

	do {
		val = readl(base + ESDCTL_V4_DGCTRL0);
	} while (val & ESDCTL_V4_ESDDGCTRL0_HW_DG_EN);

	if (val & ESDCTL_V4_ESDDGCTRL0_HW_DG_ERR)
		hang();

	/* configure RAMs back to normal operation */
	val = ESDCTL_V4_DDR3_MR3_MPR_DISABLE | ESDCTL_V4_ESDSCR_CMD_CS0 |
		ESDCTL_V4_ESDSCR_CON_REQ | ESDCTL_V4_ESDSCR_CMD_LMR |
		ESDCTL_V4_DDR3_REG_MR3;
	writel(val, base + ESDCTL_V4_ESDSCR);

	val = ESDCTL_V4_DDR3_MR3_MPR_DISABLE | ESDCTL_V4_ESDSCR_CMD_CS1 |
		ESDCTL_V4_ESDSCR_CON_REQ | ESDCTL_V4_ESDSCR_CMD_LMR |
		ESDCTL_V4_DDR3_REG_MR3;
	writel(val, base + ESDCTL_V4_ESDSCR);
}

void imx_esdctlv4_do_zq_calibration(void)
{
	u32 val;
	void __iomem *base = IOMEM(MX53_ESDCTL_BASE_ADDR);

	/*
	 * configure ZQ parameters
	 * Note: TZQ_CS is only required to be 64cy by Jedec and RAM
	 * manufacturers and i.MX53TO1 provides a 64cy setting, but
	 * TO2 according to official documentation not...
	 */
	val = ESDCTL_V4_ESDZQHWC_ZQ_PARA_EN |
		(ESDCTL_V4_ESDZQHWC_128CYC << ESDCTL_V4_ESDZQHWC_TZQ_CS_SHIFT) |
		(ESDCTL_V4_ESDZQHWC_256CYC << ESDCTL_V4_ESDZQHWC_TZQ_OPER_SHIFT) |
		(ESDCTL_V4_ESDZQHWC_512CYC << ESDCTL_V4_ESDZQHWC_TZQ_INIT_SHIFT) |
		(0 << ESDCTL_V4_ESDZQHWC_ZQ_HW_PD_RES_SHIFT) |
		(0 << ESDCTL_V4_ESDZQHWC_ZQ_HW_PU_RES_SHIFT) |
		(0 << ESDCTL_V4_ESDZQHWC_ZQ_HW_PER_SHIFT) |
		ESDCTL_V4_ESDZQHWC_ZQ_MODE_BOTH_PER |
		ESDCTL_V4_ESDZQHWC_ZQ_HW_FOR;

	/* force ZQ calibration */
	writel(val, base + ESDCTL_V4_ZQHWCTRL);

	while (readl(base + ESDCTL_V4_ZQHWCTRL) & ESDCTL_V4_ESDZQHWC_ZQ_HW_FOR);
}

/*
 * start-up a DDR3 SDRAM chip
 */
void imx_esdctlv4_start_ddr3_sdram(int cs)
{
	void __iomem *base = IOMEM(MX53_ESDCTL_BASE_ADDR);
	u32 val;
	u32 val_cs1;

	if (cs)
		val_cs1 = ESDCTL_V4_ESDSCR_CMD_CS1;
	else
		val_cs1 = ESDCTL_V4_ESDSCR_CMD_CS0;

	/* initialize MR2 */
	val = ESDCTL_V4_DDR3_MR2_RTTWR_OFF | ESDCTL_V4_DDR3_MR2_SRT_NORMAL |
		ESDCTL_V4_DDR3_MR2_CWL_5 | ESDCTL_V4_DDR3_MR2_PASR_1_1 |
		ESDCTL_V4_ESDSCR_CON_REQ | ESDCTL_V4_ESDSCR_CMD_LMR |
		ESDCTL_V4_DDR3_REG_MR2 | val_cs1;
	writel(val, base + ESDCTL_V4_ESDSCR);

	/* initialize MR3 */
	val =  ESDCTL_V4_DDR3_MR3_MPR_DISABLE | ESDCTL_V4_ESDSCR_CON_REQ |
		ESDCTL_V4_ESDSCR_CMD_LMR | ESDCTL_V4_DDR3_REG_MR3 | val_cs1;
	writel(val, base + ESDCTL_V4_ESDSCR);

	/* initialize MR1 */
	val = ESDCTL_V4_DDR3_MR1_ODIC_RZQ7 | ESDCTL_V4_DDR3_MR1_RTTN_RZQ2 |
		ESDCTL_V4_DDR3_MR1_AL_DISABLE | ESDCTL_V4_ESDSCR_CON_REQ |
		ESDCTL_V4_ESDSCR_CMD_LMR | ESDCTL_V4_DDR3_REG_MR1 | val_cs1;
	writel(val, base + ESDCTL_V4_ESDSCR);

	/* initialize MR0 */
	val = ESDCTL_V4_DDR3_MR0_PPD_SLOW | ESDCTL_V4_DDR3_MR0_WR_6 |
		ESDCTL_V4_DDR3_MR0_CL_6 | ESDCTL_V4_DDR3_MR0_BL_FIXED8 |
		ESDCTL_V4_DDR3_MR0_RBT_NIBBLE | ESDCTL_V4_DDR3_DLL_RESET |
		ESDCTL_V4_ESDSCR_CON_REQ | ESDCTL_V4_ESDSCR_CMD_LMR |
		ESDCTL_V4_DDR3_REG_MR0 | val_cs1;

	if (cs)
		val = ESDCTL_V4_ESDSCR_DLL_RST1;
	else
		val = ESDCTL_V4_ESDSCR_DLL_RST0;

	writel(val, base + ESDCTL_V4_ESDSCR);

	/* perform ZQ calibration */
	val = 0x04000000 | ESDCTL_V4_ESDSCR_CON_REQ |
		ESDCTL_V4_ESDSCR_CMD_ZQCALIB_OLD;
	val |= val_cs1;
	writel(val, base + ESDCTL_V4_ESDSCR);
}

void imx_esdctlv4_do_read_delay_line_calibration(void)
{
	void __iomem *base = IOMEM(MX53_ESDCTL_BASE_ADDR);
	u32 val;

	/* configure ESDCTL comparator to use MPR pattern */
	val = ESDCTL_V4_PDCMPR2_MPR_FULL_CMP | ESDCTL_V4_PDCMPR2_MPR_CMP;
	writel(val, base + ESDCTL_V4_PDCMPR2);

	/* pre-charge all RAM banks */
	val = ESDCTL_V4_ESDSCR_CON_REQ | ESDCTL_V4_ESDSCR_CMD_CS0 |
		ESDCTL_V4_ESDSCR_CMD_PRE_ALL;
	writel(val, base + ESDCTL_V4_ESDSCR);

	val = ESDCTL_V4_ESDSCR_CON_REQ | ESDCTL_V4_ESDSCR_CMD_CS1 |
		ESDCTL_V4_ESDSCR_CMD_PRE_ALL;
	writel(val, base + ESDCTL_V4_ESDSCR);

	/* configure RAMs to generate MPR pattern */
	val = ESDCTL_V4_DDR3_MR3_MPR_ENABLE | ESDCTL_V4_DDR3_MR3_MPR_PATTERN |
		ESDCTL_V4_ESDSCR_CMD_CS0 | ESDCTL_V4_ESDSCR_CON_REQ |
		ESDCTL_V4_ESDSCR_CMD_LMR | ESDCTL_V4_DDR3_REG_MR3;
	writel(val, base + ESDCTL_V4_ESDSCR);

	val = ESDCTL_V4_DDR3_MR3_MPR_ENABLE | ESDCTL_V4_DDR3_MR3_MPR_PATTERN |
		ESDCTL_V4_ESDSCR_CMD_CS1 | ESDCTL_V4_ESDSCR_CON_REQ |
		ESDCTL_V4_ESDSCR_CMD_LMR | ESDCTL_V4_DDR3_REG_MR3;
	writel(val, base + ESDCTL_V4_ESDSCR);

	/* start read delay-line calibration */
	writel(ESDCTL_V4_RDDLHWCTL_HW_RDL_EN, base + ESDCTL_V4_RDDLHWCTL);

	do {
		val = readl(base + ESDCTL_V4_RDDLHWCTL);
	} while (val & ESDCTL_V4_RDDLHWCTL_HW_RDL_EN);

	val &= ESDCTL_V4_RDDLHWCTL_HW_RDL_ERR3 |
		ESDCTL_V4_RDDLHWCTL_HW_RDL_ERR2 |
		ESDCTL_V4_RDDLHWCTL_HW_RDL_ERR1 |
		ESDCTL_V4_RDDLHWCTL_HW_RDL_ERR0;

	if (val)
		hang();

	/* configure RAMs back to normal operation */
	val = ESDCTL_V4_DDR3_MR3_MPR_DISABLE | ESDCTL_V4_ESDSCR_CMD_CS0 |
		ESDCTL_V4_ESDSCR_CON_REQ | ESDCTL_V4_ESDSCR_CMD_LMR |
		ESDCTL_V4_DDR3_REG_MR3;
	writel(val, base + ESDCTL_V4_ESDSCR);

	val = ESDCTL_V4_DDR3_MR3_MPR_DISABLE | ESDCTL_V4_ESDSCR_CMD_CS1 |
		ESDCTL_V4_ESDSCR_CON_REQ | ESDCTL_V4_ESDSCR_CMD_LMR |
		ESDCTL_V4_DDR3_REG_MR3;
	writel(val, base + ESDCTL_V4_ESDSCR);
}

void imx_esdctlv4_do_write_delay_line_calibration(void)
{
	void __iomem *base = IOMEM(MX53_ESDCTL_BASE_ADDR);
	void __iomem *adr;
	u32 val;

	/*
	 * configure ESCTL to normal operation so we can
	 * write the compare values
	 */
	writel(ESDCTL_V4_ESDSCR_CMD_NOP, base + ESDCTL_V4_ESDSCR);

	/* write test-pattern to RAM */

	/* ESCTL uses this address for calibration */
	adr = IOMEM(MX53_CSD0_BASE_ADDR) + 0x10000000;
	writel(0, adr + 0x00);
	writel(0, adr + 0x0c);
	writel(0, adr + 0x10);
	writel(0, adr + 0x1c);
	writel(0, adr + 0x20);
	writel(0, adr + 0x2c);
	writel(0, adr + 0x30);
	writel(0, adr + 0x4c);
	writel(0xffffffff, adr + 0x04);
	writel(0xffffffff, adr + 0x08);
	writel(0xffffffff, adr + 0x14);
	writel(0xffffffff, adr + 0x18);
	writel(0xffffffff, adr + 0x24);
	writel(0xffffffff, adr + 0x28);
	writel(0xffffffff, adr + 0x34);
	writel(0xffffffff, adr + 0x48);

	/* pre-charge all RAM banks */
	val = ESDCTL_V4_ESDSCR_CON_REQ | ESDCTL_V4_ESDSCR_CMD_CS0 |
		ESDCTL_V4_ESDSCR_CMD_PRE_ALL;
	writel(val, base + ESDCTL_V4_ESDSCR);

	val = ESDCTL_V4_ESDSCR_CON_REQ | ESDCTL_V4_ESDSCR_CMD_CS1 |
		ESDCTL_V4_ESDSCR_CMD_PRE_ALL;
	writel(val, base + ESDCTL_V4_ESDSCR);

	/* write test-pattern to ESCTL */
	writel(0x00ffff00, base + ESDCTL_V4_PDCMPR1);
	writel(0, base + ESDCTL_V4_PDCMPR2);

	/* start write delay-line calibration */
	writel(ESDCTL_V4_WRDLHWCTL_HW_WDL_EN, base + ESDCTL_V4_WRDLHWCTL);

	do {
		val = readl(base + ESDCTL_V4_WRDLHWCTL);
	} while (val & ESDCTL_V4_WRDLHWCTL_HW_WDL_EN);

	val &= ESDCTL_V4_WRDLHWCTL_HW_WDL_ERR3 |
		ESDCTL_V4_WRDLHWCTL_HW_WDL_ERR2 |
		ESDCTL_V4_WRDLHWCTL_HW_WDL_ERR1 |
		ESDCTL_V4_WRDLHWCTL_HW_WDL_ERR0;

	if (val)
		hang();
}

#define SDRAM_COMPARE_CONST1 0x12345678
#define SDRAM_COMPARE_CONST2 0xAAAAAAAA

/*
 * write magic values to RAM for testing purposes
 */
static void imx_esdctlv4_write_magic_values(void __iomem *adr)
{
	/*
	 * Freescale asks for first access to be a write to properly
	 * initialize DQS pin-state and keepers
	 */
	writel(SDRAM_COMPARE_CONST1, adr);

	/*
	 * write two magic constants to RAM, to test for bus-size and
	 * row-/column-configuraion
	 */
	writel(SDRAM_COMPARE_CONST1, adr);
	writel(SDRAM_COMPARE_CONST2, adr + 4);
	dsb();
}

/*
 * check if given DRAM addresses match expected values for row/col configuration
 */
static u32 check_ram_address_line(void __iomem *adr, u32 compare, u32 mask)
{
	u32 val;

	val = readl(adr);	/* load data from RAM-address to check */
	val &= mask;		/* mask data for our bus-size */

	if (compare == val)	/* compare read data with expected value */
		return 1;	/* if data is identical, update ESDCTL configuration */
	else
		return 0;
}

/*
 * determine RAM chip-density and configure tRFC and tXS accordingly
 */
void imx_esdctlv4_set_tRFC_timing(void)
{
	void __iomem *base = IOMEM(MX53_ESDCTL_BASE_ADDR);
	u32 val, trfc, r2, esdcfg;

	/* determine chip-density */
	val = readl(base + ESDCTL_V4_ESDMISC);
	if ((val & ESDCTL_V4_ESDMISC_BANKS_MASK) == ESDCTL_V4_ESDMISC_BANKS_4)
		r2 = 2;
	else
		r2 = 3;

	val = readl(base + ESDCTL_V4_ESDCTL0);
	if ((val & ESDCTL_V4_ESDCTLx_DSIZ_MASK) == ESDCTL_V4_ESDCTLx_DSIZ_32B)
		r2 += 1;

	switch (val & ESDCTL_V4_ESDCTLx_ROW_MASK) {
		case ESDCTL_V4_ESDCTLx_ROW_11: r2 += 1; break;
		case ESDCTL_V4_ESDCTLx_ROW_12: r2 += 2; break;
		case ESDCTL_V4_ESDCTLx_ROW_13: r2 += 3; break;
		case ESDCTL_V4_ESDCTLx_ROW_14: r2 += 4; break;
		case ESDCTL_V4_ESDCTLx_ROW_15: r2 += 5; break;
		case ESDCTL_V4_ESDCTLx_ROW_16: r2 += 6; break;
	}

	switch (val & ESDCTL_V4_ESDCTLx_COL_MASK) {
		case ESDCTL_V4_ESDCTLx_COL_8: r2 += 8; break;
		case ESDCTL_V4_ESDCTLx_COL_9: r2 += 9; break;
		case ESDCTL_V4_ESDCTLx_COL_10: r2 += 10; break;
		case ESDCTL_V4_ESDCTLx_COL_11: r2 += 11; break;
		case ESDCTL_V4_ESDCTLx_COL_12: r2 += 12; break;
	}

	/* save current tRFC timing */
	esdcfg = readl(base + ESDCTL_V4_ESDCFG0);

	trfc = (esdcfg & ESDCTL_V4_ESDCFG0_tRFC_MASK) >> ESDCTL_V4_ESDCFG0_tRFC_SHIFT;

	/* clear tRFC and tXS timings */
	esdcfg &= ~(ESDCTL_V4_ESDCFG0_tRFC_MASK | ESDCTL_V4_ESDCFG0_tXS_MASK);

	/*
	 * determine tRFC depending on density
	 * (the timings and density-associations are taken
	 * from JEDEC JESD79-3E DDR3-RAM specification)
	 */
	if (r2 >= 16)
		trfc = 36 - 1;
	if (r2 >= 17)
		trfc = 44 - 1;
	if (r2 >= 18)
		trfc = 64 - 1;
	if (r2 >= 19)
		trfc = 120 - 1;
	if (r2 >= 20)
		trfc = 140 - 1;

	/* calculate new tRFC and tXS timings */
	esdcfg |= (trfc << ESDCTL_V4_ESDCFG0_tRFC_SHIFT) |
		(trfc + 4) << ESDCTL_V4_ESDCFG0_tXS_SHIFT;

	writel(esdcfg, base + ESDCTL_V4_ESDCFG0);
}

/*
 * disable chip-selects that are not equipped
 */
void imx_esdctlv4_detect_sdrams(void)
{
	void __iomem *base = IOMEM(MX53_ESDCTL_BASE_ADDR);
	u32 esdctl0;

	esdctl0 = readl(base + ESDCTL_V4_ESDCTL0);

	writel(MX53_CSD0_BASE_ADDR, MX53_CSD0_BASE_ADDR);
	writel(MX53_CSD1_BASE_ADDR, MX53_CSD1_BASE_ADDR);

	if (readl(MX53_CSD0_BASE_ADDR) != MX53_CSD0_BASE_ADDR)
		esdctl0 &= ~ESDCTL_V4_ESDCTLx_SDE0;
	if (readl(MX53_CSD1_BASE_ADDR) != MX53_CSD1_BASE_ADDR)
		esdctl0 &= ~ESDCTL_V4_ESDCTLx_SDE1;

	writel(esdctl0, base + ESDCTL_V4_ESDCTL0);
}

void imx_esdctlv4_init(void)
{
	void __iomem *base = IOMEM(MX53_ESDCTL_BASE_ADDR);
	u32 val, r1, esdctl0, mask, rows, cols;

	/*
	 * assume worst-case here: 4Gb chips. this will be optimized
	 * further down, when we can determine the actual chip density
	 * in imx_esdctlv4_set_tRFC_timing()
	 */
	val = (((140 - 1) << ESDCTL_V4_ESDCFG0_tRFC_SHIFT) |
				  ((144 - 1) << ESDCTL_V4_ESDCFG0_tXS_SHIFT) |
				  ((3 - 1) << ESDCTL_V4_ESDCFG0_tXP_SHIFT) |
				  ((10 - 1) << ESDCTL_V4_ESDCFG0_tXPDLL_SHIFT) |
				  ((20 - 1) << ESDCTL_V4_ESDCFG0_tFAW_SHIFT) |
				  ((6 - 3) << ESDCTL_V4_ESDCFG0_tCL_SHIFT));
	writel(val, base + ESDCTL_V4_ESDCFG0);

	val = (((6 - 1) << ESDCTL_V4_ESDCFG1_tRCD_SHIFT) |
				  ((6 - 1) << ESDCTL_V4_ESDCFG1_tRP_SHIFT) |
				  ((21 - 1) << ESDCTL_V4_ESDCFG1_tRC_SHIFT) |
				  ((15 - 1) << ESDCTL_V4_ESDCFG1_tRAS_SHIFT) |
				  (1 << ESDCTL_V4_ESDCFG1_tRPA_SHIFT) |
				  ((6 - 1) << ESDCTL_V4_ESDCFG1_tWR_SHIFT) |
				  ((4 - 1) << ESDCTL_V4_ESDCFG1_tMRD_SHIFT) |
				  ((5 - 2) << ESDCTL_V4_ESDCFG1_tCWL_SHIFT));
	writel(val, base + ESDCTL_V4_ESDCFG1);

	val = (((512 - 1) << ESDCTL_V4_ESDCFG2_tDLLK_SHIFT) |
				  ((4 - 1) << ESDCTL_V4_ESDCFG2_tRTP_SHIFT) |
				  ((4 - 1) << ESDCTL_V4_ESDCFG2_tWTR_SHIFT) |
				  ((4 - 1) << ESDCTL_V4_ESDCFG2_tRRD_SHIFT));
	writel(val, base + ESDCTL_V4_ESDCFG2);

	/*
	 * we don't touch the ESDRWD register as documentation claims
	 * the power-on defaults are minimum required values and we don't
	 * want to interfere with changes of these in new chip or
	 * BootROM revisions.
	 */

	val = (((3 - 1) << ESDCTL_V4_ESDOTC_tAOFPD_SHIFT) |
				  ((3 - 1) << ESDCTL_V4_ESDOTC_tAONPD_SHIFT) |
				  ((4 - 1) << ESDCTL_V4_ESDOTC_tANPD_SHIFT) |
				  ((4 - 1) << ESDCTL_V4_ESDOTC_tAXPD_SHIFT) |
				  (3 << ESDCTL_V4_ESDOTC_tODTLon_SHIFT) |
				  (3 << ESDCTL_V4_ESDOTC_tODT_idle_off_SHIFT));
	writel(val, base + ESDCTL_V4_ESDOTC);

	/* currently we only support DDR3 RAM, which always has 8 banks */
	val = ESDCTL_V4_ESDMISC_WALAT_0 | ESDCTL_V4_ESDMISC_RALAT_2 |
				  ESDCTL_V4_ESDMISC_MIF3_MODE_EFAM |
				  ESDCTL_V4_ESDMISC_DDR_DDR3 |
				  ESDCTL_V4_ESDMISC_BANKS_8;
	writel(val, base + ESDCTL_V4_ESDMISC);

	val = (((144 - 1) << ESDCTL_V4_ESDOR_tXPR_SHIFT) |
			((13 + 1) << ESDCTL_V4_ESDOR_SDE_to_RST_SHIFT) |
			((32 + 1) << ESDCTL_V4_ESDOR_RST_to_CKE_SHIFT));
	writel(val, base + ESDCTL_V4_ESDOR);

	/*
	 * we assume maximum address line configuration here (32b, 16rows, 12cols,
	 * both chips) once the RAM is initialized, we determine actual configuration
	 */
	val = ESDCTL_V4_ESDCTLx_SDE0 | ESDCTL_V4_ESDCTLx_SDE1 |
			ESDCTL_V4_ESDCTLx_ROW_16 | ESDCTL_V4_ESDCTLx_COL_8 |
			ESDCTL_V4_ESDCTLx_BL_8_8 | ESDCTL_V4_ESDCTLx_DSIZ_32B;
	writel(val, base + ESDCTL_V4_ESDCTL0);

	imx_esdctlv4_start_ddr3_sdram(0);
	imx_esdctlv4_start_ddr3_sdram(1);

	val = (ESDCTL_V4_ESDPDC_PRCT_DISABLE << ESDCTL_V4_ESDPDC_PRCT1_SHIFT) |
		(ESDCTL_V4_ESDPDC_PRCT_DISABLE << ESDCTL_V4_ESDPDC_PRCT0_SHIFT) |
		((3 - 1) << ESDCTL_V4_ESDPDC_tCKE_SHIFT) |
		(ESDCTL_V4_ESDPDC_PWDT_DISABLE << ESDCTL_V4_ESDPDC_PWDT1_SHIFT) |
		(ESDCTL_V4_ESDPDC_PWDT_DISABLE << ESDCTL_V4_ESDPDC_PWDT0_SHIFT) |
		(5 << ESDCTL_V4_ESDPDC_tCKSRX_SHIFT) |
		(5 << ESDCTL_V4_ESDPDC_tCKSRE_SHIFT);
	writel(val, base + ESDCTL_V4_ESDPDC);

	/* configure ODT */
	val = (ESDCTL_V4_ESDODTC_RTT_120 << ESDCTL_V4_ESDODTC_ODT3_INT_RES_SHIFT) |
		(ESDCTL_V4_ESDODTC_RTT_120 << ESDCTL_V4_ESDODTC_ODT2_INT_RES_SHIFT) |
		(ESDCTL_V4_ESDODTC_RTT_120 << ESDCTL_V4_ESDODTC_ODT1_INT_RES_SHIFT) |
		(ESDCTL_V4_ESDODTC_RTT_120 << ESDCTL_V4_ESDODTC_ODT0_INT_RES_SHIFT) |
		ESDCTL_V4_ESDODTC_ODT_RD_PAS_EN |
		ESDCTL_V4_ESDODTC_ODT_WR_ACT_EN |
		ESDCTL_V4_ESDODTC_ODT_WR_PAS_EN;
	writel(val, base + ESDCTL_V4_ODTCTRL);

	/*
	 * ensure refresh and ZQ calibration are turned off as
	 * nothing may interfere with the first few calibrations
	 */
	writel(ESDCTL_V4_ESDREF_REF_SEL_MASK, base + ESDCTL_V4_ESDREF);

	writel(ESDCTL_V4_ESDZQHWC_ZQ_MODE_NO_CAL, base + ESDCTL_V4_ZQHWCTRL);

	/*
	 * ensure that read/write delays are configured to
	 * (working) 1/4 cycle default delay, proper delays
	 * will be calibrated below.
	 */
	writel(0x30303030, base + ESDCTL_V4_RDDLCTL);
	writel(0x40404040, base + ESDCTL_V4_WRDLCTL);

	/* DQS Gating */
	imx_esdctlv4_do_dqs_gating();

	/* Write Leveling */
	imx_esdctlv4_do_write_leveling();

	/*
	 * ZQ calibration (this will also enable regular
	 * automatic ZQ calibration again)
	 */
	imx_esdctlv4_do_zq_calibration();

	/* configure and enable refresh */
	val = (0 << ESDCTL_V4_ESDREF_REF_CNT_SHIFT) |
		ESDCTL_V4_ESDREF_REF_SEL_64K |
		((2 - 1) << ESDCTL_V4_ESDREF_REFR_SHIFT);
	writel(val, base + ESDCTL_V4_ESDREF);


	/* Now do proper Delay Line Calibration */
	imx_esdctlv4_do_read_delay_line_calibration();
	imx_esdctlv4_do_write_delay_line_calibration();

	/* enable regular operation of ESDCTL */
	writel(ESDCTL_V4_ESDSCR_CMD_NOP, base + ESDCTL_V4_ESDSCR);

	dsb();

	/*
	 * detect RAM configuration (Note: both CSDx must be
	 * equipped with identical RAMs, so we only need to detect
	 * the configuration of CSD0 and anything on CSD1)
	 */
	esdctl0 = readl(base + ESDCTL_V4_ESDCTL0);

	imx_esdctlv4_write_magic_values((void *)MX53_CSD0_BASE_ADDR);

	/* check for bus-configuration first */
	esdctl0 &= ~ESDCTL_V4_ESDCTLx_DSIZ_MASK;

	/* assume we're on a 32b bus */
	mask = 0xffffffff;

	/* data correct? */
	if (readl(MX53_CSD0_BASE_ADDR) == SDRAM_COMPARE_CONST1) {
		esdctl0 |= ESDCTL_V4_ESDCTLx_DSIZ_32B; /* yep, indeed 32b bus */
		goto sdram_bussize_found;
	}

	/*
	 * ok, last possibility is 16b bus on low data-lines, check that
	 * (i.MX25 also suports 16b on high data-lines, but i.MX53 doesn't)
	 */
	if (readl(MX53_CSD0_BASE_ADDR) << 16 == SDRAM_COMPARE_CONST1 << 16) {
		esdctl0 |= ESDCTL_V4_ESDCTLx_DSIZ_16B_LOW;
		mask >>= 16;
		goto sdram_bussize_found;
	}

	/* nope, no working SDRAM, leave. */
	hang();

sdram_bussize_found:

	/* Now determine actual row/column configuration of the RAMs */

	/* mask unused bits from our compare constant */
	r1 = SDRAM_COMPARE_CONST1 & mask;
	/*
	 * So far we asssumed that we have 16 rows, check for copies of our
	 * SDRAM_COMPARE_CONST1 due to missing row lines...
	 */

	if (check_ram_address_line((void *)MX53_CSD0_BASE_ADDR + (1 << 25), r1, mask))
		rows = ESDCTL_V4_ESDCTLx_ROW_15;
	if (check_ram_address_line((void *)MX53_CSD0_BASE_ADDR + (1 << 24), r1, mask))
		rows = ESDCTL_V4_ESDCTLx_ROW_14;
	if (check_ram_address_line((void *)MX53_CSD0_BASE_ADDR + (1 << 23), r1, mask))
		rows = ESDCTL_V4_ESDCTLx_ROW_13;
	if (check_ram_address_line((void *)MX53_CSD0_BASE_ADDR + (1 << 22), r1, mask))
		rows = ESDCTL_V4_ESDCTLx_ROW_12;
	if (check_ram_address_line((void *)MX53_CSD0_BASE_ADDR + (1 << 21), r1, mask))
		rows = ESDCTL_V4_ESDCTLx_ROW_11;

	esdctl0 &= ~ESDCTL_V4_ESDCTLx_ROW_MASK;
	esdctl0 |= rows;

	/*
	 * To detect columns we have to switch from the (max rows, min cols)
	 * configuration we used so far to a (min rows, max cols) configuration
	 */

	/* switch ESDCTL to configuration mode */
	val = ESDCTL_V4_ESDSCR_CMD_NOP | ESDCTL_V4_ESDSCR_CON_REQ;
	writel(val, base + ESDCTL_V4_ESDSCR);

	/* reconfigure row-/column-lines */
	val = readl(base + ESDCTL_V4_ESDCTL0);
	val &= ~(ESDCTL_V4_ESDCTLx_ROW_MASK | ESDCTL_V4_ESDCTLx_COL_MASK);
	val |= ESDCTL_V4_ESDCTLx_ROW_11 | ESDCTL_V4_ESDCTLx_COL_12;
	writel(val, base + ESDCTL_V4_ESDCTL0);

	dsb();

	/* switch ESDCTL back to normal operation */
	writel(ESDCTL_V4_ESDSCR_CMD_NOP, base + ESDCTL_V4_ESDSCR);

	/*
	 * not quite sure why, but the row-/col-reconfiguration destroys the
	 * contents of the RAM so we have to write our magic values back
	 * (maybe because refresh is suspended during that time)
	 */
	imx_esdctlv4_write_magic_values((void *)MX53_CSD0_BASE_ADDR);

	/*
	 * So far we asssumed that we have 12 columns, check for copies of our
	 * SDRAM_COMPARE_CONST1 due to missing column lines...
	 */

	if (check_ram_address_line((void *)MX53_CSD0_BASE_ADDR + (1 << 13), r1, mask))
		cols = ESDCTL_V4_ESDCTLx_COL_11;
	if (check_ram_address_line((void *)MX53_CSD0_BASE_ADDR + (1 << 12), r1, mask))
		cols = ESDCTL_V4_ESDCTLx_COL_10;
	if (check_ram_address_line((void *)MX53_CSD0_BASE_ADDR + (1 << 11), r1, mask))
		cols = ESDCTL_V4_ESDCTLx_COL_9;
	if (check_ram_address_line((void *)MX53_CSD0_BASE_ADDR + (1 << 10), r1, mask))
		cols = ESDCTL_V4_ESDCTLx_COL_8;

	esdctl0 &= ~ESDCTL_V4_ESDCTLx_COL_MASK;
	esdctl0 |= cols;

	/* setup proper row-/column-configuration */

	/* switch ESDCTL to configuration mode */
	val = ESDCTL_V4_ESDSCR_CMD_NOP | ESDCTL_V4_ESDSCR_CON_REQ;
	writel(val, base + ESDCTL_V4_ESDSCR);

	/* reconfigure row-/column-lines */
	writel(esdctl0, base + ESDCTL_V4_ESDCTL0);

	/* configure densitiy dependent timing parameters */
	imx_esdctlv4_set_tRFC_timing();

	dsb();

	/* switch ESDCTL back to normal operation */
	writel(ESDCTL_V4_ESDSCR_CMD_NOP, base + ESDCTL_V4_ESDSCR);

	/* see at which CSx we actually have working RAM */
	imx_esdctlv4_detect_sdrams();
}
