2 * Copyright (C) 2011-2014 Panasonic Corporation
3 * Copyright (C) 2015-2016 Socionext Inc.
5 * SPDX-License-Identifier: GPL-2.0+
8 #include <linux/bitops.h>
9 #include <linux/delay.h>
10 #include <linux/errno.h>
12 #include <linux/kernel.h>
13 #include <linux/printk.h>
16 #include "ddrphy-init.h"
17 #include "ddrphy-regs.h"
19 /* for LD4, Pro4, sLD8 */
20 #define NR_DATX8_PER_DDRPHY 2
22 void ddrphy_prepare_training(void __iomem *phy_base, int rank)
24 void __iomem *dx_base = phy_base + PHY_DX_BASE;
28 for (dx = 0; dx < NR_DATX8_PER_DDRPHY; dx++) {
29 tmp = readl(dx_base + PHY_DX_GCR);
30 /* Specify the rank that should be write leveled */
31 tmp &= ~PHY_DX_GCR_WLRKEN_MASK;
32 tmp |= (1 << (PHY_DX_GCR_WLRKEN_SHIFT + rank)) &
33 PHY_DX_GCR_WLRKEN_MASK;
34 writel(tmp, dx_base + PHY_DX_GCR);
35 dx_base += PHY_DX_STRIDE;
38 tmp = readl(phy_base + PHY_DTCR);
39 /* Specify the rank used during data bit deskew and eye centering */
40 tmp &= ~PHY_DTCR_DTRANK_MASK;
41 tmp |= (rank << PHY_DTCR_DTRANK_SHIFT) & PHY_DTCR_DTRANK_MASK;
42 /* Use Multi-Purpose Register for DQS gate training */
43 tmp |= PHY_DTCR_DTMPR;
44 /* Specify the rank enabled for data-training */
45 tmp &= ~PHY_DTCR_RANKEN_MASK;
46 tmp |= (1 << (PHY_DTCR_RANKEN_SHIFT + rank)) & PHY_DTCR_RANKEN_MASK;
47 writel(tmp, phy_base + PHY_DTCR);
50 struct ddrphy_init_sequence {
57 static const struct ddrphy_init_sequence init_sequence[] = {
59 "DRAM Initialization",
60 PHY_PIR_DRAMRST | PHY_PIR_DRAMINIT,
71 "Read DQS Gate Training",
77 "Write Leveling Adjustment",
101 "Write Eye Training",
108 int ddrphy_training(void __iomem *phy_base)
112 u32 init_flag = PHY_PIR_INIT;
113 u32 done_flag = PHY_PGSR0_IDONE;
114 int timeout = 50000; /* 50 msec is long enough */
116 ulong start = get_timer(0);
119 for (i = 0; i < ARRAY_SIZE(init_sequence); i++) {
120 init_flag |= init_sequence[i].init_flag;
121 done_flag |= init_sequence[i].done_flag;
124 writel(init_flag, phy_base + PHY_PIR);
128 pr_err("timeout during DDR training\n");
132 pgsr0 = readl(phy_base + PHY_PGSR0);
133 } while ((pgsr0 & done_flag) != done_flag);
135 for (i = 0; i < ARRAY_SIZE(init_sequence); i++) {
136 if (pgsr0 & init_sequence[i].err_flag) {
137 pr_err("%s failed\n", init_sequence[i].description);
143 pr_debug("DDR training: elapsed time %ld msec\n", get_timer(start));