[U-Boot] [PATCH 1/3] ARM: UniPhier: add DDR PHY training code

Masahiro Yamada yamada.m at jp.panasonic.com
Fri Dec 19 12:20:52 CET 2014


This training code provides run-time adjustment of DDR PHY parameters
for stable DDR operation.

Signed-off-by: Masahiro Yamada <yamada.m at jp.panasonic.com>
---

 arch/arm/cpu/armv7/uniphier/Makefile               |   1 +
 arch/arm/cpu/armv7/uniphier/ddrphy_training.c      | 144 +++++++++++++++++
 arch/arm/cpu/armv7/uniphier/ph1-ld4/Makefile       |   2 +-
 arch/arm/cpu/armv7/uniphier/ph1-ld4/ddrphy_init.c  |  70 +++++++++
 arch/arm/cpu/armv7/uniphier/ph1-ld4/umc_init.c     |  13 ++
 arch/arm/cpu/armv7/uniphier/ph1-pro4/Makefile      |   2 +-
 arch/arm/cpu/armv7/uniphier/ph1-pro4/ddrphy_init.c |  70 +++++++++
 arch/arm/cpu/armv7/uniphier/ph1-pro4/umc_init.c    |  25 +++
 arch/arm/cpu/armv7/uniphier/ph1-sld8/Makefile      |   2 +-
 arch/arm/cpu/armv7/uniphier/ph1-sld8/ddrphy_init.c |  75 +++++++++
 arch/arm/cpu/armv7/uniphier/ph1-sld8/umc_init.c    |  13 ++
 arch/arm/include/asm/arch-uniphier/ddrphy-regs.h   | 172 +++++++++++++++++++++
 12 files changed, 586 insertions(+), 3 deletions(-)
 create mode 100644 arch/arm/cpu/armv7/uniphier/ddrphy_training.c
 create mode 100644 arch/arm/cpu/armv7/uniphier/ph1-ld4/ddrphy_init.c
 create mode 100644 arch/arm/cpu/armv7/uniphier/ph1-pro4/ddrphy_init.c
 create mode 100644 arch/arm/cpu/armv7/uniphier/ph1-sld8/ddrphy_init.c
 create mode 100644 arch/arm/include/asm/arch-uniphier/ddrphy-regs.h

diff --git a/arch/arm/cpu/armv7/uniphier/Makefile b/arch/arm/cpu/armv7/uniphier/Makefile
index 4a7b8a9..1d84696 100644
--- a/arch/arm/cpu/armv7/uniphier/Makefile
+++ b/arch/arm/cpu/armv7/uniphier/Makefile
@@ -10,6 +10,7 @@ obj-y += reset.o
 obj-y += cache_uniphier.o
 obj-$(CONFIG_BOARD_POSTCLK_INIT) += board_postclk_init.o
 obj-y += dram_init.o
+obj-$(CONFIG_DRAM_INIT) += ddrphy_training.o
 obj-$(CONFIG_DISPLAY_CPUINFO) += cpu_info.o
 obj-$(CONFIG_BOARD_EARLY_INIT_R) += board_early_init_r.o
 obj-$(CONFIG_BOARD_LATE_INIT) += board_late_init.o
diff --git a/arch/arm/cpu/armv7/uniphier/ddrphy_training.c b/arch/arm/cpu/armv7/uniphier/ddrphy_training.c
new file mode 100644
index 0000000..cc8b8ad
--- /dev/null
+++ b/arch/arm/cpu/armv7/uniphier/ddrphy_training.c
@@ -0,0 +1,144 @@
+/*
+ * Copyright (C) 2011-2014 Panasonic Corporation
+ *   Author: Masahiro Yamada <yamada.m at jp.panasonic.com>
+ *
+ * SPDX-License-Identifier:	GPL-2.0+
+ */
+
+#include <common.h>
+#include <asm/io.h>
+#include <asm/arch/ddrphy-regs.h>
+
+void ddrphy_prepare_training(struct ddrphy __iomem *phy, int rank)
+{
+	int dx;
+	u32 __iomem tmp, *p;
+
+	for (dx = 0; dx < NR_DATX8_PER_DDRPHY; dx++) {
+		p = &phy->dx[dx].gcr;
+
+		tmp = readl(p);
+		/* Specify the rank that should be write leveled */
+		tmp &= ~DXGCR_WLRKEN_MASK;
+		tmp |= (1 << (DXGCR_WLRKEN_SHIFT + rank)) & DXGCR_WLRKEN_MASK;
+		writel(tmp, p);
+	}
+
+	p = &phy->dtcr;
+
+	tmp = readl(p);
+	/* Specify the rank used during data bit deskew and eye centering */
+	tmp &= ~DTCR_DTRANK_MASK;
+	tmp |= (rank << DTCR_DTRANK_SHIFT) & DTCR_DTRANK_MASK;
+	/* Use Multi-Purpose Register for DQS gate training */
+	tmp |= DTCR_DTMPR;
+	/* Specify the rank enabled for data-training */
+	tmp &= ~DTCR_RNKEN_MASK;
+	tmp |= (1 << (DTCR_RNKEN_SHIFT + rank)) & DTCR_RNKEN_MASK;
+	writel(tmp, p);
+}
+
+struct ddrphy_init_sequence {
+	char *description;
+	u32 init_flag;
+	u32 done_flag;
+	u32 err_flag;
+};
+
+static struct ddrphy_init_sequence init_sequence[] = {
+	{
+		"DRAM Initialization",
+		PIR_DRAMRST | PIR_DRAMINIT,
+		PGSR0_DIDONE,
+		PGSR0_DIERR
+	},
+	{
+		"Write Leveling",
+		PIR_WL,
+		PGSR0_WLDONE,
+		PGSR0_WLERR
+	},
+	{
+		"Read DQS Gate Training",
+		PIR_QSGATE,
+		PGSR0_QSGDONE,
+		PGSR0_QSGERR
+	},
+	{
+		"Write Leveling Adjustment",
+		PIR_WLADJ,
+		PGSR0_WLADONE,
+		PGSR0_WLAERR
+	},
+	{
+		"Read Bit Deskew",
+		PIR_RDDSKW,
+		PGSR0_RDDONE,
+		PGSR0_RDERR
+	},
+	{
+		"Write Bit Deskew",
+		PIR_WRDSKW,
+		PGSR0_WDDONE,
+		PGSR0_WDERR
+	},
+	{
+		"Read Eye Training",
+		PIR_RDEYE,
+		PGSR0_REDONE,
+		PGSR0_REERR
+	},
+	{
+		"Write Eye Training",
+		PIR_WREYE,
+		PGSR0_WEDONE,
+		PGSR0_WEERR
+	}
+};
+
+int ddrphy_training(struct ddrphy __iomem *phy)
+{
+	int i;
+	u32 pgsr0;
+	u32 init_flag = PIR_INIT;
+	u32 done_flag = PGSR0_IDONE;
+	int timeout = 50000; /* 50 msec is long enough */
+#ifdef DISPLAY_ELAPSED_TIME
+	ulong start = get_timer(0);
+#endif
+
+	for (i = 0; i < ARRAY_SIZE(init_sequence); i++) {
+		init_flag |= init_sequence[i].init_flag;
+		done_flag |= init_sequence[i].done_flag;
+	}
+
+	writel(init_flag, &phy->pir);
+
+	do {
+		if (--timeout < 0) {
+#ifndef CONFIG_SPL_BUILD
+			printf("%s: error: timeout during DDR training\n",
+								__func__);
+#endif
+			return -1;
+		}
+		udelay(1);
+		pgsr0 = readl(&phy->pgsr[0]);
+	} while ((pgsr0 & done_flag) != done_flag);
+
+	for (i = 0; i < ARRAY_SIZE(init_sequence); i++) {
+		if (pgsr0 & init_sequence[i].err_flag) {
+#ifndef CONFIG_SPL_BUILD
+			printf("%s: error: %s failed\n", __func__,
+						init_sequence[i].description);
+#endif
+			return -1;
+		}
+	}
+
+#ifdef DISPLAY_ELAPSED_TIME
+	printf("%s: info: elapsed time %ld msec\n", get_timer(start));
+#endif
+
+	return 0;
+}
diff --git a/arch/arm/cpu/armv7/uniphier/ph1-ld4/Makefile b/arch/arm/cpu/armv7/uniphier/ph1-ld4/Makefile
index 5d682d3..8794629 100644
--- a/arch/arm/cpu/armv7/uniphier/ph1-ld4/Makefile
+++ b/arch/arm/cpu/armv7/uniphier/ph1-ld4/Makefile
@@ -8,4 +8,4 @@ obj-y += boot-mode.o
 obj-$(CONFIG_SOC_INIT) += bcu_init.o sbc_init.o sg_init.o pll_init.o \
 								clkrst_init.o
 obj-$(CONFIG_BOARD_POSTCLK_INIT) += pinctrl.o
-obj-$(CONFIG_DRAM_INIT) += pll_spectrum.o umc_init.o
+obj-$(CONFIG_DRAM_INIT) += pll_spectrum.o umc_init.o ddrphy_init.o
diff --git a/arch/arm/cpu/armv7/uniphier/ph1-ld4/ddrphy_init.c b/arch/arm/cpu/armv7/uniphier/ph1-ld4/ddrphy_init.c
new file mode 100644
index 0000000..60fc5ad
--- /dev/null
+++ b/arch/arm/cpu/armv7/uniphier/ph1-ld4/ddrphy_init.c
@@ -0,0 +1,70 @@
+/*
+ * Copyright (C) 2014 Panasonic Corporation
+ *
+ * SPDX-License-Identifier:	GPL-2.0+
+ */
+
+#include <linux/types.h>
+#include <asm/io.h>
+#include <asm/arch/ddrphy-regs.h>
+
+void ddrphy_init(struct ddrphy __iomem *phy, int freq, int size)
+{
+	u32 tmp;
+
+	writel(0x0300c473, &phy->pgcr[1]);
+	if (freq == 1333) {
+		writel(0x0a806844, &phy->ptr[0]);
+		writel(0x208e0124, &phy->ptr[1]);
+	} else {
+		writel(0x0c807d04, &phy->ptr[0]);
+		writel(0x2710015E, &phy->ptr[1]);
+	}
+	writel(0x00083DEF, &phy->ptr[2]);
+	if (freq == 1333) {
+		writel(0x0f051616, &phy->ptr[3]);
+		writel(0x06ae08d6, &phy->ptr[4]);
+	} else {
+		writel(0x12061A80, &phy->ptr[3]);
+		writel(0x08027100, &phy->ptr[4]);
+	}
+	writel(0xF004001A, &phy->dsgcr);
+
+	/* change the value of the on-die pull-up/pull-down registors */
+	tmp = readl(&phy->dxccr);
+	tmp &= ~0x0ee0;
+	tmp |= DXCCR_DQSNRES_688_OHM | DXCCR_DQSRES_688_OHM;
+	writel(tmp, &phy->dxccr);
+
+	writel(0x0000040B, &phy->dcr);
+	if (freq == 1333) {
+		writel(0x85589955, &phy->dtpr[0]);
+		if (size == 1)
+			writel(0x1a8253c0, &phy->dtpr[1]);
+		else
+			writel(0x1a8363c0, &phy->dtpr[1]);
+		writel(0x5002c200, &phy->dtpr[2]);
+		writel(0x00000b51, &phy->mr0);
+	} else {
+		writel(0x999cbb66, &phy->dtpr[0]);
+		if (size == 1)
+			writel(0x1a82dbc0, &phy->dtpr[1]);
+		else
+			writel(0x1a878400, &phy->dtpr[1]);
+		writel(0xa00214f8, &phy->dtpr[2]);
+		writel(0x00000d71, &phy->mr0);
+	}
+	writel(0x00000006, &phy->mr1);
+	if (freq == 1333)
+		writel(0x00000290, &phy->mr2);
+	else
+		writel(0x00000298, &phy->mr2);
+
+	writel(0x00000800, &phy->mr3);
+
+	while (!(readl(&phy->pgsr[0]) & PGSR0_IDONE))
+		;
+
+	writel(0x0300C473, &phy->pgcr[1]);
+	writel(0x0000005D, &phy->zq[0].cr[1]);
+}
diff --git a/arch/arm/cpu/armv7/uniphier/ph1-ld4/umc_init.c b/arch/arm/cpu/armv7/uniphier/ph1-ld4/umc_init.c
index ebcbaab..8788916 100644
--- a/arch/arm/cpu/armv7/uniphier/ph1-ld4/umc_init.c
+++ b/arch/arm/cpu/armv7/uniphier/ph1-ld4/umc_init.c
@@ -7,6 +7,7 @@
 #include <common.h>
 #include <asm/io.h>
 #include <asm/arch/umc-regs.h>
+#include <asm/arch/ddrphy-regs.h>
 
 static inline void umc_start_ssif(void __iomem *ssif_base)
 {
@@ -125,6 +126,8 @@ static inline int umc_init_sub(int freq, int size_ch0, int size_ch1)
 	void __iomem *ca_base1 = (void __iomem *)UMC_CA_BASE(1);
 	void __iomem *dramcont0 = (void __iomem *)UMC_DRAMCONT_BASE(0);
 	void __iomem *dramcont1 = (void __iomem *)UMC_DRAMCONT_BASE(1);
+	void __iomem *phy0_0 = (void __iomem *)DDRPHY_BASE(0, 0);
+	void __iomem *phy1_0 = (void __iomem *)DDRPHY_BASE(1, 0);
 
 	umc_dram_init_start(dramcont0);
 	umc_dram_init_start(dramcont1);
@@ -133,8 +136,18 @@ static inline int umc_init_sub(int freq, int size_ch0, int size_ch1)
 
 	writel(0x00000101, dramcont0 + UMC_DIOCTLA);
 
+	ddrphy_init(phy0_0, freq, size_ch0);
+
+	ddrphy_prepare_training(phy0_0, 0);
+	ddrphy_training(phy0_0);
+
 	writel(0x00000101, dramcont1 + UMC_DIOCTLA);
 
+	ddrphy_init(phy1_0, freq, size_ch1);
+
+	ddrphy_prepare_training(phy1_0, 1);
+	ddrphy_training(phy1_0);
+
 	umc_dramcont_init(dramcont0, ca_base0, size_ch0, freq);
 	umc_dramcont_init(dramcont1, ca_base1, size_ch1, freq);
 
diff --git a/arch/arm/cpu/armv7/uniphier/ph1-pro4/Makefile b/arch/arm/cpu/armv7/uniphier/ph1-pro4/Makefile
index fd1c432..cee7878 100644
--- a/arch/arm/cpu/armv7/uniphier/ph1-pro4/Makefile
+++ b/arch/arm/cpu/armv7/uniphier/ph1-pro4/Makefile
@@ -7,4 +7,4 @@ obj-$(if $(CONFIG_OF_CONTROL),,y) += platdevice.o
 obj-y += boot-mode.o
 obj-$(CONFIG_SOC_INIT) += sbc_init.o sg_init.o pll_init.o clkrst_init.o
 obj-$(CONFIG_BOARD_POSTCLK_INIT) += pinctrl.o
-obj-$(CONFIG_DRAM_INIT) += pll_spectrum.o umc_init.o
+obj-$(CONFIG_DRAM_INIT) += pll_spectrum.o umc_init.o ddrphy_init.o
diff --git a/arch/arm/cpu/armv7/uniphier/ph1-pro4/ddrphy_init.c b/arch/arm/cpu/armv7/uniphier/ph1-pro4/ddrphy_init.c
new file mode 100644
index 0000000..c5d1f60
--- /dev/null
+++ b/arch/arm/cpu/armv7/uniphier/ph1-pro4/ddrphy_init.c
@@ -0,0 +1,70 @@
+/*
+ * Copyright (C) 2014 Panasonic Corporation
+ *
+ * SPDX-License-Identifier:	GPL-2.0+
+ */
+
+#include <linux/types.h>
+#include <asm/io.h>
+#include <asm/arch/ddrphy-regs.h>
+
+void ddrphy_init(struct ddrphy __iomem *phy, int freq, int size)
+{
+	u32 tmp;
+
+	writel(0x0300c473, &phy->pgcr[1]);
+	if (freq == 1333) {
+		writel(0x0a806844, &phy->ptr[0]);
+		writel(0x208e0124, &phy->ptr[1]);
+	} else {
+		writel(0x0c807d04, &phy->ptr[0]);
+		writel(0x2710015E, &phy->ptr[1]);
+	}
+	writel(0x00083DEF, &phy->ptr[2]);
+	if (freq == 1333) {
+		writel(0x0f051616, &phy->ptr[3]);
+		writel(0x06ae08d6, &phy->ptr[4]);
+	} else {
+		writel(0x12061A80, &phy->ptr[3]);
+		writel(0x08027100, &phy->ptr[4]);
+	}
+	writel(0xF004001A, &phy->dsgcr);
+
+	/* change the value of the on-die pull-up/pull-down registors */
+	tmp = readl(&phy->dxccr);
+	tmp &= ~0x0ee0;
+	tmp |= DXCCR_DQSNRES_688_OHM | DXCCR_DQSRES_688_OHM;
+	writel(tmp, &phy->dxccr);
+
+	writel(0x0000040B, &phy->dcr);
+	if (freq == 1333) {
+		writel(0x85589955, &phy->dtpr[0]);
+		if (size == 1)
+			writel(0x1a8363c0, &phy->dtpr[1]);
+		else
+			writel(0x1a8363c0, &phy->dtpr[1]);
+		writel(0x5002c200, &phy->dtpr[2]);
+		writel(0x00000b51, &phy->mr0);
+	} else {
+		writel(0x999cbb66, &phy->dtpr[0]);
+		if (size == 1)
+			writel(0x1a878400, &phy->dtpr[1]);
+		else
+			writel(0x1a878400, &phy->dtpr[1]);
+		writel(0xa00214f8, &phy->dtpr[2]);
+		writel(0x00000d71, &phy->mr0);
+	}
+	writel(0x00000006, &phy->mr1);
+	if (freq == 1333)
+		writel(0x00000290, &phy->mr2);
+	else
+		writel(0x00000298, &phy->mr2);
+
+	writel(0x00000000, &phy->mr3);
+
+	while (!(readl(&phy->pgsr[0]) & PGSR0_IDONE))
+		;
+
+	writel(0x0300C473, &phy->pgcr[1]);
+	writel(0x0000005D, &phy->zq[0].cr[1]);
+}
diff --git a/arch/arm/cpu/armv7/uniphier/ph1-pro4/umc_init.c b/arch/arm/cpu/armv7/uniphier/ph1-pro4/umc_init.c
index 328b2f4..1973ab0 100644
--- a/arch/arm/cpu/armv7/uniphier/ph1-pro4/umc_init.c
+++ b/arch/arm/cpu/armv7/uniphier/ph1-pro4/umc_init.c
@@ -7,6 +7,7 @@
 #include <common.h>
 #include <asm/io.h>
 #include <asm/arch/umc-regs.h>
+#include <asm/arch/ddrphy-regs.h>
 
 static inline void umc_start_ssif(void __iomem *ssif_base)
 {
@@ -94,6 +95,10 @@ static inline int umc_init_sub(int freq, int size_ch0, int size_ch1)
 	void __iomem *ca_base1 = (void __iomem *)UMC_CA_BASE(1);
 	void __iomem *dramcont0 = (void __iomem *)UMC_DRAMCONT_BASE(0);
 	void __iomem *dramcont1 = (void __iomem *)UMC_DRAMCONT_BASE(1);
+	void __iomem *phy0_0 = (void __iomem *)DDRPHY_BASE(0, 0);
+	void __iomem *phy0_1 = (void __iomem *)DDRPHY_BASE(0, 1);
+	void __iomem *phy1_0 = (void __iomem *)DDRPHY_BASE(1, 0);
+	void __iomem *phy1_1 = (void __iomem *)DDRPHY_BASE(1, 1);
 
 	umc_dram_init_start(dramcont0);
 	umc_dram_init_start(dramcont1);
@@ -102,12 +107,32 @@ static inline int umc_init_sub(int freq, int size_ch0, int size_ch1)
 
 	writel(0x00000101, dramcont0 + UMC_DIOCTLA);
 
+	ddrphy_init(phy0_0, freq, size_ch0);
+
+	ddrphy_prepare_training(phy0_0, 0);
+	ddrphy_training(phy0_0);
+
 	writel(0x00000103, dramcont0 + UMC_DIOCTLA);
 
+	ddrphy_init(phy0_1, freq, size_ch0);
+
+	ddrphy_prepare_training(phy0_1, 1);
+	ddrphy_training(phy0_1);
+
 	writel(0x00000101, dramcont1 + UMC_DIOCTLA);
 
+	ddrphy_init(phy1_0, freq, size_ch1);
+
+	ddrphy_prepare_training(phy1_0, 0);
+	ddrphy_training(phy1_0);
+
 	writel(0x00000103, dramcont1 + UMC_DIOCTLA);
 
+	ddrphy_init(phy1_1, freq, size_ch1);
+
+	ddrphy_prepare_training(phy1_1, 1);
+	ddrphy_training(phy1_1);
+
 	umc_dramcont_init(dramcont0, ca_base0, size_ch0, freq);
 	umc_dramcont_init(dramcont1, ca_base1, size_ch1, freq);
 
diff --git a/arch/arm/cpu/armv7/uniphier/ph1-sld8/Makefile b/arch/arm/cpu/armv7/uniphier/ph1-sld8/Makefile
index 5d682d3..8794629 100644
--- a/arch/arm/cpu/armv7/uniphier/ph1-sld8/Makefile
+++ b/arch/arm/cpu/armv7/uniphier/ph1-sld8/Makefile
@@ -8,4 +8,4 @@ obj-y += boot-mode.o
 obj-$(CONFIG_SOC_INIT) += bcu_init.o sbc_init.o sg_init.o pll_init.o \
 								clkrst_init.o
 obj-$(CONFIG_BOARD_POSTCLK_INIT) += pinctrl.o
-obj-$(CONFIG_DRAM_INIT) += pll_spectrum.o umc_init.o
+obj-$(CONFIG_DRAM_INIT) += pll_spectrum.o umc_init.o ddrphy_init.o
diff --git a/arch/arm/cpu/armv7/uniphier/ph1-sld8/ddrphy_init.c b/arch/arm/cpu/armv7/uniphier/ph1-sld8/ddrphy_init.c
new file mode 100644
index 0000000..a5eafef
--- /dev/null
+++ b/arch/arm/cpu/armv7/uniphier/ph1-sld8/ddrphy_init.c
@@ -0,0 +1,75 @@
+/*
+ * Copyright (C) 2014 Panasonic Corporation
+ *
+ * SPDX-License-Identifier:	GPL-2.0+
+ */
+
+#include <config.h>
+#include <linux/types.h>
+#include <asm/io.h>
+#include <asm/arch/ddrphy-regs.h>
+
+void ddrphy_init(struct ddrphy __iomem *phy, int freq, int size)
+{
+	u32 tmp;
+
+	writel(0x0300c473, &phy->pgcr[1]);
+	if (freq == 1333) {
+		writel(0x0a806844, &phy->ptr[0]);
+		writel(0x208e0124, &phy->ptr[1]);
+	} else {
+		writel(0x0c807d04, &phy->ptr[0]);
+		writel(0x2710015E, &phy->ptr[1]);
+	}
+	writel(0x00083DEF, &phy->ptr[2]);
+	if (freq == 1333) {
+		writel(0x0f051616, &phy->ptr[3]);
+		writel(0x06ae08d6, &phy->ptr[4]);
+	} else {
+		writel(0x12061A80, &phy->ptr[3]);
+		writel(0x08027100, &phy->ptr[4]);
+	}
+	writel(0xF004001A, &phy->dsgcr);
+
+	/* change the value of the on-die pull-up/pull-down registors */
+	tmp = readl(&phy->dxccr);
+	tmp &= ~0x0ee0;
+	tmp |= DXCCR_DQSNRES_688_OHM | DXCCR_DQSRES_688_OHM;
+	writel(tmp, &phy->dxccr);
+
+	writel(0x0000040B, &phy->dcr);
+	if (freq == 1333) {
+		writel(0x85589955, &phy->dtpr[0]);
+		if (size == 1)
+			writel(0x1a8363c0, &phy->dtpr[1]);
+		else
+			writel(0x1a8363c0, &phy->dtpr[1]);
+		writel(0x5002c200, &phy->dtpr[2]);
+		writel(0x00000b51, &phy->mr0);
+	} else {
+		writel(0x999cbb66, &phy->dtpr[0]);
+		if (size == 1)
+			writel(0x1a878400, &phy->dtpr[1]);
+		else
+			writel(0x1a878400, &phy->dtpr[1]);
+		writel(0xa00214f8, &phy->dtpr[2]);
+		writel(0x00000d71, &phy->mr0);
+	}
+	writel(0x00000006, &phy->mr1);
+	if (freq == 1333)
+		writel(0x00000290, &phy->mr2);
+	else
+		writel(0x00000298, &phy->mr2);
+
+#ifdef CONFIG_DDR_STANDARD
+	writel(0x00000000, &phy->mr3);
+#else
+	writel(0x00000800, &phy->mr3);
+#endif
+
+	while (!(readl(&phy->pgsr[0]) & PGSR0_IDONE))
+		;
+
+	writel(0x0300C473, &phy->pgcr[1]);
+	writel(0x0000005D, &phy->zq[0].cr[1]);
+}
diff --git a/arch/arm/cpu/armv7/uniphier/ph1-sld8/umc_init.c b/arch/arm/cpu/armv7/uniphier/ph1-sld8/umc_init.c
index a44f999..2e0f9ae 100644
--- a/arch/arm/cpu/armv7/uniphier/ph1-sld8/umc_init.c
+++ b/arch/arm/cpu/armv7/uniphier/ph1-sld8/umc_init.c
@@ -7,6 +7,7 @@
 #include <common.h>
 #include <asm/io.h>
 #include <asm/arch/umc-regs.h>
+#include <asm/arch/ddrphy-regs.h>
 
 static inline void umc_start_ssif(void __iomem *ssif_base)
 {
@@ -105,6 +106,8 @@ static inline int umc_init_sub(int freq, int size_ch0, int size_ch1)
 	void __iomem *ca_base1 = (void __iomem *)UMC_CA_BASE(1);
 	void __iomem *dramcont0 = (void __iomem *)UMC_DRAMCONT_BASE(0);
 	void __iomem *dramcont1 = (void __iomem *)UMC_DRAMCONT_BASE(1);
+	void __iomem *phy0_0 = (void __iomem *)DDRPHY_BASE(0, 0);
+	void __iomem *phy1_0 = (void __iomem *)DDRPHY_BASE(1, 0);
 
 	umc_dram_init_start(dramcont0);
 	umc_dram_init_start(dramcont1);
@@ -113,8 +116,18 @@ static inline int umc_init_sub(int freq, int size_ch0, int size_ch1)
 
 	writel(0x00000101, dramcont0 + UMC_DIOCTLA);
 
+	ddrphy_init(phy0_0, freq, size_ch0);
+
+	ddrphy_prepare_training(phy0_0, 0);
+	ddrphy_training(phy0_0);
+
 	writel(0x00000101, dramcont1 + UMC_DIOCTLA);
 
+	ddrphy_init(phy1_0, freq, size_ch1);
+
+	ddrphy_prepare_training(phy1_0, 1);
+	ddrphy_training(phy1_0);
+
 	umc_dramcont_init(dramcont0, ca_base0, size_ch0, freq);
 	umc_dramcont_init(dramcont1, ca_base1, size_ch1, freq);
 
diff --git a/arch/arm/include/asm/arch-uniphier/ddrphy-regs.h b/arch/arm/include/asm/arch-uniphier/ddrphy-regs.h
new file mode 100644
index 0000000..484559c
--- /dev/null
+++ b/arch/arm/include/asm/arch-uniphier/ddrphy-regs.h
@@ -0,0 +1,172 @@
+/*
+ * UniPhier DDR PHY registers
+ *
+ * Copyright (C) 2014 Panasonic Corporation
+ *   Author: Masahiro Yamada <yamada.m at jp.panasonic.com>
+ *
+ * SPDX-License-Identifier:	GPL-2.0+
+ */
+
+#ifndef ARCH_DDRPHY_REGS_H
+#define ARCH_DDRPHY_REGS_H
+
+#include <linux/compiler.h>
+
+#ifndef __ASSEMBLY__
+
+struct ddrphy {
+	u32 ridr;		/* Revision Identification Register */
+	u32 pir;		/* PHY Initialixation Register */
+	u32 pgcr[2];		/* PHY General Configuration Register */
+	u32 pgsr[2];		/* PHY General Status Register */
+	u32 pllcr;		/* PLL Control Register */
+	u32 ptr[5];		/* PHY Timing Register */
+	u32 acmdlr;		/* AC Master Delay Line Register */
+	u32 acbdlr;		/* AC Bit Delay Line Register */
+	u32 aciocr;		/* AC I/O Configuration Register */
+	u32 dxccr;		/* DATX8 Common Configuration Register */
+	u32 dsgcr;		/* DDR System General Configuration Register */
+	u32 dcr;		/* DRAM Configuration Register */
+	u32 dtpr[3];		/* DRAM Timing Parameters Register */
+	u32 mr0;		/* Mode Register 0 */
+	u32 mr1;		/* Mode Register 1 */
+	u32 mr2;		/* Mode Register 2 */
+	u32 mr3;		/* Mode Register 3 */
+	u32 odtcr;		/* ODT Configuration Register */
+	u32 dtcr;		/* Data Training Configuration Register */
+	u32 dtar[4];		/* Data Training Address Register */
+	u32 dtdr[2];		/* Data Training Data Register */
+	u32 dtedr[2];		/* Data Training Eye Data Register */
+	u32 rsv0[13];		/* Reserved */
+	u32 dcuar;		/* DCU Address Register */
+	u32 dcudr;		/* DCU Data Register */
+	u32 dcurr;		/* DCU Run Register */
+	u32 dculr;		/* DCU Loop Register */
+	u32 dcugcr;		/* DCU General Configuration Register */
+	u32 dcutpr;		/* DCU Timing Parameters Register */
+	u32 dcusr[2];		/* DCU Status Register */
+	u32 rsv1[8];		/* Reserved */
+	u32 bistrr;		/* BIST Run Register */
+	u32 bistwcr;		/* BIST Word Count Register */
+	u32 bistmskr[3];	/* BIST Mask Register */
+	u32 bistlsr;		/* BIST LFSR Sed Register */
+	u32 bistar[3];		/* BIST Address Register */
+	u32 bistudpr;		/* BIST User Data Pattern Register */
+	u32 bistgsr;		/* BIST General Status Register */
+	u32 bistwer;		/* BIST Word Error Register */
+	u32 bistber[4];		/* BIST Bit Error Register */
+	u32 bistwcsr;		/* BIST Word Count Status Register */
+	u32 bistfwr[3];		/* BIST Fail Word Register */
+	u32 rsv2[10];		/* Reserved */
+	u32 gpr[2];		/* General Purpose Register */
+	struct ddrphy_zq {	/* ZQ */
+		u32 cr[2];	/* Impedance Control Register */
+		u32 sr[2];	/* Impedance Status Register */
+	} zq[4];
+	struct ddrphy_datx8 {	/* DATX8 */
+		u32 gcr;	/* General Configuration Register */
+		u32 gsr[2];	/* General Status Register */
+		u32 bdlr[5];	/* Bit Delay Line Register */
+		u32 lcdlr[3];	/* Local Calibrated Delay Line Register */
+		u32 mdlr;	/* Master Delay Line Register */
+		u32 gtr;	/* General Timing Register */
+		u32 rsv[3];	/* Reserved */
+	} dx[9];
+} __packed;
+
+#endif /* __ASSEMBLY__ */
+
+#define PIR_INIT		(1 <<  0)	/* Initialization Trigger */
+#define PIR_ZCAL		(1 <<  1)	/* Impedance Calibration */
+#define PIR_PLLINIT		(1 <<  4)	/* PLL Initialization */
+#define PIR_DCAL		(1 <<  5)	/* DDL Calibration */
+#define PIR_PHYRST		(1 <<  6)	/* PHY Reset */
+#define PIR_DRAMRST		(1 <<  7)	/* DRAM Reset */
+#define PIR_DRAMINIT		(1 <<  8)	/* DRAM Initialization */
+#define PIR_WL			(1 <<  9)	/* Write Leveling */
+#define PIR_QSGATE		(1 << 10)	/* Read DQS Gate Training */
+#define PIR_WLADJ		(1 << 11)	/* Write Leveling Adjust */
+#define PIR_RDDSKW		(1 << 12)	/* Read Data Bit Deskew */
+#define PIR_WRDSKW		(1 << 13)	/* Write Data Bit Deskew */
+#define PIR_RDEYE		(1 << 14)	/* Read Data Eye Training */
+#define PIR_WREYE		(1 << 15)	/* Write Data Eye Training */
+#define PIR_LOCKBYP		(1 << 28)	/* PLL Lock Bypass */
+#define PIR_DCALBYP		(1 << 29)	/* DDL Calibration Bypass */
+#define PIR_ZCALBYP		(1 << 30)	/* Impedance Calib Bypass */
+#define PIR_INITBYP		(1 << 31)	/* Initialization Bypass */
+
+#define PGSR0_IDONE		(1 <<  0)	/* Initialization Done */
+#define PGSR0_PLDONE		(1 <<  1)	/* PLL Lock Done */
+#define PGSR0_DCDONE		(1 <<  2)	/* DDL Calibration Done */
+#define PGSR0_ZCDONE		(1 <<  3)	/* Impedance Calibration Done */
+#define PGSR0_DIDONE		(1 <<  4)	/* DRAM Initialization Done */
+#define PGSR0_WLDONE		(1 <<  5)	/* Write Leveling Done */
+#define PGSR0_QSGDONE		(1 <<  6)	/* DQS Gate Training Done */
+#define PGSR0_WLADONE		(1 <<  7)	/* Write Leveling Adjust Done */
+#define PGSR0_RDDONE		(1 <<  8)	/* Read Bit Deskew Done */
+#define PGSR0_WDDONE		(1 <<  9)	/* Write Bit Deskew Done */
+#define PGSR0_REDONE		(1 << 10)	/* Read Eye Training Done */
+#define PGSR0_WEDONE		(1 << 11)	/* Write Eye Training Done */
+#define PGSR0_IERR		(1 << 16)	/* Initialization Error */
+#define PGSR0_PLERR		(1 << 17)	/* PLL Lock Error */
+#define PGSR0_DCERR		(1 << 18)	/* DDL Calibration Error */
+#define PGSR0_ZCERR		(1 << 19)	/* Impedance Calib Error */
+#define PGSR0_DIERR		(1 << 20)	/* DRAM Initialization Error */
+#define PGSR0_WLERR		(1 << 21)	/* Write Leveling Error */
+#define PGSR0_QSGERR		(1 << 22)	/* DQS Gate Training Error */
+#define PGSR0_WLAERR		(1 << 23)	/* Write Leveling Adj Error */
+#define PGSR0_RDERR		(1 << 24)	/* Read Bit Deskew Error */
+#define PGSR0_WDERR		(1 << 25)	/* Write Bit Deskew Error */
+#define PGSR0_REERR		(1 << 26)	/* Read Eye Training Error */
+#define PGSR0_WEERR		(1 << 27)	/* Write Eye Training Error */
+#define PGSR0_DTERR_SHIFT	28		/* Data Training Error Status*/
+#define PGSR0_DTERR		(7 << (PGSR0_DTERR_SHIFT))
+#define PGSR0_APLOCK		(1 << 31)	/* AC PLL Lock */
+
+#define DXCCR_DQSRES_OPEN	(0 << 5)
+#define DXCCR_DQSRES_688_OHM	(1 << 5)
+#define DXCCR_DQSRES_611_OHM	(2 << 5)
+#define DXCCR_DQSRES_550_OHM	(3 << 5)
+#define DXCCR_DQSRES_500_OHM	(4 << 5)
+#define DXCCR_DQSRES_458_OHM	(5 << 5)
+#define DXCCR_DQSRES_393_OHM	(6 << 5)
+#define DXCCR_DQSRES_344_OHM	(7 << 5)
+
+#define DXCCR_DQSNRES_OPEN	(0 << 9)
+#define DXCCR_DQSNRES_688_OHM	(1 << 9)
+#define DXCCR_DQSNRES_611_OHM	(2 << 9)
+#define DXCCR_DQSNRES_550_OHM	(3 << 9)
+#define DXCCR_DQSNRES_500_OHM	(4 << 9)
+#define DXCCR_DQSNRES_458_OHM	(5 << 9)
+#define DXCCR_DQSNRES_393_OHM	(6 << 9)
+#define DXCCR_DQSNRES_344_OHM	(7 << 9)
+
+#define DTCR_DTRANK_SHIFT	4		/* Data Training Rank */
+#define DTCR_DTRANK_MASK	(0x3 << (DTCR_DTRANK_SHIFT))
+#define DTCR_DTMPR		(1 << 6)	/* Data Training using MPR */
+#define DTCR_RNKEN_SHIFT	24		/* Rank Enable */
+#define DTCR_RNKEN_MASK		(0xf << (DTCR_RNKEN_SHIFT))
+
+#define DXGCR_WLRKEN_SHIFT	26		/* Write Level Rank Enable */
+#define DXGCR_WLRKEN_MASK	(0xf << (DXGCR_WLRKEN_SHIFT))
+
+/* SoC-specific parameters */
+#define NR_DATX8_PER_DDRPHY	2
+
+#if defined(CONFIG_MACH_PH1_LD4) || defined(CONFIG_MACH_PH1_SLD8)
+#define NR_DDRPHY_PER_CH		1
+#else
+#define NR_DDRPHY_PER_CH		2
+#endif
+
+#define NR_DDRCH		2
+
+#define DDRPHY_BASE(ch, phy)	(0x5bc01000 + 0x200000 * (ch) + 0x1000 * (phy))
+
+#ifndef __ASSEMBLY__
+void ddrphy_init(struct ddrphy __iomem *phy, int freq, int size);
+void ddrphy_prepare_training(struct ddrphy __iomem *phy, int rank);
+int ddrphy_training(struct ddrphy __iomem *phy);
+#endif
+
+#endif /* ARCH_DDRPHY_REGS_H */
-- 
1.9.1



More information about the U-Boot mailing list