[U-Boot] [PATCH 6/9 V2] Exynos5: DDR3: Adding the DDR3 memory setup for Exynos5250 Rev 1.0

Rajeshwari Shinde rajeshwari.s at samsung.com
Wed Jun 20 13:11:52 CEST 2012


The patch adds the memory initialization sequence of DDR3.

Signed-off-by: Hatim Ali <hatim.rv at samsung.com>
Signed-off-by: Rajeshwari Shinde <rajeshwari.s at samsung.com>
---
Chnages in V2:
	- None
 arch/arm/include/asm/arch-exynos/dmc.h |   65 +++++
 board/samsung/smdk5250/Makefile        |    2 +-
 board/samsung/smdk5250/dmc_common.c    |  199 ++++++++++++++
 board/samsung/smdk5250/dmc_init.c      |  462 --------------------------------
 board/samsung/smdk5250/dmc_init_ddr3.c |  228 ++++++++++++++++
 board/samsung/smdk5250/setup.h         |   59 ++++-
 6 files changed, 551 insertions(+), 464 deletions(-)
 create mode 100644 board/samsung/smdk5250/dmc_common.c
 delete mode 100644 board/samsung/smdk5250/dmc_init.c
 create mode 100644 board/samsung/smdk5250/dmc_init_ddr3.c

diff --git a/arch/arm/include/asm/arch-exynos/dmc.h b/arch/arm/include/asm/arch-exynos/dmc.h
index bd52d16..f65c676 100644
--- a/arch/arm/include/asm/arch-exynos/dmc.h
+++ b/arch/arm/include/asm/arch-exynos/dmc.h
@@ -251,5 +251,70 @@ struct exynos5_phy_control {
 	unsigned int phy_con41;
 	unsigned int phy_con42;
 };
+
+enum ddr_mode {
+	DDR_MODE_DDR2,
+	DDR_MODE_DDR3,
+	DDR_MODE_LPDDR2,
+	DDR_MODE_LPDDR3,
+
+	DDR_MODE_COUNT,
+};
+
+enum mem_manuf {
+	MEM_MANUF_AUTODETECT,
+	MEM_MANUF_ELPIDA,
+	MEM_MANUF_SAMSUNG,
+
+	MEM_MANUF_COUNT,
+};
+
+/* CONCONTROL register fields */
+#define CONCONTROL_DFI_INIT_START_SHIFT	28
+#define CONCONTROL_RD_FETCH_SHIFT	12
+#define CONCONTROL_RD_FETCH_MASK	(0x7 << CONCONTROL_RD_FETCH_SHIFT)
+#define CONCONTROL_AREF_EN_SHIFT	5
+
+/* PRECHCONFIG register field */
+#define PRECHCONFIG_TP_CNT_SHIFT	24
+
+/* PWRDNCONFIG register field */
+#define PWRDNCONFIG_DPWRDN_CYC_SHIFT	0
+#define PWRDNCONFIG_DSREF_CYC_SHIFT	16
+
+/* PHY_CON0 register fields */
+#define PHY_CON0_T_WRRDCMD_SHIFT	17
+#define PHY_CON0_T_WRRDCMD_MASK		(0x7 << PHY_CON0_T_WRRDCMD_SHIFT)
+#define PHY_CON0_CTRL_DDR_MODE_SHIFT	11
+
+/* PHY_CON1 register fields */
+#define PHY_CON1_RDLVL_RDDATA_ADJ_SHIFT	0
+
+/* PHY_CON12 register fields */
+#define PHY_CON12_CTRL_START_POINT_SHIFT	24
+#define PHY_CON12_CTRL_INC_SHIFT	16
+#define PHY_CON12_CTRL_FORCE_SHIFT	8
+#define PHY_CON12_CTRL_START_SHIFT	6
+#define PHY_CON12_CTRL_START_MASK	(1 << PHY_CON12_CTRL_START_SHIFT)
+#define PHY_CON12_CTRL_DLL_ON_SHIFT	5
+#define PHY_CON12_CTRL_DLL_ON_MASK	(1 << PHY_CON12_CTRL_DLL_ON_SHIFT)
+#define PHY_CON12_CTRL_REF_SHIFT	1
+
+/* PHY_CON16 register fields */
+#define PHY_CON16_ZQ_MODE_DDS_SHIFT	24
+#define PHY_CON16_ZQ_MODE_DDS_MASK	(0x7 << PHY_CON16_ZQ_MODE_DDS_SHIFT)
+
+#define PHY_CON16_ZQ_MODE_TERM_SHIFT 21
+#define PHY_CON16_ZQ_MODE_TERM_MASK	(0x7 << PHY_CON16_ZQ_MODE_TERM_SHIFT)
+
+#define PHY_CON16_ZQ_MODE_NOTERM_MASK	(1 << 19)
+
+/* PHY_CON42 register fields */
+#define PHY_CON42_CTRL_BSTLEN_SHIFT	8
+#define PHY_CON42_CTRL_BSTLEN_MASK	(0xff << PHY_CON42_CTRL_BSTLEN_SHIFT)
+
+#define PHY_CON42_CTRL_RDLAT_SHIFT	0
+#define PHY_CON42_CTRL_RDLAT_MASK	(0x1f << PHY_CON42_CTRL_RDLAT_SHIFT)
+
 #endif
 #endif
diff --git a/board/samsung/smdk5250/Makefile b/board/samsung/smdk5250/Makefile
index 3675fad..1474fa8 100644
--- a/board/samsung/smdk5250/Makefile
+++ b/board/samsung/smdk5250/Makefile
@@ -27,7 +27,7 @@ LIB	= $(obj)lib$(BOARD).o
 SOBJS	:= lowlevel_init.o
 
 COBJS	:= clock_init.o
-COBJS	+= dmc_init.o
+COBJS	+= dmc_common.o dmc_init_ddr3.o
 COBJS	+= tzpc_init.o
 COBJS	+= smdk5250_spl.o
 
diff --git a/board/samsung/smdk5250/dmc_common.c b/board/samsung/smdk5250/dmc_common.c
new file mode 100644
index 0000000..109602a
--- /dev/null
+++ b/board/samsung/smdk5250/dmc_common.c
@@ -0,0 +1,199 @@
+/*
+ * Mem setup common file for different types of DDR present on SMDK5250 boards.
+ *
+ * Copyright (C) 2012 Samsung Electronics
+ *
+ * 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 as
+ * published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
+ * MA 02111-1307 USA
+ */
+
+#include <common.h>
+#include <asm/arch/spl.h>
+
+#include "clock_init.h"
+#include "setup.h"
+
+#define ZQ_INIT_TIMEOUT	10000
+
+int dmc_config_zq(struct mem_timings *mem,
+		  struct exynos5_phy_control *phy0_ctrl,
+		  struct exynos5_phy_control *phy1_ctrl)
+{
+	unsigned long val = 0;
+	int i;
+
+	/*
+	 * ZQ Calibration:
+	 * Select Driver Strength,
+	 * long calibration for manual calibration
+	 */
+	val = PHY_CON16_RESET_VAL;
+	val |= mem->zq_mode_dds << PHY_CON16_ZQ_MODE_DDS_SHIFT;
+	val |= mem->zq_mode_term << PHY_CON16_ZQ_MODE_TERM_SHIFT;
+	val |= ZQ_CLK_DIV_EN;
+	writel(val, &phy0_ctrl->phy_con16);
+	writel(val, &phy1_ctrl->phy_con16);
+
+	/* Disable termination */
+	if (mem->zq_mode_noterm)
+		val |= PHY_CON16_ZQ_MODE_NOTERM_MASK;
+	writel(val, &phy0_ctrl->phy_con16);
+	writel(val, &phy1_ctrl->phy_con16);
+
+	/* ZQ_MANUAL_START: Enable */
+	val |= ZQ_MANUAL_STR;
+	writel(val, &phy0_ctrl->phy_con16);
+	writel(val, &phy1_ctrl->phy_con16);
+
+	/* ZQ_MANUAL_START: Disable */
+	val &= ~ZQ_MANUAL_STR;
+
+	/*
+	 * Since we are manaully calibrating the ZQ values,
+	 * we are looping for the ZQ_init to complete.
+	 */
+	i = ZQ_INIT_TIMEOUT;
+	while ((readl(&phy0_ctrl->phy_con17) & ZQ_DONE) != ZQ_DONE && i > 0) {
+		sdelay(100);
+		i--;
+	}
+	if (!i)
+		return -1;
+	writel(val, &phy0_ctrl->phy_con16);
+
+	i = ZQ_INIT_TIMEOUT;
+	while ((readl(&phy1_ctrl->phy_con17) & ZQ_DONE) != ZQ_DONE && i > 0) {
+		sdelay(100);
+		i--;
+	}
+	if (!i)
+		return -1;
+	writel(val, &phy1_ctrl->phy_con16);
+
+	return 0;
+}
+
+void update_reset_dll(struct exynos5_dmc *dmc, enum ddr_mode mode)
+{
+	unsigned long val;
+
+	if (mode == DDR_MODE_DDR3) {
+		val = MEM_TERM_EN | PHY_TERM_EN | DMC_CTRL_SHGATE;
+		writel(val, &dmc->phycontrol0);
+	}
+
+	/* Update DLL Information: Force DLL Resyncronization */
+	val = readl(&dmc->phycontrol0);
+	val |= FP_RSYNC;
+	writel(val, &dmc->phycontrol0);
+
+	/* Reset Force DLL Resyncronization */
+	val = readl(&dmc->phycontrol0);
+	val &= ~FP_RSYNC;
+	writel(val, &dmc->phycontrol0);
+}
+
+void dmc_config_mrs(struct mem_timings *mem, struct exynos5_dmc *dmc)
+{
+	int channel, chip;
+
+	for (channel = 0; channel < mem->dmc_channels; channel++) {
+		unsigned long mask;
+
+		mask = channel << DIRECT_CMD_CHANNEL_SHIFT;
+		for (chip = 0; chip < mem->chips_to_configure; chip++) {
+			int i;
+
+			mask |= chip << DIRECT_CMD_CHIP_SHIFT;
+
+			/* Sending NOP command */
+			writel(DIRECT_CMD_NOP | mask, &dmc->directcmd);
+
+			/*
+			 * TODO(alim.akhtar at samsung.com): Do we need these
+			 * delays? This one and the next were not there for
+			 * DDR3.
+			 */
+			sdelay(0x10000);
+
+			/* Sending EMRS/MRS commands */
+			for (i = 0; i < MEM_TIMINGS_MSR_COUNT; i++) {
+				writel(mem->direct_cmd_msr[i] | mask,
+				       &dmc->directcmd);
+				sdelay(0x10000);
+			}
+
+			if (mem->send_zq_init) {
+				/* Sending ZQINIT command */
+				writel(DIRECT_CMD_ZQINIT | mask,
+				       &dmc->directcmd);
+
+				sdelay(10000);
+			}
+		}
+	}
+}
+
+void dmc_config_prech(struct mem_timings *mem, struct exynos5_dmc *dmc)
+{
+	int channel, chip;
+
+	for (channel = 0; channel < mem->dmc_channels; channel++) {
+		unsigned long mask;
+
+		mask = channel << DIRECT_CMD_CHANNEL_SHIFT;
+		for (chip = 0; chip < mem->chips_per_channel; chip++) {
+			mask |= chip << DIRECT_CMD_CHIP_SHIFT;
+
+			/* PALL (all banks precharge) CMD */
+			writel(DIRECT_CMD_PALL | mask, &dmc->directcmd);
+			sdelay(0x10000);
+		}
+	}
+}
+
+void dmc_config_memory(struct mem_timings *mem, struct exynos5_dmc *dmc)
+{
+	writel(mem->memconfig, &dmc->memconfig0);
+	writel(mem->memconfig, &dmc->memconfig1);
+	writel(DMC_MEMBASECONFIG0_VAL, &dmc->membaseconfig0);
+	writel(DMC_MEMBASECONFIG1_VAL, &dmc->membaseconfig1);
+}
+
+void mem_ctrl_init()
+{
+	struct spl_machine_param *param = spl_get_machine_params();
+	struct mem_timings *mem;
+	int ret;
+
+	mem = clock_get_mem_timings();
+
+	/* If there are any other memory variant, add their init call below */
+	if (param->mem_type == DDR_MODE_DDR3) {
+		ret = ddr3_mem_ctrl_init(mem, param->mem_iv_size);
+		if (ret) {
+			/* will hang if failed to init memory control */
+			while (1)
+				;
+		}
+	} else {
+		/* will hang if unknow memory type  */
+		while (1)
+			;
+	}
+}
diff --git a/board/samsung/smdk5250/dmc_init.c b/board/samsung/smdk5250/dmc_init.c
deleted file mode 100644
index 7881074..0000000
--- a/board/samsung/smdk5250/dmc_init.c
+++ /dev/null
@@ -1,462 +0,0 @@
-/*
- * Memory setup for SMDK5250 board based on EXYNOS5
- *
- * Copyright (C) 2012 Samsung Electronics
- *
- * 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 as
- * published by the Free Software Foundation; either version 2 of
- * the License, or (at your option) any later version.
- *
- * 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.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
- * MA 02111-1307 USA
- */
-
-#include <config.h>
-#include <asm/io.h>
-#include <asm/arch/dmc.h>
-#include <asm/arch/clock.h>
-#include <asm/arch/cpu.h>
-#include "setup.h"
-
-/* APLL : 1GHz */
-/* MCLK_CDREX: MCLK_CDREX_533*/
-/* LPDDR support: LPDDR2 */
-static void reset_phy_ctrl(void);
-static void config_zq(struct exynos5_phy_control *,
-			struct exynos5_phy_control *);
-static void update_reset_dll(struct exynos5_dmc *);
-static void config_cdrex(void);
-static void config_mrs(struct exynos5_dmc *);
-static void sec_sdram_phy_init(struct exynos5_dmc *);
-static void config_prech(struct exynos5_dmc *);
-static void config_rdlvl(struct exynos5_dmc *,
-			struct exynos5_phy_control *,
-			struct exynos5_phy_control *);
-static void config_memory(struct exynos5_dmc *);
-
-static void config_offsets(unsigned int,
-				struct exynos5_phy_control *,
-				struct exynos5_phy_control *);
-
-static void reset_phy_ctrl(void)
-{
-	struct exynos5_clock *clk = (struct exynos5_clock *)EXYNOS5_CLOCK_BASE;
-
-	writel(PHY_RESET_VAL, &clk->lpddr3phy_ctrl);
-	sdelay(0x10000);
-}
-
-static void config_zq(struct exynos5_phy_control *phy0_ctrl,
-			struct exynos5_phy_control *phy1_ctrl)
-{
-	unsigned long val = 0;
-	/*
-	 * ZQ Calibration:
-	 * Select Driver Strength,
-	 * long calibration for manual calibration
-	 */
-	val = PHY_CON16_RESET_VAL;
-	SET_ZQ_MODE_DDS_VAL(val);
-	SET_ZQ_MODE_TERM_VAL(val);
-	val |= ZQ_CLK_DIV_EN;
-	writel(val, &phy0_ctrl->phy_con16);
-	writel(val, &phy1_ctrl->phy_con16);
-
-	/* Disable termination */
-	val |= ZQ_MODE_NOTERM;
-	writel(val, &phy0_ctrl->phy_con16);
-	writel(val, &phy1_ctrl->phy_con16);
-
-	/* ZQ_MANUAL_START: Enable */
-	val |= ZQ_MANUAL_STR;
-	writel(val, &phy0_ctrl->phy_con16);
-	writel(val, &phy1_ctrl->phy_con16);
-	sdelay(0x10000);
-
-	/* ZQ_MANUAL_START: Disable */
-	val &= ~ZQ_MANUAL_STR;
-	writel(val, &phy0_ctrl->phy_con16);
-	writel(val, &phy1_ctrl->phy_con16);
-}
-
-static void update_reset_dll(struct exynos5_dmc *dmc)
-{
-	unsigned long val;
-	/*
-	 * Update DLL Information:
-	 * Force DLL Resyncronization
-	 */
-	val = readl(&dmc->phycontrol0);
-	val |= FP_RSYNC;
-	writel(val, &dmc->phycontrol0);
-
-	/* Reset Force DLL Resyncronization */
-	val = readl(&dmc->phycontrol0);
-	val &= ~FP_RSYNC;
-	writel(val, &dmc->phycontrol0);
-}
-
-static void config_mrs(struct exynos5_dmc *dmc)
-{
-	unsigned long channel, chip, mask = 0, val;
-
-	for (channel = 0; channel < CONFIG_DMC_CHANNELS; channel++) {
-		SET_CMD_CHANNEL(mask, channel);
-		for (chip = 0; chip < CONFIG_CHIPS_PER_CHANNEL; chip++) {
-			/*
-			 * NOP CMD:
-			 * Assert and hold CKE to logic high level
-			 */
-			SET_CMD_CHIP(mask, chip);
-			val = DIRECT_CMD_NOP | mask;
-			writel(val, &dmc->directcmd);
-			sdelay(0x10000);
-
-			/* EMRS, MRS Cmds(Mode Reg Settings) Using Direct Cmd */
-			val = DIRECT_CMD_MRS1 | mask;
-			writel(val, &dmc->directcmd);
-			sdelay(0x10000);
-
-			val = DIRECT_CMD_MRS2 | mask;
-			writel(val, &dmc->directcmd);
-			sdelay(0x10000);
-
-			/* MCLK_CDREX_533 */
-			val = DIRECT_CMD_MRS3 | mask;
-			writel(val, &dmc->directcmd);
-			sdelay(0x10000);
-
-			val = DIRECT_CMD_MRS4 | mask;
-			writel(val, &dmc->directcmd);
-			sdelay(0x10000);
-		}
-	}
-}
-
-static void config_prech(struct exynos5_dmc *dmc)
-{
-	unsigned long channel, chip, mask = 0, val;
-
-	for (channel = 0; channel < CONFIG_DMC_CHANNELS; channel++) {
-		SET_CMD_CHANNEL(mask, channel);
-		for (chip = 0; chip < CONFIG_CHIPS_PER_CHANNEL; chip++) {
-			SET_CMD_CHIP(mask, chip);
-			/* PALL (all banks precharge) CMD */
-			val = DIRECT_CMD_PALL | mask;
-			writel(val, &dmc->directcmd);
-			sdelay(0x10000);
-		}
-	}
-}
-
-static void sec_sdram_phy_init(struct exynos5_dmc *dmc)
-{
-	unsigned long val;
-	val = readl(&dmc->concontrol);
-	val |= DFI_INIT_START;
-	writel(val, &dmc->concontrol);
-	sdelay(0x10000);
-
-	val = readl(&dmc->concontrol);
-	val &= ~DFI_INIT_START;
-	writel(val, &dmc->concontrol);
-}
-
-static void config_offsets(unsigned int state,
-				struct exynos5_phy_control *phy0_ctrl,
-				struct exynos5_phy_control *phy1_ctrl)
-{
-	unsigned long val;
-	/* Set Offsets to read DQS */
-	val = (state == SET) ? SET_DQS_OFFSET_VAL : RESET_DQS_OFFSET_VAL;
-	writel(val, &phy0_ctrl->phy_con4);
-	writel(val, &phy1_ctrl->phy_con4);
-
-	/* Set Offsets to read DQ */
-	val = (state == SET) ? SET_DQ_OFFSET_VAL : RESET_DQ_OFFSET_VAL;
-	writel(val, &phy0_ctrl->phy_con6);
-	writel(val, &phy1_ctrl->phy_con6);
-
-	/* Debug Offset */
-	val = (state == SET) ? SET_DEBUG_OFFSET_VAL : RESET_DEBUG_OFFSET_VAL;
-	writel(val, &phy0_ctrl->phy_con10);
-	writel(val, &phy1_ctrl->phy_con10);
-}
-
-static void config_cdrex(void)
-{
-	struct exynos5_clock *clk = (struct exynos5_clock *)EXYNOS5_CLOCK_BASE;
-	writel(CLK_DIV_CDREX_VAL, &clk->div_cdrex);
-	writel(CLK_SRC_CDREX_VAL, &clk->src_cdrex);
-	sdelay(0x30000);
-}
-
-static void config_ctrl_dll_on(unsigned int state,
-			unsigned int ctrl_force_val,
-			struct exynos5_phy_control *phy0_ctrl,
-			struct exynos5_phy_control *phy1_ctrl)
-{
-	unsigned long val;
-	val = readl(&phy0_ctrl->phy_con12);
-	CONFIG_CTRL_DLL_ON(val, state);
-	SET_CTRL_FORCE_VAL(val, ctrl_force_val);
-	writel(val, &phy0_ctrl->phy_con12);
-
-	val = readl(&phy1_ctrl->phy_con12);
-	CONFIG_CTRL_DLL_ON(val, state);
-	SET_CTRL_FORCE_VAL(val, ctrl_force_val);
-	writel(val, &phy1_ctrl->phy_con12);
-}
-
-static void config_ctrl_start(unsigned int state,
-			struct exynos5_phy_control *phy0_ctrl,
-			struct exynos5_phy_control *phy1_ctrl)
-{
-	unsigned long val;
-	val = readl(&phy0_ctrl->phy_con12);
-	CONFIG_CTRL_START(val, state);
-	writel(val, &phy0_ctrl->phy_con12);
-
-	val = readl(&phy1_ctrl->phy_con12);
-	CONFIG_CTRL_START(val, state);
-	writel(val, &phy1_ctrl->phy_con12);
-}
-
-#if defined(CONFIG_RD_LVL)
-static void config_rdlvl(struct exynos5_dmc *dmc,
-			struct exynos5_phy_control *phy0_ctrl,
-			struct exynos5_phy_control *phy1_ctrl)
-{
-	unsigned long val;
-
-	/* Disable CTRL_DLL_ON and set ctrl_force */
-	config_ctrl_dll_on(RESET, 0x2D, phy0_ctrl, phy1_ctrl);
-
-	/*
-	 * Set ctrl_gateadj, ctrl_readadj
-	 * ctrl_gateduradj, rdlvl_pass_adj
-	 * rdlvl_rddataPadj
-	 */
-	val = SET_RDLVL_RDDATAPADJ;
-	writel(val, &phy0_ctrl->phy_con1);
-	writel(val, &phy1_ctrl->phy_con1);
-
-	/* LPDDR2 Address */
-	writel(LPDDR2_ADDR, &phy0_ctrl->phy_con22);
-	writel(LPDDR2_ADDR, &phy1_ctrl->phy_con22);
-
-	/* Enable Byte Read Leveling set ctrl_ddr_mode */
-	val = readl(&phy0_ctrl->phy_con0);
-	val |= BYTE_RDLVL_EN;
-	writel(val, &phy0_ctrl->phy_con0);
-	val = readl(&phy1_ctrl->phy_con0);
-	val |= BYTE_RDLVL_EN;
-	writel(val, &phy1_ctrl->phy_con0);
-
-	/* rdlvl_en: Use levelling offset instead ctrl_shiftc */
-	val = PHY_CON2_RESET_VAL | RDLVL_EN;
-	writel(val, &phy0_ctrl->phy_con2);
-	writel(val, &phy1_ctrl->phy_con2);
-	sdelay(0x10000);
-
-	/* Enable Data Eye Training */
-	val = readl(&dmc->rdlvl_config);
-	val |= CTRL_RDLVL_DATA_EN;
-	writel(val, &dmc->rdlvl_config);
-	sdelay(0x10000);
-
-	/* Disable Data Eye Training */
-	val = readl(&dmc->rdlvl_config);
-	val &= ~CTRL_RDLVL_DATA_EN;
-	writel(val, &dmc->rdlvl_config);
-
-	/* RdDeSkew_clear: Clear */
-	val = readl(&phy0_ctrl->phy_con2);
-	val |= RDDSKEW_CLEAR;
-	writel(val, &phy0_ctrl->phy_con2);
-	val = readl(&phy1_ctrl->phy_con2);
-	val |= RDDSKEW_CLEAR;
-	writel(val, &phy1_ctrl->phy_con2);
-
-	/* Enable CTRL_DLL_ON */
-	config_ctrl_dll_on(SET, 0x0, phy0_ctrl, phy1_ctrl);
-
-	update_reset_dll(dmc);
-	sdelay(0x10000);
-
-	/* ctrl_atgte: ctrl_gate_p*, ctrl_read_p* generated by PHY */
-	val = readl(&phy0_ctrl->phy_con0);
-	val &= ~CTRL_ATGATE;
-	writel(val, &phy0_ctrl->phy_con0);
-	val = readl(&phy1_ctrl->phy_con0);
-	val &= ~CTRL_ATGATE;
-	writel(val, &phy1_ctrl->phy_con0);
-}
-#endif
-
-static void config_memory(struct exynos5_dmc *dmc)
-{
-	/*
-	 * Memory Configuration Chip 0
-	 * Address Mapping: Interleaved
-	 * Number of Column address Bits: 10 bits
-	 * Number of Rows Address Bits: 14
-	 * Number of Banks: 8
-	 */
-	writel(DMC_MEMCONFIG0_VAL, &dmc->memconfig0);
-
-	/*
-	 * Memory Configuration Chip 1
-	 * Address Mapping: Interleaved
-	 * Number of Column address Bits: 10 bits
-	 * Number of Rows Address Bits: 14
-	 * Number of Banks: 8
-	 */
-	writel(DMC_MEMCONFIG1_VAL, &dmc->memconfig1);
-
-	/*
-	 * Chip0: AXI
-	 * AXI Base Address: 0x40000000
-	 * AXI Base Address Mask: 0x780
-	 */
-	writel(DMC_MEMBASECONFIG0_VAL, &dmc->membaseconfig0);
-
-	/*
-	 * Chip1: AXI
-	 * AXI Base Address: 0x80000000
-	 * AXI Base Address Mask: 0x780
-	 */
-	writel(DMC_MEMBASECONFIG1_VAL, &dmc->membaseconfig1);
-}
-
-void mem_ctrl_init()
-{
-	struct exynos5_phy_control *phy0_ctrl, *phy1_ctrl;
-	struct exynos5_dmc *dmc;
-	unsigned long val;
-
-	phy0_ctrl = (struct exynos5_phy_control *)EXYNOS5_DMC_PHY0_BASE;
-	phy1_ctrl = (struct exynos5_phy_control *)EXYNOS5_DMC_PHY1_BASE;
-	dmc = (struct exynos5_dmc *)EXYNOS5_DMC_CTRL_BASE;
-
-	/* Reset PHY Controllor: PHY_RESET[0] */
-	reset_phy_ctrl();
-
-	/*set Read Latancy and Burst Length for PHY0 and PHY1 */
-	writel(PHY_CON42_VAL, &phy0_ctrl->phy_con42);
-	writel(PHY_CON42_VAL, &phy1_ctrl->phy_con42);
-
-	/* ZQ Cofiguration */
-	config_zq(phy0_ctrl, phy1_ctrl);
-
-	/* Operation Mode : LPDDR2 */
-	val = PHY_CON0_RESET_VAL;
-	SET_CTRL_DDR_MODE(val, DDR_MODE_LPDDR2);
-	writel(val, &phy0_ctrl->phy_con0);
-	writel(val, &phy1_ctrl->phy_con0);
-
-	/* DQS, DQ: Signal, for LPDDR2: Always Set */
-	val = CTRL_PULLD_DQ | CTRL_PULLD_DQS;
-	writel(val, &phy0_ctrl->phy_con14);
-	writel(val, &phy1_ctrl->phy_con14);
-
-	/* Init SEC SDRAM PHY */
-	sec_sdram_phy_init(dmc);
-	sdelay(0x10000);
-
-	update_reset_dll(dmc);
-
-	/*
-	 * Dynamic Clock: Always Running
-	 * Memory Burst length: 4
-	 * Number of chips: 2
-	 * Memory Bus width: 32 bit
-	 * Memory Type: LPDDR2-S4
-	 * Additional Latancy for PLL: 1 Cycle
-	 */
-	writel(DMC_MEMCONTROL_VAL, &dmc->memcontrol);
-
-	config_memory(dmc);
-
-	/* Precharge Configuration */
-	writel(DMC_PRECHCONFIG_VAL, &dmc->prechconfig);
-
-	/* Power Down mode Configuration */
-	writel(DMC_PWRDNCONFIG_VAL, &dmc->pwrdnconfig);
-
-	/* Periodic Refrese Interval */
-	writel(DMC_TIMINGREF_VAL, &dmc->timingref);
-
-	/*
-	 * TimingRow, TimingData, TimingPower Setting:
-	 * Values as per Memory AC Parameters
-	 */
-	writel(DMC_TIMINGROW_VAL, &dmc->timingrow);
-
-	writel(DMC_TIMINGDATA_VAL, &dmc->timingdata);
-
-	writel(DMC_TIMINGPOWER_VAL, &dmc->timingpower);
-
-	/* Memory Channel Inteleaving Size: 128 Bytes */
-	writel(CONFIG_IV_SIZE, &dmc->ivcontrol);
-
-	/* Set DQS, DQ and DEBUG offsets */
-	config_offsets(SET, phy0_ctrl, phy1_ctrl);
-
-	/* Disable CTRL_DLL_ON and set ctrl_force */
-	config_ctrl_dll_on(RESET, 0x7F, phy0_ctrl, phy1_ctrl);
-	sdelay(0x10000);
-
-	update_reset_dll(dmc);
-
-	/* Config MRS(Mode Register Settingg) */
-	config_mrs(dmc);
-
-	config_cdrex();
-
-	/* Reset DQS DQ and DEBUG offsets */
-	config_offsets(RESET, phy0_ctrl, phy1_ctrl);
-
-	/* Enable CTRL_DLL_ON */
-	config_ctrl_dll_on(SET, 0x0, phy0_ctrl, phy1_ctrl);
-
-	/* Stop DLL Locking */
-	config_ctrl_start(RESET, phy0_ctrl, phy1_ctrl);
-	sdelay(0x10000);
-
-	/* Start DLL Locking */
-	config_ctrl_start(SET, phy0_ctrl, phy1_ctrl);
-	sdelay(0x10000);
-
-	update_reset_dll(dmc);
-
-#if defined(CONFIG_RD_LVL)
-	config_rdlvl(dmc, phy0_ctrl, phy1_ctrl);
-#endif
-	config_prech(dmc);
-
-	/*
-	 * Dynamic Clock: Stops During Idle Period
-	 * Dynamic Power Down: Enable
-	 * Dynamic Self refresh: Enable
-	 */
-	val = readl(&dmc->memcontrol);
-	val |= CLK_STOP_EN | DPWRDN_EN | DSREF_EN;
-	writel(val, &dmc->memcontrol);
-
-	/* Start Auto refresh */
-	val = readl(&dmc->concontrol);
-	val |= AREF_EN;
-	writel(val, &dmc->concontrol);
-}
diff --git a/board/samsung/smdk5250/dmc_init_ddr3.c b/board/samsung/smdk5250/dmc_init_ddr3.c
new file mode 100644
index 0000000..e050790
--- /dev/null
+++ b/board/samsung/smdk5250/dmc_init_ddr3.c
@@ -0,0 +1,228 @@
+/*
+ * DDR3 mem setup file for SMDK5250 board based on EXYNOS5
+ *
+ * Copyright (C) 2012 Samsung Electronics
+ *
+ * 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 as
+ * published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
+ * MA 02111-1307 USA
+ */
+
+#include <config.h>
+#include <asm/io.h>
+#include <asm/arch/clock.h>
+#include <asm/arch/cpu.h>
+#include <asm/arch/dmc.h>
+#include "setup.h"
+#include "clock_init.h"
+
+#define RDLVL_COMPLETE_TIMEOUT	10000
+
+static void reset_phy_ctrl(void)
+{
+	struct exynos5_clock *clk = (struct exynos5_clock *)EXYNOS5_CLOCK_BASE;
+
+	writel(DDR3PHY_CTRL_PHY_RESET_OFF, &clk->lpddr3phy_ctrl);
+	writel(DDR3PHY_CTRL_PHY_RESET, &clk->lpddr3phy_ctrl);
+}
+
+int ddr3_mem_ctrl_init(struct mem_timings *mem, unsigned long mem_iv_size)
+{
+	unsigned int val;
+	struct exynos5_phy_control *phy0_ctrl, *phy1_ctrl;
+	struct exynos5_dmc *dmc;
+	int i;
+
+	phy0_ctrl = (struct exynos5_phy_control *)EXYNOS5_DMC_PHY0_BASE;
+	phy1_ctrl = (struct exynos5_phy_control *)EXYNOS5_DMC_PHY1_BASE;
+	dmc = (struct exynos5_dmc *)EXYNOS5_DMC_CTRL_BASE;
+
+	reset_phy_ctrl();
+
+	/* Set Impedance Output Driver */
+	val = (mem->impedance << CA_CK_DRVR_DS_OFFSET) |
+		(mem->impedance << CA_CKE_DRVR_DS_OFFSET) |
+		(mem->impedance << CA_CS_DRVR_DS_OFFSET) |
+		(mem->impedance << CA_ADR_DRVR_DS_OFFSET);
+	writel(val, &phy0_ctrl->phy_con39);
+	writel(val, &phy1_ctrl->phy_con39);
+
+	/* Set Read Latency and Burst Length for PHY0 and PHY1 */
+	val = (mem->ctrl_bstlen << PHY_CON42_CTRL_BSTLEN_SHIFT) |
+		(mem->ctrl_rdlat << PHY_CON42_CTRL_RDLAT_SHIFT);
+	writel(val, &phy0_ctrl->phy_con42);
+	writel(val, &phy1_ctrl->phy_con42);
+
+	/* ZQ Calibration */
+	if (dmc_config_zq(mem, phy0_ctrl, phy1_ctrl))
+		return SETUP_ERR_ZQ_CALIBRATION_FAILURE;
+
+	/* DQ Signal */
+	writel(mem->phy0_pulld_dqs, &phy0_ctrl->phy_con14);
+	writel(mem->phy1_pulld_dqs, &phy1_ctrl->phy_con14);
+
+	writel(mem->concontrol | (mem->rd_fetch << CONCONTROL_RD_FETCH_SHIFT)
+		| (mem->dfi_init_start << CONCONTROL_DFI_INIT_START_SHIFT),
+		&dmc->concontrol);
+
+	update_reset_dll(dmc, DDR_MODE_DDR3);
+
+	/* DQS Signal */
+	writel(mem->phy0_dqs, &phy0_ctrl->phy_con4);
+	writel(mem->phy1_dqs, &phy1_ctrl->phy_con4);
+
+	writel(mem->phy0_dq, &phy0_ctrl->phy_con6);
+	writel(mem->phy1_dq, &phy1_ctrl->phy_con6);
+
+	writel(mem->phy0_tFS, &phy0_ctrl->phy_con10);
+	writel(mem->phy1_tFS, &phy1_ctrl->phy_con10);
+
+	val = (mem->ctrl_start_point << PHY_CON12_CTRL_START_POINT_SHIFT) |
+		(mem->ctrl_inc << PHY_CON12_CTRL_INC_SHIFT) |
+		(mem->ctrl_dll_on << PHY_CON12_CTRL_DLL_ON_SHIFT) |
+		(mem->ctrl_ref << PHY_CON12_CTRL_REF_SHIFT);
+	writel(val, &phy0_ctrl->phy_con12);
+	writel(val, &phy1_ctrl->phy_con12);
+
+	/* Start DLL locking */
+	writel(val | (mem->ctrl_start << PHY_CON12_CTRL_START_SHIFT),
+		&phy0_ctrl->phy_con12);
+	writel(val | (mem->ctrl_start << PHY_CON12_CTRL_START_SHIFT),
+		&phy1_ctrl->phy_con12);
+
+	update_reset_dll(dmc, DDR_MODE_DDR3);
+
+	writel(mem->concontrol | (mem->rd_fetch << CONCONTROL_RD_FETCH_SHIFT),
+		&dmc->concontrol);
+
+	/* Memory Channel Inteleaving Size */
+	writel(mem->iv_size, &dmc->ivcontrol);
+
+	writel(mem->memconfig, &dmc->memconfig0);
+	writel(mem->memconfig, &dmc->memconfig1);
+	writel(mem->membaseconfig0, &dmc->membaseconfig0);
+	writel(mem->membaseconfig1, &dmc->membaseconfig1);
+
+	/* Precharge Configuration */
+	writel(mem->prechconfig_tp_cnt << PRECHCONFIG_TP_CNT_SHIFT,
+		&dmc->prechconfig);
+
+	/* Power Down mode Configuration */
+	writel(mem->dpwrdn_cyc << PWRDNCONFIG_DPWRDN_CYC_SHIFT |
+		mem->dsref_cyc << PWRDNCONFIG_DSREF_CYC_SHIFT,
+		&dmc->pwrdnconfig);
+
+	/* TimingRow, TimingData, TimingPower and Timingaref
+	 * values as per Memory AC parameters
+	 */
+	writel(mem->timing_ref, &dmc->timingref);
+	writel(mem->timing_row, &dmc->timingrow);
+	writel(mem->timing_data, &dmc->timingdata);
+	writel(mem->timing_power, &dmc->timingpower);
+
+	/* Send PALL command */
+	dmc_config_prech(mem, dmc);
+
+	/* Send NOP, MRS and ZQINIT commands */
+	dmc_config_mrs(mem, dmc);
+
+	if (mem->gate_leveling_enable) {
+		val = PHY_CON0_RESET_VAL;
+		val |= P0_CMD_EN;
+		writel(val, &phy0_ctrl->phy_con0);
+		writel(val, &phy1_ctrl->phy_con0);
+
+		val = PHY_CON2_RESET_VAL;
+		val |= INIT_DESKEW_EN;
+		writel(val, &phy0_ctrl->phy_con2);
+		writel(val, &phy1_ctrl->phy_con2);
+
+		val = PHY_CON0_RESET_VAL;
+		val |= P0_CMD_EN;
+		val |= BYTE_RDLVL_EN;
+		writel(val, &phy0_ctrl->phy_con0);
+		writel(val, &phy1_ctrl->phy_con0);
+
+		val = (mem->ctrl_start_point <<
+				PHY_CON12_CTRL_START_POINT_SHIFT) |
+			(mem->ctrl_inc << PHY_CON12_CTRL_INC_SHIFT) |
+			(mem->ctrl_force << PHY_CON12_CTRL_FORCE_SHIFT) |
+			(mem->ctrl_start << PHY_CON12_CTRL_START_SHIFT) |
+			(mem->ctrl_ref << PHY_CON12_CTRL_REF_SHIFT);
+		writel(val, &phy0_ctrl->phy_con12);
+		writel(val, &phy1_ctrl->phy_con12);
+
+		val = PHY_CON2_RESET_VAL;
+		val |= INIT_DESKEW_EN;
+		val |= RDLVL_GATE_EN;
+		writel(val, &phy0_ctrl->phy_con2);
+		writel(val, &phy1_ctrl->phy_con2);
+
+		val = PHY_CON0_RESET_VAL;
+		val |= P0_CMD_EN;
+		val |= BYTE_RDLVL_EN;
+		val |= CTRL_SHGATE;
+		writel(val, &phy0_ctrl->phy_con0);
+		writel(val, &phy1_ctrl->phy_con0);
+
+		val = PHY_CON1_RESET_VAL;
+		val &= ~(CTRL_GATEDURADJ_MASK);
+		writel(val, &phy0_ctrl->phy_con1);
+		writel(val, &phy1_ctrl->phy_con1);
+
+		writel(CTRL_RDLVL_GATE_ENABLE, &dmc->rdlvl_config);
+		i = RDLVL_COMPLETE_TIMEOUT;
+		while ((readl(&dmc->phystatus) &
+			(RDLVL_COMPLETE_CHO | RDLVL_COMPLETE_CH1)) !=
+			(RDLVL_COMPLETE_CHO | RDLVL_COMPLETE_CH1) && i > 0) {
+			/*
+			 * TODO(waihong): Comment on how long this take to
+			 * timeout
+			 */
+			sdelay(100);
+			i--;
+		}
+		if (!i)
+			return SETUP_ERR_RDLV_COMPLETE_TIMEOUT;
+		writel(CTRL_RDLVL_GATE_DISABLE, &dmc->rdlvl_config);
+
+		writel(0, &phy0_ctrl->phy_con14);
+		writel(0, &phy1_ctrl->phy_con14);
+
+		val = (mem->ctrl_start_point <<
+				PHY_CON12_CTRL_START_POINT_SHIFT) |
+			(mem->ctrl_inc << PHY_CON12_CTRL_INC_SHIFT) |
+			(mem->ctrl_force << PHY_CON12_CTRL_FORCE_SHIFT) |
+			(mem->ctrl_start << PHY_CON12_CTRL_START_SHIFT) |
+			(mem->ctrl_dll_on << PHY_CON12_CTRL_DLL_ON_SHIFT) |
+			(mem->ctrl_ref << PHY_CON12_CTRL_REF_SHIFT);
+		writel(val, &phy0_ctrl->phy_con12);
+		writel(val, &phy1_ctrl->phy_con12);
+
+		update_reset_dll(dmc, DDR_MODE_DDR3);
+	}
+
+	/* Send PALL command */
+	dmc_config_prech(mem, dmc);
+
+	writel(mem->memcontrol, &dmc->memcontrol);
+
+	/* Set DMC Concontrol and enable auto-refresh counter */
+	writel(mem->concontrol | (mem->rd_fetch << CONCONTROL_RD_FETCH_SHIFT)
+		| (mem->aref_en << CONCONTROL_AREF_EN_SHIFT), &dmc->concontrol);
+	return 0;
+}
diff --git a/board/samsung/smdk5250/setup.h b/board/samsung/smdk5250/setup.h
index a0b71ce..b32c4fd 100644
--- a/board/samsung/smdk5250/setup.h
+++ b/board/samsung/smdk5250/setup.h
@@ -524,8 +524,65 @@ enum {
 };
 
 
+/*
+ * Memory variant specific initialization code
+ *
+ * @param mem		Memory timings for this memory type.
+ * @param mem_iv_size	Memory interleaving size is a configurable parameter
+ *			which the DMC uses to decide how to split a memory
+ *			chunk into smaller chunks to support concurrent
+ *			accesses; may vary across boards.
+ * @return 0 if ok, SETUP_ERR_... if there is a problem
+ */
+int ddr3_mem_ctrl_init(struct mem_timings *mem, unsigned long mem_iv_size);
+
+/*
+ * Configure ZQ I/O interface
+ *
+ * @param mem		Memory timings for this memory type.
+ * @param phy0_ctrl	Pointer to struct containing PHY0 control reg
+ * @param phy1_ctrl	Pointer to struct containing PHY1 control reg
+ * @return 0 if ok, -1 on error
+ */
+int dmc_config_zq(struct mem_timings *mem,
+		  struct exynos5_phy_control *phy0_ctrl,
+		  struct exynos5_phy_control *phy1_ctrl);
+
+/*
+ * Send NOP and MRS/EMRS Direct commands
+ *
+ * @param mem		Memory timings for this memory type.
+ * @param dmc		Pointer to struct of DMC registers
+ */
+void dmc_config_mrs(struct mem_timings *mem, struct exynos5_dmc *dmc);
+
+/*
+ * Send PALL Direct commands
+ *
+ * @param mem		Memory timings for this memory type.
+ * @param dmc		Pointer to struct of DMC registers
+ */
+void dmc_config_prech(struct mem_timings *mem, struct exynos5_dmc *dmc);
+
+/*
+ * Configure the memconfig and membaseconfig registers
+ *
+ * @param mem		Memory timings for this memory type.
+ * @param exynos5_dmc	Pointer to struct of DMC registers
+ */
+void dmc_config_memory(struct mem_timings *mem, struct exynos5_dmc *dmc);
+
+/*
+ * Reset the DLL. This function is common between DDR3 and LPDDR2.
+ * However, the reset value is different. So we are passing a flag
+ * ddr_mode to distinguish between LPDDR2 and DDR3.
+ *
+ * @param exynos5_dmc	Pointer to struct of DMC registers
+ * @param ddr_mode	Type of DDR memory
+ */
+void update_reset_dll(struct exynos5_dmc *, enum ddr_mode);
+
 void mem_ctrl_init(void);
 void system_clock_init(void);
 void tzpc_init(void);
-
 #endif
-- 
1.7.4.4



More information about the U-Boot mailing list