blob: 4852f2dec81f4f74f4dd2db1f38810ada3b56ed8 [file] [log] [blame]
Masahiro Yamada04191e52014-12-19 20:20:52 +09001/*
Masahiro Yamada663a23f2015-05-29 17:30:00 +09002 * Copyright (C) 2011-2015 Masahiro Yamada <yamada.masahiro@socionext.com>
Masahiro Yamada04191e52014-12-19 20:20:52 +09003 *
4 * SPDX-License-Identifier: GPL-2.0+
5 */
6
7#include <common.h>
Masahiro Yamada5f369212015-12-16 10:50:26 +09008#include <linux/err.h>
Masahiro Yamada663a23f2015-05-29 17:30:00 +09009#include <linux/io.h>
Masahiro Yamada95387e22015-02-27 02:26:44 +090010#include <mach/ddrphy-regs.h>
Masahiro Yamada04191e52014-12-19 20:20:52 +090011
12void ddrphy_prepare_training(struct ddrphy __iomem *phy, int rank)
13{
14 int dx;
15 u32 __iomem tmp, *p;
16
17 for (dx = 0; dx < NR_DATX8_PER_DDRPHY; dx++) {
18 p = &phy->dx[dx].gcr;
19
20 tmp = readl(p);
21 /* Specify the rank that should be write leveled */
22 tmp &= ~DXGCR_WLRKEN_MASK;
23 tmp |= (1 << (DXGCR_WLRKEN_SHIFT + rank)) & DXGCR_WLRKEN_MASK;
24 writel(tmp, p);
25 }
26
27 p = &phy->dtcr;
28
29 tmp = readl(p);
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 */
34 tmp |= DTCR_DTMPR;
35 /* Specify the rank enabled for data-training */
Masahiro Yamada9100c0f2015-12-16 10:42:29 +090036 tmp &= ~DTCR_RANKEN_MASK;
37 tmp |= (1 << (DTCR_RANKEN_SHIFT + rank)) & DTCR_RANKEN_MASK;
Masahiro Yamada04191e52014-12-19 20:20:52 +090038 writel(tmp, p);
39}
40
41struct ddrphy_init_sequence {
42 char *description;
43 u32 init_flag;
44 u32 done_flag;
45 u32 err_flag;
46};
47
Masahiro Yamadabe5b8472015-12-16 10:36:13 +090048static const struct ddrphy_init_sequence init_sequence[] = {
Masahiro Yamada04191e52014-12-19 20:20:52 +090049 {
50 "DRAM Initialization",
51 PIR_DRAMRST | PIR_DRAMINIT,
52 PGSR0_DIDONE,
53 PGSR0_DIERR
54 },
55 {
56 "Write Leveling",
57 PIR_WL,
58 PGSR0_WLDONE,
59 PGSR0_WLERR
60 },
61 {
62 "Read DQS Gate Training",
63 PIR_QSGATE,
64 PGSR0_QSGDONE,
65 PGSR0_QSGERR
66 },
67 {
68 "Write Leveling Adjustment",
69 PIR_WLADJ,
70 PGSR0_WLADONE,
71 PGSR0_WLAERR
72 },
73 {
74 "Read Bit Deskew",
75 PIR_RDDSKW,
76 PGSR0_RDDONE,
77 PGSR0_RDERR
78 },
79 {
80 "Write Bit Deskew",
81 PIR_WRDSKW,
82 PGSR0_WDDONE,
83 PGSR0_WDERR
84 },
85 {
86 "Read Eye Training",
87 PIR_RDEYE,
88 PGSR0_REDONE,
89 PGSR0_REERR
90 },
91 {
92 "Write Eye Training",
93 PIR_WREYE,
94 PGSR0_WEDONE,
95 PGSR0_WEERR
96 }
97};
98
99int ddrphy_training(struct ddrphy __iomem *phy)
100{
101 int i;
102 u32 pgsr0;
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);
108#endif
109
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;
113 }
114
115 writel(init_flag, &phy->pir);
116
117 do {
118 if (--timeout < 0) {
Masahiro Yamada04191e52014-12-19 20:20:52 +0900119 printf("%s: error: timeout during DDR training\n",
120 __func__);
Masahiro Yamada5f369212015-12-16 10:50:26 +0900121 return -ETIMEDOUT;
Masahiro Yamada04191e52014-12-19 20:20:52 +0900122 }
123 udelay(1);
124 pgsr0 = readl(&phy->pgsr[0]);
125 } while ((pgsr0 & done_flag) != done_flag);
126
127 for (i = 0; i < ARRAY_SIZE(init_sequence); i++) {
128 if (pgsr0 & init_sequence[i].err_flag) {
Masahiro Yamada04191e52014-12-19 20:20:52 +0900129 printf("%s: error: %s failed\n", __func__,
130 init_sequence[i].description);
Masahiro Yamada5f369212015-12-16 10:50:26 +0900131 return -EIO;
Masahiro Yamada04191e52014-12-19 20:20:52 +0900132 }
133 }
134
135#ifdef DISPLAY_ELAPSED_TIME
136 printf("%s: info: elapsed time %ld msec\n", get_timer(start));
137#endif
138
139 return 0;
140}