blob: a3481363fe6fb0938c74744fcdcfbfa473e82cfa [file] [log] [blame]
Masahiro Yamadab614e162014-12-19 20:20:52 +09001/*
Masahiro Yamadaf6e7f072015-05-29 17:30:00 +09002 * Copyright (C) 2011-2015 Masahiro Yamada <yamada.masahiro@socionext.com>
Masahiro Yamadab614e162014-12-19 20:20:52 +09003 *
4 * SPDX-License-Identifier: GPL-2.0+
5 */
6
7#include <common.h>
Masahiro Yamada6a9f6ba2015-12-16 10:50:26 +09008#include <linux/err.h>
Masahiro Yamadaf6e7f072015-05-29 17:30:00 +09009#include <linux/io.h>
Masahiro Yamada107b3fb2016-01-09 01:51:13 +090010
11#include "ddrphy-regs.h"
Masahiro Yamadab614e162014-12-19 20:20:52 +090012
13void ddrphy_prepare_training(struct ddrphy __iomem *phy, int rank)
14{
15 int dx;
16 u32 __iomem tmp, *p;
17
18 for (dx = 0; dx < NR_DATX8_PER_DDRPHY; dx++) {
19 p = &phy->dx[dx].gcr;
20
21 tmp = readl(p);
22 /* Specify the rank that should be write leveled */
23 tmp &= ~DXGCR_WLRKEN_MASK;
24 tmp |= (1 << (DXGCR_WLRKEN_SHIFT + rank)) & DXGCR_WLRKEN_MASK;
25 writel(tmp, p);
26 }
27
28 p = &phy->dtcr;
29
30 tmp = readl(p);
31 /* Specify the rank used during data bit deskew and eye centering */
32 tmp &= ~DTCR_DTRANK_MASK;
33 tmp |= (rank << DTCR_DTRANK_SHIFT) & DTCR_DTRANK_MASK;
34 /* Use Multi-Purpose Register for DQS gate training */
35 tmp |= DTCR_DTMPR;
36 /* Specify the rank enabled for data-training */
Masahiro Yamadab04ed732015-12-16 10:42:29 +090037 tmp &= ~DTCR_RANKEN_MASK;
38 tmp |= (1 << (DTCR_RANKEN_SHIFT + rank)) & DTCR_RANKEN_MASK;
Masahiro Yamadab614e162014-12-19 20:20:52 +090039 writel(tmp, p);
40}
41
42struct ddrphy_init_sequence {
43 char *description;
44 u32 init_flag;
45 u32 done_flag;
46 u32 err_flag;
47};
48
Masahiro Yamadaa1c4bf82015-12-16 10:36:13 +090049static const struct ddrphy_init_sequence init_sequence[] = {
Masahiro Yamadab614e162014-12-19 20:20:52 +090050 {
51 "DRAM Initialization",
52 PIR_DRAMRST | PIR_DRAMINIT,
53 PGSR0_DIDONE,
54 PGSR0_DIERR
55 },
56 {
57 "Write Leveling",
58 PIR_WL,
59 PGSR0_WLDONE,
60 PGSR0_WLERR
61 },
62 {
63 "Read DQS Gate Training",
64 PIR_QSGATE,
65 PGSR0_QSGDONE,
66 PGSR0_QSGERR
67 },
68 {
69 "Write Leveling Adjustment",
70 PIR_WLADJ,
71 PGSR0_WLADONE,
72 PGSR0_WLAERR
73 },
74 {
75 "Read Bit Deskew",
76 PIR_RDDSKW,
77 PGSR0_RDDONE,
78 PGSR0_RDERR
79 },
80 {
81 "Write Bit Deskew",
82 PIR_WRDSKW,
83 PGSR0_WDDONE,
84 PGSR0_WDERR
85 },
86 {
87 "Read Eye Training",
88 PIR_RDEYE,
89 PGSR0_REDONE,
90 PGSR0_REERR
91 },
92 {
93 "Write Eye Training",
94 PIR_WREYE,
95 PGSR0_WEDONE,
96 PGSR0_WEERR
97 }
98};
99
100int ddrphy_training(struct ddrphy __iomem *phy)
101{
102 int i;
103 u32 pgsr0;
104 u32 init_flag = PIR_INIT;
105 u32 done_flag = PGSR0_IDONE;
106 int timeout = 50000; /* 50 msec is long enough */
107#ifdef DISPLAY_ELAPSED_TIME
108 ulong start = get_timer(0);
109#endif
110
111 for (i = 0; i < ARRAY_SIZE(init_sequence); i++) {
112 init_flag |= init_sequence[i].init_flag;
113 done_flag |= init_sequence[i].done_flag;
114 }
115
116 writel(init_flag, &phy->pir);
117
118 do {
119 if (--timeout < 0) {
Masahiro Yamadab614e162014-12-19 20:20:52 +0900120 printf("%s: error: timeout during DDR training\n",
121 __func__);
Masahiro Yamada6a9f6ba2015-12-16 10:50:26 +0900122 return -ETIMEDOUT;
Masahiro Yamadab614e162014-12-19 20:20:52 +0900123 }
124 udelay(1);
125 pgsr0 = readl(&phy->pgsr[0]);
126 } while ((pgsr0 & done_flag) != done_flag);
127
128 for (i = 0; i < ARRAY_SIZE(init_sequence); i++) {
129 if (pgsr0 & init_sequence[i].err_flag) {
Masahiro Yamadab614e162014-12-19 20:20:52 +0900130 printf("%s: error: %s failed\n", __func__,
131 init_sequence[i].description);
Masahiro Yamada6a9f6ba2015-12-16 10:50:26 +0900132 return -EIO;
Masahiro Yamadab614e162014-12-19 20:20:52 +0900133 }
134 }
135
136#ifdef DISPLAY_ELAPSED_TIME
137 printf("%s: info: elapsed time %ld msec\n", get_timer(start));
138#endif
139
140 return 0;
141}