2 * Copyright (C) 2011-2014 Panasonic Corporation
3 * Author: Masahiro Yamada <yamada.m@jp.panasonic.com>
5 * SPDX-License-Identifier: GPL-2.0+
10 #include <mach/ddrphy-regs.h>
12 void ddrphy_prepare_training(struct ddrphy __iomem *phy, int rank)
17 for (dx = 0; dx < NR_DATX8_PER_DDRPHY; dx++) {
21 /* Specify the rank that should be write leveled */
22 tmp &= ~DXGCR_WLRKEN_MASK;
23 tmp |= (1 << (DXGCR_WLRKEN_SHIFT + rank)) & DXGCR_WLRKEN_MASK;
30 /* Specify the rank used during data bit deskew and eye centering */
31 tmp &= ~DTCR_DTRANK_MASK;
32 tmp |= (rank << DTCR_DTRANK_SHIFT) & DTCR_DTRANK_MASK;
33 /* Use Multi-Purpose Register for DQS gate training */
35 /* Specify the rank enabled for data-training */
36 tmp &= ~DTCR_RNKEN_MASK;
37 tmp |= (1 << (DTCR_RNKEN_SHIFT + rank)) & DTCR_RNKEN_MASK;
41 struct ddrphy_init_sequence {
48 static struct ddrphy_init_sequence init_sequence[] = {
50 "DRAM Initialization",
51 PIR_DRAMRST | PIR_DRAMINIT,
62 "Read DQS Gate Training",
68 "Write Leveling Adjustment",
99 int ddrphy_training(struct ddrphy __iomem *phy)
103 u32 init_flag = PIR_INIT;
104 u32 done_flag = PGSR0_IDONE;
105 int timeout = 50000; /* 50 msec is long enough */
106 #ifdef DISPLAY_ELAPSED_TIME
107 ulong start = get_timer(0);
110 for (i = 0; i < ARRAY_SIZE(init_sequence); i++) {
111 init_flag |= init_sequence[i].init_flag;
112 done_flag |= init_sequence[i].done_flag;
115 writel(init_flag, &phy->pir);
119 #ifndef CONFIG_SPL_BUILD
120 printf("%s: error: timeout during DDR training\n",
126 pgsr0 = readl(&phy->pgsr[0]);
127 } while ((pgsr0 & done_flag) != done_flag);
129 for (i = 0; i < ARRAY_SIZE(init_sequence); i++) {
130 if (pgsr0 & init_sequence[i].err_flag) {
131 #ifndef CONFIG_SPL_BUILD
132 printf("%s: error: %s failed\n", __func__,
133 init_sequence[i].description);
139 #ifdef DISPLAY_ELAPSED_TIME
140 printf("%s: info: elapsed time %ld msec\n", get_timer(start));