aboutsummaryrefslogtreecommitdiffstats
path: root/roms/u-boot/arch/arm/mach-uniphier/dram/ddrphy-training.c
diff options
context:
space:
mode:
Diffstat (limited to 'roms/u-boot/arch/arm/mach-uniphier/dram/ddrphy-training.c')
-rw-r--r--roms/u-boot/arch/arm/mach-uniphier/dram/ddrphy-training.c146
1 files changed, 146 insertions, 0 deletions
diff --git a/roms/u-boot/arch/arm/mach-uniphier/dram/ddrphy-training.c b/roms/u-boot/arch/arm/mach-uniphier/dram/ddrphy-training.c
new file mode 100644
index 000000000..1decdf1cb
--- /dev/null
+++ b/roms/u-boot/arch/arm/mach-uniphier/dram/ddrphy-training.c
@@ -0,0 +1,146 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2011-2014 Panasonic Corporation
+ * Copyright (C) 2015-2016 Socionext Inc.
+ */
+
+#include <linux/bitops.h>
+#include <linux/delay.h>
+#include <linux/errno.h>
+#include <linux/io.h>
+#include <linux/kernel.h>
+#include <linux/printk.h>
+#include <time.h>
+
+#include "ddrphy-init.h"
+#include "ddrphy-regs.h"
+
+/* for LD4, Pro4, sLD8 */
+#define NR_DATX8_PER_DDRPHY 2
+
+void ddrphy_prepare_training(void __iomem *phy_base, int rank)
+{
+ void __iomem *dx_base = phy_base + PHY_DX_BASE;
+ int dx;
+ u32 tmp;
+
+ for (dx = 0; dx < NR_DATX8_PER_DDRPHY; dx++) {
+ tmp = readl(dx_base + PHY_DX_GCR);
+ /* Specify the rank that should be write leveled */
+ tmp &= ~PHY_DX_GCR_WLRKEN_MASK;
+ tmp |= (1 << (PHY_DX_GCR_WLRKEN_SHIFT + rank)) &
+ PHY_DX_GCR_WLRKEN_MASK;
+ writel(tmp, dx_base + PHY_DX_GCR);
+ dx_base += PHY_DX_STRIDE;
+ }
+
+ tmp = readl(phy_base + PHY_DTCR);
+ /* Specify the rank used during data bit deskew and eye centering */
+ tmp &= ~PHY_DTCR_DTRANK_MASK;
+ tmp |= (rank << PHY_DTCR_DTRANK_SHIFT) & PHY_DTCR_DTRANK_MASK;
+ /* Use Multi-Purpose Register for DQS gate training */
+ tmp |= PHY_DTCR_DTMPR;
+ /* Specify the rank enabled for data-training */
+ tmp &= ~PHY_DTCR_RANKEN_MASK;
+ tmp |= (1 << (PHY_DTCR_RANKEN_SHIFT + rank)) & PHY_DTCR_RANKEN_MASK;
+ writel(tmp, phy_base + PHY_DTCR);
+}
+
+struct ddrphy_init_sequence {
+ char *description;
+ u32 init_flag;
+ u32 done_flag;
+ u32 err_flag;
+};
+
+static const struct ddrphy_init_sequence init_sequence[] = {
+ {
+ "DRAM Initialization",
+ PHY_PIR_DRAMRST | PHY_PIR_DRAMINIT,
+ PHY_PGSR0_DIDONE,
+ PHY_PGSR0_DIERR
+ },
+ {
+ "Write Leveling",
+ PHY_PIR_WL,
+ PHY_PGSR0_WLDONE,
+ PHY_PGSR0_WLERR
+ },
+ {
+ "Read DQS Gate Training",
+ PHY_PIR_QSGATE,
+ PHY_PGSR0_QSGDONE,
+ PHY_PGSR0_QSGERR
+ },
+ {
+ "Write Leveling Adjustment",
+ PHY_PIR_WLADJ,
+ PHY_PGSR0_WLADONE,
+ PHY_PGSR0_WLAERR
+ },
+ {
+ "Read Bit Deskew",
+ PHY_PIR_RDDSKW,
+ PHY_PGSR0_RDDONE,
+ PHY_PGSR0_RDERR
+ },
+ {
+ "Write Bit Deskew",
+ PHY_PIR_WRDSKW,
+ PHY_PGSR0_WDDONE,
+ PHY_PGSR0_WDERR
+ },
+ {
+ "Read Eye Training",
+ PHY_PIR_RDEYE,
+ PHY_PGSR0_REDONE,
+ PHY_PGSR0_REERR
+ },
+ {
+ "Write Eye Training",
+ PHY_PIR_WREYE,
+ PHY_PGSR0_WEDONE,
+ PHY_PGSR0_WEERR
+ }
+};
+
+int ddrphy_training(void __iomem *phy_base)
+{
+ int i;
+ u32 pgsr0;
+ u32 init_flag = PHY_PIR_INIT;
+ u32 done_flag = PHY_PGSR0_IDONE;
+ int timeout = 50000; /* 50 msec is long enough */
+#ifdef DEBUG
+ 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_base + PHY_PIR);
+
+ do {
+ if (--timeout < 0) {
+ pr_err("timeout during DDR training\n");
+ return -ETIMEDOUT;
+ }
+ udelay(1);
+ pgsr0 = readl(phy_base + PHY_PGSR0);
+ } while ((pgsr0 & done_flag) != done_flag);
+
+ for (i = 0; i < ARRAY_SIZE(init_sequence); i++) {
+ if (pgsr0 & init_sequence[i].err_flag) {
+ pr_err("%s failed\n", init_sequence[i].description);
+ return -EIO;
+ }
+ }
+
+#ifdef DEBUG
+ pr_debug("DDR training: elapsed time %ld msec\n", get_timer(start));
+#endif
+
+ return 0;
+}